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