@titan-design/react-ui 0.11.0 → 0.12.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.
Files changed (152) hide show
  1. package/dist/bodymap.js +173 -343
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +173 -343
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/index.d.mts +27 -22
  6. package/dist/index.d.ts +27 -22
  7. package/dist/index.js +366 -561
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +360 -553
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/pages.js +242 -407
  12. package/dist/pages.js.map +1 -1
  13. package/dist/pages.mjs +242 -407
  14. package/dist/pages.mjs.map +1 -1
  15. package/dist/theme/index.d.mts +250 -1157
  16. package/dist/theme/index.d.ts +250 -1157
  17. package/dist/theme/index.js +234 -408
  18. package/dist/theme/index.js.map +1 -1
  19. package/dist/theme/index.mjs +227 -399
  20. package/dist/theme/index.mjs.map +1 -1
  21. package/dist/theme/tokens-css.js +141 -140
  22. package/dist/theme/tokens-css.js.map +1 -1
  23. package/dist/theme/tokens-css.mjs +141 -140
  24. package/dist/theme/tokens-css.mjs.map +1 -1
  25. package/dist/theme/tokens.d.mts +904 -0
  26. package/dist/theme/tokens.d.ts +904 -0
  27. package/dist/theme/tokens.js +856 -0
  28. package/dist/theme/tokens.js.map +1 -0
  29. package/dist/theme/tokens.mjs +833 -0
  30. package/dist/theme/tokens.mjs.map +1 -0
  31. package/package.json +7 -1
  32. package/src/arch/Architecture.stories.tsx +9 -9
  33. package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
  34. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +3 -3
  35. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  36. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +3 -3
  37. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +1 -1
  38. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +2 -2
  39. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  40. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +4 -4
  41. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +3 -3
  42. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  43. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -12
  44. package/src/components/custom/Sidebar/Sidebar.tsx +4 -4
  45. package/src/components/custom/Table/Table.tsx +2 -2
  46. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +3 -3
  47. package/src/components/custom/Workout/BaseBadge.test.tsx +6 -1
  48. package/src/components/custom/Workout/BaseBadge.tsx +1 -1
  49. package/src/components/custom/Workout/BodyMap.tsx +2 -2
  50. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +2 -2
  51. package/src/components/custom/Workout/DeviationBar.test.tsx +2 -1
  52. package/src/components/custom/Workout/DeviationBar.tsx +3 -2
  53. package/src/components/custom/Workout/ExerciseCard.test.tsx +3 -3
  54. package/src/components/custom/Workout/ExerciseCard.tsx +2 -2
  55. package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -4
  56. package/src/components/custom/Workout/InputBar.tsx +2 -2
  57. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +1 -1
  58. package/src/components/custom/Workout/LiveAuraFrame.test.tsx +1 -1
  59. package/src/components/custom/Workout/LiveAuraFrame.tsx +3 -3
  60. package/src/components/custom/Workout/MesoCard.tsx +3 -1
  61. package/src/components/custom/Workout/MuscleGroupChip.tsx +4 -3
  62. package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
  63. package/src/components/custom/Workout/ProgramPlanningPage.tsx +1 -1
  64. package/src/components/custom/Workout/README.md +1 -1
  65. package/src/components/custom/Workout/ReadinessCheck.tsx +2 -2
  66. package/src/components/custom/Workout/RestTimer.tsx +2 -2
  67. package/src/components/custom/Workout/S3SetTypes.stories.tsx +3 -3
  68. package/src/components/custom/Workout/SessionHeader.stories.tsx +2 -2
  69. package/src/components/custom/Workout/SessionHeader.tsx +1 -1
  70. package/src/components/custom/Workout/SessionRail.stories.tsx +2 -2
  71. package/src/components/custom/Workout/SessionRail.tsx +13 -5
  72. package/src/components/custom/Workout/SetBar.tsx +3 -3
  73. package/src/components/custom/Workout/SetRow.test.tsx +6 -3
  74. package/src/components/custom/Workout/StatusDot.tsx +5 -4
  75. package/src/components/custom/Workout/StrengthTrendChart.tsx +1 -1
  76. package/src/components/custom/Workout/TrainingStatusPage.tsx +2 -2
  77. package/src/components/custom/Workout/VelocityStrip.test.tsx +5 -2
  78. package/src/components/custom/Workout/VelocityStrip.tsx +4 -4
  79. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +3 -3
  80. package/src/components/custom/Workout/WeightBadge.test.tsx +6 -1
  81. package/src/components/custom/Workout/WeightBadge.tsx +2 -1
  82. package/src/components/custom/Workout/WorkoutCard.tsx +3 -1
  83. package/src/components/custom/Workout/WorkoutPill.tsx +5 -2
  84. package/src/components/custom/Workout/ZoneTrack.tsx +4 -4
  85. package/src/components/custom/Workout/setHeadingKit.tsx +14 -8
  86. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  87. package/src/components/custom/charts/SetBarChart.tsx +3 -3
  88. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  89. package/src/components/custom/stepper/Stepper.tsx +3 -3
  90. package/src/components/shell/DeviceMenu.tsx +2 -2
  91. package/src/components/shell/README.md +1 -1
  92. package/src/components/shell/SessionRailSpecimen.stories.tsx +1 -1
  93. package/src/components/shell/SideNav.tsx +1 -1
  94. package/src/components/shell/TopBar.tsx +1 -1
  95. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  96. package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
  97. package/src/components/ui/avatar/Avatar.tsx +2 -2
  98. package/src/components/ui/badge/Badge.tsx +2 -2
  99. package/src/components/ui/card/Card.tsx +3 -3
  100. package/src/components/ui/checkbox/Checkbox.tsx +1 -1
  101. package/src/components/ui/collapse/Collapse.tsx +1 -1
  102. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  103. package/src/components/ui/drawer/Drawer.tsx +3 -3
  104. package/src/components/ui/form-field/FormField.tsx +1 -1
  105. package/src/components/ui/help-tip/HelpTip.tsx +1 -1
  106. package/src/components/ui/menu/Menu.tsx +2 -2
  107. package/src/components/ui/pill/Pill.tsx +2 -2
  108. package/src/components/ui/popover/Popover.stories.tsx +2 -2
  109. package/src/components/ui/popover/Popover.tsx +1 -1
  110. package/src/components/ui/progress/Progress.tsx +3 -3
  111. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  112. package/src/components/ui/radio/Radio.tsx +4 -4
  113. package/src/components/ui/select/Select.tsx +3 -3
  114. package/src/components/ui/surface/Surface.stories.tsx +3 -3
  115. package/src/components/ui/surface/Surface.test.tsx +32 -19
  116. package/src/components/ui/surface/Surface.tsx +3 -3
  117. package/src/components/ui/surface/SurfaceContext.ts +19 -29
  118. package/src/components/ui/surface/surface.contract.test.ts +58 -45
  119. package/src/components/ui/switch/Switch.tsx +1 -1
  120. package/src/components/ui/tabs/Tabs.tsx +2 -2
  121. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +5 -5
  122. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +1 -1
  123. package/src/components/ui/toolbar-button/ToolbarButton.tsx +36 -22
  124. package/src/stories/PresetShowcase.stories.tsx +21 -21
  125. package/src/theme/ColorPalettes.stories.tsx +41 -38
  126. package/src/theme/ColorPrimitives.stories.tsx +89 -41
  127. package/src/theme/Depth.stories.tsx +312 -0
  128. package/src/theme/DepthCalibration.stories.tsx +655 -0
  129. package/src/theme/ThemeProvider.tsx +1 -1
  130. package/src/theme/barrel.ts +7 -14
  131. package/src/theme/color-stories.coverage.test.ts +15 -20
  132. package/src/theme/color-story-kit.tsx +1 -1
  133. package/src/theme/color-utils.ts +197 -0
  134. package/src/theme/config.ts +0 -8
  135. package/src/theme/depth-calibration.test.tsx +97 -0
  136. package/src/theme/elevation.test.ts +62 -1
  137. package/src/theme/elevation.ts +34 -8
  138. package/src/theme/global.css +47 -53
  139. package/src/theme/grey-ramp.test.ts +166 -0
  140. package/src/theme/materials.test.ts +106 -0
  141. package/src/theme/materials.ts +211 -0
  142. package/src/theme/presets/audiobook.ts +4 -4
  143. package/src/theme/presets/types.ts +3 -3
  144. package/src/theme/semantic-contrast.test.ts +83 -0
  145. package/src/theme/token-export-guard.test.ts +104 -0
  146. package/src/theme/tokens/primitives.ts +63 -116
  147. package/src/theme/tokens/semantic.ts +130 -82
  148. package/src/utils/colors.ts +1 -1
  149. package/tailwind.config.js +2 -5
  150. package/src/theme/bar-paper.ts +0 -59
  151. package/src/theme/shadows.stories.tsx +0 -523
  152. package/src/theme/shadows.ts +0 -439
@@ -7,93 +7,50 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
8
  // src/theme/tokens/primitives.ts
9
9
  var primitiveColors = {
10
- // Blue scale
11
- blue: {
12
- 50: "#EFF6FF",
13
- 100: "#DBEAFE",
14
- 200: "#BFDBFE",
15
- 300: "#93C5FD",
16
- 400: "#60A5FA",
17
- 500: "#3B82F6",
18
- 600: "#5048E5",
19
- 700: "#3832A0",
20
- 800: "#1E40AF",
21
- 900: "#1E3A8A"
22
- },
23
- // Red scale
24
- red: {
25
- 50: "#FEF2F2",
26
- 100: "#FEE2E2",
27
- 200: "#FECACA",
28
- 300: "#FCA5A5",
29
- 400: "#DA6868",
30
- 500: "#EF4444",
31
- 600: "#D14343",
32
- 700: "#922E2E",
33
- 800: "#991B1B",
34
- 900: "#7F1D1D"
35
- },
36
- // Vivid red scale
37
- redVivid: {
38
- 50: "#FFECEE",
39
- 100: "#FFD6DB",
40
- 200: "#FFB3BC",
41
- 300: "#FF8593",
42
- 400: "#FF6070",
43
- 500: "#FF4757",
44
- 600: "#E63548",
45
- 700: "#C42539",
46
- 800: "#9E1C2C",
47
- 900: "#7A1520"
48
- },
49
- // Neutral/Gray scale
50
- neutral: {
51
- 50: "#FAFAFA",
52
- 100: "#F3F4F6",
53
- 200: "#E5E7EB",
54
- 300: "#D1D5DB",
55
- 400: "#9CA3AF",
56
- 500: "#6B7280",
57
- 600: "#4B5563",
58
- 700: "#374151",
59
- 800: "#1F2937",
60
- 900: "#111827",
61
- 950: "#030712"
62
- },
63
- // Charcoal scale (dark backgrounds)
64
- charcoal: {
65
- 0: "#6E6E6E",
66
- 50: "#5D5D5D",
67
- 100: "#4C4C4C",
68
- 200: "#3C3C3C",
69
- 300: "#2C2C2C",
70
- 400: "#1F1F1F",
71
- 500: "#1C1C1C",
72
- 600: "#191919",
73
- 700: "#161616",
74
- 800: "#131313",
75
- 900: "#101010"
76
- },
77
- // Pure colors
78
10
  white: "#FFFFFF",
79
11
  black: "#000000",
80
12
  transparent: "transparent"
81
13
  };
82
- var surfaceRampDark = {
83
- inset: "#13100D",
84
- // L*4.5 — sub-shell well / pressed
85
- background: "#1C1916",
86
- // L*9 — shell / frame
87
- base: "#252321",
88
- // L*13.5 main content plane
89
- elevated: "#2C2A28",
90
- // L*17 — nav / rail
91
- raised: "#31302F",
92
- // L*20 — cards
93
- overlay: "#373635"
94
- // L*22.5 — hero / popover
14
+ var greyRamp = {
15
+ 50: "#F9F6F3",
16
+ // L*97.0 W6
17
+ 100: "#EDEAE7",
18
+ // L*92.8 W6
19
+ 200: "#D4D1CE",
20
+ // L*84.0 W6 warm silver
21
+ 300: "#BDBAB7",
22
+ // L*75.7 W6
23
+ 400: "#A29F9D",
24
+ // L*65.7 W5
25
+ 500: "#888684",
26
+ // L*56.0 W4
27
+ 600: "#72716F",
28
+ // L*47.7 W3
29
+ 700: "#5A5958",
30
+ // L*37.9 W2
31
+ 800: "#424140",
32
+ // L*27.6 W2 — generated bridge
33
+ 850: "#373635",
34
+ // L*22.7 W2 — surface overlay
35
+ 875: "#31302F",
36
+ // L*19.9 W2 — surface raised
37
+ 900: "#2C2A28",
38
+ // L*17.2 W4 — surface elevated
39
+ 925: "#252321",
40
+ // L*13.9 W4 — surface base
41
+ 950: "#1C1916",
42
+ // L*9.0 W6 — background base / shell
43
+ 975: "#100D0A"
44
+ // L*3.8 W6 — background frame / bezel
45
+ };
46
+ var SURFACE_PLANE_STEPS = {
47
+ frame: 975,
48
+ background: 950,
49
+ base: 925,
50
+ elevated: 900,
51
+ raised: 875,
52
+ overlay: 850
95
53
  };
96
- var backgroundFrameDark = "#100D0A";
97
54
  var primitiveRamps = {
98
55
  red: {
99
56
  50: "#FFF4F4",
@@ -244,8 +201,8 @@ var sequentialEffort = [
244
201
  ];
245
202
  function bestTextColor(hex) {
246
203
  const [r, g, b] = [0, 2, 4].map((i) => {
247
- const c = parseInt(hex.replace("#", "").slice(i, i + 2), 16) / 255;
248
- return c >= 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92;
204
+ const c2 = parseInt(hex.replace("#", "").slice(i, i + 2), 16) / 255;
205
+ return c2 >= 0.04045 ? ((c2 + 0.055) / 1.055) ** 2.4 : c2 / 12.92;
249
206
  });
250
207
  const l = 0.2126 * r + 0.7152 * g + 0.0722 * b;
251
208
  return (l + 0.05) / 0.05 >= 1.05 / (l + 0.05) ? "#000000" : "#FFFFFF";
@@ -421,16 +378,6 @@ var primitiveBorderRadius = {
421
378
  "2xl": "24px",
422
379
  full: "9999px"
423
380
  };
424
- var primitiveShadows = {
425
- none: "none",
426
- sm: "0px 1px 2px rgba(100, 116, 139, 0.12)",
427
- DEFAULT: "0px 1px 3px rgba(100, 116, 139, 0.12), 0px 1px 2px rgba(100, 116, 139, 0.24)",
428
- md: "0px 4px 6px rgba(100, 116, 139, 0.12)",
429
- lg: "0px 10px 15px rgba(100, 116, 139, 0.12)",
430
- xl: "0px 20px 25px rgba(100, 116, 139, 0.12)",
431
- "2xl": "0px 25px 50px rgba(100, 116, 139, 0.25)",
432
- inner: "inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)"
433
- };
434
381
  var primitiveBreakpoints = {
435
382
  xs: 0,
436
383
  sm: 600,
@@ -486,10 +433,10 @@ var semanticColorsLight = {
486
433
  "status-error-light": primitiveRamps.red[500],
487
434
  "status-error-dark": primitiveRamps.red[700],
488
435
  "status-error-subtle": primitiveRamps.red[50],
489
- "status-error-vivid": primitiveColors.redVivid[500],
436
+ "status-error-vivid": primitiveRamps.red[600],
490
437
  // vivid alert red
491
- "status-error-vivid-light": primitiveColors.redVivid[400],
492
- "status-error-vivid-dark": primitiveColors.redVivid[700],
438
+ "status-error-vivid-light": primitiveRamps.red[500],
439
+ "status-error-vivid-dark": primitiveRamps.red[700],
493
440
  "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
494
441
  "status-warning": primitiveRamps.amber[300],
495
442
  "status-warning-light": primitiveRamps.amber[200],
@@ -516,7 +463,7 @@ var semanticColorsLight = {
516
463
  "result-inconclusive": "#9E9A97",
517
464
  // Gray - no clear result
518
465
  "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
519
- "result-neutral": primitiveColors.neutral[500],
466
+ "result-neutral": greyRamp[600],
520
467
  // Neutral baseline
521
468
  // Text on result backgrounds (on-result-*)
522
469
  "on-result-improve": primitiveColors.white,
@@ -547,47 +494,38 @@ var semanticColorsLight = {
547
494
  // Text colors (text-*)
548
495
  "text-primary": "#121828",
549
496
  "text-secondary": "#65748B",
550
- "text-tertiary": primitiveColors.neutral[400],
497
+ "text-tertiary": greyRamp[400],
551
498
  "text-disabled": "rgba(55, 65, 81, 0.48)",
552
499
  "text-inverse": primitiveColors.white,
553
- "text-link": primitiveColors.blue[600],
554
- "text-link-hover": primitiveColors.blue[700],
500
+ "text-link": primitiveRamps.blue[600],
501
+ "text-link-hover": primitiveRamps.blue[700],
555
502
  // Surface colors (surface-*) - for elevated containers
556
503
  "surface-base": primitiveColors.white,
557
- "surface-elevated": primitiveColors.neutral[50],
504
+ "surface-elevated": greyRamp[50],
558
505
  // slightly off-white for elevated cards
559
- "surface-raised": primitiveColors.neutral[100],
506
+ "surface-raised": greyRamp[100],
560
507
  // light gray for raised cards
561
508
  "surface-overlay": primitiveColors.white,
562
- "surface-input": primitiveColors.neutral[50],
509
+ "surface-input": greyRamp[50],
563
510
  // Input field background (filled variant)
564
- // Deepest pressed pit (TD-surface-tokens, S-3) — light-mode counterpart of
565
- // the dark ramp's `inset` plane; a placeholder pairing, not part of the
566
- // dark-ramp investigation (surface.contract.test.ts is dark-mode only).
567
- "surface-inset": primitiveColors.neutral[300],
568
511
  // Background colors (background-*)
569
512
  "background-base": "#EBEBEB",
570
513
  "background-default": primitiveColors.white,
571
- "background-subtle": primitiveColors.neutral[50],
572
- // Frame/bezel chrome (TD-surface-tokens, S-3) — top bar + side nav shell,
573
- // one step below `background-base` (darker still than `surface-inset`
574
- // above, mirroring the dark-mode ordering). Placeholder pairing for light
575
- // mode (see dark-mode note on `surface-inset` above).
576
- "background-frame": primitiveColors.neutral[400],
514
+ "background-subtle": greyRamp[50],
515
+ // Frame/bezel chrome — top bar + side nav shell, one step below
516
+ // `background-base`. Placeholder pairing for light mode, which is deferred.
517
+ "background-frame": greyRamp[400],
577
518
  // Border colors (border-*)
578
- "border-default": "#E8E9EB",
579
- "border-subtle": primitiveColors.neutral[100],
580
- "border-strong": primitiveColors.neutral[300],
581
- "border-prominent": primitiveColors.neutral[400],
519
+ "border-prominent": greyRamp[400],
582
520
  // high-visibility divider
583
- "border-focus": primitiveColors.blue[600],
584
- "border-input": primitiveColors.neutral[300],
521
+ "border-focus": primitiveRamps.blue[600],
522
+ "border-input": greyRamp[200],
585
523
  // Input field border
586
- "border-input-hover": primitiveColors.neutral[400],
524
+ "border-input-hover": greyRamp[400],
587
525
  // Input field border on hover
588
- "border-input-focus": primitiveColors.blue[600],
526
+ "border-input-focus": primitiveRamps.blue[600],
589
527
  // Input field border on focus
590
- "border-input-error": primitiveColors.red[600],
528
+ "border-input-error": primitiveRamps.red[600],
591
529
  // Input field border on error
592
530
  // Alpha hairline separators (surface-independent — composite toward black on
593
531
  // light surfaces, mirroring the dark-mode white-alpha family). See §4/S-2.
@@ -604,7 +542,7 @@ var semanticColorsLight = {
604
542
  // Divider
605
543
  "divider": "#E8E9EB",
606
544
  // Avatar default
607
- "avatar-background": primitiveColors.neutral[500],
545
+ "avatar-background": greyRamp[600],
608
546
  "avatar-text": primitiveColors.white
609
547
  };
610
548
  var semanticColorsDark = {
@@ -636,9 +574,9 @@ var semanticColorsDark = {
636
574
  "status-error-light": primitiveRamps.red[500],
637
575
  "status-error-dark": primitiveRamps.red[700],
638
576
  "status-error-subtle": "rgba(209, 67, 67, 0.12)",
639
- "status-error-vivid": primitiveColors.redVivid[500],
640
- "status-error-vivid-light": primitiveColors.redVivid[400],
641
- "status-error-vivid-dark": primitiveColors.redVivid[700],
577
+ "status-error-vivid": primitiveRamps.red[600],
578
+ "status-error-vivid-light": primitiveRamps.red[500],
579
+ "status-error-vivid-dark": primitiveRamps.red[700],
642
580
  "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
643
581
  "status-warning": primitiveRamps.amber[300],
644
582
  "status-warning-light": primitiveRamps.amber[200],
@@ -662,7 +600,7 @@ var semanticColorsDark = {
662
600
  "result-degrade-dark": "#b30000",
663
601
  "result-inconclusive": "#9E9A97",
664
602
  "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
665
- "result-neutral": primitiveColors.neutral[400],
603
+ "result-neutral": greyRamp[400],
666
604
  // Text on result backgrounds
667
605
  "on-result-improve": primitiveColors.white,
668
606
  "on-result-degrade": primitiveColors.white,
@@ -679,74 +617,106 @@ var semanticColorsDark = {
679
617
  "data-9": discreteRainbow[3],
680
618
  "data-10": discreteRainbow[22],
681
619
  // Text colors - inverted for dark mode
682
- "text-primary": primitiveColors.neutral[100],
683
- "text-secondary": primitiveColors.neutral[400],
684
- "text-tertiary": primitiveColors.neutral[500],
620
+ "text-primary": greyRamp[50],
621
+ "text-secondary": greyRamp[400],
622
+ // Deliberately ONE step lighter than the L*-nearest match (`greyRamp[600]`).
623
+ // The colour this replaced failed WCAG large-text outright on the three
624
+ // lightest planes — 2.96 / 2.72 / 2.49 against elevated / raised / overlay —
625
+ // and `600` carried that failure forward almost exactly (2.93 / 2.70 / 2.47).
626
+ // `500` clears 3:1 everywhere (3.32–5.34). Contrast beats colour fidelity for
627
+ // text roles; borders and surfaces still use strict L*-nearest. See
628
+ // semantic-contrast.test.ts, which fails if this is moved back down.
629
+ "text-tertiary": greyRamp[500],
685
630
  "text-disabled": "rgba(255, 255, 255, 0.38)",
686
- "text-inverse": primitiveColors.neutral[900],
631
+ "text-inverse": greyRamp[950],
687
632
  "text-link": "#828DF8",
688
- "text-link-hover": primitiveColors.blue[400],
633
+ "text-link-hover": primitiveRamps.blue[400],
689
634
  // Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
690
635
  // S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
691
636
  // `surfaceRampDark` in primitives.ts for the full derivation note. `surface-overlay`
692
637
  // and `surface-elevated` are distinct (were both #191919 pre-S-1); `surface-input`
693
638
  // tracks one plane above `surface-base`, same relative position as before the remap.
694
639
  // `background-base` backs `Surface level="background"` (SurfaceContext.SURFACE_LEVEL_TOKEN),
695
- // so it takes the ramp's `background` (shell/frame) role, NOT `inset`. The ramp's
696
- // deepest `inset` plane is now promoted to `surface-inset` below (S-3) still no
697
- // `SurfaceLevel` member for it (follow-up, alongside the elevation.ts -1/-2 pressed
698
- // levels — parallel workstream, not this branch).
699
- "surface-base": surfaceRampDark.base,
700
- // main surface (#252321, L*13.5)
701
- "surface-elevated": surfaceRampDark.elevated,
702
- // elevated surface (#2C2A28, L*17 nav/rail)
703
- "surface-raised": surfaceRampDark.raised,
704
- // raised surface (#31302F, L*20 cards)
705
- "surface-overlay": surfaceRampDark.overlay,
706
- // overlay surface (#373635, L*22.5 — hero/popover)
707
- "surface-input": surfaceRampDark.elevated,
640
+ // so it takes the ramp's shell role. The deepest plane is `background-frame`,
641
+ // which is also the `SurfaceLevel` floor a pressed surface clamps at.
642
+ "surface-base": greyRamp[925],
643
+ // main surface (#252321, L*13.9)
644
+ "surface-elevated": greyRamp[900],
645
+ // elevated surface (#2C2A28, L*17.2 — nav/rail)
646
+ "surface-raised": greyRamp[875],
647
+ // raised surface (#31302F, L*19.9 cards)
648
+ "surface-overlay": greyRamp[850],
649
+ // overlay surface (#373635, L*22.7 hero/popover)
650
+ "surface-input": greyRamp[900],
708
651
  // input surface (#2C2A28 — one plane above base)
709
- "surface-inset": surfaceRampDark.inset,
710
- // deepest pressed pit (#13100D, L*4.5 — sub-shell well)
711
652
  // Background colors — same ramp, the frame/shell end of it.
712
- "background-base": surfaceRampDark.background,
713
- // shell / frame (#1C1916, L*9 — Surface level="background")
714
- "background-default": surfaceRampDark.base,
715
- // main background (#252321, L*13.5 — matches surface-base)
716
- "background-subtle": surfaceRampDark.elevated,
717
- // subtle background (#2C2A28, L*17 — matches surface-elevated)
718
- // Frame/bezel chrome (S-3) — the top bar + side nav shell; one step BELOW
719
- // `background-base`, darker even than `surface-inset` it exits the content
720
- // ramp entirely rather than sitting in it. See `backgroundFrameDark` in
721
- // primitives.ts.
722
- "background-frame": backgroundFrameDark,
723
- // frame / bezel (#100D0A, L*3.79)
724
- // Border colors `border-subtle` (#1C1C1C) previously collided with
725
- // `surface-raised` (also #1C1C1C, an invisible border on raised cards). The
726
- // re-spaced ramp above moved `surface-raised` to #2D2C2B, so this value is now
727
- // distinct from every surface hex without needing to change it see
728
- // `surface.contract.test.ts` R2/R4.
729
- "border-default": primitiveColors.charcoal[400],
730
- // default border
731
- "border-subtle": primitiveColors.charcoal[500],
732
- // subtle border
733
- "border-strong": primitiveColors.charcoal[300],
734
- // strong border
735
- "border-prominent": primitiveColors.charcoal[200],
653
+ "background-base": greyRamp[950],
654
+ // shell (#1C1916, L*9 — Surface level="background")
655
+ "background-default": greyRamp[925],
656
+ // main background (#252321, L*13.9 — matches surface-base)
657
+ "background-subtle": greyRamp[900],
658
+ // subtle background (#2C2A28, L*17.2 — matches surface-elevated)
659
+ // Frame/bezel chrome — the top bar + side nav shell, one step BELOW
660
+ // `background-base`. It used to be described as sitting OUTSIDE the ramp; it
661
+ // is simply the ramp's last step now, and the floor `<Surface pressed>` clamps at.
662
+ "background-frame": greyRamp[975],
663
+ // frame / bezel (#100D0A, L*3.8)
664
+ // Border colors — solid dark borders are RETIRED (TD-07.14). Not a preference — a structural
665
+ // consequence of one ramp. Every step from 850 to 975 is now a surface plane,
666
+ // so a solid border dark enough to read as a border necessarily equals some
667
+ // plane's fill, which is exactly the collision `surface.contract.test.ts` R2
668
+ // forbids. The only non-plane steps left in that band are 800 and 700, and
669
+ // borders there jump from L*~10-25 to L*~28-38 — a restyle, not a migration.
670
+ //
671
+ // So separation moves to the alpha hairlines, which dark mode already called
672
+ // "the primary separation cue". Being alpha, they composite against whatever
673
+ // plane they land on and cannot collide with any of them — the problem stops
674
+ // existing rather than being re-solved per plane. The three solid tokens were
675
+ // deleted outright; consumers point at the `hairline-*` family below.
676
+ //
677
+ // `border-prominent` stays SOLID: it is the one border meant to be seen
678
+ // outright (4 call sites, high-visibility dividers), and grey-800 is not a
679
+ // plane, so it keeps its job without collision.
680
+ "border-prominent": greyRamp[800],
736
681
  // high-visibility divider
737
682
  "border-focus": "#828DF8",
738
- "border-input": primitiveColors.neutral[600],
739
- "border-input-hover": primitiveColors.neutral[500],
683
+ "border-input": greyRamp[700],
684
+ "border-input-hover": greyRamp[600],
740
685
  "border-input-focus": "#828DF8",
741
- "border-input-error": primitiveColors.red[500],
686
+ "border-input-error": primitiveRamps.red[500],
742
687
  // Alpha hairline separators — the primary separation cue (§4/S-2). Self-
743
688
  // normalizing: composites toward white by ~the same amount on ANY plane, so
744
689
  // one family works at every elevation instead of per-surface border tokens.
745
690
  // Shadows are demoted to floating-overlay use only (not shipped as a fill
746
691
  // separator here — see elevation.ts, follow-up S-4).
747
- "hairline-subtle": "rgba(255, 255, 255, 0.06)",
748
- "hairline-default": "rgba(255, 255, 255, 0.09)",
749
- "hairline-strong": "rgba(255, 255, 255, 0.14)",
692
+ //
693
+ // RETUNED on the wall (VW-99, S-6). The original .06/.09/.14 were set at a
694
+ // desk. On the panel at ~3 m `subtle` rendered but was indiscernible, and
695
+ // `default` — the load-bearing one, with no solid-border fallback since
696
+ // TD-07.14 — went weak on the lightest plane. `strong` read cleanly with room
697
+ // to spare, which is what made raising the whole family viable.
698
+ //
699
+ // Each tier steps up onto the next MEASURED-GOOD rung rather than an
700
+ // interpolated one: the new `subtle` is the old `default`, and the new
701
+ // `default` is the old `strong` — a value just confirmed legible on all four
702
+ // planes. Spacing goes 1 : 1.56 : 2.33, against the original 1 : 1.5 : 2.33.
703
+ //
704
+ // RUN 2 (VW-99): all three tiers were seen on all four planes — the retune
705
+ // worked — but `subtle` was still "a bit hard to read", so the whole family
706
+ // took a further +.01. That is a comfort margin on a passing row, not a fix
707
+ // for a failing one, which is why it is a flat offset: the 1 : 1.56 : 2.33
708
+ // spacing that the run validated is preserved rather than re-derived.
709
+ //
710
+ // Keep these to TWO decimals. react-native-web quantises alpha to 8 bits, so
711
+ // a 3-decimal value silently rounds on the way to the DOM (.135 renders as
712
+ // .13) — the token would say one thing and paint another.
713
+ //
714
+ // DARK ONLY. The light family above composites toward BLACK on light planes,
715
+ // which this run says nothing about — do not mirror these numbers into it
716
+ // without its own verification.
717
+ "hairline-subtle": "rgba(255, 255, 255, 0.10)",
718
+ "hairline-default": "rgba(255, 255, 255, 0.15)",
719
+ "hairline-strong": "rgba(255, 255, 255, 0.22)",
750
720
  // Interactive states
751
721
  "interactive-hover": "rgba(255, 255, 255, 0.04)",
752
722
  "interactive-focus": "rgba(255, 255, 255, 0.12)",
@@ -755,10 +725,9 @@ var semanticColorsDark = {
755
725
  "interactive-disabled": "rgba(255, 255, 255, 0.12)",
756
726
  "interactive-disabled-text": "rgba(255, 255, 255, 0.26)",
757
727
  // Divider
758
- "divider": primitiveColors.charcoal[400],
759
- // divider color
728
+ "divider": "rgba(255, 255, 255, 0.09)",
760
729
  // Avatar default
761
- "avatar-background": primitiveColors.neutral[600],
730
+ "avatar-background": greyRamp[700],
762
731
  "avatar-text": primitiveColors.white
763
732
  };
764
733
  var semanticTypography = {
@@ -924,14 +893,10 @@ var lightThemeCSSVars = {
924
893
  "--color-surface-base": semanticColorsLight["surface-base"],
925
894
  "--color-surface-elevated": semanticColorsLight["surface-elevated"],
926
895
  "--color-surface-raised": semanticColorsLight["surface-raised"],
927
- "--color-surface-inset": semanticColorsLight["surface-inset"],
928
896
  "--color-background-base": semanticColorsLight["background-base"],
929
897
  "--color-background-default": semanticColorsLight["background-default"],
930
898
  "--color-background-subtle": semanticColorsLight["background-subtle"],
931
899
  "--color-background-frame": semanticColorsLight["background-frame"],
932
- "--color-border-default": semanticColorsLight["border-default"],
933
- "--color-border-subtle": semanticColorsLight["border-subtle"],
934
- "--color-border-strong": semanticColorsLight["border-strong"],
935
900
  "--color-border-prominent": semanticColorsLight["border-prominent"],
936
901
  "--color-border-focus": semanticColorsLight["border-focus"],
937
902
  "--color-interactive-hover": semanticColorsLight["interactive-hover"],
@@ -1027,14 +992,10 @@ var darkThemeCSSVars = {
1027
992
  "--color-surface-base": semanticColorsDark["surface-base"],
1028
993
  "--color-surface-elevated": semanticColorsDark["surface-elevated"],
1029
994
  "--color-surface-raised": semanticColorsDark["surface-raised"],
1030
- "--color-surface-inset": semanticColorsDark["surface-inset"],
1031
995
  "--color-background-base": semanticColorsDark["background-base"],
1032
996
  "--color-background-default": semanticColorsDark["background-default"],
1033
997
  "--color-background-subtle": semanticColorsDark["background-subtle"],
1034
998
  "--color-background-frame": semanticColorsDark["background-frame"],
1035
- "--color-border-default": semanticColorsDark["border-default"],
1036
- "--color-border-subtle": semanticColorsDark["border-subtle"],
1037
- "--color-border-strong": semanticColorsDark["border-strong"],
1038
999
  "--color-border-prominent": semanticColorsDark["border-prominent"],
1039
1000
  "--color-border-focus": semanticColorsDark["border-focus"],
1040
1001
  "--color-interactive-hover": semanticColorsDark["interactive-hover"],
@@ -1167,6 +1128,54 @@ var gluestackConfig = {
1167
1128
  rounded: "borderRadius"
1168
1129
  }
1169
1130
  };
1131
+
1132
+ // src/theme/materials.ts
1133
+ var c = getSemanticColors("dark");
1134
+ function parseHex(hex) {
1135
+ const h = hex.replace("#", "");
1136
+ const full = h.length === 3 ? h.split("").map((ch) => ch + ch).join("") : h;
1137
+ const rgb = [
1138
+ parseInt(full.slice(0, 2), 16),
1139
+ parseInt(full.slice(2, 4), 16),
1140
+ parseInt(full.slice(4, 6), 16)
1141
+ ];
1142
+ return rgb.some(Number.isNaN) ? null : rgb;
1143
+ }
1144
+ function perceivedLuminance(hex) {
1145
+ const rgb = parseHex(hex);
1146
+ if (!rgb) return 0.5;
1147
+ const [r, g, b] = rgb;
1148
+ return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
1149
+ }
1150
+ function grainOpacityForTone(baseColor) {
1151
+ return Math.min(0.24, Math.max(0.04, 0.02 + 0.31 * perceivedLuminance(baseColor)));
1152
+ }
1153
+ function grainForTone(baseColor) {
1154
+ const op = grainOpacityForTone(baseColor).toFixed(3);
1155
+ return `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='${op}'/%3E%3C/svg%3E")`;
1156
+ }
1157
+ function paperSheet(tone = c["surface-raised"]) {
1158
+ return {
1159
+ backgroundColor: tone,
1160
+ // Grain alone. The dither went with the gradient it protected — see header.
1161
+ backgroundImage: grainForTone(tone),
1162
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.20), 0 8px 22px rgba(0,0,0,0.50)"
1163
+ };
1164
+ }
1165
+ function insetWell(tone = c["surface-input"]) {
1166
+ return {
1167
+ backgroundColor: tone,
1168
+ boxShadow: "inset 0 2px 6px rgba(0,0,0,0.55), inset 0 -1px 0 rgba(255,255,255,0.12)"
1169
+ };
1170
+ }
1171
+ function barPaper(color, flip = false) {
1172
+ return {
1173
+ backgroundImage: grainForTone(color),
1174
+ boxShadow: flip ? "0 -6px 16px rgba(0,0,0,0.45)" : "inset 0 1.5px 0 rgba(255,255,255,0.22), 0 6px 16px rgba(0,0,0,0.45)"
1175
+ };
1176
+ }
1177
+
1178
+ // src/theme/color-utils.ts
1170
1179
  function hexToRgb(hex) {
1171
1180
  const cleaned = hex.replace("#", "");
1172
1181
  let r, g, b;
@@ -1206,32 +1215,32 @@ function rgbToHsv(r, g, b) {
1206
1215
  return { h, s, v };
1207
1216
  }
1208
1217
  function hsvToRgb(h, s, v) {
1209
- const c = v * s;
1210
- const x = c * (1 - Math.abs(h / 60 % 2 - 1));
1211
- const m = v - c;
1218
+ const c2 = v * s;
1219
+ const x = c2 * (1 - Math.abs(h / 60 % 2 - 1));
1220
+ const m = v - c2;
1212
1221
  let rPrime, gPrime, bPrime;
1213
1222
  if (h >= 0 && h < 60) {
1214
- rPrime = c;
1223
+ rPrime = c2;
1215
1224
  gPrime = x;
1216
1225
  bPrime = 0;
1217
1226
  } else if (h >= 60 && h < 120) {
1218
1227
  rPrime = x;
1219
- gPrime = c;
1228
+ gPrime = c2;
1220
1229
  bPrime = 0;
1221
1230
  } else if (h >= 120 && h < 180) {
1222
1231
  rPrime = 0;
1223
- gPrime = c;
1232
+ gPrime = c2;
1224
1233
  bPrime = x;
1225
1234
  } else if (h >= 180 && h < 240) {
1226
1235
  rPrime = 0;
1227
1236
  gPrime = x;
1228
- bPrime = c;
1237
+ bPrime = c2;
1229
1238
  } else if (h >= 240 && h < 300) {
1230
1239
  rPrime = x;
1231
1240
  gPrime = 0;
1232
- bPrime = c;
1241
+ bPrime = c2;
1233
1242
  } else {
1234
- rPrime = c;
1243
+ rPrime = c2;
1235
1244
  gPrime = 0;
1236
1245
  bPrime = x;
1237
1246
  }
@@ -1284,194 +1293,6 @@ function isDark(hex) {
1284
1293
  const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
1285
1294
  return hsv.v < 0.6;
1286
1295
  }
1287
- var shadowColors = {
1288
- // For charcoal surfaces (~#2C2C2C to #3C3C3C)
1289
- charcoal: {
1290
- dark: "rgba(0, 0, 0, 0.5)",
1291
- light: "rgba(255, 255, 255, 0.12)",
1292
- // brighter for crisp rim light
1293
- darker: "rgba(0, 0, 0, 0.6)"
1294
- },
1295
- // For neutral dark surfaces (~#1F2937)
1296
- neutralDark: {
1297
- dark: "rgba(0, 0, 0, 0.4)",
1298
- light: "rgba(255, 255, 255, 0.08)",
1299
- darker: "rgba(0, 0, 0, 0.5)"
1300
- },
1301
- // For light surfaces (white/light gray backgrounds)
1302
- neutralLight: {
1303
- dark: "rgba(0, 0, 0, 0.15)",
1304
- // Visible dark shadow for depth on light backgrounds
1305
- light: "rgba(255, 255, 255, 1)",
1306
- // Bright highlight for raised effect (visible on light gray)
1307
- darker: "rgba(0, 0, 0, 0.2)"
1308
- // Deeper shadow for pressed states
1309
- }
1310
- };
1311
- var intensityConfig = {
1312
- subtle: { offset: 1, blur: 2 },
1313
- // very light, barely visible
1314
- medium: { offset: 2, blur: 4 },
1315
- // noticeable but restrained
1316
- strong: { offset: 2, blur: 5 }
1317
- // slightly deeper blur, same offset as medium
1318
- };
1319
- function createRaisedShadow(surface = "charcoal", intensity = "medium") {
1320
- const colors = shadowColors[surface];
1321
- const { offset, blur } = intensityConfig[intensity];
1322
- const rimBlur = 1;
1323
- return reactNative.Platform.select({
1324
- web: {
1325
- boxShadow: `${offset}px ${offset}px ${blur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`
1326
- },
1327
- default: {
1328
- // React Native shadow approximation (limited compared to web)
1329
- shadowColor: "#000",
1330
- shadowOffset: { width: offset, height: offset },
1331
- shadowOpacity: 0.3,
1332
- shadowRadius: blur / 2,
1333
- elevation: offset
1334
- }
1335
- });
1336
- }
1337
- function createPressedShadow(surface = "charcoal", intensity = "medium") {
1338
- const colors = shadowColors[surface];
1339
- const { offset, blur } = intensityConfig[intensity];
1340
- const rimOffset = 1;
1341
- const rimBlur = 1;
1342
- return reactNative.Platform.select({
1343
- web: {
1344
- boxShadow: `inset ${offset}px ${offset}px ${blur}px ${colors.darker}, inset -${rimOffset}px -${rimOffset}px ${rimBlur}px ${colors.light}`
1345
- },
1346
- default: {
1347
- // React Native doesn't support inset shadows, approximate with darker bg
1348
- shadowOpacity: 0,
1349
- elevation: 0
1350
- }
1351
- });
1352
- }
1353
- function createFlatShadow() {
1354
- return reactNative.Platform.select({
1355
- web: {
1356
- boxShadow: "none"
1357
- },
1358
- default: {
1359
- shadowOpacity: 0,
1360
- elevation: 0
1361
- }
1362
- });
1363
- }
1364
- function createRaisedHoverShadow(surface = "charcoal", intensity = "medium") {
1365
- const colors = shadowColors[surface];
1366
- const { offset, blur } = intensityConfig[intensity];
1367
- const hoverOffset = Math.max(1, Math.round(offset / 2));
1368
- const hoverBlur = Math.max(1, Math.round(blur / 2));
1369
- const rimBlur = 1;
1370
- return reactNative.Platform.select({
1371
- web: {
1372
- // Keep same offsets as normal state for rim to prevent "shrinking" effect
1373
- boxShadow: `${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`
1374
- },
1375
- default: {
1376
- shadowColor: "#000",
1377
- shadowOffset: { width: hoverOffset, height: hoverOffset },
1378
- shadowOpacity: 0.2,
1379
- shadowRadius: hoverBlur / 2,
1380
- elevation: hoverOffset
1381
- }
1382
- });
1383
- }
1384
- function createPressedHoverShadow(surface = "charcoal", intensity = "medium") {
1385
- const colors = shadowColors[surface];
1386
- const { offset, blur } = intensityConfig[intensity];
1387
- const hoverOffset = Math.max(1, Math.round(offset / 2));
1388
- const hoverBlur = Math.max(1, Math.round(blur / 2));
1389
- const rimBlur = 1;
1390
- return reactNative.Platform.select({
1391
- web: {
1392
- // Keep rim offset at 1px (same as pressed normal) to prevent "shrinking" effect
1393
- boxShadow: `inset ${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.darker}, inset -1px -1px ${rimBlur}px ${colors.light}`
1394
- },
1395
- default: {
1396
- shadowOpacity: 0,
1397
- elevation: 0
1398
- }
1399
- });
1400
- }
1401
- var neumorphicShadows = {
1402
- // Charcoal surface shadows (for dark toolbars, cards)
1403
- charcoal: {
1404
- raised: {
1405
- subtle: createRaisedShadow("charcoal", "subtle"),
1406
- medium: createRaisedShadow("charcoal", "medium"),
1407
- strong: createRaisedShadow("charcoal", "strong")
1408
- },
1409
- raisedHover: {
1410
- subtle: createRaisedHoverShadow("charcoal", "subtle"),
1411
- medium: createRaisedHoverShadow("charcoal", "medium"),
1412
- strong: createRaisedHoverShadow("charcoal", "strong")
1413
- },
1414
- pressed: {
1415
- subtle: createPressedShadow("charcoal", "subtle"),
1416
- medium: createPressedShadow("charcoal", "medium"),
1417
- strong: createPressedShadow("charcoal", "strong")
1418
- },
1419
- pressedHover: {
1420
- subtle: createPressedHoverShadow("charcoal", "subtle"),
1421
- medium: createPressedHoverShadow("charcoal", "medium"),
1422
- strong: createPressedHoverShadow("charcoal", "strong")
1423
- },
1424
- flat: createFlatShadow()
1425
- },
1426
- // Neutral dark surface shadows
1427
- neutralDark: {
1428
- raised: {
1429
- subtle: createRaisedShadow("neutralDark", "subtle"),
1430
- medium: createRaisedShadow("neutralDark", "medium"),
1431
- strong: createRaisedShadow("neutralDark", "strong")
1432
- },
1433
- raisedHover: {
1434
- subtle: createRaisedHoverShadow("neutralDark", "subtle"),
1435
- medium: createRaisedHoverShadow("neutralDark", "medium"),
1436
- strong: createRaisedHoverShadow("neutralDark", "strong")
1437
- },
1438
- pressed: {
1439
- subtle: createPressedShadow("neutralDark", "subtle"),
1440
- medium: createPressedShadow("neutralDark", "medium"),
1441
- strong: createPressedShadow("neutralDark", "strong")
1442
- },
1443
- pressedHover: {
1444
- subtle: createPressedHoverShadow("neutralDark", "subtle"),
1445
- medium: createPressedHoverShadow("neutralDark", "medium"),
1446
- strong: createPressedHoverShadow("neutralDark", "strong")
1447
- },
1448
- flat: createFlatShadow()
1449
- },
1450
- // Neutral light surface shadows
1451
- neutralLight: {
1452
- raised: {
1453
- subtle: createRaisedShadow("neutralLight", "subtle"),
1454
- medium: createRaisedShadow("neutralLight", "medium"),
1455
- strong: createRaisedShadow("neutralLight", "strong")
1456
- },
1457
- raisedHover: {
1458
- subtle: createRaisedHoverShadow("neutralLight", "subtle"),
1459
- medium: createRaisedHoverShadow("neutralLight", "medium"),
1460
- strong: createRaisedHoverShadow("neutralLight", "strong")
1461
- },
1462
- pressed: {
1463
- subtle: createPressedShadow("neutralLight", "subtle"),
1464
- medium: createPressedShadow("neutralLight", "medium"),
1465
- strong: createPressedShadow("neutralLight", "strong")
1466
- },
1467
- pressedHover: {
1468
- subtle: createPressedHoverShadow("neutralLight", "subtle"),
1469
- medium: createPressedHoverShadow("neutralLight", "medium"),
1470
- strong: createPressedHoverShadow("neutralLight", "strong")
1471
- },
1472
- flat: createFlatShadow()
1473
- }
1474
- };
1475
1296
  var elevationSystem = {
1476
1297
  // Deep inset (recessed input fields, deeply pressed buttons)
1477
1298
  [-2]: {
@@ -1500,7 +1321,13 @@ var elevationSystem = {
1500
1321
  // Level 1: Subtle elevation (outline cards, subtle containers)
1501
1322
  [1]: {
1502
1323
  name: "subtle",
1503
- colorAdjustment: 0.01,
1324
+ // The whole ladder was re-spaced in TD-07.16. It used to run
1325
+ // 0.01/0.02/0.04/0.06/0.08, where the first step was ~2 RGB values — under
1326
+ // the perceptual floor, so level 1 existed in the type system and nowhere on
1327
+ // screen. With drop-shadow now gated off below FLOATING_ELEVATION_MIN, TONE
1328
+ // is the only thing separating 0-3, so every step has to actually land.
1329
+ // Even 0.025 spacing, verified in elevation.test.ts as ΔL* > 1 per step.
1330
+ colorAdjustment: 0.025,
1504
1331
  shadowIntensity: "subtle",
1505
1332
  shadowStyle: "raised",
1506
1333
  description: "Subtle elevation (outline cards, subtle containers)"
@@ -1508,7 +1335,7 @@ var elevationSystem = {
1508
1335
  // Level 2: Standard elevation (default cards, buttons)
1509
1336
  [2]: {
1510
1337
  name: "standard",
1511
- colorAdjustment: 0.02,
1338
+ colorAdjustment: 0.05,
1512
1339
  shadowIntensity: "medium",
1513
1340
  shadowStyle: "raised",
1514
1341
  description: "Standard elevation (default cards, raised buttons)"
@@ -1516,7 +1343,7 @@ var elevationSystem = {
1516
1343
  // Level 3: Prominent elevation (important cards, modals)
1517
1344
  [3]: {
1518
1345
  name: "prominent",
1519
- colorAdjustment: 0.04,
1346
+ colorAdjustment: 0.075,
1520
1347
  shadowIntensity: "strong",
1521
1348
  shadowStyle: "raised",
1522
1349
  description: "Prominent elevation (important cards, modals, dropdowns)"
@@ -1524,7 +1351,7 @@ var elevationSystem = {
1524
1351
  // Level 4: High elevation (overlays, popovers)
1525
1352
  [4]: {
1526
1353
  name: "high",
1527
- colorAdjustment: 0.06,
1354
+ colorAdjustment: 0.1,
1528
1355
  shadowIntensity: "strong",
1529
1356
  shadowStyle: "raised",
1530
1357
  description: "High elevation (overlays, popovers, tooltips)"
@@ -1532,7 +1359,7 @@ var elevationSystem = {
1532
1359
  // Level 5: Floating (toasts, floating action buttons)
1533
1360
  [5]: {
1534
1361
  name: "floating",
1535
- colorAdjustment: 0.08,
1362
+ colorAdjustment: 0.13,
1536
1363
  shadowIntensity: "strong",
1537
1364
  shadowStyle: "raised",
1538
1365
  description: "Floating elevation (toasts, floating action buttons, highest z-index)"
@@ -1651,12 +1478,12 @@ function calculateElevationShadow(baseColor, level, theme, isHovered = false) {
1651
1478
  const elevatedSurfaceColor = calculateElevationSurface(baseColor, level);
1652
1479
  const rimLightColor = calculateRimLightColor(baseColor, level);
1653
1480
  const darkShadowColors = calculateDarkShadowColors(elevatedSurfaceColor);
1654
- const intensityConfig2 = {
1481
+ const intensityConfig = {
1655
1482
  subtle: { offset: 1, blur: 2 },
1656
1483
  medium: { offset: 2, blur: 4 },
1657
1484
  strong: { offset: 2, blur: 5 }
1658
1485
  };
1659
- const { offset, blur } = intensityConfig2[config.shadowIntensity];
1486
+ const { offset, blur } = intensityConfig[config.shadowIntensity];
1660
1487
  const rimConfig = getRimLightConfig();
1661
1488
  if (config.shadowStyle === "pressed") {
1662
1489
  if (isHovered) {
@@ -1712,12 +1539,13 @@ function calculateElevationShadow(baseColor, level, theme, isHovered = false) {
1712
1539
  });
1713
1540
  }
1714
1541
  }
1542
+ var FLOATING_ELEVATION_MIN = 4;
1715
1543
  function getElevationShadow(baseColor, level, theme, isHovered = false) {
1716
1544
  const cacheKey = getShadowCacheKey(baseColor, level, theme, isHovered);
1717
1545
  if (elevationShadowCache.has(cacheKey)) {
1718
1546
  return elevationShadowCache.get(cacheKey);
1719
1547
  }
1720
- const result = calculateElevationShadow(baseColor, level, theme, isHovered);
1548
+ const result = level > 0 && level < FLOATING_ELEVATION_MIN ? {} : calculateElevationShadow(baseColor, level, theme, isHovered);
1721
1549
  if (elevationShadowCache.size >= MAX_SHADOW_CACHE_SIZE) {
1722
1550
  const firstKey = elevationShadowCache.keys().next().value;
1723
1551
  elevationShadowCache.delete(firstKey);
@@ -1987,7 +1815,7 @@ var audiobookPreset = {
1987
1815
  "text-primary": "#f4f3f1",
1988
1816
  "text-secondary": "#b5b3af",
1989
1817
  "text-tertiary": "#7a7875",
1990
- // Surfaces: warm charcoals (not pure gray)
1818
+ // Surfaces: warm greys (not pure gray)
1991
1819
  "surface-base": "#101114",
1992
1820
  "surface-elevated": "#15171c",
1993
1821
  "surface-raised": "#1a1d24",
@@ -1998,9 +1826,9 @@ var audiobookPreset = {
1998
1826
  "background-default": "#101114",
1999
1827
  "background-subtle": "#1a1d24",
2000
1828
  // Borders
2001
- "border-default": "rgba(255, 255, 255, 0.06)",
2002
- "border-subtle": "rgba(255, 255, 255, 0.03)",
2003
- "border-strong": "#2d323d",
1829
+ "hairline-default": "rgba(255, 255, 255, 0.06)",
1830
+ "hairline-subtle": "rgba(255, 255, 255, 0.03)",
1831
+ "hairline-strong": "#2d323d",
2004
1832
  "border-focus": "#d4782a",
2005
1833
  "border-input": "#2d323d",
2006
1834
  "border-input-hover": "#454c5c",
@@ -2060,20 +1888,17 @@ function ThemeProvider({
2060
1888
  }
2061
1889
 
2062
1890
  exports.CATEGORICAL_CVD_SAFE_MAX = CATEGORICAL_CVD_SAFE_MAX;
1891
+ exports.FLOATING_ELEVATION_MIN = FLOATING_ELEVATION_MIN;
2063
1892
  exports.PRESSED_ELEVATION_LEVEL = PRESSED_ELEVATION_LEVEL;
1893
+ exports.SURFACE_PLANE_STEPS = SURFACE_PLANE_STEPS;
2064
1894
  exports.ThemeProvider = ThemeProvider;
2065
1895
  exports.applyThemePreset = applyThemePreset;
2066
1896
  exports.audiobookPreset = audiobookPreset;
2067
- exports.backgroundFrameDark = backgroundFrameDark;
1897
+ exports.barPaper = barPaper;
2068
1898
  exports.bestTextColor = bestTextColor;
2069
1899
  exports.buttonSizes = buttonSizes;
2070
1900
  exports.categoricalPalette = categoricalPalette;
2071
1901
  exports.componentElevationRanges = componentElevationRanges;
2072
- exports.createFlatShadow = createFlatShadow;
2073
- exports.createPressedHoverShadow = createPressedHoverShadow;
2074
- exports.createPressedShadow = createPressedShadow;
2075
- exports.createRaisedHoverShadow = createRaisedHoverShadow;
2076
- exports.createRaisedShadow = createRaisedShadow;
2077
1902
  exports.darkThemeCSSVars = darkThemeCSSVars;
2078
1903
  exports.darken = darken;
2079
1904
  exports.defaultPreset = defaultPreset;
@@ -2093,19 +1918,22 @@ exports.getSemanticColors = getSemanticColors;
2093
1918
  exports.getThemeCSSVars = getThemeCSSVars;
2094
1919
  exports.getValidatedElevation = getValidatedElevation;
2095
1920
  exports.gluestackConfig = gluestackConfig;
1921
+ exports.grainForTone = grainForTone;
1922
+ exports.grainOpacityForTone = grainOpacityForTone;
1923
+ exports.greyRamp = greyRamp;
2096
1924
  exports.hexToRgb = hexToRgb;
2097
1925
  exports.hsvToRgb = hsvToRgb;
1926
+ exports.insetWell = insetWell;
2098
1927
  exports.isCssPropertyManifest = isCssPropertyManifest;
2099
1928
  exports.isDark = isDark;
2100
1929
  exports.lightThemeCSSVars = lightThemeCSSVars;
2101
1930
  exports.lighten = lighten;
2102
1931
  exports.linearGradient = linearGradient;
2103
- exports.neumorphicShadows = neumorphicShadows;
1932
+ exports.paperSheet = paperSheet;
2104
1933
  exports.primitiveBorderRadius = primitiveBorderRadius;
2105
1934
  exports.primitiveBreakpoints = primitiveBreakpoints;
2106
1935
  exports.primitiveColors = primitiveColors;
2107
1936
  exports.primitiveRamps = primitiveRamps;
2108
- exports.primitiveShadows = primitiveShadows;
2109
1937
  exports.primitiveSpacing = primitiveSpacing;
2110
1938
  exports.primitiveTypography = primitiveTypography;
2111
1939
  exports.primitiveZIndex = primitiveZIndex;
@@ -2116,9 +1944,7 @@ exports.semanticColorsDark = semanticColorsDark;
2116
1944
  exports.semanticColorsLight = semanticColorsLight;
2117
1945
  exports.semanticTypography = semanticTypography;
2118
1946
  exports.sequentialEffort = sequentialEffort;
2119
- exports.shadowColors = shadowColors;
2120
1947
  exports.surfaceGradient = surfaceGradient;
2121
- exports.surfaceRampDark = surfaceRampDark;
2122
1948
  exports.validateCssPropertyManifest = validateCssPropertyManifest;
2123
1949
  //# sourceMappingURL=index.js.map
2124
1950
  //# sourceMappingURL=index.js.map