@panphora/clayjs 1.11.3 → 1.12.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,231 @@
1
+ import { INTRO_CSS } from "./intro-style.js";
2
+
3
+ export const COPY = {
4
+ kicker: "Free app",
5
+ download: "Download free",
6
+ signupLabel: "I make one of these about every two weeks. Get the next one by email.",
7
+ emailPlaceholder: "you@example.com",
8
+ signup: "Send me the next one",
9
+ signupThanks: "Thanks. Check your email for a link to confirm.",
10
+ signupFailed: "That didn't go through. Check the address and try again.",
11
+ start: (appName) => `Start using ${appName}`,
12
+ about: "About this app",
13
+ label: (appName) => `About ${appName}`,
14
+ };
15
+
16
+ const FOCUSABLE = "a[href], button, input, select, textarea, [tabindex]:not([tabindex='-1'])";
17
+
18
+ function forced() {
19
+ try {
20
+ return new URLSearchParams(window.location.search).get("intro") === "1";
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ function stylesOnce() {
27
+ if (document.getElementById("clay-intro-style")) return;
28
+ const style = document.createElement("style");
29
+ style.id = "clay-intro-style";
30
+ style.setAttribute("clay", "editor-ui");
31
+ style.textContent = INTRO_CSS;
32
+ document.head.appendChild(style);
33
+ }
34
+
35
+ function focusApp() {
36
+ const next = [...document.querySelectorAll(FOCUSABLE)].find(
37
+ (el) => !el.disabled && !el.closest("[clay~='editor-ui']") && (typeof el.checkVisibility !== "function" || el.checkVisibility())
38
+ );
39
+ if (next) next.focus();
40
+ else document.body.focus();
41
+ }
42
+
43
+ export function mountIntro({ memory, template, appName, link, tag, source, hadDraft, prompt, snapshot }) {
44
+ if (!template) return null;
45
+ const opts = { memory, template, appName, link, tag, source, hadDraft, prompt, snapshot };
46
+ if (forced()) return mount(opts);
47
+ if (memory.get().intro.done || hadDraft) return mountAbout(opts);
48
+ if (window.location.hash) return null;
49
+ if (window.matchMedia && window.matchMedia("print").matches) return null;
50
+ return mount(opts);
51
+ }
52
+
53
+ function mountAbout(opts) {
54
+ stylesOnce();
55
+ const button = document.createElement("button");
56
+ button.type = "button";
57
+ button.className = "clay-about";
58
+ button.setAttribute("clay", "editor-ui");
59
+ button.setAttribute("sap-ignore", "");
60
+ button.textContent = COPY.about;
61
+ button.addEventListener("click", () => {
62
+ button.remove();
63
+ mount({ ...opts, fromAbout: true });
64
+ });
65
+ document.body.appendChild(button);
66
+ return button;
67
+ }
68
+
69
+ function mount(opts) {
70
+ const { memory, template, appName, link, tag, source, prompt, snapshot, fromAbout } = opts;
71
+ stylesOnce();
72
+
73
+ const section = document.createElement("section");
74
+ section.className = "clay-intro";
75
+ section.setAttribute("clay", "editor-ui");
76
+ section.setAttribute("sap-ignore", "");
77
+ section.setAttribute("aria-label", COPY.label(appName));
78
+
79
+ const inner = document.createElement("div");
80
+ inner.className = "clay-intro__inner";
81
+
82
+ const kicker = document.createElement("p");
83
+ kicker.className = "clay-intro__kicker";
84
+ kicker.textContent = COPY.kicker;
85
+
86
+ const name = document.createElement("h1");
87
+ name.className = "clay-intro__name";
88
+ name.textContent = appName;
89
+
90
+ const actions = document.createElement("div");
91
+ actions.className = "clay-intro__actions";
92
+
93
+ const download = document.createElement("a");
94
+ download.className = "clay-intro__download";
95
+ download.href = link;
96
+ download.textContent = COPY.download;
97
+
98
+ const form = document.createElement("form");
99
+ form.className = "clay-intro__signup";
100
+ form.noValidate = true;
101
+
102
+ const label = document.createElement("label");
103
+ label.setAttribute("for", "clay-intro-email");
104
+ label.textContent = COPY.signupLabel;
105
+
106
+ const row = document.createElement("div");
107
+ row.className = "clay-intro__row";
108
+
109
+ const email = document.createElement("input");
110
+ email.id = "clay-intro-email";
111
+ email.type = "email";
112
+ email.autocomplete = "email";
113
+ email.required = true;
114
+ email.placeholder = COPY.emailPlaceholder;
115
+
116
+ const send = document.createElement("button");
117
+ send.type = "submit";
118
+ send.textContent = COPY.signup;
119
+
120
+ row.append(email, send);
121
+
122
+ const honey = document.createElement("input");
123
+ honey.className = "clay-intro__hp";
124
+ honey.name = "website";
125
+ honey.tabIndex = -1;
126
+ honey.autocomplete = "off";
127
+ honey.setAttribute("aria-hidden", "true");
128
+
129
+ const message = document.createElement("p");
130
+ message.className = "clay-intro__msg";
131
+ message.setAttribute("role", "status");
132
+ message.hidden = true;
133
+
134
+ form.append(label, row, honey, message);
135
+ actions.append(download, form);
136
+
137
+ const start = document.createElement("button");
138
+ start.type = "button";
139
+ start.className = "clay-intro__start";
140
+ start.textContent = COPY.start(appName);
141
+
142
+ inner.append(kicker, name, template.content.cloneNode(true), actions, start);
143
+ section.appendChild(inner);
144
+
145
+ let observer = null;
146
+ let gone = false;
147
+
148
+ function stop() {
149
+ document.removeEventListener("input", onUse, true);
150
+ document.removeEventListener("change", onUse, true);
151
+ document.removeEventListener("clay:dirty", onUse, true);
152
+ if (observer) { observer.disconnect(); observer = null; }
153
+ if (prompt) prompt.hideForIntro(false);
154
+ }
155
+
156
+ function dismiss(reason, target) {
157
+ if (gone) return;
158
+ gone = true;
159
+ const anchor = target && target.getBoundingClientRect ? target : document.activeElement;
160
+ const before = anchor && anchor.getBoundingClientRect ? anchor.getBoundingClientRect().top : 0;
161
+ memory.get().intro.done = reason;
162
+ memory.save();
163
+ stop();
164
+ section.remove();
165
+ if (reason === "button") {
166
+ window.scrollTo({ top: 0 });
167
+ focusApp();
168
+ } else if (anchor && anchor.getBoundingClientRect) {
169
+ window.scrollBy(0, anchor.getBoundingClientRect().top - before);
170
+ }
171
+ if (fromAbout) mountAbout(opts);
172
+ }
173
+
174
+ function unchanged() {
175
+ return !snapshot || snapshot() === baseline;
176
+ }
177
+
178
+ function onUse(event) {
179
+ if (section.contains(event.target)) return;
180
+ if (event.type === "clay:dirty") {
181
+ if (!unchanged()) dismiss("use", event.target);
182
+ return;
183
+ }
184
+ if (!unchanged() || event.target?.closest?.("[persist]")) {
185
+ dismiss("use", event.target);
186
+ return;
187
+ }
188
+ const target = event.target;
189
+ setTimeout(() => {
190
+ if (!gone && !unchanged()) dismiss("use", target);
191
+ }, 0);
192
+ }
193
+
194
+ form.addEventListener("submit", (event) => {
195
+ event.preventDefault();
196
+ fetch("/_/newsletter/subscribe", {
197
+ method: "POST",
198
+ headers: { "Content-Type": "application/json" },
199
+ credentials: "same-origin",
200
+ body: JSON.stringify({ email: email.value.trim(), tags: [tag], source, website: honey.value }),
201
+ }).then((response) => {
202
+ if (!response.ok) throw new Error("signup failed");
203
+ row.hidden = true;
204
+ message.textContent = COPY.signupThanks;
205
+ message.hidden = false;
206
+ }).catch(() => {
207
+ message.textContent = COPY.signupFailed;
208
+ message.hidden = false;
209
+ });
210
+ });
211
+
212
+ start.addEventListener("click", () => dismiss("button"));
213
+
214
+ const baseline = snapshot ? snapshot() : null;
215
+ document.body.prepend(section);
216
+ document.addEventListener("input", onUse, true);
217
+ document.addEventListener("change", onUse, true);
218
+ document.addEventListener("clay:dirty", onUse, true);
219
+
220
+ if (prompt) {
221
+ prompt.hideForIntro(true);
222
+ if (typeof IntersectionObserver === "function") {
223
+ observer = new IntersectionObserver((entries) => {
224
+ for (const entry of entries) prompt.hideForIntro(entry.intersectionRatio > 0.3);
225
+ });
226
+ observer.observe(section);
227
+ }
228
+ }
229
+
230
+ return section;
231
+ }
@@ -0,0 +1,151 @@
1
+ import { isDraftMode } from "../../core/is-edit-mode.js";
2
+
3
+ export const COPY = {
4
+ draft: "You're seeing a private draft, like a visitor.",
5
+ draftAction: "Edit the original",
6
+ edit: "You're editing the original. Visitors get these changes.",
7
+ editAction: "Back to my draft",
8
+ close: "Hide this note",
9
+ };
10
+
11
+ const CHIP_CSS = `.clay-owner {
12
+ --hcp-ground: #FDF8F0;
13
+ --hcp-surface: #FFFCF6;
14
+ --hcp-sunk: #F4ECDF;
15
+ --hcp-ink: #2B241B;
16
+ --hcp-ink-2: #4A4033;
17
+ --hcp-muted: #6B5942;
18
+ --hcp-line: #E8DCC6;
19
+ --hcp-line-2: #D8C8AF;
20
+ --hcp-brass: #8F6310;
21
+ --hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
22
+ --hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
23
+ --hcp-lift: 0px;
24
+ color-scheme: light;
25
+ }
26
+ @media (prefers-color-scheme: dark) {
27
+ .clay-owner {
28
+ --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
29
+ --hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
30
+ --hcp-line: #262B42; --hcp-line-2: #353B52;
31
+ --hcp-brass: #D9A445;
32
+ --hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
33
+ color-scheme: dark;
34
+ }
35
+ }
36
+ .clay-owner, .clay-owner *, .clay-owner *::before, .clay-owner *::after {
37
+ box-sizing: border-box; margin: 0; padding: 0; border: 0;
38
+ font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
39
+ line-height: inherit; color: inherit; background: none; float: none; max-width: none;
40
+ }
41
+ .clay-owner {
42
+ position: fixed; left: 12px; bottom: calc(12px + var(--hcp-lift)); z-index: 2147482000;
43
+ display: flex; align-items: center; gap: 10px;
44
+ max-width: min(460px, calc(100vw - 24px));
45
+ padding: 8px 8px 8px 12px;
46
+ font-family: var(--hcp-sans); font-size: 13px; font-weight: 400; line-height: 1.45;
47
+ color: var(--hcp-ink-2); text-align: left; background: var(--hcp-surface);
48
+ border: 1px solid var(--hcp-line-2); border-left: 3px solid var(--hcp-brass);
49
+ box-shadow: var(--hcp-shadow);
50
+ -webkit-font-smoothing: antialiased;
51
+ }
52
+ .clay-owner__action {
53
+ flex: none; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
54
+ text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
55
+ white-space: nowrap;
56
+ }
57
+ .clay-owner__action:hover { color: var(--hcp-ink); }
58
+ .clay-owner__x {
59
+ flex: none; display: inline-flex; align-items: center; justify-content: center;
60
+ width: 24px; height: 24px; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
61
+ }
62
+ .clay-owner__x:hover { color: var(--hcp-ink); }
63
+ .clay-owner__action:focus-visible, .clay-owner__x:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
64
+ @media (pointer: coarse) {
65
+ .clay-owner__action, .clay-owner__x { min-height: 44px; }
66
+ .clay-owner__x { width: 44px; }
67
+ }
68
+ @media (prefers-reduced-motion: reduce) { .clay-owner { transition: none; } }
69
+ @media (forced-colors: active) { .clay-owner { border: 1px solid CanvasText; } }
70
+ @media print { .clay-owner { display: none; } }
71
+ `;
72
+
73
+ function stylesOnce() {
74
+ if (document.getElementById("clay-owner-style")) return;
75
+ const style = document.createElement("style");
76
+ style.id = "clay-owner-style";
77
+ style.setAttribute("clay", "editor-ui");
78
+ style.textContent = CHIP_CSS;
79
+ document.head.appendChild(style);
80
+ }
81
+
82
+ export function mountOwnerChip() {
83
+ if (document.getElementById("clay-owner")) return null;
84
+ stylesOnce();
85
+
86
+ const draft = isDraftMode;
87
+ const chip = document.createElement("div");
88
+ chip.id = "clay-owner";
89
+ chip.className = "clay-owner";
90
+ chip.setAttribute("clay", "editor-ui");
91
+ chip.setAttribute("sap-ignore", "");
92
+ chip.setAttribute("role", "status");
93
+
94
+ const lead = document.createElement("span");
95
+ lead.className = "clay-owner__lead";
96
+ lead.textContent = draft ? COPY.draft : COPY.edit;
97
+
98
+ const action = document.createElement("button");
99
+ action.type = "button";
100
+ action.className = "clay-owner__action";
101
+ action.textContent = draft ? COPY.draftAction : COPY.editAction;
102
+ action.addEventListener("click", () => {
103
+ const api = window.clay && window.clay.visitorDrafts;
104
+ if (!api) return;
105
+ if (draft) api.editOriginal();
106
+ else api.backToDraft();
107
+ });
108
+
109
+ const close = document.createElement("button");
110
+ close.type = "button";
111
+ close.className = "clay-owner__x";
112
+ close.setAttribute("aria-label", COPY.close);
113
+ close.textContent = "\u00d7";
114
+ close.addEventListener("click", () => { chip.remove(); liftOff(); });
115
+
116
+ chip.append(lead, action, close);
117
+ document.body.appendChild(chip);
118
+
119
+ let frame = 0;
120
+ function lift() {
121
+ if (frame) return;
122
+ frame = requestAnimationFrame(() => {
123
+ frame = 0;
124
+ const y = window.innerHeight - 3;
125
+ let raise = 0;
126
+ const hits = document.elementsFromPoint ? document.elementsFromPoint(24, y) : [];
127
+ for (const hit of hits) {
128
+ for (let el = hit; el && el !== document.body && el !== document.documentElement; el = el.parentElement) {
129
+ if (chip.contains(el)) break;
130
+ const position = getComputedStyle(el).position;
131
+ if (position === "fixed" || position === "sticky") {
132
+ raise = Math.max(raise, Math.ceil(window.innerHeight - el.getBoundingClientRect().top));
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ chip.style.setProperty("--hcp-lift", (raise > 0 && raise < window.innerHeight / 3 ? raise : 0) + "px");
138
+ });
139
+ }
140
+
141
+ function liftOff() {
142
+ removeEventListener("scroll", lift);
143
+ removeEventListener("resize", lift);
144
+ }
145
+
146
+ addEventListener("scroll", lift, { passive: true });
147
+ addEventListener("resize", lift);
148
+ lift();
149
+
150
+ return chip;
151
+ }
@@ -0,0 +1,173 @@
1
+ export const PROMPT_CSS = `.hcp {
2
+ --hcp-ground: #FDF8F0;
3
+ --hcp-surface: #FFFCF6;
4
+ --hcp-sunk: #F4ECDF;
5
+ --hcp-ink: #2B241B;
6
+ --hcp-ink-2: #4A4033;
7
+ --hcp-muted: #6B5942;
8
+ --hcp-line: #E8DCC6;
9
+ --hcp-line-2: #D8C8AF;
10
+ --hcp-brass: #8F6310;
11
+ --hcp-brass-soft: #F0E4CC;
12
+ --hcp-face: #F1E7D4;
13
+ --hcp-edge-hi: #FFF9ED;
14
+ --hcp-edge-lo: #D6C3A5;
15
+ --hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
16
+ --hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
17
+ --hcp-serif: Newsreader, Georgia, "Times New Roman", serif;
18
+ --hcp-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
19
+ --hcp-lift: 0px;
20
+ color-scheme: light;
21
+ }
22
+ @media (prefers-color-scheme: dark) {
23
+ .hcp:not([data-hcp-theme="light"]) {
24
+ --hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
25
+ --hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
26
+ --hcp-line: #262B42; --hcp-line-2: #353B52;
27
+ --hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
28
+ --hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
29
+ --hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
30
+ color-scheme: dark;
31
+ }
32
+ }
33
+ .hcp[data-hcp-theme="dark"] {
34
+ --hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
35
+ --hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
36
+ --hcp-line: #262B42; --hcp-line-2: #353B52;
37
+ --hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
38
+ --hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
39
+ --hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
40
+ color-scheme: dark;
41
+ }
42
+
43
+ .hcp, .hcp * , .hcp *::before, .hcp *::after {
44
+ box-sizing: border-box; margin: 0; padding: 0; border: 0;
45
+ font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
46
+ line-height: inherit; color: inherit; background: none; float: none; max-width: none;
47
+ }
48
+ .hcp {
49
+ position: fixed; right: 16px; bottom: calc(16px + var(--hcp-lift)); z-index: 2147482000;
50
+ display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
51
+ font-family: var(--hcp-sans); font-size: 14px; font-weight: 400; line-height: 1.5;
52
+ color: var(--hcp-ink); text-align: left;
53
+ -webkit-font-smoothing: antialiased;
54
+ transition: bottom .25s ease, opacity .2s ease;
55
+ pointer-events: none;
56
+ }
57
+ .hcp[hidden], .hcp[data-hcp-state="dismissed"], .hcp[data-hcp-state="off"], .hcp[data-hcp-intro] { display: none; }
58
+ .hcp[data-hcp-typing] { opacity: 0; visibility: hidden; }
59
+ .hcp > * { pointer-events: auto; }
60
+ .hcp svg { display: block; flex: none; }
61
+
62
+ .hcp-btn {
63
+ display: inline-flex; align-items: center; justify-content: center; gap: 7px;
64
+ height: 34px; padding: 0 14px;
65
+ font-family: var(--hcp-sans); font-size: 14px; font-weight: 500; line-height: 1.5;
66
+ white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
67
+ color: var(--hcp-ink); background: var(--hcp-face);
68
+ border: 2px solid; border-radius: 0;
69
+ border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
70
+ outline: none;
71
+ }
72
+ .hcp-btn:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); color: var(--hcp-ink); }
73
+ .hcp-btn:active { border-color: var(--hcp-edge-lo) var(--hcp-edge-hi) var(--hcp-edge-hi) var(--hcp-edge-lo); }
74
+ .hcp-btn:active > span { translate: 1px 1px; }
75
+ .hcp-btn > span { display: inline-flex; align-items: center; gap: 7px; }
76
+ .hcp-btn:focus-visible, .hcp-x:focus-visible, .hcp-quiet:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
77
+ .hcp-btn--primary {
78
+ --hcp-face: var(--hcp-ink);
79
+ --hcp-edge-hi: color-mix(in srgb, var(--hcp-ink), white 24%);
80
+ --hcp-edge-lo: color-mix(in srgb, var(--hcp-ink), black 32%);
81
+ color: var(--hcp-ground);
82
+ }
83
+ .hcp-btn--primary:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); color: var(--hcp-ground); }
84
+
85
+ .hcp-dock { display: flex; align-items: stretch; box-shadow: 0 6px 16px -8px rgba(43, 36, 27, .45); }
86
+ .hcp-tab { font-size: 13.5px; padding: 0 12px 0 10px; }
87
+ .hcp-tab svg { color: var(--hcp-brass); }
88
+ .hcp-dock .hcp-x {
89
+ width: 26px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
90
+ background: var(--hcp-face); color: var(--hcp-muted); cursor: pointer;
91
+ border: 2px solid; border-left: 0;
92
+ border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
93
+ }
94
+ .hcp-dock .hcp-x:hover { color: var(--hcp-ink); }
95
+ .hcp-tab__label { display: inline; }
96
+ .hcp[data-hcp-state="mini"] .hcp-tab__label,
97
+ .hcp[data-hcp-state="mini"] .hcp-dock .hcp-x { display: none; }
98
+ .hcp[data-hcp-state="mini"] .hcp-tab { width: 34px; padding: 0; }
99
+ .hcp[data-hcp-state="card"] .hcp-dock { display: none; }
100
+
101
+ .hcp-card {
102
+ display: none; position: relative;
103
+ width: min(360px, calc(100vw - 32px)); max-height: calc(100dvh - 32px - var(--hcp-lift)); overflow: auto;
104
+ background: var(--hcp-surface); border: 1px solid var(--hcp-line-2); border-top: 3px solid var(--hcp-brass);
105
+ box-shadow: var(--hcp-shadow);
106
+ padding: 18px 20px 16px;
107
+ }
108
+ .hcp[data-hcp-state="card"] .hcp-card { display: block; animation: hcp-rise .32s cubic-bezier(.2, .8, .3, 1) both; }
109
+ @keyframes hcp-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
110
+
111
+ .hcp-card .hcp-x {
112
+ position: absolute; top: 10px; right: 10px; width: 28px; height: 28px;
113
+ display: inline-flex; align-items: center; justify-content: center;
114
+ color: var(--hcp-muted); cursor: pointer;
115
+ }
116
+ .hcp-card .hcp-x:hover { color: var(--hcp-ink); background: var(--hcp-sunk); }
117
+ .hcp-kicker {
118
+ display: flex; align-items: center; gap: 8px;
119
+ font-family: var(--hcp-mono); font-size: 11px; font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
120
+ color: var(--hcp-brass); margin-bottom: 8px;
121
+ }
122
+ .hcp-title {
123
+ font-family: var(--hcp-serif); font-size: 23px; font-weight: 500; line-height: 1.18;
124
+ color: var(--hcp-ink); padding-right: 26px; margin-bottom: 10px;
125
+ font-variation-settings: "opsz" 36;
126
+ }
127
+ .hcp-title [data-hcp-for] { display: none; }
128
+ .hcp[data-hcp-reason="use"] .hcp-title [data-hcp-for="use"],
129
+ .hcp[data-hcp-reason="open"] .hcp-title [data-hcp-for="open"],
130
+ .hcp[data-hcp-reason="return"] .hcp-title [data-hcp-for="return"],
131
+ .hcp[data-hcp-reason="error"] .hcp-title [data-hcp-for="error"] { display: inline; }
132
+ .hcp-body [data-hcp-for] { display: none; }
133
+ .hcp[data-hcp-reason="use"] .hcp-body [data-hcp-for="use"], .hcp[data-hcp-reason="open"] .hcp-body [data-hcp-for="open"], .hcp[data-hcp-reason="return"] .hcp-body [data-hcp-for="return"], .hcp[data-hcp-reason="error"] .hcp-body [data-hcp-for="error"] { display: inline; }
134
+ .hcp-body { font-size: 14px; line-height: 1.55; color: var(--hcp-ink-2); }
135
+ .hcp-file {
136
+ display: flex; align-items: center; gap: 10px; margin: 12px 0 14px;
137
+ padding: 9px 11px; background: var(--hcp-sunk); border: 1px solid var(--hcp-line);
138
+ }
139
+ .hcp-file svg { color: var(--hcp-brass); }
140
+ .hcp-file b { display: block; font-family: var(--hcp-sans); font-size: 14px; font-weight: 600; color: var(--hcp-ink); overflow-wrap: anywhere; }
141
+ .hcp-file small { display: block; font-size: 12px; color: var(--hcp-muted); line-height: 1.4; }
142
+ .hcp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
143
+ .hcp-actions .hcp-btn--primary { flex: 1 1 auto; }
144
+ .hcp-foot { margin-top: 12px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
145
+ .hcp-foot small { font-size: 12px; color: var(--hcp-muted); }
146
+ .hcp-quiet {
147
+ font-size: 12px; color: var(--hcp-muted); cursor: pointer;
148
+ text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
149
+ white-space: nowrap;
150
+ }
151
+ .hcp-quiet:hover { color: var(--hcp-ink); }
152
+
153
+ @media (max-width: 480px) {
154
+ .hcp { right: 12px; bottom: calc(12px + var(--hcp-lift)); }
155
+ .hcp-card { width: calc(100vw - 24px); padding: 16px 16px 14px; max-height: min(70dvh, calc(100dvh - 24px - var(--hcp-lift))); }
156
+ .hcp-title { font-size: 21px; }
157
+ }
158
+ @media (pointer: coarse) {
159
+ .hcp-btn, .hcp-dock .hcp-x, .hcp-card .hcp-x { min-height: 44px; }
160
+ .hcp-card .hcp-x { width: 44px; }
161
+ .hcp-quiet { display: inline-flex; align-items: center; min-height: 44px; }
162
+ }
163
+ @media (prefers-reduced-motion: reduce) {
164
+ .hcp { transition: none; }
165
+ .hcp[data-hcp-state="card"] .hcp-card { animation: none; }
166
+ .hcp-btn:active > span { translate: none; }
167
+ }
168
+ @media (forced-colors: active) {
169
+ .hcp-btn, .hcp-dock .hcp-x { border: 1px solid ButtonText; }
170
+ .hcp-card { border: 1px solid CanvasText; }
171
+ }
172
+ @media print { .hcp { display: none; } }
173
+ `;