design26 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,185 @@
1
+ @import "./tokens.css";
2
+
3
+ /* Styles are opt-in and scoped. No global body, heading or control reset. */
4
+ .d26-theme, [data-design26-theme="light"] { box-sizing: border-box; color: var(--color-text-primary); font-family: var(--font-body); font-weight: var(--font-weight-body); line-height: var(--line-height-base); -webkit-font-smoothing: antialiased; }
5
+ :where(.d26-theme, [data-design26-theme="light"]) *, :where(.d26-theme, [data-design26-theme="light"]) *::before, :where(.d26-theme, [data-design26-theme="light"]) *::after { box-sizing: border-box; }
6
+ :where(.d26-theme, [data-design26-theme="light"]) :is(button, a, input, select, summary, [tabindex]):focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
7
+ :where(.d26-theme, [data-design26-theme="light"]) ::selection { background: var(--color-selection); color: var(--color-text-primary); }
8
+ .d26-frame { background: var(--color-background); min-height: 100svh; padding: 0 var(--space-page-gutter); }
9
+ .d26-frame > * { max-width: calc(var(--size-content-max) - var(--space-page-gutter) * 2); margin-inline: auto; }
10
+ .d26-panel { min-width: 0; box-shadow: var(--shadow-none); }
11
+ .d26-panel--sheet { background: var(--color-surface); border-radius: var(--radius-sheet); }
12
+ .d26-panel--padded { padding: var(--space-panel-block) var(--space-panel-inline); }
13
+ .d26-panel--control { display: grid; grid-template-columns: minmax(0, 1fr); gap: 36px; background: var(--color-surface-control); border: var(--border-width) solid var(--color-border-control); border-radius: var(--radius-panel); padding: 24px 26px 20px; }
14
+ .d26-panel--toolbar { background: var(--color-surface-toolbar); border-top: var(--border-rule); padding: 21px 32px; }
15
+ .d26-eyebrow { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; color: var(--color-accent); font: var(--font-weight-heading) var(--font-size-label)/1.5 var(--font-mono); letter-spacing: var(--letter-spacing-eyebrow); }
16
+ .d26-eyebrow--compact { font-size: var(--font-size-metadata); letter-spacing: 1.2px; gap: 8px; }
17
+ .d26-status-dot { display: inline-block; width: 6px; height: 6px; flex: 0 0 6px; border-radius: var(--radius-round); background: var(--color-accent); }
18
+ .d26-heading { font-family: var(--font-body); color: var(--color-text-primary); font-weight: var(--font-weight-heading); }
19
+ .d26-heading em { font-style: normal; color: var(--color-accent); }
20
+ .d26-heading--display { font-size: var(--font-size-display); line-height: var(--line-height-display); letter-spacing: var(--letter-spacing-display); margin: 30px 0 26px; }
21
+ .d26-heading--chapter { font-size: var(--font-size-chapter); line-height: var(--line-height-chapter); letter-spacing: var(--letter-spacing-display); margin: 27px 0 22px; }
22
+ .d26-heading--inspector { font-size: var(--font-size-inspector); line-height: 1.12; letter-spacing: -1.2px; margin: 18px 0 16px; }
23
+ .d26-heading--figure { font-size: var(--font-size-figure-title); letter-spacing: -.65px; margin: 0 0 10px; }
24
+ .d26-button, .d26-icon-button { cursor: pointer; font-family: var(--font-body); border: 0; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
25
+ .d26-button { line-height: var(--line-height-base); display: inline-flex; align-items: center; justify-content: space-between; font-size: var(--font-size-control); font-weight: var(--font-weight-body); }
26
+ .d26-button--primary { background: var(--color-accent); color: var(--color-text-on-accent); border-radius: var(--radius-button); padding: 17px 20px; gap: 32px; transition: background var(--motion-interaction), transform var(--motion-interaction); }
27
+ .d26-button--primary:hover:not(:disabled) { background: var(--color-accent-hover); transform: translateY(var(--distance-hover)); }
28
+ .d26-button--text { background: transparent; color: var(--color-accent); padding: 8px 0; gap: 16px; font-size: 11px; }
29
+ .d26-button--text:hover:not(:disabled) { color: var(--color-text-primary); }
30
+ .d26-button--playback { color: var(--color-accent); background: var(--color-surface-control); border: var(--border-width) solid var(--color-border-disclosure); padding: 9px 12px; border-radius: var(--radius-pill); font-size: 11px; gap: 9px; }
31
+ .d26-button-icon, .d26-icon-button > span { display: inline-flex; align-items: center; justify-content: center; }
32
+ .d26-button-icon svg { width: var(--size-icon); height: var(--size-icon); }
33
+ .d26-button:disabled, .d26-icon-button:disabled { opacity: .6; cursor: default; }
34
+ .d26-button[aria-busy="true"] { cursor: progress; }
35
+ .d26-text-link { display: inline-flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; border-bottom: var(--border-width) solid var(--color-border-link); font-size: 11px; padding: 7px 0; }
36
+ .d26-text-link:hover { color: var(--color-accent); border-color: var(--color-accent); }
37
+ .d26-icon-button { display: inline-grid; place-items: center; flex-shrink: 0; color: var(--color-text-primary); background: transparent; padding: 0; width: var(--size-icon-control); height: var(--size-icon-control); border: var(--border-width) solid var(--color-border-interactive); border-radius: var(--radius-round); }
38
+ .d26-icon-button:hover:not(:disabled) { color: var(--color-accent); }
39
+ .d26-icon-button svg { width: 19px; height: 19px; }
40
+ .d26-icon-button--step { width: var(--size-step-control); height: var(--size-step-control); }
41
+ .d26-icon-button--step:hover:not(:disabled) { background: var(--color-accent-soft); border-color: var(--color-border-interactive-hover); }
42
+ .d26-icon-button--plain { width: 25px; height: 26px; border: 0; border-radius: 0; }
43
+ .d26-icon-button--plain svg { width: 14px; height: 14px; }
44
+ .d26-navigation { display: flex; align-items: center; min-height: 105px; color: var(--color-text-on-accent); gap: 36px; }
45
+ .d26-brand { font-size: 39px; font-weight: 700; letter-spacing: -2px; line-height: 1; }
46
+ .d26-brand a { color: inherit; text-decoration: none; }
47
+ .d26-navigation-description { font-size: 12px; letter-spacing: .1px; color: var(--color-text-on-frame); border-left: var(--border-width) solid var(--color-border-on-frame); padding-left: 28px; }
48
+ .d26-navigation-actions { margin-left: auto; display: flex; align-items: center; gap: 22px; font-size: 12px; }
49
+ .d26-navigation-actions a { color: inherit; text-decoration: none; }
50
+ .d26-navigation-actions a:hover { color: var(--color-text-on-frame); }
51
+ .d26-steps { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); padding: 0 36px; border-bottom: var(--border-rule); }
52
+ .d26-steps button { position: relative; display: flex; align-items: center; gap: 16px; min-height: 60px; padding: 12px 24px; text-align: left; background: transparent; border: 0; color: var(--color-step-inactive); font: 12px/1.5 var(--font-body); cursor: pointer; transition: color var(--motion-interaction); }
53
+ .d26-steps button + button::before { content: ""; position: absolute; left: 0; width: 1px; height: 22px; background: var(--color-border); }
54
+ .d26-steps button::after { content: ""; position: absolute; bottom: -1px; left: 24px; right: 24px; height: var(--border-width-active); background: transparent; }
55
+ .d26-steps button[aria-current="step"]::after { background: var(--color-accent); }
56
+ .d26-steps button:hover:not(:disabled), .d26-steps button[aria-current="step"] { color: var(--color-text-primary); }
57
+ .d26-step-number { font: 10px/1.5 var(--font-mono); color: var(--color-step-number); }
58
+ .d26-steps button[aria-current="step"] .d26-step-number { color: var(--color-accent); }
59
+ .d26-step-dot { margin-left: auto; width: 5px; height: 5px; border-radius: var(--radius-round); background: var(--color-step-dot); }
60
+ .d26-steps button[aria-current="step"] .d26-step-dot { background: var(--color-accent); box-shadow: var(--shadow-status); }
61
+ .d26-tab-list { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0,1fr); border-bottom: var(--border-width) solid var(--color-border-tabs); }
62
+ .d26-tab-list button { display: flex; align-items: center; justify-content: center; gap: 13px; background: transparent; color: var(--color-tab-inactive); padding: 17px 8px; font: 13px/1.5 var(--font-body); border: 0; border-bottom: var(--border-width-active) solid transparent; margin-bottom: -1px; cursor: pointer; }
63
+ .d26-tab-list button > span { font: 9px/1.5 var(--font-mono); color: var(--color-tab-number); }
64
+ .d26-tab-list button[aria-selected="true"], .d26-tab-list button[aria-selected="true"] > span { color: var(--color-accent); border-bottom-color: var(--color-accent); }
65
+ .d26-tab-list button:hover:not(:disabled) { color: var(--color-text-primary); }
66
+ .d26-tab-panel { padding-top: 16px; }
67
+ .d26-tab-panel[hidden] { display: none; }
68
+ .d26-tab-list button:disabled, .d26-steps button:disabled { opacity: .5; cursor: default; }
69
+ .d26-slider { appearance: none; -webkit-appearance: none; display: block; width: 100%; height: var(--size-track); background: var(--color-track); border: 0; border-radius: 2px; margin: 10px 0; padding: 0; cursor: pointer; accent-color: var(--color-accent); }
70
+ .d26-slider::-webkit-slider-thumb { appearance: none; width: var(--size-thumb); height: var(--size-thumb); background: var(--color-accent); border: 3px solid var(--color-surface); border-radius: var(--radius-round); box-shadow: 0 0 0 1px var(--color-accent); }
71
+ .d26-slider::-moz-range-thumb { box-sizing: border-box; width: var(--size-thumb); height: var(--size-thumb); background: var(--color-accent); border: 3px solid var(--color-surface); border-radius: var(--radius-round); box-shadow: 0 0 0 1px var(--color-accent); }
72
+ .d26-slider--timeline { height: 3px; border-radius: 3px; background: linear-gradient(to right, var(--color-accent) var(--d26-progress, 0%), var(--color-track-timeline) var(--d26-progress, 0%)); }
73
+ .d26-slider--timeline::-webkit-slider-thumb { width: 9px; height: 9px; border: 0; box-shadow: 0 0 0 3px var(--color-surface-toolbar); }
74
+ .d26-slider--timeline::-moz-range-thumb { width: 9px; height: 9px; border: 0; box-shadow: 0 0 0 3px var(--color-surface-toolbar); }
75
+ .d26-slider:disabled, .d26-select:disabled, .d26-checkbox input:disabled { cursor: default; opacity: .5; }
76
+ .d26-field { display: flex; flex-direction: column; gap: 8px; }
77
+ .d26-field > label { font-size: 9px; color: var(--color-text-field); }
78
+ .d26-select { appearance: auto; width: 100%; background: var(--color-surface-input); border: var(--border-width) solid var(--color-border-input); border-radius: var(--radius-input); padding: 10px 12px; color: var(--color-text-primary); font: 11px/1.5 var(--font-body); cursor: pointer; }
79
+ .d26-field-description { display: block; color: var(--color-text-secondary); font: 10px/1.8 var(--font-body); margin: 8px 0 0; }
80
+ .d26-checkbox { display: inline-flex; align-items: center; gap: 6px; font-size: 9px; color: var(--color-text-secondary); cursor: pointer; }
81
+ .d26-checkbox input { accent-color: var(--color-accent); cursor: pointer; width: 12px; height: 12px; margin: 0; }
82
+ .d26-disclosure { border: var(--border-width) solid var(--color-border-disclosure); border-radius: var(--radius-input); background: var(--color-surface-disclosure); }
83
+ .d26-disclosure summary { display: flex; justify-content: space-between; align-items: center; gap: 20px; padding: 12px 14px; color: var(--color-accent); font-size: 11px; list-style: none; cursor: pointer; }
84
+ .d26-disclosure summary::-webkit-details-marker { display: none; }
85
+ .d26-disclosure summary > span { font-size: 17px; }
86
+ .d26-disclosure[open] summary > span { transform: rotate(180deg); }
87
+ .d26-disclosure > div { padding: 15px; border-top: var(--border-width) solid var(--color-border-disclosure); }
88
+ .d26-dialog { padding: 0; border: 0; border-radius: var(--radius-dialog); background: var(--color-surface); color: var(--color-text-primary); font-family: var(--font-body); width: min(var(--size-dialog-max), calc(100% - 32px)); max-height: calc(100svh - 48px); margin: auto; box-shadow: var(--shadow-dialog); }
89
+ .d26-dialog::backdrop { background: var(--color-overlay); backdrop-filter: blur(var(--backdrop-blur)); }
90
+ .d26-dialog-content { padding: 34px 38px; }
91
+ .d26-dialog-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
92
+ .d26-dialog-header h2 { font-size: 43px; line-height: 1.05; letter-spacing: -2px; font-weight: var(--font-weight-heading); margin: 0 0 20px; }
93
+ .d26-dialog-header .d26-icon-button { font-size: 27px; }
94
+ .d26-note { color: var(--color-text-secondary); background: var(--color-accent-soft); padding: 9px 11px; border-radius: var(--radius-note); font: 10px/1.8 var(--font-body); margin: 16px 0 0; }
95
+ .d26-note--positive { color: var(--color-positive); background: var(--color-positive-surface); }
96
+ .d26-note--warning { color: var(--color-warning); background: var(--color-warning-surface); }
97
+ .d26-note--error { color: var(--color-error); background: var(--color-warning-surface); }
98
+ .d26-parameter { min-width: 0; }
99
+ .d26-parameter > label { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-control); color: var(--color-text-control); font-size: 12px; margin-bottom: 17px; }
100
+ .d26-parameter output { color: var(--color-accent); font: 17px/1.5 var(--font-mono); white-space: nowrap; }
101
+ .d26-parameter output > span { color: var(--color-text-parameter-unit); font-size: 10px; }
102
+ .d26-parameter-range { display: flex; justify-content: space-between; color: var(--color-text-range); font: 8px/1.5 var(--font-mono); margin-top: 11px; }
103
+ .d26-data { margin: 0; }
104
+ .d26-data > div { display: flex; justify-content: space-between; align-items: baseline; gap: 15px; border-bottom: var(--border-width) solid var(--color-border-data); padding: var(--space-data-row) 0; font-size: 10px; }
105
+ .d26-data dt { color: var(--color-text-data-label); }
106
+ .d26-data dd { margin: 0; font: 10px/1.5 var(--font-mono); text-align: right; color: var(--color-text-data); overflow-wrap: anywhere; }
107
+ .d26-data[aria-busy="true"] { opacity: .5; transition: opacity var(--motion-busy) var(--ease-interaction); }
108
+ .d26-metric { min-width: 0; }
109
+ .d26-metric > span { font-size: 10px; color: var(--color-text-label); display: block; margin-bottom: 12px; }
110
+ .d26-metric strong { color: var(--color-accent); font-size: var(--font-size-metric); line-height: 1; letter-spacing: -1.2px; font-weight: var(--font-weight-body); }
111
+ .d26-metric small { font-size: 13px; color: var(--color-text-metric-unit); letter-spacing: 0; }
112
+ .d26-metric p { color: var(--color-text-secondary); font: 12px/1.8 var(--font-body); margin-bottom: 0; }
113
+ .d26-metric--result strong { font-size: var(--font-size-result); letter-spacing: -1.5px; }
114
+ .d26-metric--result small { font-size: 15px; }
115
+ .d26-figure { min-width: 0; margin: 0; }
116
+ .d26-figure figcaption { padding-bottom: var(--space-figure-caption); }
117
+ .d26-figure--caption-after figcaption { padding: var(--space-figure-caption) 0 0; }
118
+ .d26-figure-number { display: block; font: 7px/1.5 var(--font-mono); letter-spacing: .7px; color: var(--color-text-figure-number); margin-bottom: 12px; }
119
+ .d26-figure h3 { font-size: var(--font-size-figure-title); font-weight: var(--font-weight-heading); letter-spacing: -.65px; margin: 0 0 10px; }
120
+ .d26-figure figcaption p { color: var(--color-text-caption); font: 11px/1.8 var(--font-body); margin: 0; max-width: 340px; }
121
+ .d26-plot { min-width: 0; width: 100%; }
122
+ .d26-plot--framed { border: var(--border-width) solid var(--color-plot-border); border-radius: var(--radius-panel); background: var(--color-surface-elevated); overflow: hidden; }
123
+ .d26-plot > :is(svg, canvas, img) { display: block; width: 100%; height: auto; }
124
+ .d26-plot-grid { stroke: var(--color-plot-grid); stroke-width: 1; }
125
+ .d26-plot-axis { stroke: var(--color-plot-axis); stroke-width: 1; }
126
+ .d26-plot-tick { fill: var(--color-plot-tick); font: 11px/1.5 var(--font-mono); }
127
+ .d26-plot-label { fill: var(--color-plot-label); font: 12px/1.5 var(--font-body); }
128
+ .d26-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 8px 12px; padding: 12px 0 0 15px; margin: 0; font-size: 8px; color: var(--color-text-caption); }
129
+ .d26-legend li { display: inline-flex; align-items: center; gap: 5px; }
130
+ .d26-legend i { width: 5px; height: 5px; border-radius: var(--radius-round); background: var(--color-plot-series); }
131
+ .d26-legend .d26-legend--accent { background: var(--color-accent); }
132
+ .d26-legend .d26-legend--highlight { background: var(--color-highlight); }
133
+ .d26-legend .d26-legend--reference { background: var(--color-plot-reference); }
134
+ .d26-legend .d26-legend--dashed { width: 13px; height: 0; border-radius: 0; background: transparent; border-top: 2px dashed var(--color-highlight); }
135
+ .d26-reveal { animation: d26-enter var(--motion-enter) var(--ease-enter) both; }
136
+ .d26-pulse { animation: d26-pulse var(--motion-pulse) ease-in-out infinite; }
137
+ @keyframes d26-enter { from { opacity: 0; transform: translateY(var(--distance-enter)); } to { opacity: 1; transform: translateY(0); } }
138
+ @keyframes d26-pulse { 50% { opacity: .35; } }
139
+ @media (min-width: 1500px) { .d26-navigation { min-height: 118px; } }
140
+ @media (max-width: 1100px) {
141
+ .d26-navigation { min-height: 86px; }
142
+ .d26-button--primary { padding: 15px 16px; gap: 20px; }
143
+ .d26-steps { padding: 0 18px; }
144
+ .d26-steps button { padding: 12px 15px; gap: 10px; font-size: 11px; }
145
+ .d26-steps button::after { left: 15px; right: 15px; }
146
+ .d26-tab-list button { font-size: 11px; gap: 8px; }
147
+ }
148
+ @media (max-width: 760px) {
149
+ .d26-navigation { min-height: 79px; gap: 15px; padding: 0 7px; }
150
+ .d26-brand { font-size: 32px; }
151
+ .d26-navigation-description { display: none; }
152
+ .d26-navigation-actions { font-size: 10px; gap: 13px; }
153
+ .d26-heading--display { margin: 23px 0 21px; }
154
+ .d26-heading--chapter { margin: 22px 0 18px; }
155
+ .d26-eyebrow { font-size: 8px; letter-spacing: 1.5px; }
156
+ .d26-eyebrow--compact { font-size: 7px; letter-spacing: .8px; }
157
+ .d26-button--primary { font-size: 11px; padding: 15px 17px; gap: 22px; }
158
+ .d26-panel--control { padding: 20px; gap: 25px; }
159
+ .d26-metric > span { font-size: 9px; line-height: 1.6; }
160
+ .d26-panel--toolbar { padding: 17px 20px; }
161
+ .d26-steps { padding: 0 12px; }
162
+ .d26-steps button { min-height: 62px; padding: 11px 10px; gap: 4px; align-items: flex-start; flex-direction: column; font-size: 9px; line-height: 1.4; }
163
+ .d26-step-number { font-size: 8px; }
164
+ .d26-steps button::after { left: 10px; right: 10px; }
165
+ .d26-steps button + button::before { top: 21px; }
166
+ .d26-step-dot { display: none; }
167
+ .d26-tab-list button { flex-direction: column; justify-content: flex-start; padding: 13px 5px; gap: 8px; font-size: 10px; line-height: 1.5; }
168
+ .d26-tab-list button > span { font-size: 8px; }
169
+ .d26-select { min-height: 40px; }
170
+ .d26-dialog-content { padding: 26px; }
171
+ .d26-dialog-header h2 { font-size: 38px; }
172
+ .d26-figure-number { font-size: 8px; }
173
+ .d26-figure figcaption p { font-size: 12px; max-width: none; }
174
+ .d26-plot-tick { font-size: 18px; }
175
+ .d26-plot-label { font-size: 16px; }
176
+ .d26-legend { font-size: 9px; }
177
+ }
178
+ @media (prefers-reduced-motion: reduce) {
179
+ :where(.d26-theme, [data-design26-theme="light"]) *, :where(.d26-theme, [data-design26-theme="light"]) *::before, :where(.d26-theme, [data-design26-theme="light"]) *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
180
+ }
181
+ /* Optional backdrop extracted from the reference's concurrent grid refinement. */
182
+ .d26-technical-grid { position: absolute; inset: 0; z-index: 0; pointer-events: none; mask-image: radial-gradient(ellipse 38% 62% at 74% 46%, #000 8%, #000c 30%, #0003 72%, #0001 100%); }
183
+ .d26-technical-grid::before { content: ""; position: absolute; inset: 0; background-image: linear-gradient(var(--color-grid) .8px, transparent .8px), linear-gradient(90deg, var(--color-grid) .8px, transparent .8px); background-size: var(--size-grid-spacing) var(--size-grid-spacing); }
184
+ .d26-technical-grid:has(canvas[data-ready="true"])::before { display: none; }
185
+ .d26-technical-grid canvas { display: block; width: 100%; height: 100%; }
@@ -0,0 +1,165 @@
1
+ /* One reference theme. Override semantic roles on the same theme wrapper. */
2
+ .d26-theme, [data-design26-theme="light"] {
3
+ --d26-blue: #075afa;
4
+ --d26-navy: #072754;
5
+ --d26-paper: #f7f9fc;
6
+ --color-background: var(--d26-blue);
7
+ --color-brand: var(--d26-blue);
8
+ --color-surface: var(--d26-paper);
9
+ --color-surface-elevated: #fbfcfe;
10
+ --color-surface-toolbar: #f3f6fb;
11
+ --color-surface-control: #edf3fc;
12
+ --color-surface-input: #eff4fc;
13
+ --color-surface-disclosure: #f0f5fd;
14
+ --color-text-primary: var(--d26-navy);
15
+ --color-text-secondary: #61718a;
16
+ --color-text-description: #5c6d84;
17
+ --color-text-subtitle: #355274;
18
+ --color-text-caption: #7b90ac;
19
+ --color-text-label: #7d91ac;
20
+ --color-text-field: #7d90a8;
21
+ --color-text-metric-unit: #8397b3;
22
+ --color-text-parameter-unit: #91a3bb;
23
+ --color-text-range: #96a7bd;
24
+ --color-text-figure-number: #8da0b9;
25
+ --color-text-data-label: #7488a2;
26
+ --color-step-inactive: #7d8ca1;
27
+ --color-step-number: #91a0b4;
28
+ --color-step-dot: #d3ddeb;
29
+ --color-border-tabs: #d8e2ee;
30
+ --color-text-data: #284d7b;
31
+ --color-text-control: #35577f;
32
+ --color-text-on-accent: #fff;
33
+ --color-text-on-frame: #deebff;
34
+ --color-text-footer: #c7dcff;
35
+ --color-text-disabled: #a7b5c8;
36
+ --color-accent: var(--d26-blue);
37
+ --color-accent-hover: #004ad7;
38
+ --color-accent-soft: #eaf1ff;
39
+ --color-highlight: #ed9019;
40
+ --color-focus: var(--color-highlight);
41
+ --color-border: #dee5ee;
42
+ --color-border-data: #e0e7f1;
43
+ --color-border-control: #dce6f5;
44
+ --color-border-input: #d8e3f1;
45
+ --color-border-link: #b8c8dd;
46
+ --color-border-interactive-hover: #a4badd;
47
+ --color-border-interactive: #cfdae9;
48
+ --color-border-disclosure: #d7e2f1;
49
+ --color-border-on-frame: #ffffff45;
50
+ --color-tab-inactive: #7a8da6;
51
+ --color-tab-number: #9aacbf;
52
+ --color-track: #ccdaf0;
53
+ --color-track-timeline: #d6e0ed;
54
+ --color-positive: #306b72;
55
+ --color-positive-surface: #edf5f4;
56
+ --color-warning: #9a6a20;
57
+ --color-warning-surface: #fbf0de;
58
+ --color-error: #9d6b24;
59
+ --color-selection: #bdd6ff;
60
+ --color-overlay: #021b4b80;
61
+ --color-status-halo: #e7efff;
62
+ --color-grid: rgba(150, 174, 207, .32);
63
+ --size-grid-spacing: 38px;
64
+ --color-plot-grid: #e0e8f2;
65
+ --color-plot-axis: #9bb0cb;
66
+ --color-plot-tick: #8a9db5;
67
+ --color-plot-label: #6681a5;
68
+ --color-plot-border: #dce5f0;
69
+ --color-plot-series: #7eaafb;
70
+ --color-plot-point-muted: #768ca8;
71
+ --color-plot-reference: #c5b568;
72
+ --color-plot-heat-low: #eff4fc;
73
+ --color-plot-heat-mid: #9cc1ff;
74
+ --font-body: Arial, Helvetica, sans-serif;
75
+ --font-mono: var(--font-geist-mono, "Geist Mono"), monospace;
76
+ --font-weight-body: 400;
77
+ --font-weight-heading: 500;
78
+ --font-weight-strong: 600;
79
+ --font-size-display: clamp(48px, 5.1vw, 82px);
80
+ --font-size-chapter: clamp(56px, 5.6vw, 88px);
81
+ --font-size-inspector: clamp(25px, 2.3vw, 36px);
82
+ --font-size-subtitle: clamp(17px, 1.5vw, 22px);
83
+ --font-size-hero-copy: clamp(13px, 1.12vw, 17px);
84
+ --font-size-body: 13px;
85
+ --font-size-copy-compact: 12px;
86
+ --font-size-control: 12px;
87
+ --font-size-label: 10px;
88
+ --font-size-metadata: 8px;
89
+ --font-size-figure-title: 20px;
90
+ --font-size-metric: 34px;
91
+ --font-size-result: 42px;
92
+ --line-height-base: 1.5;
93
+ --line-height-display: .995;
94
+ --line-height-chapter: 1.02;
95
+ --line-height-copy: 1.9;
96
+ --line-height-compact: 1.85;
97
+ --letter-spacing-display: -.065em;
98
+ --letter-spacing-eyebrow: 1.8px;
99
+ --space-page-gutter: 32px;
100
+ --space-panel-block: 40px;
101
+ --space-panel-inline: clamp(35px, 5vw, 80px);
102
+ --space-section: 36px;
103
+ --space-grid: 30px;
104
+ --space-stack: 22px;
105
+ --space-control: 14px;
106
+ --space-data-row: 10px;
107
+ --space-figure-caption: 17px;
108
+ --size-content-max: 1920px;
109
+ --size-media-max: 465px;
110
+ --size-dialog-max: 540px;
111
+ --size-icon: 18px;
112
+ --size-icon-control: 34px;
113
+ --size-step-control: 44px;
114
+ --size-track: 4px;
115
+ --size-thumb: 15px;
116
+ --radius-sheet: 20px;
117
+ --radius-dialog: 16px;
118
+ --radius-panel: 8px;
119
+ --radius-media: 10px;
120
+ --radius-button: 6px;
121
+ --radius-input: 5px;
122
+ --radius-note: 4px;
123
+ --radius-tag: 3px;
124
+ --radius-pill: 24px;
125
+ --radius-round: 50%;
126
+ --border-width: 1px;
127
+ --border-width-active: 2px;
128
+ --border-rule: var(--border-width) solid var(--color-border);
129
+ --shadow-dialog: 0 30px 100px #001e6a45;
130
+ --shadow-none: none;
131
+ --shadow-status: 0 0 0 4px var(--color-status-halo);
132
+ --focus-width: 3px;
133
+ --focus-offset: 5px;
134
+ --motion-interaction: 200ms;
135
+ --motion-busy: 180ms;
136
+ --motion-enter: 420ms;
137
+ --motion-surface: 500ms;
138
+ --motion-pulse: 1500ms;
139
+ --ease-interaction: ease;
140
+ --ease-enter: cubic-bezier(.25, .46, .45, .94);
141
+ --distance-hover: -2px;
142
+ --distance-enter: 12px;
143
+ --backdrop-blur: 7px;
144
+ /* Documentation mirrors. Native media-query conditions below use literals. */
145
+ --breakpoint-mobile: 760px;
146
+ --breakpoint-tablet: 1100px;
147
+ --breakpoint-wide: 1500px;
148
+ }
149
+ @media (min-width: 1500px) {
150
+ .d26-theme, [data-design26-theme="light"] { --space-page-gutter: 44px; --size-media-max: 525px; }
151
+ }
152
+ @media (max-width: 1100px) {
153
+ .d26-theme, [data-design26-theme="light"] { --space-page-gutter: 24px; --space-panel-block: 35px; --space-panel-inline: 35px; --font-size-chapter: 56px; }
154
+ }
155
+ @media (max-width: 760px) {
156
+ .d26-theme, [data-design26-theme="light"] {
157
+ --space-page-gutter: 14px; --space-panel-block: 32px; --space-panel-inline: 25px;
158
+ --radius-sheet: 14px; --font-size-display: clamp(32px, 8.9vw, 60px);
159
+ --line-height-base: 1.5;
160
+ --line-height-display: .99; --font-size-chapter: 49px; --font-size-inspector: 28px;
161
+ --font-size-subtitle: 17px; --font-size-hero-copy: 13px; --font-size-metric: 29px;
162
+ --font-size-figure-title: 22px; --space-section: 27px; --space-grid: 26px;
163
+ --size-media-max: 465px;
164
+ }
165
+ }
@@ -0,0 +1,15 @@
1
+ import { type InputHTMLAttributes, type ReactNode, type SelectHTMLAttributes } from "react";
2
+ export type SliderProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
3
+ variant?: "parameter" | "timeline";
4
+ "aria-label"?: string;
5
+ };
6
+ export declare function Slider({ variant, min, max, value, defaultValue, style, className, ...props }: SliderProps): import("react").JSX.Element;
7
+ export type SelectProps = SelectHTMLAttributes<HTMLSelectElement> & {
8
+ label: ReactNode;
9
+ description?: string;
10
+ };
11
+ export declare function Select({ label, description, id, className, children, ...props }: SelectProps): import("react").JSX.Element;
12
+ export type CheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
13
+ label: ReactNode;
14
+ };
15
+ export declare function Checkbox({ label, className, id, ...props }: CheckboxProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { cx } from "./primitives.js";
5
+ export function Slider({ variant = "parameter", min = 0, max = 100, value, defaultValue, style, className, ...props }) {
6
+ const lower = Number(min), upper = Number(max);
7
+ const numeric = Number(value ?? defaultValue ?? (lower + upper) / 2);
8
+ const progress = upper > lower ? Math.max(0, Math.min(100, (numeric - lower) / (upper - lower) * 100)) : 0;
9
+ return _jsx("input", { ...props, type: "range", min: min, max: max, value: value, defaultValue: defaultValue, style: { "--d26-progress": `${progress}%`, ...style }, className: cx("d26-slider", `d26-slider--${variant}`, className), onInput: event => { const input = event.currentTarget; input.style.setProperty("--d26-progress", `${(Number(input.value) - lower) / (upper - lower || 1) * 100}%`); props.onInput?.(event); } });
10
+ }
11
+ export function Select({ label, description, id, className, children, ...props }) {
12
+ const generated = useId(), inputId = id ?? generated;
13
+ const describedBy = [props["aria-describedby"], description ? `${inputId}-description` : undefined].filter(Boolean).join(" ") || undefined;
14
+ return _jsxs("div", { className: "d26-field", children: [_jsx("label", { htmlFor: inputId, children: label }), _jsx("select", { ...props, id: inputId, "aria-describedby": describedBy, className: cx("d26-select", className), children: children }), description && _jsx("span", { id: `${inputId}-description`, className: "d26-field-description", children: description })] });
15
+ }
16
+ export function Checkbox({ label, className, id, ...props }) { const generated = useId(); return _jsxs("label", { className: cx("d26-checkbox", className), htmlFor: id ?? generated, children: [_jsx("input", { ...props, id: id ?? generated, type: "checkbox" }), _jsx("span", { children: label })] }); }
@@ -0,0 +1,8 @@
1
+ import { type DialogHTMLAttributes, type ReactNode } from "react";
2
+ export type DialogProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "title" | "onClose"> & {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ title: ReactNode;
6
+ closeLabel?: string;
7
+ };
8
+ export declare function Dialog({ open, onOpenChange, title, closeLabel, className, children, ...props }: DialogProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef } from "react";
4
+ import { cx, IconButton } from "./primitives.js";
5
+ export function Dialog({ open, onOpenChange, title, closeLabel = "Close dialog", className, children, ...props }) {
6
+ const ref = useRef(null), titleId = useId();
7
+ useEffect(() => { const dialog = ref.current; if (!dialog)
8
+ return; if (open && !dialog.open)
9
+ dialog.showModal(); if (!open && dialog.open)
10
+ dialog.close(); }, [open]);
11
+ return _jsx("dialog", { ...props, ref: ref, "aria-labelledby": titleId, className: cx("d26-dialog", className), onClose: () => onOpenChange(false), onCancel: event => { props.onCancel?.(event); if (!event.defaultPrevented)
12
+ onOpenChange(false); }, children: _jsxs("div", { className: "d26-dialog-content", children: [_jsxs("div", { className: "d26-dialog-header", children: [_jsx("h2", { id: titleId, children: title }), _jsx(IconButton, { variant: "plain", "aria-label": closeLabel, onClick: () => onOpenChange(false), children: "\u00D7" })] }), children] }) });
13
+ }
@@ -0,0 +1,59 @@
1
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
2
+ export declare const cx: (...values: Array<string | undefined | false>) => string;
3
+ export type PageFrameProps = HTMLAttributes<HTMLDivElement>;
4
+ export declare function PageFrame({ className, ...props }: PageFrameProps): import("react").JSX.Element;
5
+ export type PanelProps = HTMLAttributes<HTMLDivElement> & {
6
+ variant?: "sheet" | "control" | "toolbar";
7
+ padding?: "none" | "content";
8
+ };
9
+ export declare function Panel({ variant, padding, className, ...props }: PanelProps): import("react").JSX.Element;
10
+ export type EyebrowProps = HTMLAttributes<HTMLDivElement> & {
11
+ dot?: boolean;
12
+ compact?: boolean;
13
+ };
14
+ export declare function Eyebrow({ dot, compact, className, children, ...props }: EyebrowProps): import("react").JSX.Element;
15
+ export type SectionHeadingProps = HTMLAttributes<HTMLHeadingElement> & {
16
+ as?: "h1" | "h2" | "h3" | "h4";
17
+ size?: "display" | "chapter" | "inspector" | "figure";
18
+ };
19
+ export declare function SectionHeading({ as: Tag, size, className, ...props }: SectionHeadingProps): import("react").JSX.Element;
20
+ export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
21
+ variant?: "primary" | "text" | "playback";
22
+ endIcon?: ReactNode;
23
+ busy?: boolean;
24
+ };
25
+ export declare function Button({ variant, type, busy, endIcon, className, children, disabled, ...props }: ButtonProps): import("react").JSX.Element;
26
+ export type TextLinkProps = AnchorHTMLAttributes<HTMLAnchorElement>;
27
+ export declare function TextLink({ className, ...props }: TextLinkProps): import("react").JSX.Element;
28
+ export type IconButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "aria-label"> & {
29
+ "aria-label": string;
30
+ variant?: "circle" | "step" | "plain";
31
+ };
32
+ export declare function IconButton({ variant, type, className, children, ...props }: IconButtonProps): import("react").JSX.Element;
33
+ export type NavigationProps = HTMLAttributes<HTMLElement> & {
34
+ brand: ReactNode;
35
+ description?: ReactNode;
36
+ actions?: ReactNode;
37
+ };
38
+ export declare function Navigation({ brand, description, actions, className, ...props }: NavigationProps): import("react").JSX.Element;
39
+ export type StepNavigationProps = Omit<HTMLAttributes<HTMLElement>, "onChange"> & {
40
+ items: {
41
+ id: string;
42
+ label: string;
43
+ disabled?: boolean;
44
+ }[];
45
+ value: string;
46
+ onValueChange: (value: string) => void;
47
+ "aria-label": string;
48
+ };
49
+ export declare function StepNavigation({ items, value, onValueChange, className, ...props }: StepNavigationProps): import("react").JSX.Element;
50
+ export type DisclosureProps = Omit<HTMLAttributes<HTMLDetailsElement>, "title"> & {
51
+ title: ReactNode;
52
+ open?: boolean;
53
+ name?: string;
54
+ };
55
+ export declare function Disclosure({ title, className, children, ...props }: DisclosureProps): import("react").JSX.Element;
56
+ export type StatusNoteProps = HTMLAttributes<HTMLParagraphElement> & {
57
+ tone?: "info" | "positive" | "warning" | "error";
58
+ };
59
+ export declare function StatusNote({ tone, className, ...props }: StatusNoteProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const cx = (...values) => values.filter(Boolean).join(" ");
3
+ export function PageFrame({ className, ...props }) { return _jsx("div", { ...props, className: cx("d26-theme d26-frame", className) }); }
4
+ export function Panel({ variant = "sheet", padding = "content", className, ...props }) { return _jsx("div", { ...props, className: cx("d26-panel", `d26-panel--${variant}`, padding === "content" && "d26-panel--padded", className) }); }
5
+ export function Eyebrow({ dot = false, compact = false, className, children, ...props }) { return _jsxs("div", { ...props, className: cx("d26-eyebrow", compact && "d26-eyebrow--compact", className), children: [dot && _jsx("span", { className: "d26-status-dot", "aria-hidden": "true" }), children] }); }
6
+ export function SectionHeading({ as: Tag = "h2", size = "chapter", className, ...props }) { return _jsx(Tag, { ...props, className: cx("d26-heading", `d26-heading--${size}`, className) }); }
7
+ export function Button({ variant = "primary", type = "button", busy, endIcon, className, children, disabled, ...props }) { return _jsxs("button", { ...props, type: type, disabled: disabled || busy, "aria-busy": busy || undefined, className: cx("d26-button", `d26-button--${variant}`, className), children: [children, endIcon && _jsx("span", { className: "d26-button-icon", "aria-hidden": "true", children: endIcon })] }); }
8
+ export function TextLink({ className, ...props }) { return _jsx("a", { ...props, className: cx("d26-text-link", className) }); }
9
+ export function IconButton({ variant = "circle", type = "button", className, children, ...props }) { return _jsx("button", { ...props, type: type, className: cx("d26-icon-button", `d26-icon-button--${variant}`, className), children: _jsx("span", { "aria-hidden": "true", children: children }) }); }
10
+ export function Navigation({ brand, description, actions, className, ...props }) { return _jsxs("header", { ...props, className: cx("d26-navigation", className), children: [_jsx("div", { className: "d26-brand", children: brand }), description && _jsx("div", { className: "d26-navigation-description", children: description }), actions && _jsx("nav", { "aria-label": "Main", className: "d26-navigation-actions", children: actions })] }); }
11
+ export function StepNavigation({ items, value, onValueChange, className, ...props }) { return _jsx("nav", { ...props, className: cx("d26-steps", className), children: items.map((item, i) => _jsxs("button", { type: "button", disabled: item.disabled, "aria-current": value === item.id ? "step" : undefined, onClick: () => onValueChange(item.id), children: [_jsx("span", { className: "d26-step-number", children: String(i + 1).padStart(2, "0") }), _jsx("span", { children: item.label }), _jsx("span", { className: "d26-step-dot", "aria-hidden": "true" })] }, item.id)) }); }
12
+ export function Disclosure({ title, className, children, ...props }) { return _jsxs("details", { ...props, className: cx("d26-disclosure", className), children: [_jsxs("summary", { children: [title, _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), _jsx("div", { children: children })] }); }
13
+ export function StatusNote({ tone = "info", className, ...props }) { return _jsx("p", { ...props, className: cx("d26-note", `d26-note--${tone}`, className) }); }
@@ -0,0 +1,15 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export type TabItem = {
3
+ value: string;
4
+ label: string;
5
+ content: ReactNode;
6
+ disabled?: boolean;
7
+ };
8
+ export type TabsProps = Omit<HTMLAttributes<HTMLDivElement>, "onChange"> & {
9
+ items: TabItem[];
10
+ value?: string;
11
+ defaultValue?: string;
12
+ onValueChange?: (value: string) => void;
13
+ "aria-label": string;
14
+ };
15
+ export declare function Tabs({ items, value, defaultValue, onValueChange, className, "aria-label": label, ...props }: TabsProps): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useRef, useState } from "react";
4
+ import { cx } from "./primitives.js";
5
+ export function Tabs({ items, value, defaultValue, onValueChange, className, "aria-label": label, ...props }) {
6
+ const [internal, setInternal] = useState(defaultValue ?? items.find(item => !item.disabled)?.value);
7
+ const active = value ?? internal;
8
+ const selected = items.findIndex(item => item.value === active && !item.disabled);
9
+ const current = selected >= 0 ? selected : items.findIndex(item => !item.disabled);
10
+ const uid = useId(), refs = useRef([]);
11
+ function choose(index) { const item = items[index]; if (!item || item.disabled)
12
+ return; setInternal(item.value); onValueChange?.(item.value); }
13
+ return _jsxs("div", { ...props, className: cx("d26-tabs", className), children: [_jsx("div", { role: "tablist", "aria-label": label, className: "d26-tab-list", children: items.map((item, i) => _jsxs("button", { type: "button", ref: element => { refs.current[i] = element; }, role: "tab", disabled: item.disabled, tabIndex: i === current ? 0 : -1, "aria-selected": i === current, "aria-controls": `${uid}-panel-${i}`, id: `${uid}-tab-${i}`, onClick: () => choose(i), onKeyDown: event => {
14
+ const enabled = items.map((entry, index) => entry.disabled ? -1 : index).filter(index => index >= 0);
15
+ const position = enabled.indexOf(i);
16
+ let next;
17
+ if (event.key === "ArrowRight")
18
+ next = enabled[(position + 1) % enabled.length];
19
+ if (event.key === "ArrowLeft")
20
+ next = enabled[(position - 1 + enabled.length) % enabled.length];
21
+ if (event.key === "Home")
22
+ next = enabled[0];
23
+ if (event.key === "End")
24
+ next = enabled[enabled.length - 1];
25
+ if (next === undefined)
26
+ return;
27
+ event.preventDefault();
28
+ event.stopPropagation();
29
+ choose(next);
30
+ refs.current[next]?.focus();
31
+ }, children: [_jsx("span", { children: String(i + 1).padStart(2, "0") }), item.label] }, item.value)) }), items.map((item, i) => _jsx("div", { role: "tabpanel", hidden: i !== current, tabIndex: 0, id: `${uid}-panel-${i}`, "aria-labelledby": `${uid}-tab-${i}`, className: "d26-tab-panel", children: item.content }, item.value))] });
32
+ }
@@ -0,0 +1,7 @@
1
+ import { type HTMLAttributes } from "react";
2
+ export type TechnicalGridProps = HTMLAttributes<HTMLDivElement> & {
3
+ active?: boolean;
4
+ interactive?: boolean;
5
+ };
6
+ /** Decorative, self-contained grid. Place inside a positioned figure/panel. */
7
+ export declare function TechnicalGrid({ active, interactive, className, ...props }: TechnicalGridProps): import("react").JSX.Element;
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ import { cx } from "./primitives.js";
5
+ /** Decorative, self-contained grid. Place inside a positioned figure/panel. */
6
+ export function TechnicalGrid({ active = true, interactive = true, className, ...props }) {
7
+ const ref = useRef(null);
8
+ useEffect(() => {
9
+ const canvas = ref.current, host = canvas?.parentElement?.parentElement;
10
+ const context = canvas?.getContext("2d");
11
+ if (!canvas || !host || !context)
12
+ return;
13
+ const motion = matchMedia("(prefers-reduced-motion: reduce)"), fine = matchMedia("(any-hover: hover) and (any-pointer: fine)");
14
+ const pointer = { x: 0, y: 0, targetX: 0, targetY: 0, strength: 0, targetStrength: 0 };
15
+ let width = 0, height = 0, frame = 0, last = 0, drift = 0, visible = true;
16
+ const spacing = Number.parseFloat(getComputedStyle(canvas).getPropertyValue("--size-grid-spacing")) || 38;
17
+ const stroke = getComputedStyle(canvas).getPropertyValue("--color-grid").trim() || "rgba(150,174,207,.32)";
18
+ function draw() {
19
+ if (!context)
20
+ return;
21
+ context.clearRect(0, 0, width, height);
22
+ context.strokeStyle = stroke;
23
+ context.lineWidth = .8;
24
+ context.beginPath();
25
+ function point(x, y, first) {
26
+ const dx = x - pointer.x, dy = y - pointer.y;
27
+ const force = Math.exp(-(dx * dx + dy * dy) / (2 * 110 * 110)) * .3 * pointer.strength;
28
+ if (first)
29
+ context.moveTo(x + dx * force, y + dy * force);
30
+ else
31
+ context.lineTo(x + dx * force, y + dy * force);
32
+ }
33
+ for (let x = drift * 2 % spacing - spacing; x <= width + spacing; x += spacing)
34
+ for (let y = -spacing; y <= height + spacing; y += 12)
35
+ point(x, y, y === -spacing);
36
+ for (let y = drift * 1.2 % spacing - spacing; y <= height + spacing; y += spacing)
37
+ for (let x = -spacing; x <= width + spacing; x += 12)
38
+ point(x, y, x === -spacing);
39
+ context.stroke();
40
+ }
41
+ function tick(time) {
42
+ frame = requestAnimationFrame(tick);
43
+ if (time - last < 1000 / 30)
44
+ return;
45
+ const delta = last ? Math.min((time - last) / 1000, .1) : 0;
46
+ last = time;
47
+ drift += delta;
48
+ const easing = 1 - Math.exp(-delta * 9);
49
+ pointer.x += (pointer.targetX - pointer.x) * easing;
50
+ pointer.y += (pointer.targetY - pointer.y) * easing;
51
+ pointer.strength += (pointer.targetStrength - pointer.strength) * easing;
52
+ draw();
53
+ }
54
+ function sync() { cancelAnimationFrame(frame); last = 0; pointer.targetStrength = 0; pointer.strength = 0; draw(); if (active && !motion.matches && !document.hidden && visible)
55
+ frame = requestAnimationFrame(tick); }
56
+ function resize() { if (!canvas || !context)
57
+ return; const rect = canvas.getBoundingClientRect(), scale = Math.min(devicePixelRatio || 1, 2); width = rect.width; height = rect.height; canvas.width = Math.round(width * scale); canvas.height = Math.round(height * scale); context.setTransform(scale, 0, 0, scale, 0, 0); draw(); canvas.dataset.ready = "true"; }
58
+ function move(event) { if (!canvas || !active || !interactive || motion.matches || !fine.matches || event.pointerType === "touch")
59
+ return; const rect = canvas.getBoundingClientRect(); pointer.targetX = event.clientX - rect.left; pointer.targetY = event.clientY - rect.top; if (pointer.targetStrength === 0 && pointer.strength < .01) {
60
+ pointer.x = pointer.targetX;
61
+ pointer.y = pointer.targetY;
62
+ } pointer.targetStrength = 1; }
63
+ function leave() { pointer.targetStrength = 0; }
64
+ const observer = new ResizeObserver(resize), visibility = new IntersectionObserver(([entry]) => { visible = entry.isIntersecting; sync(); });
65
+ observer.observe(canvas);
66
+ visibility.observe(host);
67
+ host.addEventListener("pointermove", move, { passive: true });
68
+ host.addEventListener("pointerleave", leave);
69
+ host.addEventListener("pointercancel", leave);
70
+ window.addEventListener("blur", leave);
71
+ document.addEventListener("visibilitychange", sync);
72
+ motion.addEventListener("change", sync);
73
+ fine.addEventListener("change", leave);
74
+ resize();
75
+ sync();
76
+ return () => { cancelAnimationFrame(frame); observer.disconnect(); visibility.disconnect(); host.removeEventListener("pointermove", move); host.removeEventListener("pointerleave", leave); host.removeEventListener("pointercancel", leave); window.removeEventListener("blur", leave); document.removeEventListener("visibilitychange", sync); motion.removeEventListener("change", sync); fine.removeEventListener("change", leave); };
77
+ }, [active, interactive]);
78
+ return _jsx("div", { ...props, className: cx("d26-technical-grid", className), "aria-hidden": "true", children: _jsx("canvas", { ref: ref }) });
79
+ }