@hlang-tech/editor-ui 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,146 @@
1
+ /**
2
+ * AssetNode — a framed photograph as a node. The image is the hero; a thin
3
+ * frosted-glass frame + silver edge holds it, a gradient scrim carries the
4
+ * caption. Output ports ride the right edge, revealed on hover/selection so the
5
+ * resting card stays clean (nover-style: the picture, not the plumbing).
6
+ */
7
+
8
+ .hl-asset {
9
+ position: relative;
10
+ width: 232px;
11
+ border-radius: var(--hl-r-md);
12
+ padding: 5px;
13
+ background: var(--hl-glass);
14
+ backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
15
+ -webkit-backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
16
+ box-shadow: var(--hl-glass-edge), var(--hl-shadow-node);
17
+ transition: box-shadow 0.2s var(--hl-ease), transform 0.14s var(--hl-ease);
18
+ }
19
+ .hl-asset:hover {
20
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node);
21
+ transform: translateY(-1px);
22
+ }
23
+ .hl-asset.is-selected {
24
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node), var(--hl-glow-silver);
25
+ }
26
+ .hl-asset.is-active {
27
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
28
+ 0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
29
+ 0 0 28px -6px var(--hl-spark);
30
+ animation: hl-asset-pulse 0.7s var(--hl-ease);
31
+ }
32
+ @keyframes hl-asset-pulse {
33
+ 0% {
34
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
35
+ 0 0 0 2px var(--hl-spark), 0 0 36px -4px var(--hl-spark);
36
+ }
37
+ }
38
+ .hl-asset.is-live-error {
39
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
40
+ 0 0 0 1px var(--hl-error), 0 0 22px -4px color-mix(in oklab, var(--hl-error) 75%, transparent);
41
+ }
42
+
43
+ .hl-asset-frame {
44
+ position: relative;
45
+ border-radius: calc(var(--hl-r-md) - 4px);
46
+ overflow: hidden;
47
+ aspect-ratio: 4 / 3;
48
+ background: var(--hl-void-2);
49
+ }
50
+ .hl-asset-img {
51
+ width: 100%;
52
+ height: 100%;
53
+ object-fit: cover;
54
+ display: block;
55
+ }
56
+ .hl-asset-placeholder {
57
+ width: 100%;
58
+ height: 100%;
59
+ background: linear-gradient(135deg, var(--hl-surface-2), var(--hl-void-2));
60
+ }
61
+
62
+ /* Bottom scrim so the caption is legible over any image. */
63
+ .hl-asset-scrim {
64
+ position: absolute;
65
+ inset: auto 0 0 0;
66
+ height: 56%;
67
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.72), transparent);
68
+ pointer-events: none;
69
+ }
70
+ .hl-asset-caption {
71
+ position: absolute;
72
+ inset: auto 10px 8px 10px;
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 8px;
76
+ }
77
+ .hl-asset-name {
78
+ font-size: 12.5px;
79
+ font-weight: 600;
80
+ color: #fff;
81
+ letter-spacing: -0.01em;
82
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
83
+ flex: 1;
84
+ min-width: 0;
85
+ overflow: hidden;
86
+ text-overflow: ellipsis;
87
+ white-space: nowrap;
88
+ }
89
+ /* Semantic role tag (Character / Product / Background…) — a frosted pill. */
90
+ .hl-asset-role {
91
+ flex: none;
92
+ font-size: 9.5px;
93
+ color: var(--hl-ink);
94
+ background: rgba(255, 255, 255, 0.14);
95
+ backdrop-filter: blur(6px);
96
+ border: 1px solid rgba(255, 255, 255, 0.2);
97
+ border-radius: var(--hl-r-pill);
98
+ padding: 1px 8px;
99
+ }
100
+
101
+ /* Output ports centered on the right edge, revealed on hover/select. Each row
102
+ is a fixed box so its absolutely-positioned Handle anchors cleanly to the
103
+ card's right edge (not bunched near the middle). */
104
+ .hl-asset-ports {
105
+ list-style: none;
106
+ margin: 0;
107
+ padding: 0;
108
+ position: absolute;
109
+ top: 0;
110
+ right: 0;
111
+ bottom: 0;
112
+ display: flex;
113
+ flex-direction: column;
114
+ justify-content: center;
115
+ gap: 14px;
116
+ opacity: 0;
117
+ transition: opacity 0.16s var(--hl-ease);
118
+ }
119
+ .hl-asset:hover .hl-asset-ports,
120
+ .hl-asset.is-selected .hl-asset-ports {
121
+ opacity: 1;
122
+ }
123
+ .hl-asset-ports .hl-port {
124
+ position: relative;
125
+ height: 14px;
126
+ width: 14px;
127
+ }
128
+ .hl-asset-ports .hl-port-label {
129
+ display: none;
130
+ }
131
+
132
+ /* Clean SILVER filled port on the photo edge — not the dark hollow node dot. */
133
+ .hl-asset-ports .hl-handle.hl-handle {
134
+ width: 11px;
135
+ height: 11px;
136
+ border-radius: 50%;
137
+ background: var(--hl-silver);
138
+ border: 2px solid var(--hl-void);
139
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.18),
140
+ 0 0 10px -2px color-mix(in oklab, var(--hl-silver) 70%, transparent);
141
+ transition: transform 0.12s var(--hl-ease), background 0.12s var(--hl-ease);
142
+ }
143
+ .hl-asset-ports .hl-handle.hl-handle:hover {
144
+ background: var(--hl-silver-hi);
145
+ transform: scale(1.2);
146
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * hlang node styling. Ports are anchored per-row: each row is `position:
3
+ * relative`, so the React Flow Handle (absolute, Left/Right) centers on its own
4
+ * row instead of the whole node — keeping every port, including @node/error,
5
+ * aligned to its label. No overflow:hidden on the node, or edge-anchored ports
6
+ * would be clipped; the signal bar rounds its own top corners.
7
+ */
8
+
9
+ .hl-node {
10
+ --hl-kind: var(--hl-silver);
11
+ position: relative;
12
+ width: 208px;
13
+ border-radius: var(--hl-r-md);
14
+ /* Frosted glass card: translucent graphite + blur + a lit inner edge. */
15
+ background: var(--hl-glass);
16
+ backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
17
+ -webkit-backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
18
+ box-shadow: var(--hl-glass-edge), var(--hl-shadow-node);
19
+ color: var(--hl-ink);
20
+ font-family: var(--hl-font-ui);
21
+ /* Live dimension: idle nodes fade slightly. Default --hl-activity=1 (lit). */
22
+ opacity: calc(0.6 + 0.4 * var(--hl-activity));
23
+ transition: box-shadow 0.2s var(--hl-ease), opacity 0.3s var(--hl-ease),
24
+ transform 0.14s var(--hl-ease);
25
+ }
26
+
27
+ .hl-r { --hl-kind: var(--hl-r); }
28
+ .hl-t { --hl-kind: var(--hl-t); }
29
+ .hl-w { --hl-kind: var(--hl-w); }
30
+
31
+ .hl-node:hover {
32
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node);
33
+ transform: translateY(-1px);
34
+ }
35
+
36
+ /* Selection is SILVER (the primary accent), not the kind hue. */
37
+ .hl-node.is-selected {
38
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node), var(--hl-glow-silver);
39
+ }
40
+
41
+ .hl-node.is-error {
42
+ border-color: var(--hl-error);
43
+ box-shadow: var(--hl-shadow-node), 0 0 0 1px var(--hl-error),
44
+ 0 0 18px -4px color-mix(in srgb, var(--hl-error) 70%, transparent);
45
+ }
46
+
47
+ /* --- Live execution state (driven imperatively by useLiveSignals) --- */
48
+ /* A node lights with the cold spark when it emits during a run. */
49
+ .hl-node.is-active {
50
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
51
+ 0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
52
+ 0 0 28px -6px var(--hl-spark);
53
+ animation: hl-node-pulse 0.7s var(--hl-ease);
54
+ }
55
+ @keyframes hl-node-pulse {
56
+ 0% {
57
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
58
+ 0 0 0 2px var(--hl-spark), 0 0 36px -4px var(--hl-spark);
59
+ }
60
+ }
61
+ /* A node that threw during the run goes coral. */
62
+ .hl-node.is-live-error {
63
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
64
+ 0 0 0 1px var(--hl-error),
65
+ 0 0 22px -4px color-mix(in oklab, var(--hl-error) 75%, transparent);
66
+ }
67
+
68
+ /* Thin top signal bar — the node's kind, expressed with restraint */
69
+ .hl-signal {
70
+ height: 2px;
71
+ border-top-left-radius: var(--hl-r-md);
72
+ border-top-right-radius: var(--hl-r-md);
73
+ background: linear-gradient(
74
+ 90deg,
75
+ transparent,
76
+ var(--hl-kind) 30%,
77
+ var(--hl-kind) 70%,
78
+ transparent
79
+ );
80
+ opacity: 0.7;
81
+ }
82
+
83
+ .hl-head {
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 7px;
87
+ padding: 8px 11px 6px;
88
+ }
89
+
90
+ .hl-kind {
91
+ font-size: 9px;
92
+ font-weight: 600;
93
+ color: var(--hl-void);
94
+ background: var(--hl-kind);
95
+ border-radius: 3px;
96
+ padding: 1px 4px;
97
+ line-height: 1.4;
98
+ }
99
+
100
+ .hl-name {
101
+ font-size: 12px;
102
+ font-weight: 550;
103
+ letter-spacing: -0.01em;
104
+ color: var(--hl-ink);
105
+ flex: 1;
106
+ min-width: 0;
107
+ overflow: hidden;
108
+ text-overflow: ellipsis;
109
+ white-space: nowrap;
110
+ }
111
+
112
+ .hl-role {
113
+ font-size: 9px;
114
+ color: var(--hl-ink-3);
115
+ }
116
+
117
+ /* Optional custom body content (extension point). Empty by default. */
118
+ .hl-body:empty {
119
+ display: none;
120
+ }
121
+ .hl-body {
122
+ padding: 0 11px 4px;
123
+ }
124
+
125
+ .hl-ports {
126
+ display: grid;
127
+ grid-template-columns: 1fr 1fr;
128
+ gap: 1px 8px;
129
+ padding: 3px 0 9px;
130
+ }
131
+
132
+ .hl-col {
133
+ list-style: none;
134
+ margin: 0;
135
+ padding: 0;
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 1px;
139
+ }
140
+
141
+ .hl-port {
142
+ position: relative; /* anchor for the absolutely-positioned Handle */
143
+ display: flex;
144
+ align-items: center;
145
+ height: 20px;
146
+ font-size: 10.5px;
147
+ color: var(--hl-ink-2);
148
+ }
149
+
150
+ .hl-in .hl-port { justify-content: flex-start; padding-left: 11px; }
151
+ .hl-out .hl-port { justify-content: flex-end; padding-right: 11px; }
152
+
153
+ .hl-port-label {
154
+ font-family: var(--hl-font-mono);
155
+ font-variant-numeric: tabular-nums;
156
+ white-space: nowrap;
157
+ transition: color 0.15s var(--hl-ease);
158
+ }
159
+ .hl-port:hover .hl-port-label { color: var(--hl-ink); }
160
+ .hl-port.is-error .hl-port-label {
161
+ color: color-mix(in srgb, var(--hl-error) 80%, var(--hl-ink-2));
162
+ }
163
+
164
+ /* Ports — small glowing nodes, kind-tinted, red for error. `value` ports are
165
+ hollow (change-only); `live` ports are filled (hot stream). */
166
+ .hl-handle.hl-handle {
167
+ width: 8px;
168
+ height: 8px;
169
+ border-radius: 50%;
170
+ background: var(--hl-void-2);
171
+ border: 1.5px solid var(--hl-kind);
172
+ box-shadow: 0 0 0 3px rgba(10, 11, 15, 0.9);
173
+ transition: box-shadow 0.15s var(--hl-ease), background 0.15s var(--hl-ease),
174
+ transform 0.12s var(--hl-ease);
175
+ }
176
+ .hl-handle.is-live {
177
+ background: color-mix(in srgb, var(--hl-kind) 60%, var(--hl-void-2));
178
+ }
179
+ .hl-port.is-error .hl-handle.hl-handle {
180
+ border-color: var(--hl-error);
181
+ }
182
+ .hl-node:hover .hl-handle.hl-handle {
183
+ box-shadow: 0 0 0 3px rgba(10, 11, 15, 0.9),
184
+ 0 0 10px -1px color-mix(in srgb, var(--hl-kind) 80%, transparent);
185
+ }
186
+ .hl-handle.hl-handle:hover {
187
+ background: var(--hl-kind);
188
+ transform: scale(1.25);
189
+ }
190
+ .hl-port.is-error .hl-handle.hl-handle:hover {
191
+ background: var(--hl-error);
192
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * PillNode — a nover-style capsule. A frosted, fully-rounded pill holding a
3
+ * kind-tinted domain dot, the name, and a status dot. Ports ride the left/right
4
+ * edges as small silver dots, hidden at rest and revealed on hover/selection so
5
+ * the resting canvas reads as intents, not wiring.
6
+ */
7
+
8
+ .hl-pill {
9
+ --hl-kind: var(--hl-silver);
10
+ position: relative;
11
+ display: inline-flex;
12
+ align-items: center;
13
+ gap: 9px;
14
+ min-width: 120px;
15
+ max-width: 240px;
16
+ padding: 8px 16px;
17
+ border-radius: var(--hl-r-pill);
18
+ background: var(--hl-glass);
19
+ backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
20
+ -webkit-backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
21
+ box-shadow: var(--hl-glass-edge), var(--hl-shadow-node);
22
+ color: var(--hl-ink);
23
+ font-family: var(--hl-font-ui);
24
+ opacity: calc(0.6 + 0.4 * var(--hl-activity));
25
+ transition: box-shadow 0.2s var(--hl-ease), transform 0.14s var(--hl-ease),
26
+ opacity 0.3s var(--hl-ease);
27
+ }
28
+ .hl-pill-r { --hl-kind: var(--hl-r); }
29
+ .hl-pill-t { --hl-kind: var(--hl-t); }
30
+ .hl-pill-w { --hl-kind: var(--hl-w); }
31
+
32
+ .hl-pill:hover {
33
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node);
34
+ transform: translateY(-1px);
35
+ }
36
+ .hl-pill.is-selected {
37
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node), var(--hl-glow-silver);
38
+ }
39
+ .hl-pill.is-active {
40
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
41
+ 0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
42
+ 0 0 26px -6px var(--hl-spark);
43
+ animation: hl-pill-pulse 0.7s var(--hl-ease);
44
+ }
45
+ @keyframes hl-pill-pulse {
46
+ 0% {
47
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
48
+ 0 0 0 2px var(--hl-spark), 0 0 34px -4px var(--hl-spark);
49
+ }
50
+ }
51
+ .hl-pill.is-live-error {
52
+ box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
53
+ 0 0 0 1px var(--hl-error), 0 0 22px -4px color-mix(in oklab, var(--hl-error) 75%, transparent);
54
+ }
55
+
56
+ /* Kind-tinted domain bead. */
57
+ .hl-pill-dot {
58
+ flex: none;
59
+ width: 20px;
60
+ height: 20px;
61
+ display: grid;
62
+ place-items: center;
63
+ border-radius: 50%;
64
+ background: radial-gradient(circle at 32% 28%,
65
+ color-mix(in oklab, var(--hl-kind) 85%, white),
66
+ var(--hl-kind) 75%);
67
+ color: var(--hl-void);
68
+ font-size: 10px;
69
+ font-weight: 700;
70
+ box-shadow: 0 0 10px -3px var(--hl-kind), var(--hl-glass-edge-hi);
71
+ }
72
+
73
+ .hl-pill-name {
74
+ flex: 1;
75
+ min-width: 0;
76
+ font-size: 12.5px;
77
+ font-weight: 550;
78
+ letter-spacing: -0.01em;
79
+ color: var(--hl-ink);
80
+ overflow: hidden;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ }
84
+
85
+ /* Inline rename field — sits in place of the name, styled to blend into the pill. */
86
+ .hl-pill-rename {
87
+ flex: 1;
88
+ min-width: 0;
89
+ font-family: var(--hl-font-ui);
90
+ font-size: 12.5px;
91
+ font-weight: 550;
92
+ letter-spacing: -0.01em;
93
+ color: var(--hl-ink);
94
+ background: rgba(255, 255, 255, 0.06);
95
+ border: 1px solid color-mix(in oklab, var(--hl-silver) 40%, transparent);
96
+ border-radius: 6px;
97
+ padding: 1px 6px;
98
+ outline: none;
99
+ }
100
+
101
+ /* Edit-time diagnostic badge on the pill — distinct from the runtime error glow. */
102
+ .hl-pill-diag {
103
+ flex: none;
104
+ display: grid;
105
+ place-items: center;
106
+ width: 16px;
107
+ height: 16px;
108
+ }
109
+ .hl-pill-diag.hl-diag-warning {
110
+ color: var(--hl-warning, #e0b877);
111
+ }
112
+ .hl-pill-diag.hl-diag-error {
113
+ color: var(--hl-error);
114
+ }
115
+ /* A soft edit-time ring on the pill (calmer than the run-time spark/error). */
116
+ .hl-pill.has-diag-warning {
117
+ box-shadow: var(--hl-glass-edge), var(--hl-shadow-node),
118
+ inset 0 0 0 1px color-mix(in oklab, var(--hl-warning, #e0b877) 45%, transparent);
119
+ }
120
+ .hl-pill.has-diag-error {
121
+ box-shadow: var(--hl-glass-edge), var(--hl-shadow-node),
122
+ inset 0 0 0 1px color-mix(in oklab, var(--hl-error) 55%, transparent);
123
+ }
124
+
125
+ /* Status dot — dim at rest, lights (spark) while active. */
126
+ .hl-pill-status {
127
+ flex: none;
128
+ width: 6px;
129
+ height: 6px;
130
+ border-radius: 50%;
131
+ background: var(--hl-ink-3);
132
+ transition: background 0.2s var(--hl-ease), box-shadow 0.2s var(--hl-ease);
133
+ }
134
+ .hl-pill.is-active .hl-pill-status {
135
+ background: var(--hl-spark);
136
+ box-shadow: 0 0 8px -1px var(--hl-spark);
137
+ }
138
+ .hl-pill.is-live-error .hl-pill-status {
139
+ background: var(--hl-error);
140
+ }
141
+
142
+ /* Ports on the capsule edges — hidden at rest, revealed on hover/select. */
143
+ .hl-pill-ports {
144
+ list-style: none;
145
+ margin: 0;
146
+ padding: 0;
147
+ position: absolute;
148
+ top: 0;
149
+ bottom: 0;
150
+ display: flex;
151
+ flex-direction: column;
152
+ justify-content: center;
153
+ gap: 12px;
154
+ opacity: 0;
155
+ transition: opacity 0.16s var(--hl-ease);
156
+ }
157
+ .hl-pill-ports.hl-in { left: 0; }
158
+ .hl-pill-ports.hl-out { right: 0; }
159
+ .hl-pill:hover .hl-pill-ports,
160
+ .hl-pill.is-selected .hl-pill-ports {
161
+ opacity: 1;
162
+ }
163
+ .hl-pill-ports .hl-port {
164
+ position: relative;
165
+ height: 12px;
166
+ width: 12px;
167
+ }
168
+ .hl-pill-ports .hl-port-label {
169
+ display: none;
170
+ }
171
+ /* Clean silver edge port (not the dark hollow node dot). */
172
+ .hl-pill-ports .hl-handle.hl-handle {
173
+ width: 10px;
174
+ height: 10px;
175
+ border-radius: 50%;
176
+ background: var(--hl-silver);
177
+ border: 2px solid var(--hl-void);
178
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.18),
179
+ 0 0 9px -2px color-mix(in oklab, var(--hl-silver) 70%, transparent);
180
+ transition: transform 0.12s var(--hl-ease), background 0.12s var(--hl-ease);
181
+ }
182
+ .hl-pill-ports .hl-handle.hl-handle:hover {
183
+ background: var(--hl-silver-hi);
184
+ transform: scale(1.2);
185
+ }
@@ -0,0 +1,107 @@
1
+ /** Cheatsheet — a centered black/silver glass reference card. */
2
+
3
+ .hl-cheat-scrim {
4
+ position: fixed;
5
+ inset: 0;
6
+ z-index: 60;
7
+ background: rgba(6, 7, 9, 0.5);
8
+ backdrop-filter: blur(2px);
9
+ -webkit-backdrop-filter: blur(2px);
10
+ }
11
+
12
+ .hl-cheat {
13
+ position: fixed;
14
+ z-index: 61;
15
+ top: 50%;
16
+ left: 50%;
17
+ transform: translate(-50%, -50%);
18
+ width: min(680px, calc(100vw - 48px));
19
+ max-height: calc(100vh - 96px);
20
+ overflow: auto;
21
+ padding: 20px 22px 24px;
22
+ background: var(--hl-glass-2);
23
+ backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
24
+ -webkit-backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
25
+ border: 1px solid var(--hl-line-strong);
26
+ border-radius: var(--hl-r-lg);
27
+ box-shadow: var(--hl-glass-edge), 0 32px 64px -28px rgba(0, 0, 0, 0.9);
28
+ animation: hl-cheat-in 0.16s var(--hl-ease);
29
+ }
30
+ @keyframes hl-cheat-in {
31
+ from {
32
+ opacity: 0;
33
+ transform: translate(-50%, calc(-50% + 8px)) scale(0.98);
34
+ }
35
+ }
36
+
37
+ .hl-cheat-head {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+ margin-bottom: 16px;
42
+ }
43
+ .hl-cheat-title {
44
+ font-family: var(--hl-font-ui);
45
+ font-size: 15px;
46
+ font-weight: 600;
47
+ color: var(--hl-ink);
48
+ letter-spacing: 0.01em;
49
+ }
50
+ .hl-cheat-close {
51
+ width: 26px;
52
+ height: 26px;
53
+ display: grid;
54
+ place-items: center;
55
+ border: none;
56
+ background: transparent;
57
+ color: var(--hl-ink-3);
58
+ border-radius: 6px;
59
+ cursor: pointer;
60
+ font-size: 13px;
61
+ }
62
+ .hl-cheat-close:hover {
63
+ background: rgba(255, 255, 255, 0.08);
64
+ color: var(--hl-ink);
65
+ }
66
+
67
+ .hl-cheat-grid {
68
+ display: grid;
69
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
70
+ gap: 18px 28px;
71
+ }
72
+ .hl-cheat-group-title {
73
+ font-size: 10px;
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.16em;
76
+ color: var(--hl-silver-lo);
77
+ margin: 0 0 8px;
78
+ }
79
+ .hl-cheat-rows {
80
+ list-style: none;
81
+ margin: 0;
82
+ padding: 0;
83
+ display: flex;
84
+ flex-direction: column;
85
+ gap: 6px;
86
+ }
87
+ .hl-cheat-row {
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: space-between;
91
+ gap: 12px;
92
+ }
93
+ .hl-cheat-label {
94
+ font-family: var(--hl-font-ui);
95
+ font-size: 12.5px;
96
+ color: var(--hl-ink-2);
97
+ }
98
+ .hl-cheat-keys {
99
+ flex: none;
100
+ font-size: 11px;
101
+ color: var(--hl-ink);
102
+ background: rgba(255, 255, 255, 0.06);
103
+ border: 1px solid var(--hl-line-strong);
104
+ border-radius: 6px;
105
+ padding: 2px 7px;
106
+ white-space: nowrap;
107
+ }
@@ -0,0 +1,34 @@
1
+ /** Toast — transient glass confirmations, stacked bottom-center. */
2
+
3
+ .hl-toast-stack {
4
+ position: fixed;
5
+ z-index: 70;
6
+ left: 50%;
7
+ bottom: 28px;
8
+ transform: translateX(-50%);
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ gap: 8px;
13
+ pointer-events: none;
14
+ }
15
+
16
+ .hl-toast {
17
+ font-family: var(--hl-font-ui);
18
+ font-size: 12.5px;
19
+ color: var(--hl-ink);
20
+ background: var(--hl-glass-2);
21
+ backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
22
+ -webkit-backdrop-filter: blur(var(--hl-glass-blur)) saturate(var(--hl-glass-sat));
23
+ border: 1px solid var(--hl-line-strong);
24
+ border-radius: var(--hl-r-pill);
25
+ padding: 8px 16px;
26
+ box-shadow: var(--hl-glass-edge), 0 12px 28px -14px rgba(0, 0, 0, 0.8);
27
+ animation: hl-toast-in 0.18s var(--hl-ease);
28
+ }
29
+ @keyframes hl-toast-in {
30
+ from {
31
+ opacity: 0;
32
+ transform: translateY(8px);
33
+ }
34
+ }
@@ -0,0 +1,38 @@
1
+ /** AssetGrid — two-column thumbnail grid of renders/assets. */
2
+
3
+ .hl-assetgrid {
4
+ display: grid;
5
+ grid-template-columns: 1fr 1fr;
6
+ gap: 8px;
7
+ padding: 8px 12px 12px;
8
+ overflow-y: auto;
9
+ min-height: 0;
10
+ }
11
+
12
+ .hl-assetgrid-cell {
13
+ position: relative;
14
+ aspect-ratio: 3 / 4;
15
+ padding: 0;
16
+ border: 1px solid var(--hl-line);
17
+ border-radius: var(--hl-r-sm);
18
+ overflow: hidden;
19
+ background: var(--hl-void-2);
20
+ cursor: pointer;
21
+ transition: border-color 0.14s var(--hl-ease), transform 0.14s var(--hl-ease);
22
+ }
23
+ .hl-assetgrid-cell:hover {
24
+ border-color: color-mix(in oklab, var(--hl-silver) 45%, var(--hl-line-strong));
25
+ transform: translateY(-1px);
26
+ }
27
+ .hl-assetgrid-img {
28
+ width: 100%;
29
+ height: 100%;
30
+ object-fit: cover;
31
+ display: block;
32
+ }
33
+
34
+ .hl-assetgrid-empty {
35
+ padding: 14px 12px;
36
+ font-size: 11px;
37
+ color: var(--hl-ink-3);
38
+ }