Skip to main content

max / makenotwork

305.0 KB · 11762 lines History Blame Raw
1 /* ============================================================================
2 MNW global stylesheet. Charter: docs/design-system.md.
3
4 Build new features by COMPOSING these primitives, in this order of preference:
5
6 1. Use a UTILITY class for one-off adjustments (.mb-section, .text-sm, .nowrap)
7 2. Use a LAYOUT PRIMITIVE to position content (.container, .stack-row, .field-row, .list-row)
8 3. Use a COMPONENT PRIMITIVE for a UI element (.card, .callout, .badge, .progress-bar, .empty-state, .input--sm, .small)
9 4. Extend a primitive with a modifier (.card--bordered, .callout--warning, .stack-row--bordered)
10 5. ONLY THEN consider a new class, and add it to the right section below
11
12 A new class is a smell, not a goal. Before writing one:
13 - grep this file for the visual shape you want; almost everything is here
14 - if you can't find it, consider whether the shape belongs in the design system
15 (then add it to the appropriate section here AND to docs/design-system.md)
16 - page-scoped rules (`.foo-page .bar`) are a last resort, not a first move
17
18 --------------------------------------------------------------------------
19 FILE STRUCTURE (search by section name for anchor)
20
21 FOUNDATIONS
22 @font-face declarations (top of file)
23 :root tokens color, type, space, radius, shadow
24 Element base h1/h2/h3, p, a, button, input, table
25
26 UTILITIES
27 Spacing (.m-0, .mt-*, .mb-*, .ml-*, .my-*)
28 Sizing (.col-*, .w-*, .maxw-*, .minw-*)
29 Text (.text-sm, .text-xs, .text-center, .text-right, .fw-bold,
30 .nowrap, .muted, .dimmed, .dimmer, .is-faded, .danger-text)
31 State (.hidden, .is-selected, .is-active, .unstyled-link)
32 Shapes (.square-cover, .scroll-x)
33
34 LAYOUT PRIMITIVES (page-scaffolding shapes)
35 .container width set by body class
36 .stack-row + --bordered / --tight / --top toolbar / header bar
37 .field-row + .form-group.is-grow inline form row
38 .list-row + .list-row-title vertical item list
39 .form-row 2-col grid form
40 .cover-row + .cover-thumb + .cover-empty image picker
41
42 COMPONENT PRIMITIVES (reusable UI blocks)
43 .content-section light-bg page section box
44 .card / .card-muted / .card--bordered / .card--selectable
45 .form-group / .form-section / details.form-section
46 .section-header / .section-lead / .section-group-label
47 .badge + variants (-success/-warning/-danger)
48 .callout + --danger / --warning / --solid-warning
49 .banner + --info / --warning full-bleed page-top notice
50 .alert + -note / -tip / -warning / -caution left-border inline notice
51 .modal + .modal-overlay
52 .empty-state + --compact / --chart / --lg
53 .progress-bar-container + .progress-bar (--slim, --rounded, --highlight)
54 .upload-status + -row + -msg.is-success / .is-error
55 .field-status / .save-status + .success / .error / .saving
56 .toast + .toast--success / --error / --warning
57 .breadcrumb / .pagination
58 .tabs + .tab.is-selected
59
60 CONTROL MODIFIERS (compose onto buttons/inputs)
61 .small / .btn-compact / .btn-link / .btn-tiny button sizes
62 .btn-danger / .saved button intents
63 .input--xs / --sm / --mono / --upper / --numeric input sizes/shapes
64
65 FEATURE SUBAPPS (their own CSS scope)
66 .git-* (git source browser at /source/*)
67 Pricing calculator on /pricing
68 Markdown preview / docs-ui in /docs/*
69
70 PAGE-SCOPED LAYOUTS (when truly page-specific)
71 .item-page / .project-page / .article-page / .purchase-page / .cart-* /
72 .library-page / .feed-page / .discover-page / .landing / .creators-page /
73 .health-page / .import-page / .delete-account-page / .export-page /
74 .receipt-page / .fan-plus-page / .stripe-disclaimer-page / .buy-page /
75 .user-page / .collection-page / .changelog-page / .policy-page /
76 .tag-tree-page / .project-blog-page / .project-paywall-page /
77 .confirm-delete-page / .admin-page / .dashboard-page
78
79 DASHBOARD TABS (one section per tab partial)
80 Per-tab classes scoped under their canonical body class. Use modifier
81 composition where possible; only add tab-specific rules when the shape
82 genuinely diverges from a primitive.
83
84 RESPONSIVE (@media at end of file)
85 768px tablet
86 480px phone
87
88 --------------------------------------------------------------------------
89 When in doubt: read docs/design-system.md first. Update both files together
90 when you add a primitive.
91 ============================================================================ */
92
93 @font-face {
94 font-family: "Young Serif";
95 src: url("/static/fonts/ysrf.woff2") format("woff2"),
96 url("/static/fonts/ysrf.ttf") format("truetype");
97 font-display: swap;
98 }
99
100 @font-face {
101 font-family: "IBM Plex Mono";
102 src: url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2"),
103 url("/static/fonts/IBMPlexMono-Regular.ttf") format("truetype");
104 font-weight: normal;
105 font-display: swap;
106 }
107
108 @font-face {
109 font-family: "IBM Plex Mono";
110 src: url("/static/fonts/IBMPlexMono-Bold.woff2") format("woff2"),
111 url("/static/fonts/IBMPlexMono-Bold.ttf") format("truetype");
112 font-weight: bold;
113 font-display: swap;
114 }
115
116 @font-face {
117 font-family: "Lato";
118 src: url("/static/fonts/Lato-Regular.woff2") format("woff2"),
119 url("/static/fonts/Lato-Regular.ttf") format("truetype");
120 font-weight: normal;
121 font-display: swap;
122 }
123
124 @font-face {
125 font-family: "Lato";
126 src: url("/static/fonts/Lato-Bold.woff2") format("woff2"),
127 url("/static/fonts/Lato-Bold.ttf") format("truetype");
128 font-weight: bold;
129 font-display: swap;
130 }
131
132 :root {
133 /* Typography (three-tier system) */
134 --font-heading: "Young Serif", serif;
135 --font-mono: "IBM Plex Mono", monospace;
136 --font-body: "Lato", sans-serif;
137
138 /* --------------------------------------------------------------------
139 INTENT LAYER, the shared theme vocabulary (themeable)
140
141 These are the intent tokens emitted by theme_common::intent_css_vars():
142 colors declared by ROLE (surface/content/action/status/line/category),
143 plus the derived interactive states (hover/active/selection/row-stripe/
144 contrast) the crate computes once for every product. The values below
145 are the platform default (shared/themes/makenotwork.toml resolved) so
146 non-creator pages render without injection; creator profile/project/item
147 pages inject a <style> that supersedes this block with the chosen theme.
148 Generated. Keep in sync with `cargo run --example dump_intents makenotwork`.
149 -------------------------------------------------------------------- */
150 --surface-page: #ddd6c9;
151 --surface-raised: #efe9e0;
152 --surface-sunken: #cdc5b5;
153 --surface-overlay: #efe9e0;
154 /* The content surface cut into a raised one, so a list reads as content in a
155 container rather than as bands on a panel. Derived by makeover from
156 surface-raised, away from resolved content, so it goes lighter on a light
157 theme and darker on a dark one. */
158 --surface-well: #fffef5;
159 /* The two edges of a bevel. makeover derives both from the theme's raised
160 surface, so they arrive with the rest of the intent set through
161 intent_css_vars() and every creator theme gains them automatically. */
162 --bevel-light: #fffef5;
163 --bevel-dark: #b5afa6;
164 --content: #3d3530;
165 --content-secondary: #5c554f;
166 --content-muted: #8a8480;
167 --content-on-action: #ffffff;
168 --action: #6c5ce7;
169 --action-hover: #7a6cf8;
170 --danger: #c0392b;
171 --success: #27ae60;
172 --warning: #f59e0b;
173 --info: #17a2b8;
174 --border: #b3ab97;
175 --border-strong: #a39c88;
176 --focus-ring: #6c5ce7;
177 --hover-surface: #cdc5b5;
178 --category-one: #c0392b;
179 --category-two: #27ae60;
180 --category-three: #6c5ce7;
181 --category-four: #f59e0b;
182 --category-five: #a29bfe;
183 --category-six: #17a2b8;
184 --overlay: rgba(3, 1, 0, 0.5);
185
186 /* --------------------------------------------------------------------
187 APP-LOCAL CONSTANTS, not theme colors, not derivable from intents.
188
189 Component CSS references the intent tokens above directly (no brand
190 aliases). Status backgrounds derive from the intents inline via
191 color-mix(); these few stay fixed:
192 - pure black/white contrast anchors
193 - Stripe's brand color (an external constant)
194 - diff add/del, a hard readability convention that must not re-theme
195 -------------------------------------------------------------------- */
196 --primary-dark: #000000;
197 --primary-light: #ffffff;
198 --stripe: #635BFF;
199
200 /* The faint accent tint every selection and highlight recipe fills with.
201 Derived from --action, so it re-tints with the creator's theme. Named
202 because the charter names it and because a dozen rules read it. */
203 --highlight-faint: color-mix(in oklch, var(--action) 8%, transparent);
204
205 /* Diff / code review, fixed green-add / red-del convention */
206 --diff-add: #2d7d2d;
207 --diff-add-bg: rgba(45, 125, 45, 0.08);
208 --diff-del: #a83232;
209 --diff-del-bg: rgba(168, 50, 50, 0.08);
210
211 /* Radius lives in geometry.css now, generated from makeover-geometry:
212 --radius-square / -fine / -control / -panel / -round. Containers are
213 square and controls keep a corner, so the rounding survives only where a
214 thing is meant to be pressed. */
215
216 /* Syntax highlighting for the git source browser: base16 Tomorrow, used
217 whole rather than assembled colour by colour. It stays fixed across all
218 31 themes on purpose, the same argument as the diff add/del pair above:
219 a reader recognises a highlighting palette, and re-tinting it per theme
220 costs that recognition to gain nothing. Named so it is legible as one
221 palette instead of seven hex literals scattered through the git rules. */
222 --syntax-keyword: #8959a8;
223 --syntax-string: #718c00;
224 --syntax-comment: #8e908c;
225 --syntax-constant: #f5871f;
226 --syntax-entity: #4271ae;
227 --syntax-variable: #c82829;
228 --syntax-support: #3e999f;
229
230 /* Stacking order, named for what floats over what. Local rather than
231 generated: this is a claim about MNW's own furniture, not an invariant
232 like spacing. The numbers are spaced so a value can be slotted between
233 two of them without renumbering, and nothing outside this block should
234 write a z-index literal. Anything not listed here does not float. */
235 --z-raised: 1; /* lifted within the flow: a saved badge on a card */
236 --z-nav: 10; /* the nav panel that drops out of the mobile header */
237 --z-header: 11; /* the header's own toggle and search panel, both of
238 which have to stay above the panel they open */
239 --z-dropdown: 20; /* menus and suggestion lists opened from a control */
240 --z-picker: 30; /* pickers that open over a dropdown */
241 --z-context: 40; /* right-click / overflow context menus */
242 --z-overlay: 50; /* full-page scrims */
243 --z-modal: 60; /* dialogs, above their scrim */
244 --z-toast: 70; /* transient messages, above a dialog */
245 --z-banner: 80; /* page-level interruptions, above everything */
246 --z-skip-link: 90; /* the keyboard escape hatch outranks all of it */
247
248 /* Shadow scale.
249 --shadow-1/-2/-3 are blurred elevation (true floating: popovers,
250 dropdowns, modals). --shadow-raised/-card/-inset are everyday depth,
251 buttons feel pressable, cards feel layered, inputs feel recessed. Pick
252 by role: blurred = floats over page, bevel = is part of page.
253
254 The three used to be hard offsets over two literal greys, #bbb and #ddd,
255 picked against parchment and then applied to all 31 themes a creator can
256 select; on nord they were a light smear on a dark panel. They now alias
257 the bevel pair from layout.css, which makeover derives from each theme's
258 own raised surface, so no literal survives and every consumer re-themes
259 without being opened. --shadow-raised and --shadow-card collapse onto
260 one value: a bevel is an edge rather than a distance, so there is no
261 heavier and lighter version of it to pick between. */
262 --shadow-raised: var(--bevel-raised);
263 --shadow-card: var(--bevel-raised);
264 --shadow-inset: var(--bevel-inset);
265 --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.06);
266 --shadow-2: 0 2px 8px rgba(0, 0, 0, 0.10);
267 --shadow-3: 0 4px 12px rgba(0, 0, 0, 0.15);
268 }
269
270 /* Reset */
271 * {
272 margin: 0;
273 padding: 0;
274 box-sizing: border-box;
275 }
276
277 /* Base styles */
278 body {
279 margin: 0;
280 background-color: var(--surface-page);
281 color: var(--content);
282 font-family: var(--font-body);
283 line-height: 1.6;
284 }
285
286 /* Padded page layout for most content pages */
287 .padded-page {
288 padding: var(--gap-pane);
289 }
290
291 /* Centered page layout for landing, login, signup */
292 .centered-page {
293 display: flex;
294 flex-direction: column;
295 justify-content: center;
296 align-items: center;
297 min-height: 100vh;
298 gap: var(--gap-page);
299 }
300
301 .centered-page h1 {
302 font-size: var(--text-hero);
303 }
304
305 .message-container {
306 max-width: 500px;
307 text-align: center;
308 }
309
310 .centered-wrapper {
311 justify-content: center;
312 text-align: center;
313 }
314
315 h1 {
316 font-family: var(--font-heading);
317 font-weight: normal;
318 color: var(--content);
319 text-align: center;
320 font-size: var(--text-display);
321 }
322
323 h2,
324 h3 {
325 font-family: var(--font-mono);
326 font-weight: normal;
327 color: var(--content);
328 }
329
330 /* Heading classes (charter: docs/design-system.md, every h1/h2 in a
331 template must carry one of these so the role is explicit).
332
333 .brand-h1 the "Makenot.work" wordmark on auth/wizard pages.
334 .page-title page-level h1 (Young Serif, centered).
335 .subtitle-h2 page subtitle h2 used under .brand-h1 in auth/wizards.
336 .subsection-title, h2 inside dashboards, tabs, and prose partials
337 (mono, no border, the default-h2 role made explicit).
338 .section-header h2 prose sub-section heading with bottom border
339 (existing rule, see SECTIONS block). */
340 .brand-h1,
341 .page-title {
342 font-family: var(--font-heading);
343 font-weight: normal;
344 color: var(--content);
345 text-align: center;
346 font-size: var(--text-display);
347 }
348
349 .subtitle-h2 {
350 font-family: var(--font-mono);
351 font-weight: normal;
352 color: var(--content);
353 text-align: center;
354 font-size: var(--text-head);
355 }
356
357 .subsection-title {
358 font-family: var(--font-mono);
359 font-weight: normal;
360 color: var(--content);
361 }
362
363 p {
364 font-family: var(--font-body);
365 color: var(--content);
366 }
367
368 a {
369 color: var(--content);
370 text-decoration: none;
371 }
372
373 a:hover {
374 text-decoration: underline;
375 }
376
377 /* The signature dot */
378 .dot {
379 color: var(--action);
380 }
381
382 /* Container, default 1200px max-width. Modifier classes for narrower pages,
383 or comma-grouped page-scoped overrides below for body-class-based control. */
384 .container {
385 max-width: 1200px;
386 margin: 0 auto;
387 padding: var(--gap-page);
388 }
389
390 /* Per-page container width overrides (selected via body class). One rule
391 per width tier; new pages should add their body class here rather than
392 defining a new .foo-page .container rule. */
393 .delete-account-page .container,
394 .receipt-page .container,
395 .user-page .container { max-width: 600px; margin: 0 auto; }
396
397 .import-page .container,
398 .export-page .container { max-width: 800px; margin: 0 auto; }
399
400 .fan-plus-page .container,
401 .feed-page .container,
402 .creators-page .container { max-width: 900px; margin: 0 auto; }
403
404 /* ===========================================
405 BUTTONS
406 =========================================== */
407
408 button {
409 color: var(--content);
410 background: var(--surface-overlay);
411 padding: 6px 12px;
412 border: 1px solid var(--content);
413 font-family: inherit;
414 cursor: pointer;
415 box-shadow: var(--shadow-raised);
416 transition: box-shadow 0.1s ease, background 0.2s ease;
417 }
418
419 button:hover {
420 background: var(--hover-surface);
421 }
422
423 button:active {
424 box-shadow: var(--shadow-inset);
425 }
426
427 .btn-primary {
428 background: var(--primary-dark);
429 color: var(--primary-light);
430 border: none;
431 padding: var(--gap-section) var(--gap-pane);
432 font-family: inherit;
433 font-size: var(--text-body);
434 cursor: pointer;
435 box-shadow: var(--shadow-raised);
436 transition: box-shadow 0.1s ease, background 0.2s ease;
437 display: inline-block;
438 text-decoration: none;
439 }
440
441 /* A solid-fill button shifts its own fill rather than taking --hover-surface.
442 The charter's hover rule is "one step lighter than the resting fill", and on
443 a neutral control that step is --hover-surface; on this one it is not, since
444 the label is --primary-light and a parchment fill under it is unreadable. */
445 .btn-primary:hover {
446 background: color-mix(in oklch, var(--primary-dark) 85%, var(--primary-light));
447 text-decoration: none;
448 }
449
450 .btn-primary:active {
451 box-shadow: var(--shadow-inset);
452 }
453
454 .btn-secondary {
455 background: var(--surface-sunken);
456 color: var(--content);
457 border: none;
458 padding: var(--gap-section) var(--gap-pane);
459 font-family: var(--font-mono);
460 font-size: var(--text-body);
461 cursor: pointer;
462 box-shadow: var(--shadow-raised);
463 transition: box-shadow 0.1s ease, background 0.2s ease;
464 display: inline-block;
465 text-decoration: none;
466 }
467
468 .btn-secondary:hover {
469 background: var(--hover-surface);
470 text-decoration: none;
471 }
472
473 .btn-secondary:active {
474 box-shadow: var(--shadow-inset);
475 }
476
477 .btn-danger {
478 background: var(--danger);
479 color: var(--primary-light);
480 border: none;
481 padding: var(--gap-section) var(--gap-pane);
482 font-family: var(--font-mono);
483 font-size: var(--text-body);
484 cursor: pointer;
485 box-shadow: var(--shadow-raised);
486 transition: box-shadow 0.1s ease, background 0.2s ease;
487 display: inline-block;
488 text-decoration: none;
489 }
490
491 /* Solid fill, so the same exception as .btn-primary applies. */
492 .btn-danger:hover {
493 background: color-mix(in oklch, var(--danger) 85%, var(--primary-light));
494 text-decoration: none;
495 }
496
497 .btn-danger:active {
498 box-shadow: var(--shadow-inset);
499 }
500
501 /* Disabled state for all button variants (charter rule).
502 Applies whether disabled via attribute or aria-disabled. */
503 button:disabled,
504 button[aria-disabled="true"],
505 .btn-primary:disabled,
506 .btn-secondary:disabled,
507 .btn-danger:disabled {
508 opacity: 0.5;
509 cursor: not-allowed;
510 box-shadow: none;
511 transform: none;
512 }
513
514 /* Button size + style modifiers (charter: docs/design-system.md).
515 Use these on top of .btn-primary / -secondary / -danger.
516
517 .btn--large large CTA padding bump.
518 .btn--icon square icon-only / micro button (small padding,
519 tight line-height).
520 .btn--link visually a link, semantically a button (no bg/
521 border, opacity-fade hover, mono).
522
523 Some surfaces have tuned button variants that are NOT compositions
524 of these modifiers and keep their own classes:
525 .big-button Young Serif 200×60 anchor on splash pages.
526 .order-btn list reorder up/down (with active flash).
527 .play-button circular media-player play control.
528 .speed-button segment in media-player speed group.
529 .shortcuts-help-btn, 1.5rem square help glyph in toolbars.
530 .toast-retry-btn inline bordered button inside a toast,
531 uses currentColor to inherit toast tone.
532 These are documented variants, not deprecation targets. */
533 .btn--large {
534 padding: var(--gap-section) var(--gap-page);
535 font-size: var(--text-lead);
536 }
537
538 .btn--icon {
539 padding: var(--gap-bound) var(--gap-peer);
540 font-size: var(--text-note);
541 line-height: 1;
542 min-width: 1.75rem;
543 }
544
545 .btn--link {
546 background: none;
547 color: var(--content);
548 border: none;
549 padding: 0;
550 font-family: var(--font-mono);
551 font-size: var(--text-body);
552 cursor: pointer;
553 /* Link semantics, flat, no depth. Overrides the shadow that would
554 otherwise cascade from `button` / `form button` when a .btn--link
555 happens to be a <button> inside a <form> (e.g. logout). */
556 box-shadow: none;
557 transition: opacity 0.2s ease;
558 }
559
560 .btn--link:hover {
561 opacity: 0.6;
562 box-shadow: none;
563 }
564
565 .btn--link:active {
566 box-shadow: var(--shadow-inset);
567 }
568
569 /* Menu / disclosure openers, buttons whose job is to reveal a menu,
570 filter panel, expander, or popover. They sit flat rather than raised.
571 Add new opener selectors here as they appear. */
572 .context-menu-btn,
573 .discover-filter-toggle,
574 .tip-toggle,
575 .bundle-toggle {
576 box-shadow: none;
577 }
578
579 .context-menu-btn:hover,
580 .discover-filter-toggle:hover,
581 .tip-toggle:hover,
582 .bundle-toggle:hover {
583 box-shadow: none;
584 }
585
586 .context-menu-btn:active,
587 .discover-filter-toggle:active,
588 .tip-toggle:active,
589 .bundle-toggle:active {
590 box-shadow: var(--shadow-inset);
591 }
592
593 /* ===========================================
594 FORMS
595 =========================================== */
596
597 form {
598 display: flex;
599 flex-direction: column;
600 gap: var(--gap-section);
601 width: 100%;
602 }
603
604 .form-container {
605 max-width: 400px;
606 background: var(--surface-overlay);
607 padding: var(--gap-page);
608 }
609
610 label {
611 font-family: var(--font-mono);
612 color: var(--content);
613 font-size: var(--text-note);
614 }
615
616 input[type="text"],
617 input[type="email"],
618 input[type="password"],
619 input[type="number"],
620 input[type="date"],
621 textarea,
622 select {
623 width: 100%;
624 background: var(--surface-sunken);
625 border: 1px solid var(--border);
626 padding: var(--gap-section);
627 font-family: var(--font-mono);
628 font-size: var(--text-note);
629 color: var(--content);
630 box-shadow: var(--shadow-inset);
631 }
632
633 input::placeholder,
634 textarea::placeholder {
635 opacity: 0.5;
636 }
637
638 input[type="text"]:focus,
639 input[type="email"]:focus,
640 input[type="password"]:focus,
641 input[type="number"]:focus,
642 textarea:focus,
643 select:focus {
644 outline: none;
645 border: 1px solid var(--action);
646 }
647
648 textarea {
649 min-height: 100px;
650 resize: vertical;
651 }
652
653 select {
654 cursor: pointer;
655 }
656
657 input[type="submit"],
658 form button {
659 font-family: var(--font-heading);
660 font-size: var(--text-body);
661 color: var(--content);
662 background: var(--surface-overlay);
663 padding: 12px 24px;
664 border: 1px solid var(--content);
665 cursor: pointer;
666 box-shadow: var(--shadow-raised);
667 transition: box-shadow 0.1s ease, background 0.2s ease;
668 }
669
670 input[type="submit"]:hover,
671 form button:hover {
672 background: var(--hover-surface);
673 }
674
675 input[type="submit"]:active,
676 form button:active {
677 box-shadow: var(--shadow-inset);
678 }
679
680 .form-group {
681 margin-bottom: var(--gap-pane);
682 }
683
684 .form-group label {
685 display: block;
686 margin-bottom: var(--gap-peer);
687 font-size: var(--text-note);
688 }
689
690 .form-group input,
691 .form-group textarea,
692 .form-group select {
693 width: 100%;
694 }
695
696 .form-row {
697 display: grid;
698 grid-template-columns: 1fr 1fr;
699 gap: var(--gap-section);
700 }
701
702 .hint {
703 font-size: var(--text-note);
704 opacity: 0.6;
705 margin-top: var(--gap-peer);
706 }
707
708 /* Field-level validation error, paired with .form-group--error.
709 Canonical: `partials/_ui.html` ui::form_field macro. */
710 .form-group--error label {
711 color: var(--danger);
712 }
713
714 .form-group--error input,
715 .form-group--error textarea,
716 .form-group--error select {
717 border-color: var(--danger);
718 }
719
720 .field-error {
721 font-size: var(--text-note);
722 color: var(--danger);
723 margin-top: var(--gap-peer);
724 }
725
726 /* Checkbox group */
727 .checkbox-group {
728 display: flex;
729 align-items: start;
730 gap: var(--gap-section);
731 cursor: pointer;
732 transition: background 0.2s ease;
733 margin-bottom: var(--gap-peer);
734 }
735
736 .checkbox-group input[type="checkbox"] {
737 width: auto;
738 margin-top: var(--gap-bound);
739 margin-bottom: var(--gap-bound);
740 cursor: pointer;
741 }
742 /* Radio group */
743 .radio-group {
744 display: flex;
745 flex-direction: column;
746 gap: var(--gap-section);
747 margin-top: var(--gap-peer);
748 }
749 /* ===========================================
750 TABS
751 =========================================== */
752
753 .tabs {
754 display: flex;
755 flex-wrap: nowrap;
756 gap: 0;
757 margin-bottom: 0;
758 }
759
760 .tab {
761 background: var(--surface-sunken);
762 color: var(--content);
763 border: none;
764 padding: var(--gap-section) var(--gap-page);
765 font-family: var(--font-mono);
766 font-size: var(--text-body);
767 cursor: pointer;
768 /* Tabs navigate, they don't commit, stay flat. Overrides the base
769 `button` shadow if a tab is a <button>. */
770 box-shadow: none;
771 transition:
772 background 0.2s ease,
773 opacity 0.2s ease;
774 opacity: 0.6;
775 white-space: nowrap;
776 flex-shrink: 0;
777 }
778
779 .tab.is-selected {
780 background: var(--surface-overlay);
781 opacity: 1;
782 }
783
784 .tab:hover {
785 opacity: 1;
786 }
787
788 .tab-more-wrap {
789 position: relative;
790 flex-shrink: 0;
791 }
792
793 .tab-overflow-menu {
794 position: absolute;
795 top: 100%;
796 right: 0;
797 z-index: var(--z-dropdown);
798 background: var(--surface-page);
799 border: 1px solid var(--border);
800 min-width: 180px;
801 box-shadow: var(--shadow-2);
802 }
803
804 .tab-overflow-menu .tab {
805 display: block;
806 width: 100%;
807 text-align: left;
808 padding: var(--gap-group) var(--gap-section);
809 opacity: 0.7;
810 }
811
812 .tab-overflow-menu .tab:hover {
813 opacity: 1;
814 }
815
816 .tab-overflow-menu .tab.is-selected {
817 opacity: 1;
818 }
819
820 .tab-content {
821 display: none;
822 background: var(--surface-overlay);
823 padding: var(--gap-page);
824 }
825
826 .tab-content.active {
827 display: block;
828 }
829
830 /* ===========================================
831 TABLES
832 =========================================== */
833
834 .data-table {
835 width: 100%;
836 border-collapse: collapse;
837 font-size: var(--text-note);
838 font-family: var(--font-body);
839 margin-bottom: var(--gap-section);
840 }
841
842 .data-table thead {
843 background: var(--surface-sunken);
844 }
845
846 .data-table th {
847 text-align: left;
848 padding: var(--gap-section);
849 font-weight: normal;
850 }
851
852 .data-table th.sortable {
853 cursor: pointer;
854 transition: background 0.2s ease;
855 user-select: none;
856 }
857
858 .data-table th.sortable:hover {
859 background: var(--border);
860 }
861
862 .data-table th.sortable:focus-visible {
863 outline: 2px solid var(--focus-ring);
864 outline-offset: 2px;
865 }
866
867 .data-table th.sortable::after {
868 content: " ^";
869 opacity: 0.3;
870 font-size: var(--text-fine);
871 }
872
873 .data-table tbody tr {
874 transition: background 0.1s ease;
875 }
876
877 .data-table tbody tr:nth-child(odd) {
878 background: var(--surface-overlay);
879 }
880
881 .data-table tbody tr:nth-child(even) {
882 background: var(--surface-overlay);
883 }
884
885 .data-table tbody tr:hover {
886 background: var(--surface-sunken);
887 }
888
889 .data-table td {
890 padding: var(--gap-section);
891 border-top: 1px solid var(--border);
892 }
893
894 /* Marks the row a table is about, used across the dashboard and library
895 tables. Lived in the pricing block until the fee calculator replaced it. */
896 .data-table .highlight {
897 background: var(--surface-overlay);
898 border-left: 3px solid var(--action);
899 }
900
901 /* ===========================================
902 COMPACT TABLES (dashboard/admin, lighter than .data-table)
903 =========================================== */
904
905 .compact-table {
906 width: 100%;
907 border-collapse: collapse;
908 }
909
910 .compact-table thead tr {
911 font-size: var(--text-note);
912 opacity: 0.7;
913 }
914
915 .compact-table th {
916 padding: var(--gap-peer) var(--gap-section);
917 text-align: left;
918 }
919
920 .compact-table td {
921 padding: var(--gap-section);
922 }
923
924 .compact-table tbody tr {
925 border-bottom: 1px solid var(--border);
926 }
927
928 /* ===========================================
929 UTILITIES
930 =========================================== */
931
932 .text-sm { font-size: var(--text-note); }
933 .text-xs { font-size: var(--text-fine); }
934 .muted { opacity: 0.7; }
935 .dimmed { opacity: 0.6; }
936 .dimmer { opacity: 0.5; }
937 .meta { font-size: var(--text-note); opacity: 0.7; }
938 .nowrap { white-space: nowrap; }
939 .scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
940 .text-center { text-align: center; }
941 .text-right { text-align: right; }
942 .fw-bold { font-weight: bold; }
943 .unstyled-link { text-decoration: none; color: inherit; }
944 .square-cover { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; }
945
946 /* Spacing utilities.
947 mt = margin-top, mb = margin-bottom, my = vertical, py = vertical padding.
948 Named for the relationship, same vocabulary as the --gap-* tokens they
949 read. Use sparingly; prefer parent layout primitives. */
950 .m-0 { margin: 0; }
951 .mt-0 { margin-top: 0; }
952 .mb-0 { margin-bottom: 0; }
953 .mt-peer { margin-top: var(--gap-peer); }
954 .mt-section { margin-top: var(--gap-section); }
955 .mt-pane { margin-top: var(--gap-pane); }
956 .mt-page { margin-top: var(--gap-page); }
957 .mb-peer { margin-bottom: var(--gap-peer); }
958 .mb-section { margin-bottom: var(--gap-section); }
959 .mb-pane { margin-bottom: var(--gap-pane); }
960 .my-peer { margin-block: var(--gap-peer); }
961 .my-section { margin-block: var(--gap-section); }
962 .py-section { padding-block: var(--gap-section); }
963
964 /* Min-width utilities for tables inside .scroll-x wrappers. */
965 .minw-300 { min-width: 300px; }
966 .minw-400 { min-width: 400px; }
967 .minw-500 { min-width: 500px; }
968 .minw-600 { min-width: 600px; }
969 .minw-700 { min-width: 700px; }
970 .minw-800 { min-width: 800px; }
971
972 /* Compact button sizes. Canonical: .small (broad use, 0.25rem 0.6rem 0.8rem).
973 The tiny / row-btn / cart-row-btn aliases are visually equivalent, within
974 sub-pixel of each other in the original templates, and folded into .small. */
975 .small { padding: var(--gap-bound) var(--gap-group); font-size: var(--text-fine); }
976
977 /* Canonical empty-state primitive. Use modifiers for tight/sized contexts.
978 Markup: <div class="empty-state">…</div> (optionally with .empty-state--compact
979 inside dropdowns or .empty-state--chart inside a fixed-height chart slot). */
980 .empty-state { text-align: center; padding: var(--gap-page); opacity: 0.6; }
981 .empty-state--compact { padding: var(--gap-section); font-size: var(--text-note); }
982 .empty-state--chart { height: 200px; padding: 0; display: flex; align-items: center; justify-content: center; opacity: 0.5; }
983 .empty-state-hint { font-family: var(--font-mono); font-size: var(--text-fine); margin-top: var(--gap-peer); opacity: 0.6; }
984
985 .tab-docs {
986 display: flex;
987 justify-content: flex-end;
988 margin-bottom: var(--gap-section);
989 }
990
991 .tab-docs a {
992 font-family: var(--font-mono);
993 font-size: var(--text-fine);
994 opacity: 0.5;
995 transition: opacity 0.2s ease;
996 }
997
998 .tab-docs a:hover {
999 opacity: 1;
1000 }
1001
1002 /* ===========================================
1003 BADGES
1004 =========================================== */
1005
1006 .badge {
1007 display: inline-block;
1008 padding: var(--gap-bound) var(--gap-peer);
1009 font-size: var(--text-fine);
1010 font-family: var(--font-mono);
1011 background: var(--primary-dark);
1012 color: var(--primary-light);
1013 }
1014
1015 /* Status vocabulary (charter: docs/design-system.md). A badge names the status
1016 it reports, and the colour follows from the status. */
1017 .badge--live {
1018 background: var(--success);
1019 }
1020
1021 .badge--pending {
1022 background: var(--warning);
1023 }
1024
1025 .badge--failed {
1026 background: var(--danger);
1027 }
1028
1029 .badge.free {
1030 background: var(--action);
1031 color: var(--primary-light);
1032 }
1033
1034 /* AI disclosure tier badges. See site-docs/public/about/generative-ai.md.
1035 Colors track the brand palette, violet for the cleanest case,
1036 warm-tan for disclosed-AI-use, charcoal for primarily-generated.
1037 Deliberately not red/yellow/green; this is disclosure, not alarm. */
1038 .badge.ai-tier {
1039 letter-spacing: 0.02em;
1040 }
1041 .badge.ai-tier-handmade {
1042 background: var(--action);
1043 color: var(--primary-light);
1044 }
1045 .badge.ai-tier-assisted {
1046 background: var(--warning);
1047 color: var(--primary-light);
1048 }
1049 .badge.ai-tier-generated {
1050 background: var(--content);
1051 color: var(--primary-light);
1052 }
1053
1054 .item-ai-tier {
1055 margin: var(--gap-bound) 0 var(--gap-section);
1056 }
1057
1058 .ai-disclosure {
1059 margin: var(--gap-section) 0;
1060 padding: var(--gap-section);
1061 background: var(--surface-sunken);
1062 border-left: 3px solid var(--warning);
1063 }
1064 .ai-disclosure-label {
1065 font-family: var(--font-mono);
1066 font-size: var(--text-fine);
1067 text-transform: uppercase;
1068 letter-spacing: 0.05em;
1069 color: var(--content-muted);
1070 margin-bottom: var(--gap-bound);
1071 }
1072 .ai-disclosure-text {
1073 font-size: var(--text-note);
1074 color: var(--content);
1075 }
1076
1077 /* Discover row: plain text mention per design decision 2026-06-03:
1078 no pill in the row, so fans skim the listing without disclosure noise
1079 and use the explicit filter when they care. */
1080 .discover-row-ai-tier {
1081 font-family: var(--font-mono);
1082 font-size: var(--text-fine);
1083 color: var(--content-muted);
1084 }
1085
1086 /* ===========================================
1087 TAGS
1088 =========================================== */
1089
1090 .tag {
1091 display: inline-block;
1092 background: var(--primary-dark);
1093 color: var(--primary-light);
1094 padding: var(--gap-bound) var(--gap-peer);
1095 font-size: var(--text-fine);
1096 margin-right: var(--gap-bound);
1097 }
1098
1099 .tag-input {
1100 background: var(--surface-sunken);
1101 padding: var(--gap-peer);
1102 margin-bottom: var(--gap-peer);
1103 display: flex;
1104 flex-wrap: wrap;
1105 gap: var(--gap-peer);
1106 min-height: 40px;
1107 }
1108
1109 .tag-input .tag {
1110 display: flex;
1111 align-items: center;
1112 gap: var(--gap-peer);
1113 padding: var(--gap-bound) var(--gap-peer);
1114 font-size: var(--text-note);
1115 }
1116
1117 .tag-input .tag button {
1118 background: none;
1119 border: none;
1120 color: var(--primary-light);
1121 cursor: pointer;
1122 padding: 0;
1123 font-size: var(--text-body);
1124 }
1125
1126 /* ===========================================
1127 CARDS
1128 =========================================== */
1129
1130 /* Canonical card primitive (charter: docs/design-system.md).
1131 Variants:
1132 .card filled, hover-step (default content card).
1133 .card.card-muted surface-muted fill, no hover (dashboard stat / analytics blocks).
1134 .card.card--bordered bordered, padded, no fill (marketing cards: feature/tier/use-case).
1135 .card.card--selectable radio-card pattern; pair with .is-selected.
1136 .card.card--grid grid-cell card (discover grid).
1137 No aliases. A template names the modifier it wants and adds its own
1138 component class beside it; a recipe reached only through a per-page name is
1139 a recipe nobody can find from the charter. */
1140 /* Fill and depth come from layout.css, generated by makeover-webview: the card
1141 is `--surface-raised` plus `--bevel-raised`, and `:hover` goes to
1142 `--hover-surface` on a fine pointer only. Declaring either here would take
1143 them back, since style.css is unlayered and unlayered beats the `makeover`
1144 layer whatever the specificity. The box-shadow this used to carry was
1145 `--shadow-card`, which already aliases `--bevel-raised`, so only the fill
1146 actually moved: `--surface-overlay` to `--surface-raised`. Those are equal on
1147 the platform default and a theme may set them apart. */
1148 .card {
1149 padding: var(--gap-pane);
1150 margin-bottom: var(--gap-section);
1151 text-decoration: none;
1152 color: var(--content);
1153 display: block;
1154 border: 1px solid var(--border);
1155 transition: background 0.2s ease;
1156 }
1157
1158 /* Muted card primitive. */
1159 .card-muted {
1160 background: var(--surface-sunken);
1161 padding: var(--gap-pane);
1162 }
1163 /* Bordered card. Templates put this modifier on the element alongside any
1164 component class of their own (.tier-card, .use-case-card, .fork-card), so
1165 the name the charter teaches is the name in the markup. */
1166 .card--bordered {
1167 background: transparent;
1168 padding: var(--gap-section);
1169 border: 1px solid var(--border);
1170 margin-bottom: 0;
1171 }
1172 .use-case-card { padding: var(--gap-section); }
1173 .fork-card { padding: var(--gap-pane); display: flex; flex-direction: column; }
1174
1175 .card-title {
1176 font-size: var(--text-subhead);
1177 margin-bottom: var(--gap-peer);
1178 font-family: var(--font-heading);
1179 }
1180
1181 .card-meta {
1182 font-size: var(--text-note);
1183 color: var(--content-muted);
1184 margin-bottom: var(--gap-peer);
1185 font-family: var(--font-mono);
1186 }
1187
1188 .card-description {
1189 font-size: var(--text-note);
1190 opacity: 0.8;
1191 font-family: var(--font-body);
1192 }
1193
1194 /* ===========================================
1195 STATS
1196 =========================================== */
1197
1198 .stats-grid {
1199 display: grid;
1200 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
1201 gap: var(--gap-section);
1202 margin-bottom: var(--gap-pane);
1203 }
1204
1205 .stats-row {
1206 display: flex;
1207 gap: var(--gap-pane);
1208 margin: var(--gap-pane) 0;
1209 }
1210
1211 .stat-box {
1212 background: var(--surface-overlay);
1213 padding: var(--gap-section) var(--gap-pane);
1214 flex: 1;
1215 text-align: center;
1216 }
1217
1218 .stat-box .number {
1219 font-family: var(--font-heading);
1220 font-weight: bold;
1221 font-size: var(--text-head);
1222 }
1223
1224 .stat-box .label {
1225 font-size: var(--text-fine);
1226 opacity: 0.7;
1227 }
1228
1229 /* .stat-card base recipe defined above with .card-muted */
1230
1231 .stat-label {
1232 font-size: var(--text-note);
1233 opacity: 0.7;
1234 margin-bottom: var(--gap-bound);
1235 }
1236
1237 .stat-value {
1238 font-size: var(--text-head);
1239 }
1240
1241 .stat-change {
1242 font-size: var(--text-note);
1243 opacity: 0.6;
1244 }
1245
1246 .stat-change.positive {
1247 color: var(--success);
1248 }
1249
1250 .stat-change.negative {
1251 color: var(--danger);
1252 }
1253
1254 /* ===========================================
1255 FILTER BAR
1256 =========================================== */
1257
1258 .filter-bar {
1259 display: flex;
1260 gap: var(--gap-peer);
1261 margin: var(--gap-pane) 0;
1262 flex-wrap: wrap;
1263 align-items: center;
1264 }
1265
1266 .filter-bar > .label {
1267 font-size: var(--text-note);
1268 opacity: 0.7;
1269 margin-right: var(--gap-peer);
1270 }
1271
1272 .filter-bar button {
1273 padding: var(--gap-bound) var(--gap-section);
1274 font-size: var(--text-note);
1275 }
1276
1277 /* ===========================================
1278 DISCUSSION SECTION
1279 =========================================== */
1280
1281 /* Sits inside the parent container; previously had max-width: 720px and
1282 margin: 2rem auto, which looked indented on wide pages (.library-page,
1283 .item-page) where every neighbouring card filled the container. Let the
1284 parent dictate width, article-page already constrains via
1285 .article-container; player pages have their own narrow layout. */
1286 .discussion-section {
1287 margin: var(--gap-page) 0;
1288 padding: var(--gap-pane);
1289 border: 1px solid var(--border);
1290 font-family: var(--font-mono);
1291 }
1292
1293 .discussion-section h3 {
1294 font-family: var(--font-heading);
1295 font-size: var(--text-subhead);
1296 margin: 0 0 var(--gap-peer);
1297 }
1298
1299 .discussion-section p {
1300 margin: 0 0 var(--gap-section);
1301 color: var(--content-muted);
1302 font-size: var(--text-note);
1303 }
1304
1305 .discussion-section a {
1306 color: var(--action);
1307 text-decoration: none;
1308 font-size: var(--text-note);
1309 }
1310
1311 /* ===========================================
1312 INFO & WARNING BOXES
1313 =========================================== */
1314
1315 /* Notification surface roles (charter: docs/design-system.md).
1316 Five distinct components. Keep them distinct, don't merge:
1317 .toast transient, JS-dismissible, bottom-right corner.
1318 .banner full-bleed page-top notice (sandbox, restart,
1319 founder pricing). One short sentence + optional link.
1320 .alert inline directive callout with uppercase mono title
1321 (NOTE / TIP / IMPORTANT / WARNING / CAUTION). Used in
1322 docs and longer-form bodies.
1323 .info-box informational headed block with h3 + bullet list
1324 inside forms / wizards (distinct register from .alert).
1325 .warning-box loud yellow-on-yellow attention-grabber for
1326 page-level warnings (delete confirms, account
1327 warnings). Louder than .alert-warning intentionally.
1328 .error-message inline form-field error; hidden by default, shown
1329 with .is-active. Distinct from page-level .alert. */
1330 .info-box {
1331 background: var(--surface-sunken);
1332 padding: var(--gap-section);
1333 margin-bottom: var(--gap-section);
1334 font-size: var(--text-note);
1335 font-family: var(--font-body);
1336 }
1337
1338 .info-box h3 {
1339 font-size: var(--text-body);
1340 font-weight: normal;
1341 margin-bottom: var(--gap-peer);
1342 }
1343
1344 .info-box ul {
1345 list-style: none;
1346 margin-top: var(--gap-peer);
1347 }
1348
1349 .info-box li {
1350 padding: var(--gap-bound) 0;
1351 }
1352
1353 .info-box li::before {
1354 content: "- ";
1355 opacity: 0.5;
1356 }
1357
1358 .warning-box {
1359 background: var(--warning);
1360 color: var(--primary-light);
1361 padding: var(--gap-section);
1362 margin-bottom: var(--gap-section);
1363 font-size: var(--text-note);
1364 font-family: var(--font-body);
1365 }
1366
1367 .error-message {
1368 font-family: var(--font-mono);
1369 color: var(--danger);
1370 background: color-mix(in oklch, var(--danger) 10%, var(--surface-page));
1371 padding: 12px;
1372 border: 1px solid var(--danger);
1373 border-radius: var(--radius-fine);
1374 text-align: center;
1375 font-size: var(--text-note);
1376 display: none;
1377 }
1378
1379 /* Full-page error layout (templates/pages/error.html). Distinct from
1380 the inline `.error-message` form-error class above; named to avoid
1381 collision. */
1382 .error-page {
1383 min-height: 100vh;
1384 display: flex;
1385 align-items: center;
1386 justify-content: center;
1387 padding: var(--gap-page);
1388 background: var(--surface-page);
1389 }
1390
1391 .error-container {
1392 text-align: center;
1393 max-width: 560px;
1394 }
1395
1396 .error-status {
1397 font-size: var(--text-note);
1398 font-weight: 600;
1399 letter-spacing: 0.08em;
1400 text-transform: uppercase;
1401 color: var(--content-muted);
1402 margin-bottom: var(--gap-section);
1403 }
1404
1405 .error-page-message {
1406 font-size: var(--text-head);
1407 line-height: 1.4;
1408 color: var(--content);
1409 margin-bottom: var(--gap-page);
1410 }
1411
1412 .error-actions {
1413 display: flex;
1414 gap: var(--gap-section);
1415 justify-content: center;
1416 }
1417
1418
1419 /* Minimal direct-purchase page (templates/pages/buy.html). Standalone
1420 page used for link-in-bio sharing, no site chrome, just a card.
1421 Scoped under .buy-page so the rules don't bleed into the rest of
1422 the app. Tokenized; previously lived as a page-isolated <style>. */
1423 .buy-page {
1424 min-height: 100vh;
1425 display: flex;
1426 align-items: center;
1427 justify-content: center;
1428 padding: var(--gap-page);
1429 }
1430
1431 .buy-page .buy-card {
1432 background: var(--surface-overlay);
1433 border-radius: var(--radius-panel);
1434 padding: var(--gap-page);
1435 max-width: 420px;
1436 width: 100%;
1437 box-shadow: var(--shadow-3);
1438 }
1439
1440 .buy-page .cover,
1441 .buy-page .no-cover {
1442 width: 100%;
1443 aspect-ratio: 1;
1444 max-height: 280px;
1445 border-radius: var(--radius-panel);
1446 margin-bottom: var(--gap-pane);
1447 background: var(--surface-raised);
1448 }
1449
1450 .buy-page .cover {
1451 object-fit: cover;
1452 }
1453
1454 .buy-page .no-cover {
1455 display: flex;
1456 align-items: center;
1457 justify-content: center;
1458 font-size: var(--text-hero);
1459 opacity: 0.2;
1460 }
1461
1462 .buy-page h1 {
1463 text-align: left;
1464 font-size: var(--text-head);
1465 margin-bottom: var(--gap-bound);
1466 }
1467
1468 .buy-page .creator {
1469 font-family: var(--font-mono);
1470 font-size: var(--text-note);
1471 opacity: 0.6;
1472 margin-bottom: var(--gap-pane);
1473 }
1474
1475 .buy-page .creator a {
1476 color: inherit;
1477 }
1478
1479 .buy-page .price {
1480 font-family: var(--font-mono);
1481 font-size: var(--text-subhead);
1482 margin-bottom: var(--gap-pane);
1483 }
1484
1485 .buy-page .description {
1486 font-size: var(--text-note);
1487 line-height: 1.5;
1488 opacity: 0.8;
1489 margin-bottom: var(--gap-pane);
1490 max-height: 4.5em;
1491 overflow: hidden;
1492 }
1493
1494 .buy-page .buy-btn {
1495 display: block;
1496 width: 100%;
1497 padding: 14px;
1498 background: var(--action);
1499 color: var(--primary-light);
1500 border: none;
1501 border-radius: var(--radius-panel);
1502 font-size: var(--text-body);
1503 font-weight: 600;
1504 cursor: pointer;
1505 text-align: center;
1506 text-decoration: none;
1507 transition: opacity 0.2s ease;
1508 }
1509
1510 .buy-page .buy-btn:hover {
1511 opacity: 0.85;
1512 }
1513
1514 .buy-page .buy-btn:disabled {
1515 opacity: 0.6;
1516 cursor: wait;
1517 }
1518
1519 .buy-page .pwyw-input {
1520 display: flex;
1521 align-items: center;
1522 gap: var(--gap-bound);
1523 margin-bottom: var(--gap-section);
1524 }
1525
1526 .buy-page .pwyw-input input {
1527 width: 100px;
1528 padding: 8px;
1529 border: 1px solid var(--border);
1530 border-radius: var(--radius-control);
1531 font-size: var(--text-body);
1532 }
1533
1534 .buy-page .note {
1535 font-size: var(--text-fine);
1536 opacity: 0.5;
1537 text-align: center;
1538 margin-top: var(--gap-section);
1539 }
1540
1541 .buy-page .note a {
1542 color: var(--action);
1543 }
1544
1545 .buy-page .footer {
1546 text-align: center;
1547 margin-top: var(--gap-pane);
1548 padding-top: var(--gap-section);
1549 border-top: 1px solid var(--border);
1550 font-size: var(--text-fine);
1551 opacity: 0.5;
1552 }
1553
1554 .buy-page .footer a {
1555 color: inherit;
1556 }
1557
1558 /* Sectioned content (templates/pages/item.html, project.html,
1559 library_downloads.html). Tab+panel pattern shared by all three
1560 pages, kept global, NOT scoped under .item-page. */
1561 .section-tabs {
1562 display: flex;
1563 gap: 0;
1564 border-bottom: 1px solid var(--border);
1565 margin-bottom: var(--gap-pane);
1566 }
1567
1568 .section-tab {
1569 background: none;
1570 border: none;
1571 padding: var(--gap-group) var(--gap-section);
1572 cursor: pointer;
1573 font-size: var(--text-body);
1574 opacity: 0.6;
1575 border-bottom: 2px solid transparent;
1576 font-family: var(--font-body);
1577 color: var(--content);
1578 }
1579
1580 .section-tab.is-selected {
1581 opacity: 1;
1582 border-bottom-color: var(--content);
1583 }
1584
1585 .section-tab:hover { opacity: 0.9; }
1586
1587 /* .section-panel.active is a visibility toggle (display: none / block),
1588 not a selection, kept as .active. */
1589 .section-panel { display: none; }
1590 .section-panel.active { display: block; }
1591 .section-panel p { margin-bottom: var(--gap-section); }
1592 .section-panel ul,
1593 .section-panel ol {
1594 margin-left: var(--gap-pane);
1595 margin-bottom: var(--gap-section);
1596 }
1597 .section-panel li { margin-bottom: var(--gap-peer); }
1598 .section-panel h1,
1599 .section-panel h2,
1600 .section-panel h3 {
1601 margin-top: var(--gap-section);
1602 margin-bottom: var(--gap-peer);
1603 }
1604 .section-panel pre {
1605 background: var(--surface-sunken);
1606 padding: var(--gap-section);
1607 overflow-x: auto;
1608 margin-bottom: var(--gap-section);
1609 }
1610 .section-panel code { font-size: var(--text-note); }
1611
1612 /* Item detail page (templates/pages/item.html). Scoped under .item-page
1613 body class so the .item-title / .item-meta / .item-price / .item-footer
1614 names don't collide with the store-front item-card on project.html. */
1615 .item-page .item-layout {
1616 display: grid;
1617 grid-template-columns: 400px 1fr;
1618 gap: var(--gap-page);
1619 margin-bottom: var(--gap-page);
1620 }
1621
1622 .item-page .item-layout.no-media { grid-template-columns: 1fr; }
1623
1624 .item-page .item-title {
1625 font-size: var(--text-title);
1626 margin-bottom: var(--gap-peer);
1627 }
1628
1629 .item-page .item-creator {
1630 font-size: var(--text-body);
1631 opacity: 0.7;
1632 margin-bottom: var(--gap-pane);
1633 }
1634
1635 .item-page .item-creator a { color: var(--content); }
1636
1637 .item-page .item-price {
1638 font-size: var(--text-display);
1639 margin-bottom: var(--gap-pane);
1640 }
1641
1642 .item-page .item-meta {
1643 display: grid;
1644 grid-template-columns: 1fr 1fr;
1645 gap: var(--gap-section);
1646 margin-bottom: var(--gap-pane);
1647 padding-bottom: var(--gap-pane);
1648 border-bottom: 1px solid var(--border);
1649 }
1650
1651 .item-page .meta-item { font-size: var(--text-note); }
1652 .item-page .meta-label { opacity: 0.7; margin-bottom: var(--gap-bound); }
1653 .item-page .meta-value { font-weight: bold; }
1654
1655 .item-page .item-tags { margin-bottom: var(--gap-pane); }
1656
1657 /* purchase-box uses .card-muted for the box; only its h3/ul/li specifics remain. */
1658 .purchase-box h3 {
1659 font-size: var(--text-body);
1660 font-weight: normal;
1661 margin-bottom: var(--gap-section);
1662 }
1663 .purchase-box ul { list-style: none; margin-bottom: var(--gap-section); }
1664 .purchase-box li { padding: var(--gap-bound) 0; }
1665 .purchase-box li::before {
1666 content: "→ ";
1667 opacity: 0.5;
1668 }
1669
1670 .item-page .btn-primary {
1671 width: 100%;
1672 padding: var(--gap-section) var(--gap-page);
1673 font-size: var(--text-lead);
1674 }
1675
1676 .item-page .btn-secondary {
1677 width: 100%;
1678 margin-top: var(--gap-peer);
1679 }
1680
1681 .item-page .payment-note {
1682 font-size: var(--text-note);
1683 opacity: 0.6;
1684 text-align: center;
1685 margin-top: var(--gap-section);
1686 }
1687
1688 /* bundle-child uses .list-row co-class; only the gap-4 override remains. */
1689 .bundle-child { gap: var(--gap-section); padding: var(--gap-section) 0; }
1690
1691 .item-page .bundle-child-type {
1692 font-size: var(--text-fine);
1693 padding: var(--gap-bound) var(--gap-group);
1694 background: var(--surface-sunken);
1695 white-space: nowrap;
1696 }
1697
1698 .item-page .bundle-child-title { flex: 1; }
1699 .item-page .bundle-child-title a { color: var(--content); }
1700 .item-page .bundle-child-price { font-size: var(--text-note); opacity: 0.7; }
1701
1702 .item-page .bundle-notice {
1703 background: var(--surface-sunken);
1704 padding: var(--gap-pane);
1705 margin-bottom: var(--gap-pane);
1706 text-align: center;
1707 }
1708
1709 .item-page .bundle-notice a { color: var(--content); }
1710
1711 .item-page .item-description p { margin-bottom: var(--gap-section); text-align: left; }
1712 .item-page .item-description ul { margin-left: var(--gap-pane); margin-bottom: var(--gap-section); }
1713 .item-page .item-description li { margin-bottom: var(--gap-peer); }
1714
1715 .item-page .features-grid {
1716 display: grid;
1717 grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
1718 gap: var(--gap-pane);
1719 }
1720 .item-page .version-number { font-size: var(--text-lead); }
1721 .item-page .item-footer {
1722 margin-top: var(--gap-page);
1723 padding-top: var(--gap-page);
1724 border-top: 1px solid var(--border);
1725 text-align: center;
1726 opacity: 0.6;
1727 font-size: var(--text-note);
1728 }
1729
1730 .item-page .item-footer a { color: var(--content); }
1731
1732 @media (max-width: 839px) {
1733 .item-page .item-layout { grid-template-columns: 1fr; }
1734 }
1735
1736 @media (max-width: 599px) {
1737 .item-page .item-title { font-size: var(--text-head); }
1738 .item-page .item-price { font-size: var(--text-title); }
1739 .item-page .item-meta { grid-template-columns: 1fr; gap: var(--gap-peer); }
1740 .item-page .item-media,
1741 .item-page .item-details { padding: var(--gap-section); }
1742 .item-page .item-description { padding: var(--gap-section); }
1743 .item-page .item-description h2 { font-size: var(--text-subhead); }
1744 .item-page .purchase-box { padding: var(--gap-section); }
1745 }
1746
1747 /* Project store-front page (templates/pages/project.html). Scoped under
1748 .project-page body class, `.item-card`, `.item-title`, `.item-meta`,
1749 `.item-price`, etc. here mean store-front item cards, distinct from
1750 the item-detail-page versions scoped under .item-page above. */
1751 .project-page .store-header { margin-bottom: var(--gap-page); }
1752 .project-page .store-title { font-size: var(--text-hero); margin-bottom: var(--gap-peer); }
1753 .project-page .store-meta {
1754 font-size: var(--text-note);
1755 opacity: 0.7;
1756 margin-bottom: var(--gap-pane);
1757 }
1758 .project-page .store-meta a { color: var(--content); }
1759 .project-page .store-description {
1760 font-size: var(--text-lead);
1761 max-width: 800px;
1762 margin-bottom: var(--gap-page);
1763 text-align: left;
1764 }
1765 .project-page .store-actions {
1766 display: flex;
1767 gap: var(--gap-section);
1768 flex-wrap: wrap;
1769 }
1770
1771 .project-page .items-section { margin-bottom: var(--gap-page); }
1772
1773 .project-page .items-grid {
1774 display: grid;
1775 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
1776 gap: var(--gap-page);
1777 }
1778
1779 .project-page .item-card {
1780 background: var(--surface-overlay);
1781 transition: background 0.2s ease;
1782 cursor: pointer;
1783 display: flex;
1784 flex-direction: column;
1785 height: 100%;
1786 }
1787
1788 .project-page .item-card:hover { background: var(--surface-sunken); }
1789
1790 .project-page a.item-thumbnail {
1791 width: 100%;
1792 height: 300px;
1793 background: var(--border);
1794 display: flex;
1795 align-items: center;
1796 justify-content: center;
1797 font-size: var(--text-hero);
1798 text-decoration: none;
1799 overflow: hidden;
1800 }
1801
1802 .project-page a.item-thumbnail img {
1803 width: 100%;
1804 height: 100%;
1805 object-fit: cover;
1806 display: block;
1807 }
1808
1809 /* The dimming belongs to the placeholder, not to the slot. It was on the slot,
1810 so the day covers started rendering here they would have rendered at 30%. */
1811 .project-page a.item-thumbnail-empty { opacity: 0.3; }
1812 .project-page a.item-thumbnail-empty:hover { opacity: 0.5; }
1813
1814 .project-page .item-content {
1815 padding: var(--gap-pane);
1816 display: flex;
1817 flex-direction: column;
1818 flex-grow: 1;
1819 }
1820
1821 .project-page .item-title {
1822 font-size: var(--text-subhead);
1823 margin-bottom: var(--gap-peer);
1824 font-family: var(--font-heading);
1825 font-weight: bold;
1826 }
1827
1828 .project-page .item-title a:hover { text-decoration: underline; }
1829
1830 .project-page .item-meta {
1831 font-size: var(--text-note);
1832 opacity: 0.7;
1833 margin-bottom: var(--gap-section);
1834 }
1835
1836 .project-page .item-tags { margin-bottom: var(--gap-section); }
1837
1838 .project-page .item-description {
1839 font-size: var(--text-note);
1840 margin-bottom: var(--gap-section);
1841 opacity: 0.8;
1842 text-align: left;
1843 flex-grow: 1;
1844 }
1845
1846 .project-page .item-footer {
1847 display: flex;
1848 justify-content: space-between;
1849 align-items: center;
1850 margin-top: auto;
1851 }
1852
1853 .project-page .item-price { font-size: var(--text-subhead); }
1854 .project-page .item-stats { font-size: var(--text-note); opacity: 0.6; }
1855
1856 .project-page .store-footer {
1857 margin-top: var(--gap-page);
1858 padding-top: var(--gap-page);
1859 border-top: 1px solid var(--border);
1860 opacity: 0.6;
1861 }
1862
1863 .project-page .store-footer a { color: var(--content); }
1864
1865 .project-page .item-content .btn-primary,
1866 .project-page .item-content .btn-secondary {
1867 width: 100%;
1868 margin-top: var(--gap-section);
1869 }
1870
1871 /* project.html allows .section-tabs to wrap (other pages don't). */
1872 .project-page .section-tabs { flex-wrap: wrap; }
1873
1874 .project-page .tiers-section { margin-bottom: var(--gap-page); }
1875
1876 .project-page .tiers-grid {
1877 display: grid;
1878 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
1879 gap: var(--gap-pane);
1880 }
1881
1882 .project-page .tier-card {
1883 background: var(--surface-overlay);
1884 padding: var(--gap-page);
1885 display: flex;
1886 flex-direction: column;
1887 }
1888
1889 .project-page .tier-name {
1890 font-family: var(--font-heading);
1891 font-weight: bold;
1892 font-size: var(--text-subhead);
1893 margin-bottom: var(--gap-peer);
1894 }
1895
1896 .project-page .tier-price {
1897 font-size: var(--text-head);
1898 margin-bottom: var(--gap-section);
1899 }
1900
1901 .project-page .tier-description {
1902 font-size: var(--text-note);
1903 opacity: 0.8;
1904 margin-bottom: var(--gap-pane);
1905 flex-grow: 1;
1906 text-align: left;
1907 }
1908
1909 .project-page .tier-card form { margin-top: auto; }
1910 .project-page .tier-card button,
1911 .project-page .tier-card .btn-primary { width: 100%; }
1912
1913 .project-page .tier-active-badge {
1914 font-size: var(--text-note);
1915 padding: var(--gap-peer) var(--gap-section);
1916 background: var(--surface-sunken);
1917 text-align: center;
1918 opacity: 0.7;
1919 }
1920
1921 @media (max-width: 599px) {
1922 .project-page .store-title { font-size: var(--text-title); }
1923 .project-page a.item-thumbnail { height: 200px; }
1924 .project-page .store-description { font-size: var(--text-body); }
1925 }
1926
1927 /* Library downloads page (templates/pages/library_downloads.html).
1928 Scoped under .library-page body class. Class names like .item-footer,
1929 .bundle-child, etc. overlap with .item-page; the scope keeps them
1930 distinct. */
1931 .library-page .library-header {
1932 display: grid;
1933 grid-template-columns: 200px 1fr;
1934 gap: var(--gap-pane);
1935 margin-bottom: var(--gap-page);
1936 align-items: center;
1937 }
1938
1939 .library-page .library-header.no-cover { grid-template-columns: 1fr; }
1940
1941 .library-page .library-cover img {
1942 width: 100%;
1943 aspect-ratio: 1 / 1;
1944 object-fit: cover;
1945 display: block;
1946 }
1947
1948 .library-page .library-title {
1949 font-size: var(--text-title);
1950 margin-bottom: var(--gap-bound);
1951 }
1952
1953 /* Byline sits under the centered H1 (global `h1 { text-align: center }`),
1954 so center it too, otherwise it floats flush-left while the title is
1955 centered above. Same for the back-link breadcrumb. When a cover is
1956 present the grid scope below restores left-alignment in the text column. */
1957 .library-page .library-creator {
1958 font-size: var(--text-body);
1959 opacity: 0.7;
1960 margin-bottom: var(--gap-section);
1961 text-align: center;
1962 }
1963
1964 .library-page .library-creator a { color: var(--content); }
1965
1966 .library-page .library-back {
1967 font-size: var(--text-note);
1968 opacity: 0.7;
1969 text-align: center;
1970 }
1971 .library-page .library-back a { color: var(--content); }
1972
1973 /* When a cover is present, the title block sits in the right grid column;
1974 left-align in that context so the column reads as a coherent block next
1975 to the cover instead of a centered island. */
1976 .library-page .library-header:not(.no-cover) .library-title,
1977 .library-page .library-header:not(.no-cover) .library-creator {
1978 text-align: left;
1979 }
1980
1981 .library-page .downloads-hero h2 {
1982 font-size: var(--text-head);
1983 margin-bottom: var(--gap-section);
1984 padding-bottom: var(--gap-peer);
1985 border-bottom: 1px solid var(--border);
1986 }
1987
1988 .library-page .download-row {
1989 display: flex;
1990 align-items: center;
1991 gap: var(--gap-section);
1992 padding: var(--gap-group) 0;
1993 border-bottom: 1px solid var(--border);
1994 }
1995
1996 .library-page .download-row:last-child { border-bottom: none; }
1997
1998 .library-page .download-version {
1999 min-width: 4em;
2000 font-family: var(--font-mono);
2001 font-size: var(--text-note);
2002 }
2003
2004 .library-page .download-label { flex: 1; }
2005
2006 .library-page .download-size {
2007 font-size: var(--text-note);
2008 opacity: 0.6;
2009 min-width: 5em;
2010 text-align: right;
2011 }
2012
2013 .library-page .download-notice {
2014 background: var(--surface-sunken);
2015 padding: var(--gap-pane);
2016 margin-bottom: var(--gap-page);
2017 font-size: var(--text-note);
2018 opacity: 0.8;
2019 }
2020
2021 .library-page .download-notice strong { font-family: var(--font-mono); }
2022
2023 /* Inline download list on library_audio.html / library_video.html
2024 (below the media player). */
2025
2026 .library-page .library-downloads h3 {
2027 font-size: var(--text-lead);
2028 margin-bottom: var(--gap-section);
2029 }
2030
2031 .library-page .library-section h2 {
2032 font-size: var(--text-subhead);
2033 margin-bottom: var(--gap-section);
2034 padding-bottom: var(--gap-peer);
2035 border-bottom: 1px solid var(--border);
2036 }
2037
2038 .library-page .bundle-child {
2039 display: flex;
2040 align-items: center;
2041 gap: var(--gap-section);
2042 padding: var(--gap-section) 0;
2043 border-bottom: 1px solid var(--border);
2044 }
2045
2046 .library-page .bundle-child:last-child { border-bottom: none; }
2047
2048 .library-page .bundle-child-type {
2049 font-size: var(--text-fine);
2050 padding: var(--gap-bound) var(--gap-group);
2051 background: var(--surface-sunken);
2052 white-space: nowrap;
2053 }
2054
2055 .library-page .bundle-child-title { flex: 1; }
2056 .library-page .bundle-child-title a { color: var(--content); }
2057
2058 .library-page .item-footer {
2059 margin-top: var(--gap-page);
2060 padding-top: var(--gap-page);
2061 border-top: 1px solid var(--border);
2062 text-align: center;
2063 opacity: 0.6;
2064 font-size: var(--text-note);
2065 }
2066
2067 .library-page .item-footer a { color: var(--content); }
2068
2069 @media (max-width: 599px) {
2070 .library-page .library-header { grid-template-columns: 1fr; }
2071 .library-page .library-cover img { max-width: 200px; }
2072 }
2073
2074 /* Article reader pages (templates/pages/text_reader.html, blog_post.html,
2075 library_text.html). Long-form article layout, centered narrow column,
2076 author header, large heading, magazine-style body type. Scoped under
2077 .article-page body class. */
2078 .article-page .article-container {
2079 max-width: 680px;
2080 margin: 0 auto;
2081 padding: var(--gap-page) var(--gap-pane) var(--gap-page);
2082 }
2083
2084 .article-page .author-header {
2085 display: flex;
2086 align-items: center;
2087 gap: var(--gap-section);
2088 margin-bottom: var(--gap-page);
2089 }
2090
2091 .article-page .author-avatar {
2092 width: 48px;
2093 height: 48px;
2094 background: var(--surface-sunken);
2095 border-radius: var(--radius-round);
2096 display: flex;
2097 align-items: center;
2098 justify-content: center;
2099 font-family: var(--font-heading);
2100 font-size: var(--text-subhead);
2101 color: var(--content);
2102 }
2103
2104 .article-page .author-info { flex: 1; }
2105
2106 .article-page .author-name {
2107 font-family: var(--font-mono);
2108 font-size: var(--text-body);
2109 color: var(--content);
2110 }
2111
2112
2113 .article-page .author-name a:hover { text-decoration: underline; }
2114
2115 .article-page .article-meta {
2116 font-family: var(--font-mono);
2117 font-size: var(--text-note);
2118 color: var(--content-muted);
2119 }
2120
2121 .article-page .article-title {
2122 font-family: var(--font-heading);
2123 font-size: var(--text-display);
2124 font-weight: normal;
2125 line-height: 1.2;
2126 margin-bottom: var(--gap-pane);
2127 color: var(--content);
2128 text-align: left;
2129 }
2130
2131 .article-page .article-deck {
2132 font-family: var(--font-mono);
2133 font-size: var(--text-subhead);
2134 color: var(--content-muted);
2135 margin-bottom: var(--gap-page);
2136 line-height: 1.6;
2137 }
2138
2139 .article-page .article-body {
2140 font-family: var(--font-body);
2141 font-size: var(--text-lead);
2142 line-height: 1.9;
2143 }
2144
2145 .article-page .article-body p { margin-bottom: var(--gap-pane); }
2146
2147 .article-page .article-body h1 {
2148 font-family: var(--font-heading);
2149 font-size: var(--text-title);
2150 font-weight: normal;
2151 margin-top: var(--gap-page);
2152 margin-bottom: var(--gap-section);
2153 color: var(--content);
2154 text-align: left;
2155 }
2156
2157 .article-page .article-body h2 {
2158 font-family: var(--font-mono);
2159 font-size: var(--text-head);
2160 font-weight: normal;
2161 margin-top: var(--gap-page);
2162 margin-bottom: var(--gap-section);
2163 color: var(--content);
2164 }
2165
2166 .article-page .article-body h3 {
2167 font-family: var(--font-mono);
2168 font-size: var(--text-subhead);
2169 font-weight: normal;
2170 margin-top: var(--gap-page);
2171 margin-bottom: var(--gap-section);
2172 color: var(--content);
2173 }
2174
2175 .article-page .article-body blockquote {
2176 border-left: 3px solid var(--action);
2177 padding-left: var(--gap-pane);
2178 margin: var(--gap-page) 0;
2179 font-style: italic;
2180 color: var(--content-muted);
2181 }
2182
2183 .article-page .article-body ul,
2184 .article-page .article-body ol {
2185 margin-bottom: var(--gap-pane);
2186 padding-left: var(--gap-pane);
2187 }
2188
2189 .article-page .article-body li { margin-bottom: var(--gap-peer); }
2190
2191 .article-page .article-body a {
2192 color: var(--action);
2193 text-decoration: underline;
2194 text-decoration-color: color-mix(in oklch, var(--action) 40%, transparent);
2195 text-underline-offset: 2px;
2196 }
2197
2198 .article-page .article-body a:hover {
2199 text-decoration-color: var(--action);
2200 }
2201
2202 .article-page .article-body strong { font-weight: bold; }
2203 .article-page .article-body em { font-style: italic; }
2204
2205 .article-page .article-body code {
2206 font-family: var(--font-mono);
2207 background: var(--surface-sunken);
2208 padding: var(--gap-bound) var(--gap-peer);
2209 font-size: var(--text-body);
2210 }
2211
2212 .article-page .article-body pre {
2213 background: var(--surface-sunken);
2214 padding: var(--gap-section);
2215 overflow-x: auto;
2216 margin-bottom: var(--gap-pane);
2217 }
2218
2219 .article-page .article-body pre code {
2220 background: none;
2221 padding: 0;
2222 }
2223
2224 .article-page .article-body hr {
2225 border: none;
2226 text-align: center;
2227 margin: var(--gap-page) 0;
2228 }
2229
2230 .article-page .article-body hr::before {
2231 content: "* * *";
2232 color: var(--content-muted);
2233 letter-spacing: 1rem;
2234 }
2235
2236 .article-page .article-footer {
2237 margin-top: var(--gap-page);
2238 padding-top: var(--gap-page);
2239 border-top: 1px solid var(--border);
2240 }
2241
2242 .article-page .author-bio {
2243 display: flex;
2244 gap: var(--gap-section);
2245 padding: var(--gap-pane);
2246 background: var(--surface-overlay);
2247 }
2248
2249 .article-page .author-bio .author-avatar {
2250 width: 64px;
2251 height: 64px;
2252 font-size: var(--text-head);
2253 flex-shrink: 0;
2254 }
2255
2256 .article-page .author-bio-content { flex: 1; }
2257
2258 .article-page .author-bio-name {
2259 font-family: var(--font-mono);
2260 font-size: var(--text-lead);
2261 margin-bottom: var(--gap-peer);
2262 }
2263
2264
2265 .article-page .author-bio-name a:hover { text-decoration: underline; }
2266
2267 .article-page .article-tags {
2268 margin-top: var(--gap-page);
2269 display: flex;
2270 gap: var(--gap-peer);
2271 flex-wrap: wrap;
2272 }
2273
2274 .article-page .article-tag {
2275 font-family: var(--font-mono);
2276 background: var(--surface-sunken);
2277 color: var(--content);
2278 padding: var(--gap-peer) var(--gap-section);
2279 font-size: var(--text-fine);
2280 text-decoration: none;
2281 }
2282
2283 /* Share controls at the end of the article, inside .article-footer beside
2284 the tags or the blog crumb. Was a page-bottom footer below the comments
2285 until the site-footer consolidation; the control belongs with the piece
2286 it copies, not under the discussion. */
2287 .article-page .reader-actions {
2288 font-family: var(--font-mono);
2289 font-size: var(--text-note);
2290 margin-top: var(--gap-section);
2291 }
2292
2293 .article-page .reader-actions a { color: var(--content); text-decoration: none; }
2294 .article-page .reader-actions a:hover { text-decoration: underline; }
2295
2296 /* Blog-specific: nav crumb above the article. */
2297 .article-page .blog-nav {
2298 font-family: var(--font-mono);
2299 font-size: var(--text-note);
2300 margin-bottom: var(--gap-page);
2301 }
2302
2303 .article-page .blog-nav a { color: var(--content); text-decoration: none; }
2304 .article-page .blog-nav a:hover { text-decoration: underline; }
2305
2306 @media (max-width: 599px) {
2307 .article-page .article-title { font-size: var(--text-title); }
2308 .article-page .article-deck { font-size: var(--text-lead); }
2309 .article-page .article-body { font-size: var(--text-body); }
2310 }
2311
2312 /* Admin dashboards (templates/dashboards/admin-*.html). Every admin
2313 page had a duplicate `h1 { font-size: 2rem; margin-bottom: 0.5rem; }`
2314 inline, consolidated here. */
2315
2316 /* System health dashboard (templates/pages/health.html). */
2317 .health-page {
2318 padding: var(--gap-page);
2319 }
2320
2321 .health-page .health-container {
2322 max-width: 900px;
2323 margin: 0 auto;
2324 }
2325
2326 .health-page .health-grid {
2327 display: grid;
2328 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
2329 gap: var(--gap-pane);
2330 margin-bottom: var(--gap-page);
2331 }
2332
2333 .health-page .health-card {
2334 background: var(--surface-sunken);
2335 padding: var(--gap-pane);
2336 }
2337
2338 .health-page .health-card h3 {
2339 font-family: var(--font-mono);
2340 font-size: var(--text-note);
2341 margin-bottom: var(--gap-section);
2342 display: flex;
2343 align-items: center;
2344 gap: var(--gap-peer);
2345 }
2346
2347 .health-page .status-indicator {
2348 width: 10px;
2349 height: 10px;
2350 border-radius: var(--radius-round);
2351 display: inline-block;
2352 }
2353
2354 .health-page .status-ok { background: var(--success); }
2355 .health-page .status-warn { background: var(--warning); }
2356 .health-page .status-error { background: var(--danger); }
2357 .health-page .status-unknown { background: var(--content-muted); }
2358
2359 .health-page .health-card dl {
2360 display: grid;
2361 grid-template-columns: auto 1fr;
2362 gap: var(--gap-peer) var(--gap-section);
2363 font-size: var(--text-note);
2364 }
2365
2366 .health-page .health-card dt { opacity: 0.7; }
2367 .health-page .health-card dd {
2368 font-family: var(--font-mono);
2369 text-align: right;
2370 }
2371
2372 .health-page .section-title {
2373 font-family: var(--font-mono);
2374 font-size: var(--text-body);
2375 margin: var(--gap-page) 0 var(--gap-section);
2376 padding-bottom: var(--gap-peer);
2377 border-bottom: 1px solid var(--border);
2378 }
2379 .health-page .test-list {
2380 list-style: none;
2381 padding: 0;
2382 margin: 0;
2383 }
2384
2385 .health-page .test-list li {
2386 display: flex;
2387 justify-content: space-between;
2388 padding: var(--gap-peer) 0;
2389 border-bottom: 1px solid var(--border);
2390 font-size: var(--text-note);
2391 }
2392
2393 .health-page .test-list li:last-child { border-bottom: none; }
2394
2395 .health-page .test-name { font-family: var(--font-mono); }
2396
2397 .health-page .test-pass { color: var(--success); }
2398 .health-page .test-fail { color: var(--danger); }
2399 .health-page .summary-bar {
2400 display: flex;
2401 gap: var(--gap-page);
2402 padding: var(--gap-section);
2403 background: var(--surface-sunken);
2404 margin-bottom: var(--gap-page);
2405 font-family: var(--font-mono);
2406 font-size: var(--text-note);
2407 }
2408
2409 .health-page .summary-item {
2410 display: flex;
2411 align-items: center;
2412 gap: var(--gap-peer);
2413 }
2414
2415 .health-page .timestamp {
2416 font-size: var(--text-fine);
2417 opacity: 0.6;
2418 text-align: right;
2419 margin-top: var(--gap-page);
2420 }
2421
2422 .health-page details.check-list {
2423 background: var(--surface-overlay);
2424 padding: var(--gap-pane);
2425 margin-bottom: var(--gap-page);
2426 }
2427
2428 .health-page details.check-list summary {
2429 font-family: var(--font-mono);
2430 font-size: var(--text-note);
2431 cursor: pointer;
2432 list-style: none;
2433 display: flex;
2434 align-items: center;
2435 gap: var(--gap-peer);
2436 }
2437
2438 .health-page details.check-list summary::-webkit-details-marker { display: none; }
2439
2440 .health-page details.check-list summary::before {
2441 content: "â–¶";
2442 font-size: var(--text-fine);
2443 transition: transform 0.15s;
2444 }
2445
2446 .health-page details.check-list[open] summary::before {
2447 transform: rotate(90deg);
2448 }
2449
2450 .health-page details.check-list .test-list {
2451 margin-top: var(--gap-section);
2452 }
2453 /* Import data wizard (templates/dashboards/dashboard-import.html).
2454 Page-specific upload + column-mapping + progress flow. Migration also
2455 fixed token bypasses: --border-color → --border, --accent → --highlight,
2456 --success-background → --success-bg, --error-background → --error-bg. */
2457
2458 .import-page .back-link {
2459 display: inline-block;
2460 margin-bottom: var(--gap-pane);
2461 font-size: var(--text-note);
2462 opacity: 0.7;
2463 }
2464
2465 .import-page .back-link:hover { opacity: 1; }
2466
2467 .import-page .form-group { margin-bottom: var(--gap-pane); }
2468
2469 .import-page .form-group label {
2470 display: block;
2471 font-family: var(--font-mono);
2472 font-size: var(--text-note);
2473 font-weight: bold;
2474 margin-bottom: var(--gap-peer);
2475 }
2476
2477 .import-page .form-group select,
2478 .import-page .form-group input[type="file"] {
2479 width: 100%;
2480 padding: var(--gap-peer);
2481 font-size: var(--text-note);
2482 border: 1px solid var(--border);
2483 background: var(--surface-page);
2484 }
2485
2486 .import-page .csv-preview {
2487 margin-top: var(--gap-section);
2488 overflow-x: auto;
2489 }
2490
2491 .import-page .csv-preview table {
2492 width: 100%;
2493 border-collapse: collapse;
2494 font-size: var(--text-note);
2495 }
2496
2497 .import-page .csv-preview th,
2498 .import-page .csv-preview td {
2499 padding: var(--gap-peer) var(--gap-group);
2500 border: 1px solid var(--border);
2501 text-align: left;
2502 }
2503
2504 .import-page .csv-preview th {
2505 background: var(--surface-sunken);
2506 font-family: var(--font-mono);
2507 }
2508
2509 .import-page .column-mapping { margin-top: var(--gap-section); }
2510
2511 .import-page .mapping-row {
2512 display: flex;
2513 align-items: center;
2514 gap: var(--gap-section);
2515 margin-bottom: var(--gap-peer);
2516 }
2517
2518 .import-page .mapping-row label {
2519 min-width: 100px;
2520 font-size: var(--text-note);
2521 font-family: var(--font-mono);
2522 margin-bottom: 0;
2523 }
2524
2525 .import-page .mapping-row select {
2526 flex: 1;
2527 padding: var(--gap-bound);
2528 font-size: var(--text-note);
2529 }
2530
2531 .import-page .progress-stats {
2532 display: flex;
2533 gap: var(--gap-page);
2534 font-size: var(--text-note);
2535 font-family: var(--font-mono);
2536 }
2537
2538 .import-page .progress-stats .stat-value { font-weight: bold; }
2539
2540 .import-page .import-result {
2541 padding: var(--gap-section);
2542 margin-bottom: var(--gap-section);
2543 font-size: var(--text-note);
2544 }
2545
2546 .import-page .import-result.success { background: color-mix(in oklch, var(--success) 12%, var(--surface-page)); }
2547 .import-page .import-result.error { background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); }
2548
2549 .import-page .error-log {
2550 margin-top: var(--gap-section);
2551 padding: var(--gap-peer);
2552 background: var(--surface-sunken);
2553 font-family: var(--font-mono);
2554 font-size: var(--text-fine);
2555 max-height: 200px;
2556 overflow-y: auto;
2557 white-space: pre-wrap;
2558 }
2559
2560 .import-page .import-history { margin-top: var(--gap-page); }
2561
2562 .import-page .history-table {
2563 width: 100%;
2564 border-collapse: collapse;
2565 font-size: var(--text-note);
2566 }
2567
2568 .import-page .history-table th,
2569 .import-page .history-table td {
2570 padding: var(--gap-peer);
2571 border-bottom: 1px solid var(--border);
2572 text-align: left;
2573 }
2574
2575 .import-page .history-table th {
2576 font-family: var(--font-mono);
2577 font-weight: bold;
2578 }
2579
2580 .import-page .import-note {
2581 background: var(--surface-sunken);
2582 padding: var(--gap-section);
2583 margin-top: var(--gap-page);
2584 font-size: var(--text-note);
2585 }
2586
2587 .import-page .import-note h3 {
2588 font-family: var(--font-mono);
2589 font-size: var(--text-note);
2590 margin-bottom: var(--gap-peer);
2591 }
2592
2593 /* Delete account page (templates/dashboards/dashboard-delete-account.html).
2594 Overrides .warning-box to use --danger (delete is danger, not warning)
2595 and the .form-status partial styling. Scoped to keep the global versions
2596 intact for other pages. */
2597
2598 .delete-account-page .warning-box {
2599 background: var(--danger);
2600 color: var(--primary-light);
2601 padding: var(--gap-pane);
2602 margin-bottom: var(--gap-page);
2603 }
2604
2605 .delete-account-page .warning-box h3 {
2606 font-family: var(--font-mono);
2607 font-size: var(--text-body);
2608 margin-bottom: var(--gap-peer);
2609 }
2610
2611 .delete-account-page .warning-box ul {
2612 margin: var(--gap-peer) 0 0 var(--gap-pane);
2613 font-size: var(--text-note);
2614 }
2615
2616 .delete-account-page .warning-box li { margin-bottom: var(--gap-bound); }
2617
2618 .delete-account-page .export-prompt {
2619 background: var(--surface-sunken);
2620 padding: var(--gap-pane);
2621 margin-bottom: var(--gap-page);
2622 }
2623
2624 .delete-account-page .export-prompt h3 {
2625 font-family: var(--font-mono);
2626 font-size: var(--text-body);
2627 margin-bottom: var(--gap-peer);
2628 }
2629
2630 .delete-account-page .export-prompt p {
2631 font-size: var(--text-note);
2632 margin-bottom: var(--gap-section);
2633 }
2634
2635 .delete-account-page .delete-form {
2636 background: var(--surface-overlay);
2637 padding: var(--gap-pane);
2638 }
2639
2640 .delete-account-page .delete-form .form-group { margin-bottom: var(--gap-pane); }
2641
2642 .delete-account-page .delete-form label {
2643 display: block;
2644 margin-bottom: var(--gap-peer);
2645 font-family: var(--font-mono);
2646 font-size: var(--text-note);
2647 }
2648
2649 .delete-account-page .delete-form .hint {
2650 font-size: var(--text-note);
2651 opacity: 0.7;
2652 margin-top: var(--gap-bound);
2653 }
2654
2655 .delete-account-page .delete-form input[type="text"] {
2656 width: 100%;
2657 padding: var(--gap-section);
2658 font-size: var(--text-body);
2659 border: 2px solid var(--border);
2660 background: var(--surface-sunken);
2661 }
2662
2663 .delete-account-page .delete-form input[type="text"]:focus {
2664 border-color: var(--danger);
2665 outline: none;
2666 }
2667
2668 .delete-account-page .back-link {
2669 display: inline-block;
2670 margin-bottom: var(--gap-pane);
2671 font-size: var(--text-note);
2672 opacity: 0.7;
2673 }
2674
2675 .delete-account-page .back-link:hover { opacity: 1; }
2676
2677 .delete-account-page .form-actions {
2678 display: flex;
2679 gap: var(--gap-section);
2680 align-items: center;
2681 }
2682
2683 .delete-account-page .form-status { font-size: var(--text-note); }
2684 .delete-account-page .form-status.error { color: var(--danger); }
2685 .delete-account-page .form-status.success { color: var(--success); }
2686
2687 /* Export data page (templates/dashboards/dashboard-export.html).
2688 `.export-card` and its inner classes (`-info`, `-title`, `-desc`, `-meta`)
2689 are intentionally page-scoped: this is a row-layout card (info on left,
2690 action button on right, flex justify-between) that doesn't fit `.card-muted`
2691 cleanly. Background uses `--light-background` rather than `--surface-muted`
2692 for visual distinction from the surrounding dashboard surface. */
2693
2694 .export-page .export-cards {
2695 display: grid;
2696 gap: var(--gap-section);
2697 }
2698
2699 .export-page .export-card {
2700 background: var(--surface-overlay);
2701 padding: var(--gap-pane);
2702 display: flex;
2703 justify-content: space-between;
2704 align-items: flex-start;
2705 gap: var(--gap-section);
2706 }
2707
2708 .export-page .export-card-info { flex: 1; }
2709
2710 .export-page .export-card-title {
2711 font-family: var(--font-mono);
2712 font-weight: bold;
2713 font-size: var(--text-lead);
2714 margin-bottom: var(--gap-peer);
2715 }
2716
2717 .export-page .export-card-desc {
2718 font-size: var(--text-note);
2719 opacity: 0.8;
2720 margin-bottom: var(--gap-peer);
2721 }
2722
2723 .export-page .export-card-meta {
2724 font-size: var(--text-fine);
2725 opacity: 0.6;
2726 }
2727
2728 .export-page .export-card button { white-space: nowrap; }
2729
2730 .export-page .export-note {
2731 background: var(--surface-sunken);
2732 padding: var(--gap-section);
2733 margin-top: var(--gap-page);
2734 font-size: var(--text-note);
2735 }
2736
2737 .export-page .export-note h3 {
2738 font-family: var(--font-mono);
2739 font-size: var(--text-note);
2740 margin-bottom: var(--gap-peer);
2741 }
2742
2743 .export-page .back-link {
2744 display: inline-block;
2745 margin-bottom: var(--gap-pane);
2746 font-size: var(--text-note);
2747 opacity: 0.7;
2748 }
2749
2750 .export-page .back-link:hover { opacity: 1; }
2751
2752 .export-page .export-status {
2753 margin-top: var(--gap-peer);
2754 font-size: var(--text-note);
2755 }
2756
2757 .export-page .export-status.success { color: var(--success); }
2758 .export-page .export-status.error { color: var(--danger); }
2759
2760 /* Receipt page (templates/pages/receipt.html). */
2761
2762 .receipt-page .receipt-header {
2763 display: flex;
2764 justify-content: space-between;
2765 align-items: flex-start;
2766 margin-bottom: var(--gap-page);
2767 padding-bottom: var(--gap-pane);
2768 border-bottom: 1px solid var(--border);
2769 }
2770
2771 .receipt-page .receipt-row {
2772 display: flex;
2773 justify-content: space-between;
2774 padding: var(--gap-peer) 0;
2775 font-size: var(--text-body);
2776 }
2777
2778 .receipt-page .receipt-row.total {
2779 font-weight: bold;
2780 font-size: var(--text-lead);
2781 border-top: 1px solid var(--border);
2782 padding-top: var(--gap-section);
2783 margin-top: var(--gap-peer);
2784 }
2785
2786 .receipt-page .receipt-label { opacity: 0.7; }
2787
2788 .receipt-page .receipt-footer {
2789 margin-top: var(--gap-page);
2790 padding-top: var(--gap-pane);
2791 border-top: 1px solid var(--border);
2792 font-size: var(--text-note);
2793 opacity: 0.6;
2794 }
2795
2796 .receipt-page .back-link {
2797 display: inline-block;
2798 margin-bottom: var(--gap-pane);
2799 font-size: var(--text-note);
2800 opacity: 0.7;
2801 }
2802
2803 .receipt-page .back-link:hover { opacity: 1; }
2804
2805 @media print {
2806 .receipt-page .back-link,
2807 .receipt-page .site-header,
2808 .receipt-page .print-btn { display: none !important; }
2809 .receipt-page .receipt-box { border: 1px solid var(--border); }
2810 }
2811
2812 /* Fan+ subscription page (templates/pages/fan_plus.html). */
2813 .fan-plus-page h1 { font-size: var(--text-display); margin-bottom: var(--gap-peer); }
2814 .fan-plus-page h2 { font-size: var(--text-head); margin-top: var(--gap-page); margin-bottom: var(--gap-section); }
2815
2816 .fan-plus-page .fan-plus-section { margin-bottom: var(--gap-page); line-height: 1.7; }
2817 .fan-plus-page .fan-plus-section ul { padding-left: var(--gap-pane); margin: var(--gap-section) 0; }
2818 .fan-plus-page .fan-plus-section li { margin-bottom: var(--gap-peer); }
2819
2820 .fan-plus-page .price-callout {
2821 font-size: var(--text-subhead);
2822 font-family: var(--font-mono);
2823 margin: var(--gap-pane) 0;
2824 }
2825
2826 .fan-plus-page .subscribe-form { margin: var(--gap-page) 0; }
2827
2828 .fan-plus-page .subscribe-btn {
2829 display: inline-block;
2830 padding: var(--gap-section) var(--gap-page);
2831 background: var(--action);
2832 color: var(--primary-light);
2833 border: none;
2834 font-size: var(--text-lead);
2835 cursor: pointer;
2836 font-family: var(--font-mono);
2837 }
2838
2839 .fan-plus-page .subscribe-btn:hover { opacity: 0.9; }
2840
2841 .fan-plus-page .status-box {
2842 padding: var(--gap-pane);
2843 background: var(--surface-overlay);
2844 margin: var(--gap-pane) 0;
2845 }
2846
2847 .fan-plus-page .status-box p { margin: var(--gap-peer) 0; }
2848
2849 .fan-plus-page .success-banner {
2850 padding: var(--gap-section) var(--gap-pane);
2851 background: color-mix(in oklch, var(--success) 12%, var(--surface-page));
2852 margin-bottom: var(--gap-pane);
2853 font-family: var(--font-mono);
2854 }
2855
2856 .fan-plus-page .login-link { font-family: var(--font-mono); }
2857
2858 /* Purchase confirmation page (templates/pages/purchase.html). */
2859 .purchase-page .container {
2860 max-width: 800px;
2861 margin: var(--gap-page) auto;
2862 padding: 0 var(--gap-page);
2863 }
2864
2865 .purchase-page .tagline {
2866 font-size: var(--text-note);
2867 opacity: 0.7;
2868 margin-bottom: var(--gap-page);
2869 text-align: center;
2870 }
2871
2872 .purchase-page .checkout-box { margin-bottom: var(--gap-pane); }
2873
2874 .purchase-page h2 { font-size: var(--text-title); margin-bottom: var(--gap-peer); }
2875
2876 .purchase-page .step-indicator {
2877 font-size: var(--text-note);
2878 opacity: 0.6;
2879 margin-bottom: var(--gap-pane);
2880 }
2881
2882 .purchase-page .item-summary {
2883 background: var(--surface-page);
2884 padding: var(--gap-pane);
2885 margin-bottom: var(--gap-pane);
2886 display: flex;
2887 gap: var(--gap-pane);
2888 }
2889
2890 .purchase-page .item-thumbnail {
2891 width: 120px;
2892 height: 120px;
2893 background: var(--surface-sunken);
2894 flex-shrink: 0;
2895 display: flex;
2896 align-items: center;
2897 justify-content: center;
2898 font-size: var(--text-hero);
2899 overflow: hidden;
2900 }
2901
2902 .purchase-page .item-thumbnail img {
2903 width: 100%;
2904 height: 100%;
2905 object-fit: cover;
2906 display: block;
2907 }
2908
2909 /* See the storefront card: the dimming is the placeholder's, not the slot's. */
2910 .purchase-page .item-thumbnail-empty { opacity: 0.3; }
2911
2912 .purchase-page .item-details { flex: 1; }
2913
2914 .purchase-page .item-title {
2915 font-size: var(--text-subhead);
2916 margin-bottom: var(--gap-peer);
2917 font-family: var(--font-heading);
2918 font-weight: bold;
2919 }
2920
2921 .purchase-page .item-creator {
2922 font-size: var(--text-note);
2923 opacity: 0.7;
2924 margin-bottom: var(--gap-section);
2925 }
2926
2927 .purchase-page .item-creator a { color: var(--content); }
2928 .purchase-page .item-description { font-size: var(--text-note); opacity: 0.8; }
2929
2930 .purchase-page .price-breakdown {
2931 background: var(--surface-page);
2932 padding: var(--gap-pane);
2933 margin-bottom: var(--gap-pane);
2934 }
2935
2936 .purchase-page .price-breakdown h2 { font-size: var(--text-lead); margin-bottom: var(--gap-section); }
2937
2938 .purchase-page .price-row {
2939 display: flex;
2940 justify-content: space-between;
2941 padding: var(--gap-peer) 0;
2942 border-bottom: 1px solid var(--border);
2943 }
2944
2945 .purchase-page .price-row:last-child {
2946 border-bottom: none;
2947 margin-top: var(--gap-peer);
2948 font-size: var(--text-subhead);
2949 padding-top: var(--gap-section);
2950 border-top: 2px solid var(--border);
2951 }
2952
2953 .purchase-page .price-row.total { font-weight: bold; }
2954
2955 .purchase-page .payment-section { margin-bottom: var(--gap-pane); }
2956 .purchase-page .payment-section h2 { font-size: var(--text-lead); margin-bottom: var(--gap-section); }
2957
2958 .purchase-page .btn-primary { width: 100%; }
2959 .purchase-page .btn-secondary { width: 100%; margin-top: var(--gap-section); }
2960
2961 .purchase-page .security-note {
2962 font-size: var(--text-note);
2963 opacity: 0.6;
2964 text-align: center;
2965 margin-top: var(--gap-section);
2966 }
2967
2968 /* Replaces inline styles in pages/purchase.html. */
2969 .purchase-page h1 { text-align: center; }
2970
2971 .purchase-page .price-breakdown-note {
2972 margin-top: var(--gap-peer);
2973 font-size: var(--text-fine);
2974 opacity: 0.6;
2975 }
2976
2977 .purchase-page .creator-breakdown {
2978 background: var(--surface-page);
2979 padding: var(--gap-section) var(--gap-pane);
2980 margin-bottom: var(--gap-pane);
2981 font-size: var(--text-note);
2982 }
2983 .purchase-page .creator-breakdown h2 {
2984 font-size: var(--text-body);
2985 margin-bottom: var(--gap-section);
2986 opacity: 0.7;
2987 }
2988 .purchase-page .creator-breakdown .price-row { opacity: 0.8; }
2989 .purchase-page .creator-breakdown .price-row.is-total {
2990 opacity: 1;
2991 font-weight: bold;
2992 border-top: 1px solid var(--border);
2993 margin-top: var(--gap-peer);
2994 padding-top: var(--gap-peer);
2995 /* override .price-row:last-child upsize */
2996 font-size: inherit;
2997 border-bottom: none;
2998 }
2999 .purchase-page .creator-breakdown .platform-fee-amount { color: var(--content); }
3000 .purchase-page .creator-breakdown-note {
3001 margin-top: var(--gap-section);
3002 font-size: var(--text-note);
3003 opacity: 0.6;
3004 }
3005
3006 .purchase-page .payment-section-lead {
3007 margin-bottom: var(--gap-pane);
3008 opacity: 0.8;
3009 }
3010
3011 .purchase-page .pending-checkout {
3012 margin-bottom: var(--gap-pane);
3013 background: var(--surface-sunken);
3014 padding: var(--gap-section);
3015 }
3016 .purchase-page .pending-checkout p { margin: 0 0 var(--gap-section); }
3017 .purchase-page .pending-checkout p.is-sub {
3018 font-size: var(--text-note);
3019 opacity: 0.8;
3020 }
3021 .purchase-page .pending-checkout form { margin: 0; }
3022
3023 /* Checkout form field wrappers. */
3024 .purchase-page .checkout-field { margin-bottom: var(--gap-section); }
3025 .purchase-page .checkout-field-label {
3026 font-size: var(--text-note);
3027 display: block;
3028 margin-bottom: var(--gap-bound);
3029 }
3030 .purchase-page .pwyw-row {
3031 display: flex;
3032 align-items: center;
3033 gap: var(--gap-bound);
3034 }
3035 .purchase-page .pwyw-symbol { font-size: var(--text-lead); }
3036 .purchase-page .pwyw-input { width: 120px; }
3037 .purchase-page .pwyw-min-note {
3038 font-size: var(--text-fine);
3039 opacity: 0.6;
3040 margin-top: var(--gap-bound);
3041 }
3042 .purchase-page .promo-input {
3043 width: 200px;
3044 text-transform: uppercase;
3045 }
3046 .purchase-page .share-contact-label {
3047 font-size: var(--text-note);
3048 display: flex;
3049 align-items: center;
3050 gap: var(--gap-peer);
3051 cursor: pointer;
3052 }
3053 .purchase-page .share-contact-checkbox { width: auto; }
3054
3055 .purchase-page .cart-alt-link {
3056 text-align: center;
3057 margin-top: var(--gap-section);
3058 font-size: var(--text-note);
3059 opacity: 0.7;
3060 }
3061 .purchase-page .guest-hint {
3062 font-size: var(--text-fine);
3063 opacity: 0.6;
3064 margin-top: var(--gap-section);
3065 text-align: center;
3066 }
3067 .purchase-page .guest-login-prompt {
3068 font-size: var(--text-fine);
3069 margin-top: var(--gap-peer);
3070 text-align: center;
3071 }
3072
3073 /* ===========================================
3074 PROJECT SETTINGS TAB
3075 (replaces inline styles in partials/tabs/project_settings.html and
3076 related innerHTML in static/project-sections.js)
3077 =========================================== */
3078
3079 .proj-image-hint {
3080 font-size: var(--text-note);
3081 opacity: 0.7;
3082 margin: 0 0 var(--gap-peer);
3083 }
3084
3085 /* Canonical inline form-status text following a Save / Change button.
3086 Two class names because templates evolved with both. They're aliases.
3087 States via .success / .error / .saving modifier. */
3088 .field-status,
3089 .save-status {
3090 margin-left: var(--gap-peer);
3091 font-size: var(--text-note);
3092 }
3093 .field-status.success, .save-status.success { color: var(--success); }
3094 .field-status.error, .save-status.error { color: var(--danger); }
3095 .field-status.saving, .save-status.saving { opacity: 0.6; }
3096
3097 /* Muted lead paragraph above a form button (monetization / features / delete). */
3098 .section-lead {
3099 margin-bottom: var(--gap-section);
3100 opacity: 0.7;
3101 text-align: left;
3102 }
3103
3104 /* Large intro paragraph under a page h1 (creators, policy, changelog, fan_plus). */
3105 .page-intro {
3106 font-size: var(--text-lead);
3107 margin-bottom: var(--gap-page);
3108 line-height: 1.6;
3109 }
3110
3111 .subscription-note p {
3112 opacity: 0.7;
3113 font-size: var(--text-note);
3114 }
3115
3116 .features-grid-status {
3117 margin-top: var(--gap-peer);
3118 display: inline-block;
3119 }
3120
3121 /* "Pages" disclosure (project-level markdown sections). */
3122 .pages-toggle { margin-top: var(--gap-page); }
3123 .pages-toggle > summary { cursor: pointer; }
3124 .pages-toggle > summary h2 { display: inline; }
3125 .pages-intro {
3126 font-size: var(--text-note);
3127 opacity: 0.7;
3128 margin: var(--gap-section) 0 var(--gap-section);
3129 }
3130
3131 /* psection-* mirrors section-mgmt-* (used by item_details) but psection
3132 class hooks are also queried by static/project-sections.js. */
3133 .psection-row-title { flex: 1; font-weight: bold; }
3134 .psection-row-anchor { font-size: var(--text-fine); opacity: 0.6; }
3135 .psection-row-length { font-size: var(--text-fine); opacity: 0.6; }
3136
3137 .psection-edit-btn,
3138 .psection-del-btn {
3139 padding: var(--gap-bound) var(--gap-group);
3140 font-size: var(--text-fine);
3141 }
3142
3143 .psection-add-details { margin-top: var(--gap-section); }
3144 .psection-add-details > summary {
3145 cursor: pointer;
3146 font-size: var(--text-body);
3147 }
3148 .psection-add-fields { margin-top: var(--gap-section); }
3149
3150 .psection-edit-modal {
3151 margin-top: var(--gap-section);
3152 padding: var(--gap-section);
3153 background: var(--surface-sunken);
3154 }
3155 .psection-edit-actions {
3156 display: flex;
3157 gap: var(--gap-peer);
3158 }
3159
3160 /* ===========================================
3161 CART PAGE (replaces inline styles in pages/cart.html)
3162 =========================================== */
3163
3164 .cart-empty {
3165 text-align: center;
3166 padding: var(--gap-page) var(--gap-section);
3167 }
3168 .cart-empty-hint {
3169 font-size: var(--text-note);
3170 opacity: 0.7;
3171 margin-bottom: var(--gap-pane);
3172 }
3173
3174 /* Multi-creator summary bar (only renders when seller_groups.len() > 1). */
3175 .cart-multi-bar-note {
3176 opacity: 0.7;
3177 margin-left: var(--gap-peer);
3178 }
3179 .cart-multi-bar-form {
3180 display: flex;
3181 align-items: center;
3182 gap: var(--gap-section);
3183 }
3184 .cart-share-label {
3185 font-size: var(--text-note);
3186 display: flex;
3187 align-items: center;
3188 gap: var(--gap-peer);
3189 }
3190
3191 /* Per-seller group card. */
3192 .cart-group { margin-bottom: var(--gap-page); }
3193 .cart-group-title { margin-bottom: var(--gap-bound); }
3194 .cart-group-count {
3195 font-size: var(--text-note);
3196 opacity: 0.6;
3197 margin-bottom: var(--gap-section);
3198 }
3199
3200 /* PWYW editable price cell. */
3201 .cart-pwyw-cell {
3202 display: flex;
3203 align-items: center;
3204 justify-content: flex-end;
3205 gap: var(--gap-bound);
3206 }
3207 .cart-pwyw-symbol { font-size: var(--text-note); }
3208
3209 /* Compact action button used in cart/wishlist tables. */
3210
3211 /* Bottom summary row of each seller group. */
3212 .cart-group-summary {
3213 margin-top: var(--gap-section);
3214 display: flex;
3215 justify-content: space-between;
3216 align-items: center;
3217 flex-wrap: wrap;
3218 gap: var(--gap-section);
3219 }
3220 .cart-subtotal {
3221 font-family: var(--font-heading);
3222 font-size: var(--text-lead);
3223 }
3224 .cart-fee-line {
3225 font-size: var(--text-note);
3226 opacity: 0.6;
3227 }
3228 .cart-savings {
3229 font-size: var(--text-note);
3230 color: var(--action);
3231 margin-top: var(--gap-bound);
3232 }
3233 .cart-stripe-not-ready {
3234 font-size: var(--text-note);
3235 opacity: 0.6;
3236 }
3237
3238 /* Checkout form within a seller group. */
3239 .cart-promo-row { margin-bottom: var(--gap-peer); }
3240 .cart-checkout-share {
3241 display: flex;
3242 align-items: center;
3243 gap: var(--gap-peer);
3244 font-size: var(--text-note);
3245 margin-bottom: var(--gap-peer);
3246 }
3247
3248 /* Wishlist suggestions section below the cart. */
3249 .cart-wishlist { margin-top: var(--gap-page); }
3250
3251 /* Generic "faded out" row state, used by Add-to-Cart click handler in cart.html
3252 to indicate the wishlist row has been moved into the cart. */
3253 .is-faded { opacity: 0.4; }
3254
3255 /* ===========================================
3256 PROFILE TAB (replaces inline styles in partials/tabs/user_profile.html)
3257 =========================================== */
3258
3259 /* Inline form row: input(s) + button laid out horizontally, bottom-aligned.
3260 Distinct from .form-row (which is a 1fr 1fr grid). */
3261 .field-row {
3262 display: flex;
3263 gap: var(--gap-section);
3264 align-items: flex-end;
3265 }
3266 .field-row > .form-group { margin-bottom: 0; }
3267 .field-row > .form-group.is-grow { flex: 1; }
3268 .field-row > .form-group.is-grow-2 { flex: 2; }
3269 .field-row > button { margin-bottom: 0; }
3270
3271 /* Custom domain section. */
3272 .profile-domain-header { margin-bottom: var(--gap-section); }
3273 .profile-domain-status {
3274 margin-left: var(--gap-peer);
3275 }
3276 .profile-domain-status.is-verified { color: var(--success); }
3277 .profile-domain-status.is-pending { color: var(--warning); }
3278
3279 .dns-row {
3280 display: flex;
3281 align-items: center;
3282 gap: var(--gap-peer);
3283 margin-bottom: var(--gap-peer);
3284 }
3285 .dns-row:last-of-type { margin-bottom: 0; }
3286 .dns-row-label {
3287 font-size: var(--text-fine);
3288 opacity: 0.7;
3289 min-width: 70px;
3290 }
3291 .dns-row-value {
3292 font-size: var(--text-note);
3293 flex: 1;
3294 }
3295 .dns-row-copy {
3296 padding: var(--gap-bound) var(--gap-peer);
3297 font-size: var(--text-fine);
3298 }
3299 .profile-domain-actions {
3300 display: flex;
3301 gap: var(--gap-section);
3302 }
3303 .profile-domain-result { margin-top: var(--gap-section); }
3304 .profile-domain-lead {
3305 font-size: var(--text-note);
3306 opacity: 0.8;
3307 }
3308 .profile-domain-add-result { margin-top: var(--gap-section); }
3309
3310 /* Personal feed row: URL input + Copy button. */
3311 .profile-feed-row {
3312 display: flex;
3313 gap: var(--gap-peer);
3314 align-items: center;
3315 }
3316 .profile-feed-input {
3317 flex: 1;
3318 font-size: var(--text-note);
3319 font-family: var(--font-mono);
3320 opacity: 0.8;
3321 }
3322 .profile-feed-lead { margin-bottom: var(--gap-section); }
3323
3324 .purchase-page .checkout-footer {
3325 margin-top: var(--gap-page);
3326 padding-top: var(--gap-pane);
3327 border-top: 1px solid var(--border);
3328 opacity: 0.6;
3329 text-align: center;
3330 font-size: var(--text-note);
3331 }
3332
3333 .purchase-page .checkout-footer a { color: var(--content); }
3334
3335 @media (max-width: 599px) {
3336 .purchase-page .container { padding: 0 var(--gap-section); }
3337 .purchase-page .item-summary { flex-direction: column; }
3338 .purchase-page .item-thumbnail { width: 100%; height: 160px; }
3339 .purchase-page .checkout-box { padding: var(--gap-pane); }
3340 .purchase-page h2 { font-size: var(--text-head); }
3341 }
3342
3343 /* Feed page (templates/pages/feed.html). Compact tabular layout of
3344 items from followed users / projects / tags. */
3345 .feed-page .page-title { font-size: var(--text-head); margin-bottom: var(--gap-section); }
3346 .feed-page .feed-meta { font-size: var(--text-fine); opacity: 0.6; margin-bottom: var(--gap-section); }
3347
3348 .feed-page .table-header {
3349 display: grid;
3350 grid-template-columns: 50px 1fr 100px 70px 70px;
3351 gap: var(--gap-peer);
3352 padding: var(--gap-peer) var(--gap-section);
3353 background: var(--surface-overlay);
3354 font-size: var(--text-fine);
3355 opacity: 0.7;
3356 text-transform: uppercase;
3357 letter-spacing: 0.03em;
3358 }
3359
3360 .feed-page .col-right { text-align: right; }
3361
3362 .feed-page .results-table {
3363 border: 1px solid var(--border);
3364 border-top: none;
3365 }
3366
3367 .feed-page .table-row {
3368 display: grid;
3369 grid-template-columns: 50px 1fr 100px 70px 70px;
3370 gap: var(--gap-peer);
3371 padding: var(--gap-peer) var(--gap-section);
3372 align-items: center;
3373 text-decoration: none;
3374 color: var(--content);
3375 font-size: var(--text-note);
3376 border-bottom: 1px solid var(--border);
3377 transition: background 0.1s ease;
3378 }
3379
3380 .feed-page .table-row:last-child { border-bottom: none; }
3381 .feed-page .table-row:nth-child(odd) { background: var(--surface-overlay); }
3382 .feed-page .table-row:nth-child(even) { background: var(--surface-overlay); }
3383 .feed-page .table-row:hover { background: var(--surface-sunken); }
3384
3385 .feed-page .item-name-cell {
3386 display: flex;
3387 flex-direction: column;
3388 gap: var(--gap-bound);
3389 min-width: 0;
3390 }
3391
3392 .feed-page .item-name {
3393 white-space: nowrap;
3394 overflow: hidden;
3395 text-overflow: ellipsis;
3396 }
3397
3398 .feed-page .item-creator { font-size: var(--text-fine); opacity: 0.5; }
3399
3400 .feed-page .badge {
3401 text-transform: uppercase;
3402 letter-spacing: 0.03em;
3403 background: var(--surface-sunken);
3404 }
3405
3406 .feed-page .empty-state {
3407 text-align: center;
3408 padding: var(--gap-page) var(--gap-pane);
3409 background: var(--surface-overlay);
3410 border: 1px solid var(--border);
3411 }
3412
3413 .feed-page .empty-state h2 { font-size: var(--text-subhead); margin-bottom: var(--gap-peer); }
3414 .feed-page .empty-state p { opacity: 0.7; margin-bottom: var(--gap-pane); }
3415
3416 .feed-page .pagination {
3417 display: flex;
3418 gap: var(--gap-bound);
3419 justify-content: center;
3420 margin-top: var(--gap-section);
3421 }
3422
3423 .feed-page .pagination a,
3424 .feed-page .pagination span {
3425 padding: var(--gap-peer) var(--gap-section);
3426 font-size: var(--text-note);
3427 text-decoration: none;
3428 color: var(--content);
3429 border: 1px solid var(--border);
3430 }
3431
3432 .feed-page .pagination span.current {
3433 background: var(--primary-dark);
3434 color: var(--primary-light);
3435 border-color: var(--primary-dark);
3436 }
3437
3438 .feed-page .pagination a:hover { background: var(--surface-sunken); }
3439
3440 @media (max-width: 599px) {
3441 .feed-page .table-header,
3442 .feed-page .table-row { grid-template-columns: 1fr 70px; }
3443 .feed-page .table-header span:nth-child(1),
3444 .feed-page .table-row .badge:first-child,
3445 .feed-page .table-header span:nth-child(3),
3446 .feed-page .table-header span:nth-child(4),
3447 .feed-page .table-row span:nth-child(3),
3448 .feed-page .table-row span:nth-child(4) { display: none; }
3449 }
3450
3451 /* (Stripe Connect disclaimer page rules defined in the canonical
3452 STRIPE DISCLAIMER PAGE section near end of file.) */
3453
3454 /* User profile page (templates/pages/user.html). */
3455 .user-page .profile-header { text-align: center; margin-bottom: var(--gap-page); }
3456
3457 .user-page .profile-avatar {
3458 width: 120px;
3459 height: 120px;
3460 background: var(--surface-overlay);
3461 margin: 0 auto var(--gap-section);
3462 display: flex;
3463 align-items: center;
3464 justify-content: center;
3465 font-size: var(--text-hero);
3466 opacity: 0.5;
3467 }
3468
3469 .user-page .profile-name { font-size: var(--text-title); margin-bottom: var(--gap-peer); }
3470
3471 .user-page .profile-username {
3472 font-size: var(--text-note);
3473 opacity: 0.7;
3474 margin-bottom: var(--gap-section);
3475 }
3476
3477 .user-page .profile-bio {
3478 font-size: var(--text-body);
3479 max-width: 500px;
3480 margin: 0 auto;
3481 text-align: center;
3482 }
3483
3484 .user-page .links-section { margin-bottom: var(--gap-page); }
3485 .user-page .link-item { text-align: center; }
3486
3487 .user-page .link-title {
3488 font-size: var(--text-lead);
3489 margin-bottom: var(--gap-bound);
3490 font-family: var(--font-heading);
3491 font-weight: bold;
3492 }
3493
3494 .user-page .link-description { font-size: var(--text-note); opacity: 0.7; }
3495
3496 .user-page .project-title {
3497 font-size: var(--text-lead);
3498 margin-bottom: var(--gap-peer);
3499 font-family: var(--font-heading);
3500 font-weight: bold;
3501 }
3502
3503 .user-page .project-meta {
3504 font-size: var(--text-note);
3505 opacity: 0.7;
3506 margin-bottom: var(--gap-peer);
3507 }
3508
3509 .user-page .project-description { font-size: var(--text-note); opacity: 0.8; }
3510
3511 /* Creators / become-a-creator page (templates/pages/creators.html). */
3512 .creators-page h1 { font-size: var(--text-display); margin-bottom: var(--gap-peer); }
3513 .creators-page h2 { font-size: var(--text-head); margin-top: var(--gap-page); margin-bottom: var(--gap-section); }
3514
3515 .creators-page .how-it-works { margin-bottom: var(--gap-page); }
3516 .creators-page .how-it-works ol { padding-left: var(--gap-pane); line-height: 1.8; }
3517
3518 .creators-page .stats-row { gap: var(--gap-page); margin: var(--gap-page) 0; }
3519 .creators-page .stat-box { padding: var(--gap-pane); }
3520 .creators-page .stat-box .number { font-size: var(--text-title); }
3521 .creators-page .stat-box .label { font-size: var(--text-note); margin-top: var(--gap-bound); }
3522
3523 .creators-page .wave-table {
3524 width: 100%;
3525 border-collapse: collapse;
3526 margin: var(--gap-section) 0;
3527 }
3528
3529 .creators-page .wave-table th,
3530 .creators-page .wave-table td {
3531 padding: var(--gap-section);
3532 text-align: left;
3533 border-bottom: 1px solid var(--border);
3534 }
3535
3536 .creators-page .wave-table th {
3537 font-family: var(--font-mono);
3538 font-size: var(--text-note);
3539 opacity: 0.7;
3540 }
3541
3542 .creators-page .cta {
3543 margin-top: var(--gap-page);
3544 padding: var(--gap-pane);
3545 background: var(--surface-overlay);
3546 text-align: center;
3547 }
3548
3549 .creators-page .cta a { margin: 0 var(--gap-peer); }
3550
3551 /* Tag tree browse page (templates/pages/tag_tree.html). */
3552 .tag-tree-page .breadcrumbs {
3553 font-size: var(--text-note);
3554 margin-bottom: var(--gap-pane);
3555 opacity: 0.7;
3556 }
3557 .tag-tree-page .breadcrumbs a:hover { opacity: 0.6; }
3558 .tag-tree-page .breadcrumbs .sep { margin: 0 var(--gap-bound); opacity: 0.4; }
3559
3560 .tag-tree-page .tag-grid {
3561 display: grid;
3562 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
3563 gap: var(--gap-section);
3564 }
3565
3566 .tag-tree-page .tag-card {
3567 background: var(--surface-sunken);
3568 padding: var(--gap-section);
3569 text-decoration: none;
3570 color: var(--content);
3571 border: 1px solid var(--border);
3572 transition: background 0.1s ease;
3573 }
3574
3575 .tag-tree-page .tag-card:hover {
3576 background: var(--surface-sunken);
3577 text-decoration: none;
3578 }
3579
3580 .tag-tree-page .tag-card-name {
3581 font-family: var(--font-heading);
3582 font-weight: bold;
3583 font-size: var(--text-body);
3584 }
3585
3586 .tag-tree-page .tag-card-meta {
3587 font-size: var(--text-fine);
3588 opacity: 0.5;
3589 margin-top: var(--gap-bound);
3590 }
3591
3592 .tag-tree-page .empty-state {
3593 opacity: 0.5;
3594 font-size: var(--text-note);
3595 padding: var(--gap-page) 0;
3596 }
3597
3598 /* Confirm delete prompt (templates/pages/confirm_delete.html). Overrides
3599 the global .warning-box to use --danger-bg (matches the delete-account
3600 semantic). Also tokenized previously-broken fallbacks. */
3601 .confirm-delete-page .warning-box {
3602 background: color-mix(in oklch, var(--danger) 10%, var(--surface-page));
3603 border: 1px solid var(--danger);
3604 border-radius: var(--radius-panel);
3605 padding: var(--gap-pane);
3606 margin-bottom: var(--gap-pane);
3607 }
3608
3609 .confirm-delete-page .warning-box p {
3610 margin: 0;
3611 color: var(--danger);
3612 }
3613
3614 /* Project paywall page (templates/pages/project_paywall.html). */
3615 .project-paywall-page .paywall-cover { margin-bottom: var(--gap-pane); }
3616 .project-paywall-page .paywall-cover img {
3617 width: 120px;
3618 height: 120px;
3619 border-radius: var(--radius-panel);
3620 object-fit: cover;
3621 }
3622
3623 .project-paywall-page .paywall-title {
3624 font-family: var(--font-heading);
3625 font-size: var(--text-hero);
3626 margin-bottom: var(--gap-peer);
3627 }
3628
3629 .project-paywall-page .paywall-creator {
3630 font-size: var(--text-body);
3631 opacity: 0.7;
3632 margin-bottom: var(--gap-pane);
3633 }
3634
3635 .project-paywall-page .paywall-creator a { color: var(--content); }
3636
3637 .project-paywall-page .paywall-description {
3638 font-size: var(--text-lead);
3639 max-width: 800px;
3640 margin: 0 auto var(--gap-page);
3641 text-align: left;
3642 }
3643
3644 .project-paywall-page .paywall-item-count {
3645 font-size: var(--text-body);
3646 opacity: 0.7;
3647 margin-bottom: var(--gap-peer);
3648 }
3649
3650 .project-paywall-page .paywall-tiers {
3651 display: grid;
3652 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
3653 gap: var(--gap-pane);
3654 margin-top: var(--gap-pane);
3655 text-align: left;
3656 }
3657
3658 .project-paywall-page .paywall-tiers .tier-card {
3659 background: var(--surface-overlay);
3660 padding: var(--gap-page);
3661 display: flex;
3662 flex-direction: column;
3663 }
3664
3665 .project-paywall-page .paywall-tiers .tier-card h3 {
3666 font-family: var(--font-heading);
3667 font-weight: bold;
3668 font-size: var(--text-subhead);
3669 margin-bottom: var(--gap-peer);
3670 }
3671
3672 .project-paywall-page .paywall-tiers .tier-price {
3673 font-size: var(--text-head);
3674 margin-bottom: var(--gap-section);
3675 }
3676
3677 .project-paywall-page .paywall-tiers .tier-card form,
3678 .project-paywall-page .paywall-tiers .tier-card .button { margin-top: auto; }
3679
3680 .project-paywall-page .paywall-tiers .tier-card button,
3681 .project-paywall-page .paywall-tiers .tier-card .button { width: 100%; }
3682
3683 /* Collection detail page (templates/pages/collection.html). */
3684 .collection-page .container { max-width: 700px; }
3685 .collection-page .collection-header { margin-bottom: var(--gap-page); }
3686 .collection-page .collection-title { font-size: var(--text-title); margin-bottom: var(--gap-bound); }
3687
3688 .collection-page .collection-owner {
3689 font-size: var(--text-note);
3690 opacity: 0.7;
3691 margin-bottom: var(--gap-section);
3692 }
3693
3694 .collection-page .collection-description {
3695 font-size: var(--text-body);
3696 opacity: 0.85;
3697 margin-bottom: var(--gap-section);
3698 }
3699
3700 .collection-page .collection-meta { font-size: var(--text-note); opacity: 0.6; }
3701
3702 .collection-page .collection-item {
3703 display: flex;
3704 justify-content: space-between;
3705 align-items: flex-start;
3706 padding: var(--gap-section);
3707 background: var(--surface-overlay);
3708 margin-bottom: var(--gap-peer);
3709 }
3710
3711 .collection-page .collection-item-info { flex: 1; }
3712
3713 .collection-page .collection-item-title {
3714 font-family: var(--font-heading);
3715 font-weight: bold;
3716 font-size: var(--text-body);
3717 margin-bottom: var(--gap-bound);
3718 }
3719
3720 .collection-page .collection-item-title a { text-decoration: none; }
3721
3722 .collection-page .collection-item-meta { font-size: var(--text-note); opacity: 0.7; }
3723
3724 .collection-page .collection-item-price {
3725 font-size: var(--text-note);
3726 white-space: nowrap;
3727 margin-left: var(--gap-section);
3728 }
3729
3730 .collection-page .empty-state {
3731 text-align: center;
3732 padding: var(--gap-page) var(--gap-section);
3733 opacity: 0.6;
3734 }
3735
3736 /* Project blog index page (templates/pages/project_blog.html). */
3737 .project-blog-page .blog-header { margin-bottom: var(--gap-page); }
3738 .project-blog-page .blog-title { font-size: var(--text-display); margin-bottom: var(--gap-peer); }
3739
3740 .project-blog-page .blog-meta {
3741 font-size: var(--text-note);
3742 opacity: 0.7;
3743 margin-bottom: var(--gap-pane);
3744 }
3745
3746 .project-blog-page .blog-meta a { color: var(--content); }
3747
3748 .project-blog-page .blog-actions {
3749 display: flex;
3750 gap: var(--gap-section);
3751 }
3752
3753 .project-blog-page .posts-list { max-width: 800px; }
3754
3755 .project-blog-page .post-entry {
3756 padding: var(--gap-pane) 0;
3757 border-bottom: 1px solid var(--border);
3758 }
3759
3760 .project-blog-page .post-entry:first-child { padding-top: 0; }
3761 .project-blog-page .post-title { font-size: var(--text-head); margin-bottom: var(--gap-peer); }
3762 .project-blog-page .post-title a:hover { text-decoration: underline; }
3763
3764 .project-blog-page .post-date {
3765 font-size: var(--text-note);
3766 opacity: 0.6;
3767 font-family: var(--font-mono);
3768 }
3769
3770 .project-blog-page .empty-state { padding: var(--gap-page) 0; opacity: 0.6; }
3771
3772 /* Canonical "page-level h1", left-aligned, smaller than the global centered
3773 default. Used by dashboards, admin, health, import, delete-account, export,
3774 receipt. Aliased via comma-grouped selectors so each page picks it up via
3775 its body class without redefining. */
3776 .dashboard-page h1,
3777 .admin-page h1,
3778 .health-page h1,
3779 .import-page h1,
3780 .delete-account-page h1,
3781 .export-page h1,
3782 .receipt-page h1 {
3783 font-size: var(--text-title);
3784 margin-bottom: var(--gap-peer);
3785 text-align: left;
3786 }
3787 .delete-account-page h1 { color: var(--danger); }
3788
3789 /* Canonical subtitle under a page h1, muted, with bottom space. */
3790 .health-page .subtitle,
3791 .import-page .subtitle,
3792 .delete-account-page .subtitle,
3793 .export-page .subtitle {
3794 opacity: 0.7;
3795 margin-bottom: var(--gap-page);
3796 text-align: left;
3797 }
3798 .health-page .subtitle { text-align: center; }
3799
3800 /* User Settings tab (templates/partials/tabs/user_settings.html).
3801 Left-rail nav layout for the Settings dashboard tab. Selection uses the
3802 canonical .is-selected modifier. */
3803 .settings-layout { display: flex; gap: var(--gap-page); }
3804 .settings-nav { flex-shrink: 0; min-width: 140px; }
3805 .settings-nav a {
3806 display: block;
3807 padding: var(--gap-peer) var(--gap-section);
3808 text-decoration: none;
3809 color: var(--content);
3810 opacity: 0.6;
3811 font-family: var(--font-mono);
3812 font-size: var(--text-note);
3813 transition: opacity 0.15s ease;
3814 }
3815 .settings-nav a:hover { opacity: 1; }
3816 .settings-nav a.is-selected { opacity: 1; background: var(--surface-sunken); }
3817 .settings-body { flex: 1; min-width: 0; }
3818 @media (max-width: 839px) {
3819 .settings-layout { flex-direction: column; gap: var(--gap-section); }
3820 .settings-nav { display: flex; flex-wrap: wrap; gap: 0; min-width: 0; }
3821 .settings-nav a { padding: var(--gap-peer) var(--gap-group); font-size: var(--text-note); }
3822 }.library-page .feed-table-header {
3823 display: grid;
3824 grid-template-columns: 50px 1fr 100px 70px 70px;
3825 gap: var(--gap-peer);
3826 padding: var(--gap-peer) var(--gap-section);
3827 background: var(--surface-overlay);
3828 font-size: var(--text-fine);
3829 opacity: 0.7;
3830 text-transform: uppercase;
3831 letter-spacing: 0.03em;
3832 }
3833 .library-page .feed-col-right { text-align: right; }
3834 .library-page .feed-results-table { border: 1px solid var(--border); border-top: none; }
3835 .library-page .feed-table-row {
3836 display: grid;
3837 grid-template-columns: 50px 1fr 100px 70px 70px;
3838 gap: var(--gap-peer);
3839 padding: var(--gap-peer) var(--gap-section);
3840 align-items: center;
3841 text-decoration: none;
3842 color: var(--content);
3843 font-size: var(--text-note);
3844 border-bottom: 1px solid var(--border);
3845 transition: background 0.1s ease;
3846 }
3847 .library-page .feed-table-row:last-child { border-bottom: none; }
3848 .library-page .feed-table-row:nth-child(odd) { background: var(--surface-overlay); }
3849 .library-page .feed-table-row:nth-child(even) { background: var(--surface-overlay); }
3850 .library-page .feed-table-row:hover { background: var(--surface-sunken); }
3851 .library-page .feed-item-name-cell {
3852 display: flex;
3853 flex-direction: column;
3854 gap: var(--gap-bound);
3855 min-width: 0;
3856 }
3857 .library-page .feed-item-name {
3858 white-space: nowrap;
3859 overflow: hidden;
3860 text-overflow: ellipsis;
3861 }
3862 .library-page .feed-item-creator { font-size: var(--text-fine); opacity: 0.5; }
3863 .library-page .feed-pagination {
3864 display: flex;
3865 gap: var(--gap-bound);
3866 justify-content: center;
3867 margin-top: var(--gap-section);
3868 }
3869 .library-page .feed-pagination a,
3870 .library-page .feed-pagination span {
3871 padding: var(--gap-peer) var(--gap-section);
3872 font-size: var(--text-note);
3873 text-decoration: none;
3874 color: var(--content);
3875 border: 1px solid var(--border);
3876 }
3877 .library-page .feed-pagination span.current {
3878 background: var(--primary-dark);
3879 color: var(--primary-light);
3880 border-color: var(--primary-dark);
3881 }
3882 .library-page .feed-pagination a:hover { background: var(--surface-sunken); }
3883 @media (max-width: 599px) {
3884 .library-page .feed-table-header,
3885 .library-page .feed-table-row {
3886 grid-template-columns: 1fr 70px;
3887 }
3888 .library-page .feed-table-header span:nth-child(1),
3889 .library-page .feed-table-row .badge:first-child,
3890 .library-page .feed-table-header span:nth-child(3),
3891 .library-page .feed-table-header span:nth-child(4),
3892 .library-page .feed-table-row span:nth-child(3),
3893 .library-page .feed-table-row span:nth-child(4) {
3894 display: none;
3895 }
3896 }
3897
3898 /* User-level dashboard (templates/dashboards/dashboard-user.html).
3899 Scoped under .dashboard-user-page to avoid colliding with .project-card
3900 / .project-title / .project-meta on the public profile (user.html) and
3901 the user_projects tab partial. */
3902 .dashboard-user-page .stat-value { font-size: var(--text-title); margin-bottom: var(--gap-bound); }
3903
3904 .dashboard-user-page .project-card {
3905 background: var(--surface-overlay);
3906 padding: var(--gap-pane);
3907 margin-bottom: var(--gap-section);
3908 display: flex;
3909 justify-content: space-between;
3910 align-items: flex-start;
3911 }
3912
3913 .dashboard-user-page .project-info { flex: 1; }
3914
3915 .dashboard-user-page .project-title {
3916 font-family: var(--font-heading);
3917 font-weight: bold;
3918 font-size: var(--text-subhead);
3919 margin-bottom: var(--gap-bound);
3920 }
3921
3922 .dashboard-user-page .project-meta {
3923 font-size: var(--text-note);
3924 opacity: 0.7;
3925 margin-bottom: var(--gap-peer);
3926 }
3927
3928 .dashboard-user-page .project-stats { font-size: var(--text-note); }
3929 .dashboard-user-page .project-actions {
3930 display: flex;
3931 gap: var(--gap-peer);
3932 }
3933
3934 .dashboard-user-page .project-actions button {
3935 padding: var(--gap-peer) var(--gap-section);
3936 font-size: var(--text-note);
3937 }
3938 @media (max-width: 839px) {
3939 .dashboard-user-page .project-card { flex-direction: column; }
3940 .dashboard-user-page .project-actions { margin-top: var(--gap-section); }
3941 }
3942
3943 /* Project dashboard (templates/dashboards/dashboard-project.html). */
3944 .dashboard-project-page .project-meta { font-size: var(--text-note); opacity: 0.7; }
3945 .dashboard-project-page .stat-value { font-size: var(--text-title); margin-bottom: var(--gap-bound); }
3946
3947 .dashboard-project-page .content-header {
3948 display: flex;
3949 justify-content: space-between;
3950 align-items: center;
3951 margin-bottom: var(--gap-pane);
3952 }
3953
3954 .dashboard-project-page .content-header h2 { font-size: var(--text-subhead); }
3955
3956 .dashboard-project-page .item-thumbnail {
3957 width: 40px;
3958 height: 40px;
3959 background: var(--border);
3960 display: inline-block;
3961 vertical-align: middle;
3962 margin-right: var(--gap-peer);
3963 }
3964
3965 .dashboard-project-page .item-actions {
3966 display: flex;
3967 gap: var(--gap-peer);
3968 }
3969
3970 .dashboard-project-page .item-actions button {
3971 padding: var(--gap-peer) var(--gap-section);
3972 font-size: var(--text-note);
3973 }
3974
3975 .dashboard-project-page .data-section { margin-bottom: var(--gap-page); }
3976
3977 .dashboard-project-page .data-section h2 {
3978 font-size: var(--text-subhead);
3979 margin-bottom: var(--gap-section);
3980 padding-bottom: var(--gap-peer);
3981 border-bottom: 1px solid var(--border);
3982 }
3983
3984 /* Item dashboard (templates/dashboards/dashboard-item.html). */
3985 .dashboard-item-page .item-meta {
3986 font-size: var(--text-note);
3987 opacity: 0.7;
3988 display: flex;
3989 gap: var(--gap-section);
3990 align-items: center;
3991 }
3992
3993 .dashboard-item-page .stat-value { font-size: var(--text-head); }
3994
3995 .dashboard-item-page .section-header {
3996 display: flex;
3997 justify-content: space-between;
3998 align-items: center;
3999 margin-bottom: var(--gap-pane);
4000 padding-bottom: var(--gap-section);
4001 border-bottom: 1px solid var(--border);
4002 }
4003
4004 .dashboard-item-page .section-header h2 { font-size: var(--text-subhead); }
4005
4006 @media (max-width: 839px) {
4007 .dashboard-item-page .section-header {
4008 flex-direction: column;
4009 align-items: flex-start;
4010 gap: var(--gap-section);
4011 }
4012 .dashboard-item-page .section-header .action-buttons { width: 100%; }
4013 }
4014
4015 /* Changelog page (templates/pages/changelog.html). */
4016 .changelog-page .container { max-width: 900px; margin: 0 auto; }
4017 .changelog-page h1 { font-size: var(--text-display); margin-bottom: var(--gap-peer); }
4018 .changelog-page .changelog-entry {
4019 margin-bottom: var(--gap-page);
4020 padding-bottom: var(--gap-page);
4021 border-bottom: 1px solid var(--border);
4022 }
4023 .changelog-page .changelog-entry:last-child { border-bottom: none; }
4024 .changelog-page .changelog-date {
4025 font-family: var(--font-mono);
4026 font-size: var(--text-note);
4027 opacity: 0.6;
4028 margin-bottom: var(--gap-peer);
4029 }
4030 .changelog-page .changelog-entry h2 { font-size: var(--text-subhead); margin-bottom: var(--gap-section); }
4031 .changelog-page .changelog-entry ul {
4032 padding-left: var(--gap-pane);
4033 margin: var(--gap-peer) 0;
4034 line-height: 1.7;
4035 }
4036 .changelog-page .changelog-entry li { margin-bottom: var(--gap-bound); }
4037
4038 /* Policy page (templates/pages/policy.html). */
4039 .policy-page .container { max-width: 900px; margin: 0 auto; }
4040 .policy-page h1 { font-size: var(--text-display); margin-bottom: var(--gap-peer); }
4041 .policy-page h2 {
4042 font-size: var(--text-head);
4043 margin-top: var(--gap-page);
4044 margin-bottom: var(--gap-section);
4045 }
4046 .policy-page .policy-section { margin-bottom: var(--gap-page); line-height: 1.7; }
4047 .policy-page .policy-section ul {
4048 padding-left: var(--gap-pane);
4049 margin: var(--gap-section) 0;
4050 }
4051 .policy-page .policy-section li { margin-bottom: var(--gap-bound); }
4052 .policy-page .contact {
4053 margin-top: var(--gap-page);
4054 padding: var(--gap-pane);
4055 background: var(--surface-overlay);
4056 }
4057
4058 /* Admin page extras (in addition to global .admin-page h1). */
4059 .admin-page .metrics-grid {
4060 display: grid;
4061 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
4062 gap: var(--gap-section);
4063 margin-bottom: var(--gap-page);
4064 }
4065
4066 .admin-page .metric-card {
4067 padding: var(--gap-section);
4068 border: 1px solid var(--border);
4069 border-radius: var(--radius-square);
4070 }
4071
4072 .admin-page .metric-card .value {
4073 font-size: var(--text-title);
4074 font-family: var(--font-mono);
4075 font-weight: 600;
4076 }
4077
4078 .admin-page .metric-card .label {
4079 font-size: var(--text-note);
4080 color: var(--content-muted);
4081 margin-top: var(--gap-bound);
4082 }
4083
4084 .admin-page .metric-card.warn .value { color: var(--warning); }
4085 .admin-page .metric-card.error .value { color: var(--danger); }
4086
4087 .admin-page .section-heading {
4088 font-size: var(--text-subhead);
4089 margin: var(--gap-pane) 0 var(--gap-section);
4090 font-family: var(--font-mono);
4091 }
4092
4093 /* Scan pipeline dashboard. See docs/scan-pipeline-audit.md § 5. */
4094
4095 .admin-scan-section {
4096 margin-top: var(--gap-page);
4097 }
4098
4099 .admin-section-header {
4100 display: flex;
4101 justify-content: space-between;
4102 align-items: baseline;
4103 margin-bottom: var(--gap-section);
4104 gap: var(--gap-section);
4105 }
4106
4107 /* Typeface comes from .subsection-title on the heading itself; this sets only
4108 what the flex row needs. */
4109 .admin-section-header h2 {
4110 font-size: var(--text-lead);
4111 margin: 0;
4112 }
4113
4114 .layer-health-grid {
4115 display: grid;
4116 grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
4117 gap: var(--gap-section);
4118 }
4119
4120 .layer-health-card {
4121 padding: var(--gap-section);
4122 border: 1px solid var(--border);
4123 border-radius: var(--radius-square);
4124 border-left-width: 4px;
4125 font-size: var(--text-note);
4126 }
4127
4128 .layer-health-card .layer-name {
4129 font-family: var(--font-mono);
4130 font-weight: 600;
4131 font-size: var(--text-body);
4132 margin-bottom: var(--gap-peer);
4133 }
4134
4135 .layer-health-card .layer-stat {
4136 display: flex;
4137 justify-content: space-between;
4138 margin-bottom: 2px;
4139 }
4140
4141 .layer-health-card .layer-stat .dimmed {
4142 color: var(--content-muted);
4143 }
4144
4145 .layer-health-card.layer-ok { border-left-color: var(--success); }
4146 .layer-health-card.layer-degraded { border-left-color: var(--warning); }
4147 .layer-health-card.layer-down { border-left-color: var(--danger); }
4148
4149 .layer-chips {
4150 display: flex;
4151 flex-wrap: wrap;
4152 gap: 4px;
4153 max-width: 360px;
4154 }
4155
4156 .layer-chip {
4157 display: inline-block;
4158 font-family: var(--font-mono);
4159 font-size: var(--text-fine);
4160 padding: 1px 6px;
4161 border-radius: var(--radius-control);
4162 border: 1px solid transparent;
4163 cursor: help;
4164 line-height: 1.4;
4165 }
4166
4167 .layer-chip-pass { background: color-mix(in oklch, var(--success) 12%, var(--surface-page)); border-color: var(--success); color: var(--success); }
4168 .layer-chip-skip { background: var(--surface-page); border-color: var(--border); color: var(--content-muted); }
4169 .layer-chip-fail { background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); border-color: var(--danger); color: var(--danger); font-weight: 600; }
4170 .layer-chip-error { background: color-mix(in oklch, var(--warning) 14%, var(--surface-page)); border-color: var(--warning); color: var(--warning); font-weight: 600; }
4171
4172 .scan-history-grid table { font-size: var(--text-note); }
4173
4174 .scan-history-grid summary {
4175 cursor: pointer;
4176 font-family: var(--font-mono);
4177 padding: var(--gap-peer) 0;
4178 user-select: none;
4179 }
4180
4181 .scan-row-meta {
4182 font-size: var(--text-fine);
4183 color: var(--content-muted);
4184 margin-top: 2px;
4185 }
4186
4187 .audit-filter-form {
4188 display: flex;
4189 flex-wrap: wrap;
4190 align-items: flex-end;
4191 gap: var(--gap-section);
4192 padding: var(--gap-section) 0;
4193 margin-bottom: var(--gap-section);
4194 border-bottom: 1px solid var(--border);
4195 }
4196
4197 .audit-filter-form label {
4198 display: flex;
4199 flex-direction: column;
4200 gap: 2px;
4201 }
4202
4203 .audit-filter-form input,
4204 .audit-filter-form select {
4205 font-size: var(--text-note);
4206 }
4207
4208 .layer-chips-cell { vertical-align: top; }
4209
4210 .layer-detail-disclosure {
4211 margin-top: var(--gap-peer);
4212 }
4213
4214 .layer-detail-disclosure summary {
4215 cursor: pointer;
4216 user-select: none;
4217 padding: 2px 0;
4218 }
4219
4220 .layer-detail-list {
4221 margin: var(--gap-peer) 0 0;
4222 display: grid;
4223 grid-template-columns: max-content 1fr;
4224 gap: 2px var(--gap-section);
4225 font-size: var(--text-fine);
4226 }
4227
4228 .layer-detail-list dt {
4229 font-family: var(--font-mono);
4230 }
4231
4232 .layer-detail-list dd {
4233 margin: 0;
4234 color: var(--content-muted);
4235 word-break: break-word;
4236 }
4237
4238 .admin-page table {
4239 width: 100%;
4240 border-collapse: collapse;
4241 font-size: var(--text-note);
4242 }
4243
4244 .admin-page th,
4245 .admin-page td {
4246 text-align: left;
4247 padding: var(--gap-peer) var(--gap-group);
4248 border-bottom: 1px solid var(--border);
4249 }
4250
4251 .admin-page th {
4252 font-family: var(--font-mono);
4253 font-size: var(--text-fine);
4254 text-transform: uppercase;
4255 color: var(--content-muted);
4256 }
4257
4258 .admin-page td.mono { font-family: var(--font-mono); }
4259
4260 .admin-page .refresh-note {
4261 font-size: var(--text-fine);
4262 color: var(--content-muted);
4263 margin-bottom: var(--gap-section);
4264 font-family: var(--font-mono);
4265 }
4266
4267 .admin-page .appeal-count {
4268 font-size: var(--text-note);
4269 opacity: 0.7;
4270 margin-bottom: var(--gap-pane);
4271 }
4272
4273 .admin-page .decide-form {
4274 display: flex;
4275 gap: var(--gap-peer);
4276 align-items: flex-end;
4277 flex-wrap: wrap;
4278 }
4279
4280 .admin-page .decide-form textarea {
4281 min-width: 200px;
4282 font-size: var(--text-note);
4283 }
4284
4285 .admin-page .suspend-form {
4286 display: flex;
4287 gap: var(--gap-peer);
4288 align-items: flex-end;
4289 }
4290
4291 .admin-page .suspend-form input[type="text"] {
4292 min-width: 200px;
4293 font-size: var(--text-note);
4294 }
4295
4296 .admin-page .lottery-form {
4297 background: var(--surface-overlay);
4298 padding: var(--gap-pane);
4299 margin: var(--gap-pane) 0;
4300 display: flex;
4301 gap: var(--gap-section);
4302 align-items: flex-end;
4303 }
4304
4305 .admin-page .lottery-form .form-group { margin-bottom: 0; }
4306
4307 .admin-page .lottery-form label {
4308 font-size: var(--text-note);
4309 display: block;
4310 margin-bottom: var(--gap-bound);
4311 }
4312
4313 .admin-page .lottery-form input[type="number"] { width: 80px; }
4314
4315 /* Page-top notice (charter primitive, replaces #restart-banner and
4316 the inline sandbox banner in dashboard-project). Full-bleed across
4317 the viewport, monospace, one short sentence + optional action.
4318 Hero callouts on the landing page use `.alert` instead (multi-paragraph). */
4319 .banner {
4320 padding: var(--gap-section) var(--gap-pane);
4321 font-family: var(--font-mono);
4322 font-size: var(--text-note);
4323 text-align: center;
4324 color: var(--content);
4325 }
4326
4327 .banner--info {
4328 background: var(--highlight-faint);
4329 border-bottom: 2px solid var(--focus-ring);
4330 }
4331
4332 .banner--warning {
4333 background: color-mix(in oklch, var(--warning) 14%, var(--surface-page));
4334 border-bottom: 2px solid var(--warning);
4335 }
4336
4337 .banner a {
4338 color: inherit;
4339 text-decoration: underline;
4340 }
4341
4342 /* ===========================================
4343 SECTIONS
4344 =========================================== */
4345
4346 .section-header {
4347 font-size: var(--text-head);
4348 font-weight: normal;
4349 margin-bottom: var(--gap-section);
4350 padding-bottom: var(--gap-peer);
4351 border-bottom: 1px solid var(--border);
4352 font-family: var(--font-mono);
4353 }
4354
4355 /* Canonical "page section box", light-background card with padding + bottom
4356 space. One of these in the codebase. Aliased to the per-page class names
4357 that were just relabels of this same shape. Templates can use the alias
4358 name OR add `.content-section` as a co-class. */
4359 .content-section,
4360 .import-page .import-form,
4361 .import-page .import-progress,
4362 .library-page .library-section,
4363 .library-page .library-downloads,
4364 .library-page .downloads-hero,
4365 .receipt-page .receipt-box,
4366 .stripe-disclaimer-page .disclaimer-box,
4367 .project-page .project-sections {
4368 background: var(--surface-overlay);
4369 padding: var(--gap-page);
4370 margin-bottom: var(--gap-page);
4371 }
4372
4373 /* Variants of the canonical shape that want top-spacing instead of bottom,
4374 or a tighter --gap-pane padding. */
4375 .library-page .library-downloads,
4376 .receipt-page .receipt-box { margin-bottom: 0; margin-top: var(--gap-pane); }
4377 .import-page .import-progress { padding: var(--gap-pane); margin-bottom: var(--gap-section); }
4378 .stripe-disclaimer-page .disclaimer-box { margin-bottom: var(--gap-pane); }
4379
4380 .form-section {
4381 margin-bottom: var(--gap-page);
4382 }
4383
4384 .section-group-label {
4385 font-family: var(--font-mono);
4386 font-size: var(--text-fine);
4387 font-weight: bold;
4388 text-transform: uppercase;
4389 letter-spacing: 0.05em;
4390 color: var(--content-muted);
4391 margin: var(--gap-page) 0 var(--gap-section) 0;
4392 padding-top: var(--gap-pane);
4393 border-top: 2px solid var(--border);
4394 }
4395
4396 .form-section h2 {
4397 font-size: var(--text-subhead);
4398 font-weight: normal;
4399 margin-bottom: var(--gap-section);
4400 padding-bottom: var(--gap-peer);
4401 border-bottom: 1px solid var(--border);
4402 }
4403
4404 details.form-section > summary {
4405 cursor: pointer;
4406 list-style: disclosure-closed;
4407 }
4408
4409 details.form-section[open] > summary {
4410 list-style: disclosure-open;
4411 margin-bottom: var(--gap-section);
4412 }
4413
4414 details.form-section > summary > h2 {
4415 display: inline;
4416 }
4417
4418 details.form-section > summary:hover > h2 {
4419 color: var(--content);
4420 }
4421
4422 /* ===========================================
4423 NAVIGATION
4424 =========================================== */
4425
4426 nav {
4427 margin-bottom: var(--gap-page);
4428 }
4429
4430 .nav-links {
4431 display: flex;
4432 gap: var(--gap-page);
4433 flex-wrap: wrap;
4434 }
4435
4436 .nav-links a {
4437 color: var(--content);
4438 text-decoration: none;
4439 font-family: var(--font-mono);
4440 transition: opacity 0.2s ease;
4441 }
4442
4443 .nav-links a:hover {
4444 opacity: 0.6;
4445 }
4446
4447 .shortcuts-help-btn {
4448 font-family: var(--font-mono);
4449 color: var(--content-muted);
4450 font-size: var(--text-note);
4451 width: 1.5rem;
4452 height: 1.5rem;
4453 border: 1px solid var(--border);
4454 border-radius: var(--radius-control);
4455 display: inline-flex;
4456 align-items: center;
4457 justify-content: center;
4458 cursor: pointer;
4459 transition: opacity 0.2s ease;
4460 padding: 0;
4461 background: none;
4462 }
4463
4464 .shortcuts-help-btn:hover {
4465 opacity: 0.6;
4466 }
4467
4468 .breadcrumb {
4469 font-size: var(--text-note);
4470 color: var(--content-muted);
4471 margin-bottom: var(--gap-peer);
4472 font-family: var(--font-mono);
4473 }
4474
4475 .breadcrumb a {
4476 color: var(--content);
4477 text-decoration: none;
4478 }
4479
4480 .breadcrumb a:hover {
4481 text-decoration: underline;
4482 }
4483
4484 /* ===========================================
4485 HEADER & FOOTER
4486 =========================================== */
4487
4488 header {
4489 background: transparent;
4490 border-bottom: none;
4491 padding: 0;
4492 margin-bottom: var(--gap-page);
4493 }
4494
4495 footer {
4496 text-align: center;
4497 font-family: var(--font-mono);
4498 color: var(--content-muted);
4499 }
4500
4501 .profile-footer {
4502 margin-top: var(--gap-page);
4503 padding-top: var(--gap-pane);
4504 border-top: 1px solid var(--border);
4505 text-align: center;
4506 opacity: 0.6;
4507 font-size: var(--text-note);
4508 }
4509
4510 .profile-footer a {
4511 color: var(--content);
4512 text-decoration: none;
4513 }
4514
4515 .profile-footer a:hover {
4516 text-decoration: underline;
4517 }
4518
4519 /* Site-wide header */
4520 .site-header {
4521 display: flex;
4522 justify-content: space-between;
4523 align-items: center;
4524 padding: var(--gap-section) var(--gap-pane);
4525 margin-bottom: var(--gap-pane);
4526 }
4527
4528 .site-header nav {
4529 margin: 0;
4530 }
4531
4532 .site-header .nav-links {
4533 gap: var(--gap-pane);
4534 }
4535
4536 .site-logo {
4537 font-family: var(--font-heading);
4538 font-size: var(--text-subhead);
4539 text-decoration: none;
4540 color: var(--content);
4541 }
4542
4543 .site-logo:hover {
4544 opacity: 0.8;
4545 }
4546
4547 .nav-form {
4548 display: contents;
4549 }
4550
4551 /* Header search */
4552 .header-search {
4553 flex: 0 1 220px;
4554 margin: 0 var(--gap-section);
4555 }
4556
4557 .header-search input {
4558 width: 100%;
4559 padding: var(--gap-peer) var(--gap-group);
4560 font-size: var(--text-note);
4561 font-family: var(--font-mono);
4562 border: 1px solid var(--border);
4563 background: var(--surface-page);
4564 color: var(--content);
4565 border-radius: var(--radius-control);
4566 }
4567
4568 .header-search input:focus {
4569 outline: 2px solid var(--focus-ring);
4570 outline-offset: -1px;
4571 }
4572
4573 /* Hamburger toggle, hidden by default, shown at 768px */
4574 .nav-toggle-checkbox {
4575 display: none;
4576 }
4577
4578 .nav-toggle-label {
4579 display: none;
4580 cursor: pointer;
4581 padding: var(--gap-peer);
4582 z-index: var(--z-header);
4583 }
4584
4585 .nav-toggle-label span {
4586 display: block;
4587 width: 22px;
4588 height: 2px;
4589 background: var(--content);
4590 margin: 5px 0;
4591 transition: transform 0.2s ease, opacity 0.2s ease;
4592 }
4593
4594 /* ===========================================
4595 LAYOUT UTILITIES
4596 =========================================== */
4597
4598 .action-buttons {
4599 display: flex;
4600 gap: var(--gap-section);
4601 flex-wrap: wrap;
4602 }
4603 /* ===========================================
4604 SPECIFIC COMPONENTS
4605 =========================================== */
4606
4607 /* Login container */
4608 .login-container {
4609 display: flex;
4610 flex-direction: column;
4611 max-width: 400px;
4612 width: 100%;
4613 margin: 0 auto;
4614 background: var(--surface-overlay);
4615 padding: var(--gap-page);
4616 }
4617 /* Logo */
4618 .logo {
4619 font-family: var(--font-heading);
4620 color: var(--content);
4621 font-size: var(--text-head);
4622 }
4623
4624 .tagline {
4625 font-family: var(--font-mono);
4626 color: var(--content-muted);
4627 text-align: center;
4628 }
4629
4630 .centered-page .tagline {
4631 margin-bottom: var(--gap-bound);
4632 }
4633 /* Skip link */
4634 .skip-link {
4635 text-align: center;
4636 margin-top: var(--gap-section);
4637 }
4638
4639 .skip-link a {
4640 color: var(--primary-dark);
4641 text-decoration: none;
4642 font-size: var(--text-note);
4643 opacity: 0.7;
4644 }
4645
4646 .skip-link a:hover {
4647 text-decoration: underline;
4648 }
4649
4650 /* Footer link */
4651 .foot-link {
4652 margin-top: 0.5em;
4653 }
4654
4655 /* Status indicators */
4656 .status-indicator {
4657 display: inline-block;
4658 width: 8px;
4659 height: 8px;
4660 background: var(--primary-dark);
4661 margin-right: var(--gap-peer);
4662 }
4663
4664 .status-indicator.active {
4665 background: var(--success);
4666 }
4667
4668 /* Amount styling */
4669 .amount-in {
4670 color: var(--success);
4671 }
4672
4673 .amount-out {
4674 opacity: 0.7;
4675 }
4676
4677 /* File upload */
4678 .file-upload-area {
4679 background: var(--surface-sunken);
4680 padding: var(--gap-page);
4681 text-align: center;
4682 cursor: pointer;
4683 transition: background 0.2s ease;
4684 margin-bottom: var(--gap-section);
4685 }
4686
4687 .file-upload-area:hover,
4688 .file-upload-area.dragover {
4689 background: var(--border);
4690 }
4691 /* Chart placeholder */
4692 .chart-container {
4693 background: var(--surface-sunken);
4694 padding: var(--gap-pane);
4695 margin-bottom: var(--gap-pane);
4696 }
4697
4698 .chart-header {
4699 display: flex;
4700 justify-content: space-between;
4701 align-items: center;
4702 margin-bottom: var(--gap-section);
4703 }
4704
4705 .chart-header h2 {
4706 font-size: var(--text-subhead);
4707 font-weight: normal;
4708 }
4709
4710 .chart-bars {
4711 display: flex;
4712 align-items: flex-end;
4713 gap: 2px;
4714 height: 200px;
4715 padding: 0 var(--gap-peer);
4716 }
4717
4718 .chart-bar-col {
4719 flex: 1;
4720 display: flex;
4721 flex-direction: column;
4722 align-items: center;
4723 min-width: 0;
4724 cursor: default;
4725 position: relative;
4726 }
4727
4728 .chart-bar-col[data-tooltip]:hover::before {
4729 content: attr(data-tooltip);
4730 position: absolute;
4731 bottom: 100%;
4732 left: 50%;
4733 transform: translateX(-50%);
4734 background: var(--primary-dark);
4735 color: var(--primary-light);
4736 font-size: var(--text-fine);
4737 font-family: var(--font-mono);
4738 padding: var(--gap-bound) var(--gap-peer);
4739 white-space: nowrap;
4740 z-index: var(--z-dropdown);
4741 pointer-events: none;
4742 margin-bottom: var(--gap-bound);
4743 }
4744
4745 .chart-bar {
4746 width: 100%;
4747 height: var(--fill, 0%);
4748 background: var(--primary-dark);
4749 min-height: 2px;
4750 transition: opacity 0.15s;
4751 }
4752
4753 .chart-bar-col:hover .chart-bar {
4754 opacity: 0.8;
4755 }
4756
4757 .chart-bar-label {
4758 font-size: var(--text-fine);
4759 opacity: 0.5;
4760 margin-top: var(--gap-bound);
4761 white-space: nowrap;
4762 overflow: hidden;
4763 text-overflow: ellipsis;
4764 max-width: 100%;
4765 }
4766
4767 /* Time selector */
4768 .time-selector {
4769 display: flex;
4770 gap: var(--gap-peer);
4771 }
4772
4773 .time-selector button {
4774 background: var(--surface-overlay);
4775 border: none;
4776 padding: var(--gap-peer) var(--gap-section);
4777 font-family: inherit;
4778 font-size: var(--text-note);
4779 cursor: pointer;
4780 opacity: 0.6;
4781 }
4782
4783 .time-selector button.is-selected {
4784 opacity: 1;
4785 background: var(--primary-dark);
4786 color: var(--primary-light);
4787 }
4788
4789 /* Analytics */
4790 .analytics-grid {
4791 display: grid;
4792 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
4793 gap: var(--gap-pane);
4794 margin-bottom: var(--gap-pane);
4795 }
4796 .analytics-list {
4797 list-style: none;
4798 }
4799
4800 .analytics-list li {
4801 padding: var(--gap-peer) 0;
4802 border-bottom: 1px solid var(--border);
4803 display: flex;
4804 justify-content: space-between;
4805 }
4806
4807 .analytics-list li:last-child {
4808 border-bottom: none;
4809 }
4810
4811 /* Quick actions */
4812 .quick-actions {
4813 background: var(--surface-sunken);
4814 padding: var(--gap-pane);
4815 }
4816
4817 .quick-actions h2 {
4818 font-size: var(--text-subhead);
4819 font-weight: normal;
4820 margin-bottom: var(--gap-section);
4821 }
4822
4823 /* Filter controls */
4824 .filter-controls {
4825 display: flex;
4826 gap: var(--gap-section);
4827 margin-bottom: var(--gap-section);
4828 flex-wrap: wrap;
4829 align-items: center;
4830 }
4831
4832 .filter-group {
4833 display: flex;
4834 gap: var(--gap-peer);
4835 align-items: center;
4836 }
4837 /* UUID styling */
4838 .uuid {
4839 font-family: inherit;
4840 opacity: 0.5;
4841 font-size: var(--text-note);
4842 }
4843 /* ===========================================
4844 HTMX LOADING INDICATORS
4845 =========================================== */
4846
4847 .htmx-indicator {
4848 display: none;
4849 animation: htmx-spin 1s ease-in-out infinite;
4850 }
4851
4852 .htmx-request .htmx-indicator {
4853 display: inline;
4854 }
4855
4856 .htmx-request.htmx-indicator {
4857 display: inline;
4858 }
4859
4860 /* Loading spinner animation. Applied on .htmx-indicator above. */
4861 @keyframes htmx-spin {
4862 0% { opacity: 0.3; }
4863 50% { opacity: 1; }
4864 100% { opacity: 0.3; }
4865 }
4866
4867 /* Disable buttons during request */
4868 .htmx-request button[type="submit"] {
4869 opacity: 0.6;
4870 cursor: wait;
4871 }
4872
4873 /* ===========================================
4874 TOAST NOTIFICATIONS
4875 =========================================== */
4876
4877 .toast-container {
4878 position: fixed;
4879 bottom: 1.5rem;
4880 right: 1.5rem;
4881 z-index: var(--z-toast);
4882 display: flex;
4883 flex-direction: column;
4884 gap: var(--gap-peer);
4885 }
4886
4887 .toast {
4888 background: var(--primary-dark);
4889 color: var(--primary-light);
4890 padding: var(--gap-section);
4891 font-family: var(--font-mono);
4892 font-size: var(--text-note);
4893 box-shadow: var(--shadow-3);
4894 animation: toast-in 0.3s ease-out;
4895 max-width: 300px;
4896 display: flex;
4897 align-items: center;
4898 gap: var(--gap-section);
4899 }
4900
4901 .toast-dismiss {
4902 background: none;
4903 border: none;
4904 color: inherit;
4905 font-size: var(--text-lead);
4906 cursor: pointer;
4907 opacity: 0.7;
4908 padding: 0;
4909 line-height: 1;
4910 flex-shrink: 0;
4911 }
4912
4913 .toast-dismiss:hover {
4914 opacity: 1;
4915 }
4916
4917 .toast-success {
4918 background: var(--success);
4919 }
4920
4921 .toast-error {
4922 background: var(--danger);
4923 }
4924
4925 .toast-warning {
4926 background: var(--warning);
4927 }
4928
4929 .toast-info {
4930 background: var(--primary-dark);
4931 }
4932
4933 .toast-retry-btn {
4934 background: none;
4935 border: 1px solid currentColor;
4936 color: inherit;
4937 font-family: var(--font-mono);
4938 font-size: var(--text-fine);
4939 font-weight: 600;
4940 padding: var(--gap-bound) var(--gap-peer);
4941 margin-left: var(--gap-section);
4942 cursor: pointer;
4943 }
4944
4945 .toast-retry-btn:hover {
4946 background: color-mix(in oklch, var(--primary-light) 15%, transparent);
4947 }
4948
4949 .toast.fade-out {
4950 animation: toast-out 0.3s ease-in forwards;
4951 }
4952
4953 @keyframes toast-in {
4954 from {
4955 transform: translateX(100%);
4956 opacity: 0;
4957 }
4958 to {
4959 transform: translateX(0);
4960 opacity: 1;
4961 }
4962 }
4963
4964 @keyframes toast-out {
4965 from {
4966 transform: translateX(0);
4967 opacity: 1;
4968 }
4969 to {
4970 transform: translateX(100%);
4971 opacity: 0;
4972 }
4973 }
4974
4975 /* ===========================================
4976 "NEW" BADGE (data-new-until)
4977 ===========================================
4978 Applied by whats_new.js while today's date is <= the element's
4979 data-new-until. Renders a small violet dot to the right of the
4980 element's content. Pair with relative-positioned parents. */
4981
4982 .is-new {
4983 position: relative;
4984 }
4985
4986 .is-new::after {
4987 content: "";
4988 position: absolute;
4989 top: -2px;
4990 right: -8px;
4991 width: 6px;
4992 height: 6px;
4993 border-radius: var(--radius-round);
4994 background: var(--action);
4995 box-shadow: 0 0 0 2px var(--surface-page);
4996 }
4997
4998 /* ===========================================
4999 RESTART WARNING BANNER
5000 =========================================== */
5001
5002 /* Page-top behavior extensions on top of .banner.banner--warning.
5003 Color / typography come from the .banner primitive. */
5004 #restart-banner {
5005 position: fixed;
5006 top: 0;
5007 left: 0;
5008 right: 0;
5009 z-index: var(--z-banner);
5010 padding: var(--gap-group) var(--gap-section);
5011 animation: banner-slide-down 0.3s ease-out;
5012 }
5013
5014 @keyframes banner-slide-down {
5015 from {
5016 transform: translateY(-100%);
5017 }
5018 to {
5019 transform: translateY(0);
5020 }
5021 }
5022
5023 /* ===========================================
5024 SAVE STATUS INDICATORS
5025 =========================================== */
5026
5027 /* Username availability status */
5028 .username-status {
5029 display: block;
5030 margin-top: var(--gap-bound);
5031 font-size: var(--text-note);
5032 }
5033
5034 .username-status.available {
5035 color: var(--success);
5036 }
5037
5038 .username-status.taken {
5039 color: var(--danger);
5040 }
5041
5042 /* ===========================================
5043 HTMX TRANSITIONS
5044 =========================================== */
5045
5046 /* Fade in for swapped content */
5047 .htmx-added {
5048 animation: htmx-fade-in 0.2s ease-out;
5049 }
5050
5051 @keyframes htmx-fade-in {
5052 from {
5053 opacity: 0;
5054 }
5055 to {
5056 opacity: 1;
5057 }
5058 }
5059
5060 /* Settling animation for swapped content */
5061 .htmx-settling {
5062 opacity: 0.8;
5063 }
5064
5065 /* Swap animation */
5066 .htmx-swapping {
5067 opacity: 0.5;
5068 transition: opacity 0.2s ease-out;
5069 }
5070
5071 /* ===========================================
5072 ACCESSIBILITY - FOCUS STYLES
5073 =========================================== */
5074
5075 /* Focus-visible for keyboard navigation */
5076 *:focus-visible {
5077 outline: 2px solid var(--focus-ring);
5078 outline-offset: 2px;
5079 }
5080
5081 /* Remove default focus outline since we use focus-visible */
5082 *:focus:not(:focus-visible) {
5083 outline: none;
5084 }
5085
5086 /* Ensure buttons and links are keyboard accessible */
5087 button:focus-visible,
5088 a:focus-visible,
5089 input:focus-visible,
5090 select:focus-visible,
5091 textarea:focus-visible {
5092 outline: 2px solid var(--focus-ring);
5093 outline-offset: 2px;
5094 }
5095
5096 /* Tab focus styles */
5097 .tab:focus-visible {
5098 outline: 2px solid var(--focus-ring);
5099 outline-offset: -2px;
5100 }
5101
5102 /* Canonical "selected" recipe (charter: docs/design-system.md).
5103 Apply `.is-selected` to any interactive container to mark it as the
5104 currently-selected option. A badge never takes it: a badge reports a status
5105 and nobody selects "Enabled", so badges use the `.badge--*` status set.
5106 Visibility toggles (`.tab-content.active`, `.section-panel.active`,
5107 `.editor-panel.active`) also keep `.active` since they're not selection
5108 state. */
5109 .is-selected {
5110 background: var(--highlight-faint);
5111 border-color: var(--focus-ring);
5112 }
5113
5114 /* Skip to main content link (for screen readers) */
5115 .skip-to-main {
5116 position: absolute;
5117 top: -100px;
5118 left: 0;
5119 background: var(--primary-dark);
5120 color: var(--primary-light);
5121 padding: 8px;
5122 z-index: var(--z-skip-link);
5123 transition: top 0.2s ease;
5124 }
5125
5126 .skip-to-main:focus {
5127 top: 0;
5128 }
5129
5130 /* Visually hidden but accessible to screen readers */
5131 .sr-only {
5132 position: absolute;
5133 width: 1px;
5134 height: 1px;
5135 padding: 0;
5136 margin: -1px;
5137 overflow: hidden;
5138 clip: rect(0, 0, 0, 0);
5139 white-space: nowrap;
5140 border: 0;
5141 }
5142
5143 /* Status indicator with text for accessibility */
5144 .status-indicator::after {
5145 content: attr(data-status);
5146 position: absolute;
5147 width: 1px;
5148 height: 1px;
5149 padding: 0;
5150 margin: -1px;
5151 overflow: hidden;
5152 clip: rect(0, 0, 0, 0);
5153 white-space: nowrap;
5154 border: 0;
5155 }
5156
5157 /* ===========================================
5158 PAYWALL
5159 =========================================== */
5160
5161 .paywall-section {
5162 margin: var(--gap-page) 0;
5163 padding: var(--gap-page);
5164 background: var(--surface-sunken);
5165 text-align: center;
5166 }
5167
5168 .paywall-box h2 {
5169 font-family: var(--font-heading);
5170 font-weight: normal;
5171 margin-bottom: var(--gap-peer);
5172 }
5173
5174 .paywall-price {
5175 font-family: var(--font-mono);
5176 font-size: var(--text-head);
5177 margin-bottom: var(--gap-peer);
5178 }
5179
5180 .paywall-desc {
5181 font-family: var(--font-body);
5182 color: var(--content-muted);
5183 margin-bottom: var(--gap-pane);
5184 }
5185
5186 .paywall-note {
5187 font-family: var(--font-mono);
5188 font-size: var(--text-note);
5189 color: var(--content-muted);
5190 margin-top: var(--gap-section);
5191 }
5192
5193 .paywall-note a {
5194 color: var(--action);
5195 }
5196
5197 /* ===========================================
5198 MODALS
5199 =========================================== */
5200
5201 .modal-overlay {
5202 position: fixed;
5203 inset: 0;
5204 background: var(--overlay);
5205 display: flex;
5206 align-items: center;
5207 justify-content: center;
5208 z-index: var(--z-modal);
5209 }
5210
5211 .modal {
5212 background: var(--surface-page);
5213 padding: var(--gap-page);
5214 max-width: 500px;
5215 width: 90%;
5216 max-height: 90vh;
5217 overflow-y: auto;
5218 }
5219
5220 .modal h2 {
5221 margin-bottom: var(--gap-pane);
5222 }
5223
5224 .modal .form-actions {
5225 display: flex;
5226 gap: var(--gap-section);
5227 justify-content: flex-end;
5228 margin-top: var(--gap-pane);
5229 }
5230
5231 /* ===========================================
5232 LINK ROWS (Dashboard Links Section)
5233 =========================================== */
5234
5235 .link-row {
5236 display: flex;
5237 gap: var(--gap-section);
5238 align-items: center;
5239 margin-bottom: var(--gap-section);
5240 padding: var(--gap-section);
5241 background: var(--surface-sunken);
5242 transition: opacity 0.2s ease, background 0.2s ease;
5243 }
5244
5245 .link-row input[type="text"],
5246 .link-row input[type="url"] {
5247 flex: 1;
5248 min-width: 0;
5249 }
5250
5251 .link-row .btn-danger {
5252 padding: var(--gap-peer) var(--gap-section);
5253 font-size: var(--text-note);
5254 }
5255
5256 /* Link order buttons */
5257 .link-order-buttons {
5258 display: flex;
5259 flex-direction: column;
5260 gap: 2px;
5261 }
5262
5263 .order-btn {
5264 background: var(--surface-sunken);
5265 border: 1px solid var(--border);
5266 padding: var(--gap-bound) var(--gap-peer);
5267 font-size: var(--text-fine);
5268 line-height: 1;
5269 cursor: pointer;
5270 opacity: 0.6;
5271 transition: opacity 0.15s ease, background 0.15s ease;
5272 }
5273
5274 .order-btn:hover {
5275 opacity: 1;
5276 background: var(--border);
5277 }
5278
5279 .order-btn:active {
5280 box-shadow: var(--shadow-inset);
5281 }
5282
5283 /* ===========================================
5284 TEXT EDITOR
5285 =========================================== */
5286
5287 .text-editor {
5288 width: 100%;
5289 }
5290
5291 .editor-tabs {
5292 display: flex;
5293 gap: 0;
5294 margin-bottom: 0;
5295 }
5296
5297 .editor-tab {
5298 background: var(--surface-sunken);
5299 border: none;
5300 padding: var(--gap-peer) var(--gap-pane);
5301 font-family: var(--font-mono);
5302 font-size: var(--text-note);
5303 cursor: pointer;
5304 opacity: 0.6;
5305 transition: opacity 0.2s ease, background 0.2s ease;
5306 }
5307
5308 .editor-tab.is-selected {
5309 background: var(--surface-sunken);
5310 opacity: 1;
5311 }
5312
5313 .editor-tab:hover {
5314 opacity: 1;
5315 }
5316
5317 .editor-panel {
5318 display: none;
5319 }
5320
5321 .editor-panel.active {
5322 display: block;
5323 }
5324
5325 .editor-panel textarea {
5326 width: 100%;
5327 min-height: 400px;
5328 padding: var(--gap-section);
5329 font-family: var(--font-mono);
5330 font-size: var(--text-note);
5331 line-height: 1.6;
5332 resize: vertical;
5333 border: none;
5334 background: var(--surface-sunken);
5335 }
5336
5337 .editor-footer {
5338 display: flex;
5339 justify-content: space-between;
5340 padding: var(--gap-peer) var(--gap-section);
5341 background: var(--surface-sunken);
5342 font-size: var(--text-note);
5343 opacity: 0.7;
5344 }
5345
5346 .markdown-preview {
5347 min-height: 400px;
5348 padding: var(--gap-pane);
5349 background: var(--surface-overlay);
5350 font-family: var(--font-body);
5351 line-height: 1.8;
5352 }
5353
5354 .markdown-preview h1,
5355 .markdown-preview h2,
5356 .markdown-preview h3 {
5357 margin-top: var(--gap-pane);
5358 margin-bottom: var(--gap-peer);
5359 }
5360
5361 .markdown-preview h1 {
5362 font-size: var(--text-title);
5363 }
5364
5365 .markdown-preview h2 {
5366 font-size: var(--text-head);
5367 }
5368
5369 .markdown-preview h3 {
5370 font-size: var(--text-lead);
5371 }
5372
5373 .markdown-preview p {
5374 margin-bottom: var(--gap-section);
5375 }
5376
5377 .markdown-preview code {
5378 background: var(--surface-sunken);
5379 padding: var(--gap-bound);
5380 font-family: var(--font-mono);
5381 font-size: var(--text-note);
5382 }
5383
5384 /* ===========================================
5385 AUDIO UPLOAD
5386 =========================================== */
5387
5388 .audio-upload {
5389 width: 100%;
5390 }
5391
5392 .current-audio {
5393 display: flex;
5394 justify-content: space-between;
5395 align-items: center;
5396 padding: var(--gap-section);
5397 background: var(--surface-sunken);
5398 margin-bottom: var(--gap-section);
5399 }
5400
5401 .audio-info {
5402 display: flex;
5403 align-items: center;
5404 gap: var(--gap-section);
5405 }
5406
5407 .audio-icon {
5408 font-size: var(--text-head);
5409 opacity: 0.6;
5410 }
5411
5412 .audio-details {
5413 display: flex;
5414 flex-direction: column;
5415 }
5416
5417 .audio-filename {
5418 font-family: var(--font-mono);
5419 font-size: var(--text-note);
5420 }
5421
5422 .audio-duration {
5423 font-size: var(--text-note);
5424 opacity: 0.6;
5425 }
5426
5427 .upload-area .file-upload-area {
5428 padding: var(--gap-page);
5429 }
5430
5431 .upload-icon {
5432 font-size: var(--text-title);
5433 opacity: 0.4;
5434 margin-bottom: var(--gap-peer);
5435 }
5436
5437 .upload-text {
5438 font-size: var(--text-lead);
5439 margin-bottom: var(--gap-peer);
5440 }
5441
5442 .upload-hint {
5443 font-size: var(--text-note);
5444 opacity: 0.6;
5445 }
5446
5447 .upload-progress {
5448 padding: var(--gap-pane);
5449 background: var(--surface-sunken);
5450 }
5451
5452 .progress-info {
5453 display: flex;
5454 justify-content: space-between;
5455 margin-bottom: var(--gap-section);
5456 font-size: var(--text-note);
5457 }
5458
5459 /* Canonical progress bar primitive. One of these in the codebase.
5460 - .progress-bar-container is the track (8px default; --slim = 6px)
5461 - .progress-bar is the fill (success-green default; --highlight = purple)
5462 - Add .progress-bar-container--rounded for the 3px-radius pill variant
5463 Spacing (mb-4 / mt-2 etc.) is added by the caller, not baked in. */
5464 .progress-bar-container {
5465 height: 8px;
5466 background: var(--border);
5467 overflow: hidden;
5468 }
5469 .progress-bar-container--slim { height: 6px; }
5470 .progress-bar-container--rounded { border-radius: var(--radius-control); }
5471
5472 /* Fill proportion comes from the template as --fill, because it is
5473 per-render data. The property that reads it lives here. */
5474 .progress-bar {
5475 width: var(--fill, 0%);
5476 height: 100%;
5477 background: var(--success);
5478 transition: width 0.2s ease;
5479 }
5480 .progress-bar--highlight { background: var(--action); }
5481 .progress-bar--warn { background: var(--warning); }
5482 .progress-bar--danger { background: var(--danger); }
5483
5484 /* Upload status block (filename + percent label + progress bar). One of these
5485 in the codebase. Used by wizard upload flows and standalone audio/video upload partials. */
5486 .upload-status { margin-top: var(--gap-peer); }
5487 .upload-status-row {
5488 display: flex;
5489 align-items: center;
5490 gap: var(--gap-peer);
5491 }
5492 .upload-status-row span { font-size: var(--text-note); }
5493 .upload-status-msg { margin-top: var(--gap-peer); }
5494 .upload-status-msg.is-success { color: var(--success); }
5495 .upload-status-msg.is-error { color: var(--danger); }
5496
5497 /* Inline progress cell rendered inside a table-row status cell (batch uploads). */
5498 .upload-progress-row {
5499 display: flex;
5500 align-items: center;
5501 gap: var(--gap-peer);
5502 }
5503 .upload-progress-row .progress-bar-container { flex: 1; min-width: 60px; }
5504 .upload-progress-pct {
5505 font-size: var(--text-fine);
5506 min-width: 2.5em;
5507 text-align: right;
5508 }
5509
5510 .upload-success {
5511 padding: var(--gap-pane);
5512 background: var(--surface-sunken);
5513 text-align: center;
5514 }
5515 .upload-error {
5516 padding: var(--gap-pane);
5517 background: var(--surface-sunken);
5518 text-align: center;
5519 }
5520
5521 .hidden {
5522 display: none !important;
5523 }
5524
5525 /* ===========================================
5526 VIEW TOGGLE (Grid/List)
5527 =========================================== */
5528
5529 .view-controls {
5530 display: flex;
5531 gap: var(--gap-bound);
5532 margin-left: auto;
5533 }
5534
5535 .view-btn {
5536 background: var(--surface-sunken);
5537 border: none;
5538 padding: var(--gap-peer) var(--gap-group);
5539 font-family: var(--font-mono);
5540 font-size: var(--text-note);
5541 cursor: pointer;
5542 opacity: 0.5;
5543 transition: opacity 0.2s ease;
5544 }
5545
5546 .view-btn:hover {
5547 opacity: 0.8;
5548 }
5549
5550 .view-btn.is-selected {
5551 opacity: 1;
5552 background: var(--primary-dark);
5553 color: var(--primary-light);
5554 }
5555 .results-container.results-list .results-table {
5556 display: block;
5557 }
5558
5559 .results-container.results-list .results-grid {
5560 display: none;
5561 }
5562
5563 .results-container.results-grid .results-table {
5564 display: none;
5565 }
5566
5567 .results-container.results-grid .results-grid {
5568 display: grid;
5569 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
5570 gap: var(--gap-pane);
5571 border: 1px solid var(--border);
5572 border-top: none;
5573 padding: var(--gap-pane);
5574 background: var(--surface-overlay);
5575 }
5576
5577 /* Grid cards */
5578 .grid-card {
5579 background: var(--surface-overlay);
5580 text-decoration: none;
5581 color: var(--content);
5582 display: flex;
5583 flex-direction: column;
5584 transition: background 0.2s ease;
5585 }
5586
5587 .grid-card:hover {
5588 background: var(--surface-sunken);
5589 text-decoration: none;
5590 }
5591
5592 .grid-card-thumbnail {
5593 height: 120px;
5594 background: var(--border);
5595 display: flex;
5596 align-items: center;
5597 justify-content: center;
5598 font-size: var(--text-fine);
5599 opacity: 0.5;
5600 text-transform: uppercase;
5601 }
5602
5603 .grid-card-content {
5604 padding: var(--gap-section);
5605 display: flex;
5606 flex-direction: column;
5607 gap: var(--gap-bound);
5608 }
5609
5610 .grid-card-title {
5611 font-family: var(--font-heading);
5612 font-size: var(--text-lead);
5613 }
5614
5615 .grid-card-meta {
5616 font-size: var(--text-note);
5617 opacity: 0.6;
5618 }
5619
5620 .grid-card-footer {
5621 display: flex;
5622 justify-content: space-between;
5623 align-items: center;
5624 margin-top: var(--gap-peer);
5625 font-size: var(--text-note);
5626 }
5627
5628 .grid-card-category {
5629 opacity: 0.6;
5630 }
5631
5632 .grid-card-price {
5633 font-weight: bold;
5634 }
5635
5636 .grid-card-stat {
5637 opacity: 0.6;
5638 }
5639
5640 /* ===========================================
5641 PROJECT PAGE VIEW TOGGLE
5642 =========================================== */
5643
5644 .items-header {
5645 display: flex;
5646 justify-content: space-between;
5647 align-items: center;
5648 margin-bottom: var(--gap-pane);
5649 }
5650
5651 .items-header .section-header {
5652 margin-bottom: 0;
5653 border-bottom: none;
5654 padding-bottom: 0;
5655 }
5656
5657 /* Grid view is default on project page */
5658 .items-container.items-grid-view .items-grid {
5659 display: grid;
5660 }
5661
5662 .items-container.items-grid-view .items-list {
5663 display: none;
5664 }
5665
5666 .items-container.items-list-view .items-grid {
5667 display: none;
5668 }
5669
5670 .items-container.items-list-view .items-list {
5671 display: block;
5672 }
5673
5674 /* List items styling */
5675 .items-list {
5676 display: none;
5677 }
5678
5679 .list-item {
5680 display: flex;
5681 justify-content: space-between;
5682 align-items: center;
5683 padding: var(--gap-section);
5684 background: var(--surface-overlay);
5685 margin-bottom: var(--gap-peer);
5686 text-decoration: none;
5687 color: var(--content);
5688 transition: background 0.2s ease;
5689 }
5690
5691 .list-item:hover {
5692 background: var(--surface-sunken);
5693 text-decoration: none;
5694 }
5695
5696 .list-item-info {
5697 display: flex;
5698 flex-direction: column;
5699 gap: var(--gap-bound);
5700 }
5701
5702 .list-item-title {
5703 font-family: var(--font-heading);
5704 font-size: var(--text-lead);
5705 }
5706
5707 .list-item-meta {
5708 font-size: var(--text-note);
5709 opacity: 0.6;
5710 }
5711
5712 .list-item-price {
5713 font-size: var(--text-lead);
5714 font-weight: bold;
5715 }
5716
5717 /* ===========================================
5718 LANDING PAGE
5719 =========================================== */
5720
5721 .landing-hero {
5722 max-width: 720px;
5723 width: 100%;
5724 text-align: center;
5725 }
5726
5727 .landing-headline {
5728 font-family: var(--font-heading);
5729 font-size: var(--text-head);
5730 margin-bottom: var(--gap-peer);
5731 }
5732
5733 .landing-sub {
5734 font-size: var(--text-body);
5735 line-height: 1.6;
5736 opacity: 0.75;
5737 margin-bottom: 0;
5738 }
5739
5740 .fork-revenue-line {
5741 font-size: var(--text-note);
5742 opacity: 0.8;
5743 margin: var(--gap-section) 0 var(--gap-peer);
5744 line-height: 1.5;
5745 }
5746
5747 .fork-revenue-line a {
5748 border-bottom: 1px solid transparent;
5749 transition: border-color 0.2s ease;
5750 }
5751
5752 .fork-revenue-line a:hover {
5753 border-bottom-color: currentColor;
5754 }
5755
5756 .landing-fork {
5757 display: grid;
5758 grid-template-columns: 1fr 1fr;
5759 gap: var(--gap-section);
5760 margin-top: var(--gap-page);
5761 text-align: left;
5762 }
5763
5764 .fork-heading {
5765 font-family: var(--font-mono);
5766 font-size: var(--text-note);
5767 font-weight: 600;
5768 text-transform: uppercase;
5769 letter-spacing: 0.05em;
5770 margin-bottom: var(--gap-section);
5771 }
5772
5773 .fork-list {
5774 list-style: none;
5775 margin: 0;
5776 padding: 0;
5777 flex: 1;
5778 }
5779
5780 .fork-list li {
5781 margin-bottom: var(--gap-group);
5782 font-size: var(--text-note);
5783 line-height: 1.5;
5784 }
5785
5786 .fork-list strong {
5787 font-family: var(--font-mono);
5788 font-size: var(--text-fine);
5789 }
5790
5791 .fork-actions {
5792 margin-top: auto;
5793 padding-top: var(--gap-section);
5794 display: flex;
5795 flex-direction: column;
5796 align-items: center;
5797 gap: var(--gap-peer);
5798 }
5799
5800 .fork-actions .big-button {
5801 width: 100%;
5802 }
5803
5804 .fork-secondary-link {
5805 font-family: var(--font-mono);
5806 font-size: var(--text-fine);
5807 opacity: 0.6;
5808 transition: opacity 0.2s ease;
5809 }
5810
5811 .fork-secondary-link:hover {
5812 opacity: 1;
5813 }
5814
5815 .fork-lede {
5816 font-size: var(--text-body);
5817 line-height: 1.55;
5818 margin: 0 0 var(--gap-section);
5819 }
5820
5821 .landing-browse-cta {
5822 text-align: center;
5823 margin: var(--gap-section) 0 0;
5824 }
5825
5826 .landing-mobile-note {
5827 text-align: center;
5828 margin: var(--gap-peer) 0 0;
5829 font-size: var(--text-fine);
5830 color: var(--content-muted);
5831 }
5832
5833 .founder-tagline {
5834 font-family: var(--font-body);
5835 font-size: var(--text-body);
5836 line-height: 1.55;
5837 margin: var(--gap-section) auto 0;
5838 max-width: 560px;
5839 color: var(--content);
5840 display: flex;
5841 flex-direction: column;
5842 align-items: center;
5843 gap: var(--gap-peer);
5844 }
5845
5846 .landing-velocity {
5847 font-family: var(--font-mono);
5848 font-size: var(--text-fine);
5849 text-align: center;
5850 margin: var(--gap-section) auto 0;
5851 color: var(--content-muted);
5852 }
5853
5854 .landing-velocity a {
5855 color: var(--content);
5856 text-decoration: none;
5857 border-bottom: 1px solid var(--border);
5858 }
5859
5860 .landing-velocity a:hover {
5861 border-bottom-color: var(--content);
5862 }
5863
5864 .founder-tagline-detail {
5865 display: block;
5866 }
5867
5868 .founder-tagline-mark {
5869 font-family: var(--font-mono);
5870 font-size: var(--text-fine);
5871 font-weight: 600;
5872 text-transform: uppercase;
5873 letter-spacing: 0.08em;
5874 color: var(--action);
5875 padding: var(--gap-bound) var(--gap-peer);
5876 border: 1px solid var(--action);
5877 border-radius: var(--radius-control);
5878 white-space: nowrap;
5879 }
5880
5881 .fork-card--founder {
5882 border-color: var(--action);
5883 box-shadow: 0 0 0 1px var(--highlight-faint);
5884 }
5885
5886 .founder-tier-grid {
5887 display: grid;
5888 grid-template-columns: 1fr 1fr;
5889 gap: var(--gap-peer) var(--gap-section);
5890 margin-bottom: var(--gap-section);
5891 padding-bottom: var(--gap-section);
5892 border-bottom: 1px dashed var(--border);
5893 }
5894
5895 .founder-tier {
5896 display: flex;
5897 flex-direction: column;
5898 gap: var(--gap-bound);
5899 }
5900
5901 .founder-tier-name {
5902 font-family: var(--font-mono);
5903 font-size: var(--text-fine);
5904 font-weight: 600;
5905 text-transform: uppercase;
5906 letter-spacing: 0.05em;
5907 opacity: 0.7;
5908 }
5909
5910 .founder-tier-price {
5911 font-family: var(--font-heading);
5912 font-size: var(--text-body);
5913 line-height: 1.1;
5914 }
5915
5916 .founder-tier-price del {
5917 opacity: 0.4;
5918 margin-right: var(--gap-bound);
5919 font-weight: 400;
5920 }
5921
5922 .founder-tier-price strong {
5923 color: var(--action);
5924 font-weight: 700;
5925 }
5926
5927 .founder-tier-unit {
5928 font-family: var(--font-mono);
5929 font-size: var(--text-fine);
5930 opacity: 0.6;
5931 margin-left: var(--gap-bound);
5932 }
5933
5934 .founder-slots {
5935 font-family: var(--font-mono);
5936 font-size: var(--text-fine);
5937 margin: 0 0 var(--gap-section);
5938 line-height: 1.3;
5939 }
5940
5941 .founder-slots-number {
5942 font-family: var(--font-heading);
5943 font-size: var(--text-head);
5944 color: var(--action);
5945 font-weight: 700;
5946 margin-right: var(--gap-bound);
5947 }
5948
5949 .founder-slots-cap {
5950 opacity: 0.55;
5951 margin-left: var(--gap-bound);
5952 }
5953
5954 .founder-slots--cap {
5955 opacity: 0.7;
5956 font-size: var(--text-fine);
5957 }
5958
5959 .landing-stats-line {
5960 font-family: var(--font-mono);
5961 font-size: var(--text-note);
5962 opacity: 0.6;
5963 margin-top: var(--gap-page);
5964 }
5965
5966 .landing-stats-line a {
5967 opacity: 0.8;
5968 transition: opacity 0.2s ease;
5969 }
5970
5971 .landing-stats-line a:hover {
5972 opacity: 1;
5973 }
5974
5975 .landing-principles {
5976 margin-top: var(--gap-page);
5977 width: 100%;
5978 }
5979
5980 .landing-use-cases {
5981 margin-top: var(--gap-page);
5982 width: 100%;
5983 }
5984
5985 .use-case-line {
5986 font-family: var(--font-mono);
5987 font-size: var(--text-note);
5988 opacity: 0.7;
5989 letter-spacing: 0.02em;
5990 }
5991
5992 .landing-prose {
5993 font-size: var(--text-body);
5994 line-height: 1.7;
5995 margin-bottom: var(--gap-peer);
5996 }
5997
5998 .notify-form {
5999 display: flex;
6000 gap: var(--gap-peer);
6001 max-width: 400px;
6002 margin: 0 auto;
6003 }
6004
6005 .notify-input {
6006 flex: 1;
6007 padding: var(--gap-peer) var(--gap-section);
6008 border: 1px solid var(--border);
6009 border-radius: var(--radius-control);
6010 font-family: var(--font-body);
6011 font-size: var(--text-note);
6012 background: var(--surface-page);
6013 color: var(--content);
6014 }
6015
6016 .notify-status {
6017 font-family: var(--font-mono);
6018 font-size: var(--text-fine);
6019 text-align: center;
6020 margin-top: var(--gap-peer);
6021 min-height: 1.2em;
6022 }
6023
6024 .notify-status.success {
6025 color: var(--content);
6026 }
6027
6028 .notify-status.error {
6029 color: var(--danger);
6030 }
6031
6032 .section-label {
6033 font-family: var(--font-mono);
6034 font-size: var(--text-note);
6035 letter-spacing: 0.05em;
6036 text-transform: uppercase;
6037 opacity: 0.6;
6038 margin-bottom: var(--gap-section);
6039 }
6040
6041 .tier-section {
6042 margin: var(--gap-page) 0;
6043 width: 100%;
6044 }
6045 /* Marketing pricing variant of .card--bordered. */
6046 .tier-name {
6047 font-family: var(--font-mono);
6048 font-size: var(--text-note);
6049 font-weight: 600;
6050 margin-bottom: var(--gap-bound);
6051 }
6052
6053 .tier-price {
6054 font-family: var(--font-heading);
6055 font-size: var(--text-lead);
6056 margin-bottom: var(--gap-bound);
6057 }
6058
6059 .tier-desc {
6060 font-size: var(--text-note);
6061 opacity: 0.7;
6062 }
6063
6064 .tier-card.planned {
6065 opacity: 0.5;
6066 border-style: dashed;
6067 position: relative;
6068 }
6069 .landing-cta {
6070 display: flex;
6071 align-items: center;
6072 justify-content: center;
6073 gap: var(--gap-page);
6074 width: fit-content;
6075 margin: var(--gap-pane) auto var(--gap-page);
6076 }
6077 .secondary-links {
6078 display: flex;
6079 gap: var(--gap-pane);
6080 justify-content: center;
6081 margin-top: var(--gap-section);
6082 }
6083
6084 .secondary-links a {
6085 font-size: var(--text-note);
6086 opacity: 0.6;
6087 transition: opacity 0.2s ease;
6088 }
6089
6090 .secondary-links a:hover {
6091 opacity: 1;
6092 }
6093 .section-link {
6094 display: block;
6095 margin-top: var(--gap-section);
6096 font-family: var(--font-mono);
6097 font-size: var(--text-fine);
6098 opacity: 0.55;
6099 transition: opacity 0.2s ease;
6100 }
6101
6102 .section-link:hover {
6103 opacity: 1;
6104 }
6105
6106 .feature-grid {
6107 display: grid;
6108 grid-template-columns: 1fr 1fr;
6109 gap: var(--gap-section);
6110 text-align: left;
6111 }
6112
6113 .feature-name {
6114 font-family: var(--font-mono);
6115 font-size: var(--text-note);
6116 font-weight: 600;
6117 margin-bottom: var(--gap-bound);
6118 }
6119
6120 .feature-desc {
6121 font-size: var(--text-note);
6122 opacity: 0.7;
6123 }
6124
6125 .how-list {
6126 list-style: none;
6127 margin: 0;
6128 padding: 0;
6129 text-align: left;
6130 }
6131
6132 .how-list li {
6133 margin-bottom: var(--gap-group);
6134 font-size: var(--text-body);
6135 }
6136
6137 .how-list strong {
6138 font-family: var(--font-mono);
6139 font-size: var(--text-note);
6140 }
6141
6142 .do-rail {
6143 list-style: none;
6144 margin: var(--gap-page) 0 var(--gap-pane);
6145 padding: 0;
6146 display: grid;
6147 /* A do-card stops being readable under about 200px, which is a fact about
6148 the card. Three across is what that yields at full width. */
6149 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
6150 gap: var(--gap-section);
6151 width: 100%;
6152 }
6153
6154 .do-card {
6155 background: transparent;
6156 border: 1px solid var(--border);
6157 padding: var(--gap-section);
6158 text-align: left;
6159 display: flex;
6160 flex-direction: column;
6161 }
6162
6163 .do-card-title {
6164 font-family: var(--font-mono);
6165 font-size: var(--text-note);
6166 font-weight: 600;
6167 text-transform: uppercase;
6168 letter-spacing: 0.05em;
6169 margin: 0 0 var(--gap-peer);
6170 }
6171
6172 .do-card-lede {
6173 font-size: var(--text-note);
6174 line-height: 1.5;
6175 opacity: 0.78;
6176 margin: 0 0 var(--gap-section);
6177 }
6178
6179 .do-card-links {
6180 list-style: none;
6181 margin: auto 0 0;
6182 padding: var(--gap-section) 0 0;
6183 border-top: 1px solid var(--border);
6184 display: flex;
6185 flex-wrap: wrap;
6186 gap: var(--gap-peer) var(--gap-section);
6187 }
6188
6189 .do-card-links a {
6190 font-size: var(--text-note);
6191 border-bottom: 1px solid transparent;
6192 transition: border-color 0.2s ease;
6193 }
6194
6195 .do-card-links a:hover {
6196 border-bottom-color: currentColor;
6197 }
6198
6199 .do-card--wide {
6200 margin-top: var(--gap-page);
6201 width: 100%;
6202 }
6203
6204 .contrast-rail {
6205 list-style: none;
6206 margin: var(--gap-section) 0 0;
6207 padding: 0;
6208 display: grid;
6209 /* Title plus a short link list, so it holds up narrower than a do-card.
6210 Four across at full width. */
6211 grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
6212 gap: var(--gap-section);
6213 width: 100%;
6214 }
6215
6216 .contrast-card {
6217 border-top: 1px solid var(--border);
6218 padding: var(--gap-section) var(--gap-bound) 0;
6219 text-align: left;
6220 }
6221
6222 .contrast-card-title {
6223 font-family: var(--font-mono);
6224 font-size: var(--text-fine);
6225 letter-spacing: 0.04em;
6226 text-transform: uppercase;
6227 opacity: 0.5;
6228 text-decoration: line-through;
6229 text-decoration-thickness: 1px;
6230 margin: 0 0 var(--gap-peer);
6231 font-weight: 600;
6232 }
6233
6234 .contrast-card-links {
6235 list-style: none;
6236 margin: 0;
6237 padding: 0;
6238 display: flex;
6239 flex-wrap: wrap;
6240 gap: var(--gap-peer) var(--gap-section);
6241 }
6242
6243 .contrast-card-links a {
6244 font-size: var(--text-note);
6245 border-bottom: 1px solid transparent;
6246 transition: border-color 0.2s ease;
6247 }
6248
6249 .contrast-card-links a:hover {
6250 border-bottom-color: currentColor;
6251 }
6252
6253 .explore-links {
6254 display: flex;
6255 flex-wrap: wrap;
6256 gap: var(--gap-section) var(--gap-pane);
6257 justify-content: center;
6258 }
6259
6260 .explore-links a {
6261 font-family: var(--font-mono);
6262 font-size: var(--text-note);
6263 opacity: 0.7;
6264 transition: opacity 0.2s ease;
6265 }
6266
6267 .explore-links a:hover {
6268 opacity: 1;
6269 }
6270
6271 /* ===========================================
6272 PRICING CALCULATOR
6273 =========================================== */
6274
6275 .pricing-page {
6276 max-width: 800px;
6277 }
6278
6279 .pricing-input-wrap {
6280 display: flex;
6281 align-items: baseline;
6282 justify-content: center;
6283 gap: var(--gap-bound);
6284 }
6285
6286 .pricing-currency {
6287 font-family: var(--font-heading);
6288 font-size: var(--text-display);
6289 opacity: 0.5;
6290 }
6291
6292 .pricing-input {
6293 font-family: var(--font-heading);
6294 font-size: var(--text-display);
6295 width: 5ch;
6296 text-align: center;
6297 background: transparent;
6298 border: none;
6299 border-bottom: 2px solid var(--border);
6300 color: var(--content);
6301 padding: var(--gap-bound) 0;
6302 -moz-appearance: textfield;
6303 }
6304
6305 .pricing-input::-webkit-inner-spin-button,
6306 .pricing-input::-webkit-outer-spin-button {
6307 -webkit-appearance: none;
6308 margin: 0;
6309 }
6310
6311 .pricing-input:focus {
6312 outline: none;
6313 border-bottom-color: var(--action);
6314 }
6315
6316 .pricing-period {
6317 font-family: var(--font-mono);
6318 font-size: var(--text-body);
6319 opacity: 0.5;
6320 }
6321
6322 /* <mnw-price-mode> is a custom element (default display:inline); it wraps the
6323 toggle and the tier grid, so it has to lay out as a block. */
6324 mnw-price-mode {
6325 display: block;
6326 }
6327
6328 /* List-vs-founder toggle above the tier cards, rendered only while the founder
6329 window is open. Segmented control rather than a checkbox: both options are
6330 real prices the visitor can be shown, and neither is an "off" state. Follows
6331 .tier-option's pattern of hiding the radio and styling the label off
6332 :has(:checked), so the selection needs no JS class. */
6333 .price-mode {
6334 display: inline-flex;
6335 gap: 0;
6336 margin-bottom: var(--gap-section);
6337 border: 1px solid var(--border);
6338 border-radius: var(--radius-control);
6339 overflow: hidden;
6340 }
6341
6342 .price-mode-option {
6343 cursor: pointer;
6344 font-family: var(--font-mono);
6345 font-size: var(--text-note);
6346 padding: var(--gap-bound) var(--gap-section);
6347 transition: background-color 0.15s ease;
6348 }
6349
6350 .price-mode-option input[type="radio"] {
6351 position: absolute;
6352 opacity: 0;
6353 pointer-events: none;
6354 }
6355
6356 .price-mode-option:has(input[type="radio"]:checked) {
6357 background: var(--action);
6358 color: var(--content-on-action);
6359 }
6360
6361 .price-mode-option:focus-within {
6362 outline: 2px solid var(--focus-ring);
6363 outline-offset: -2px;
6364 }
6365
6366 .tier-selector {
6367 display: grid;
6368 grid-template-columns: 1fr 1fr;
6369 gap: var(--gap-section);
6370 text-align: left;
6371 }
6372
6373 .tier-option {
6374 cursor: pointer;
6375 transition: border-color 0.15s ease;
6376 }
6377
6378 .tier-option input[type="radio"] {
6379 position: absolute;
6380 opacity: 0;
6381 pointer-events: none;
6382 }
6383
6384 .tier-option:focus-within {
6385 outline: 2px solid var(--focus-ring);
6386 outline-offset: 2px;
6387 }
6388
6389 /* Selected tier, driven purely by the checked radio (no JS toggling a
6390 class). The `.is-selected` alias is retained for any server-rendered use. */
6391 .tier-option.is-selected,
6392 .tier-option:has(input[type="radio"]:checked) {
6393 border-color: var(--action);
6394 border-width: 2px;
6395 padding: calc(var(--gap-section) - 1px);
6396 }
6397
6398 /* Two dials side by side (price + volume, then the other platform's two).
6399 Stacks on narrow screens via the mobile block below. */
6400 .calc-dials {
6401 display: grid;
6402 grid-template-columns: 1fr 1fr;
6403 gap: var(--gap-section);
6404 align-items: end;
6405 }
6406
6407 .calc-dial {
6408 display: flex;
6409 flex-direction: column;
6410 align-items: center;
6411 gap: var(--gap-bound);
6412 }
6413
6414 .calc-dial-label {
6415 font-family: var(--font-mono);
6416 font-size: var(--text-note);
6417 opacity: 0.7;
6418 }
6419
6420 /* Who comes out ahead at the current dials. The three states are the only
6421 place the calculator uses color to make a claim, so they carry the same
6422 weight in both directions: losing is stated as plainly as winning. */
6423 .calc-verdict {
6424 font-family: var(--font-heading);
6425 font-size: var(--text-title);
6426 text-align: center;
6427 padding: var(--gap-pane);
6428 background: var(--surface-overlay);
6429 border: 1px solid var(--border);
6430 border-left-width: 3px;
6431 }
6432
6433 .verdict--ahead {
6434 border-left-color: var(--success);
6435 }
6436
6437 .verdict--even {
6438 border-left-color: var(--border);
6439 }
6440
6441 .verdict--behind {
6442 border-left-color: var(--warning);
6443 }
6444
6445 .calc-table th[scope="row"] {
6446 text-align: left;
6447 font-weight: 400;
6448 opacity: 0.7;
6449 }
6450
6451 .calc-table .calc-gross,
6452 .calc-keep-row td {
6453 font-family: var(--font-mono);
6454 }
6455
6456 .calc-keep-row td {
6457 font-size: var(--text-title);
6458 }
6459
6460 /* The win-region bar. Two segments tile the full width; the server sends both
6461 widths as percentages, so nothing here is computed in the browser. */
6462 .calc-scale {
6463 display: flex;
6464 flex-direction: column;
6465 gap: var(--gap-bound);
6466 }
6467
6468 .calc-bar {
6469 position: relative;
6470 display: flex;
6471 height: 1.5rem;
6472 border: 1px solid var(--border);
6473 overflow: hidden;
6474 }
6475
6476 .calc-bar-seg {
6477 height: 100%;
6478 }
6479
6480 .calc-bar-seg--other {
6481 background: var(--warning);
6482 opacity: 0.35;
6483 }
6484
6485 .calc-bar-seg--mnw {
6486 background: var(--success);
6487 opacity: 0.35;
6488 }
6489
6490 /* "You are here" on the sales axis. */
6491 .calc-marker {
6492 position: absolute;
6493 top: 0;
6494 bottom: 0;
6495 width: 2px;
6496 margin-left: -1px;
6497 background: var(--content);
6498 }
6499
6500 .calc-crossover {
6501 position: absolute;
6502 top: 0;
6503 bottom: 0;
6504 width: 1px;
6505 background: var(--content);
6506 opacity: 0.4;
6507 }
6508
6509 .calc-axis {
6510 display: flex;
6511 justify-content: space-between;
6512 gap: var(--gap-bound);
6513 font-family: var(--font-mono);
6514 font-size: var(--text-fine);
6515 opacity: 0.6;
6516 }
6517
6518 .calc-axis-crossover {
6519 opacity: 0.9;
6520 }
6521
6522 .calc-legend {
6523 display: flex;
6524 flex-wrap: wrap;
6525 gap: var(--gap-section);
6526 font-size: var(--text-fine);
6527 opacity: 0.7;
6528 }
6529
6530 .calc-legend-item::before {
6531 content: "";
6532 display: inline-block;
6533 width: 0.75rem;
6534 height: 0.75rem;
6535 margin-right: var(--gap-bound);
6536 vertical-align: baseline;
6537 }
6538
6539 .calc-legend-item--other::before {
6540 background: var(--warning);
6541 opacity: 0.35;
6542 }
6543
6544 .calc-legend-item--mnw::before {
6545 background: var(--success);
6546 opacity: 0.35;
6547 }
6548
6549 .calc-note {
6550 font-family: var(--font-mono);
6551 font-size: var(--text-note);
6552 padding: var(--gap-section);
6553 background: var(--surface-overlay);
6554 border-left: 3px solid var(--border);
6555 line-height: 1.6;
6556 }
6557
6558 /* ===========================================
6559 DOCUMENTATION PAGES
6560 =========================================== */
6561
6562 .doc-container {
6563 max-width: 680px;
6564 margin: 0 auto;
6565 padding: var(--gap-page) var(--gap-pane) var(--gap-page);
6566 }
6567
6568 .doc-breadcrumb {
6569 font-family: var(--font-mono);
6570 font-size: var(--text-note);
6571 margin-bottom: var(--gap-page);
6572 }
6573
6574 .doc-breadcrumb a { color: var(--content); text-decoration: none; }
6575 .doc-breadcrumb a:hover { text-decoration: underline; }
6576
6577 .doc-title {
6578 font-family: var(--font-heading);
6579 font-size: var(--text-display);
6580 font-weight: normal;
6581 line-height: 1.2;
6582 margin-bottom: var(--gap-pane);
6583 color: var(--content);
6584 text-align: left;
6585 }
6586
6587 .doc-body {
6588 font-family: var(--font-body);
6589 font-size: var(--text-lead);
6590 line-height: 1.9;
6591 }
6592
6593 .doc-body p { margin-bottom: var(--gap-pane); }
6594
6595 .doc-body h1 {
6596 font-family: var(--font-heading);
6597 font-size: var(--text-title);
6598 font-weight: normal;
6599 margin-top: var(--gap-page);
6600 margin-bottom: var(--gap-section);
6601 color: var(--content);
6602 text-align: left;
6603 }
6604
6605 .doc-body h2 {
6606 font-family: var(--font-mono);
6607 font-size: var(--text-head);
6608 font-weight: normal;
6609 margin-top: var(--gap-page);
6610 margin-bottom: var(--gap-section);
6611 color: var(--content);
6612 }
6613
6614 .doc-body h3 {
6615 font-family: var(--font-mono);
6616 font-size: var(--text-subhead);
6617 font-weight: normal;
6618 margin-top: var(--gap-page);
6619 margin-bottom: var(--gap-section);
6620 color: var(--content);
6621 }
6622
6623 .doc-body blockquote {
6624 border-left: 3px solid var(--action);
6625 padding-left: var(--gap-pane);
6626 margin: var(--gap-page) 0;
6627 font-style: italic;
6628 color: var(--content-muted);
6629 }
6630
6631 .doc-body ul, .doc-body ol { margin-bottom: var(--gap-pane); padding-left: var(--gap-pane); }
6632 .doc-body li { margin-bottom: var(--gap-peer); }
6633
6634 .doc-body a {
6635 color: var(--action);
6636 text-decoration: underline;
6637 text-decoration-color: color-mix(in oklch, var(--action) 40%, transparent);
6638 text-underline-offset: 2px;
6639 }
6640
6641 .doc-body a:hover { text-decoration-color: var(--action); }
6642 .doc-body strong { font-weight: bold; }
6643 .doc-body em { font-style: italic; }
6644
6645 .doc-body code {
6646 font-family: var(--font-mono);
6647 background: var(--surface-sunken);
6648 padding: var(--gap-bound) var(--gap-peer);
6649 font-size: var(--text-body);
6650 }
6651
6652 .doc-body pre {
6653 background: var(--surface-sunken);
6654 padding: var(--gap-section);
6655 overflow-x: auto;
6656 margin-bottom: var(--gap-pane);
6657 }
6658
6659 .doc-body pre code { background: none; padding: 0; }
6660
6661 .doc-body hr { border: none; text-align: center; margin: var(--gap-page) 0; }
6662 .doc-body hr::before { content: "* * *"; color: var(--content-muted); letter-spacing: 1rem; }
6663
6664 .doc-body table { width: 100%; border-collapse: collapse; margin-bottom: var(--gap-pane); }
6665 .doc-body th, .doc-body td { padding: var(--gap-section); text-align: left; border-bottom: 1px solid var(--border); }
6666 .doc-body th { font-family: var(--font-mono); font-weight: 600; }
6667
6668 .doc-backlinks {
6669 margin-top: var(--gap-page);
6670 padding-top: var(--gap-pane);
6671 border-top: 1px solid var(--border);
6672 }
6673
6674 .doc-backlinks-title {
6675 font-family: var(--font-mono);
6676 font-size: var(--text-note);
6677 font-weight: 600;
6678 text-transform: uppercase;
6679 letter-spacing: 0.05em;
6680 color: var(--content-muted);
6681 margin-bottom: var(--gap-section);
6682 }
6683
6684 .doc-backlinks-list {
6685 list-style: none;
6686 margin: 0;
6687 padding: 0;
6688 }
6689
6690 .doc-backlinks-list li { margin-bottom: var(--gap-peer); }
6691 .doc-backlinks-list a { color: var(--content); text-decoration: none; }
6692 .doc-backlinks-list a:hover { text-decoration: underline; }
6693
6694 /* Docs index page */
6695 .docs-index {
6696 max-width: 680px;
6697 margin: 0 auto;
6698 padding: var(--gap-page) var(--gap-pane) var(--gap-page);
6699 }
6700
6701 .docs-index-title {
6702 font-family: var(--font-heading);
6703 font-size: var(--text-display);
6704 font-weight: normal;
6705 line-height: 1.2;
6706 margin-bottom: var(--gap-page);
6707 color: var(--content);
6708 }
6709
6710 .docs-section {
6711 margin-bottom: var(--gap-page);
6712 }
6713
6714 .docs-section h2 {
6715 font-family: var(--font-mono);
6716 font-size: var(--text-subhead);
6717 font-weight: normal;
6718 color: var(--content-muted);
6719 margin-bottom: var(--gap-section);
6720 }
6721
6722 .docs-section ul {
6723 list-style: none;
6724 padding: 0;
6725 margin: 0;
6726 }
6727
6728 .docs-section li {
6729 margin-bottom: var(--gap-peer);
6730 }
6731
6732 .docs-section a {
6733 font-family: var(--font-body);
6734 font-size: var(--text-lead);
6735 color: var(--content);
6736 text-decoration: none;
6737 }
6738
6739 .docs-section a:hover {
6740 color: var(--action);
6741 text-decoration: underline;
6742 }
6743
6744 .docs-section details {
6745 margin-bottom: var(--gap-section);
6746 }
6747
6748 .docs-section summary {
6749 font-family: var(--font-mono);
6750 font-size: var(--text-body);
6751 color: var(--content-muted);
6752 cursor: pointer;
6753 padding: var(--gap-bound) 0;
6754 list-style: disclosure-closed;
6755 }
6756
6757 .docs-section details[open] > summary {
6758 list-style: disclosure-open;
6759 margin-bottom: var(--gap-bound);
6760 }
6761
6762 .docs-section summary:hover {
6763 color: var(--content);
6764 }
6765
6766 .docs-section details ul {
6767 padding-left: var(--gap-section);
6768 }
6769
6770 /* Docs search */
6771 .docs-search-container {
6772 position: relative;
6773 margin-bottom: var(--gap-page);
6774 }
6775 .docs-search-inline {
6776 display: inline-block;
6777 margin-bottom: 0;
6778 margin-left: auto;
6779 float: right;
6780 }
6781 .docs-search-input {
6782 width: 100%;
6783 max-width: 320px;
6784 padding: var(--gap-peer) var(--gap-section);
6785 font-family: var(--font-body);
6786 font-size: var(--text-body);
6787 background: var(--surface-sunken);
6788 border: 1px solid var(--border);
6789 color: var(--content);
6790 }
6791 .docs-search-input:focus {
6792 outline: 2px solid var(--action);
6793 border-color: var(--action);
6794 }
6795 .docs-search-input::placeholder { opacity: 0.5; }
6796 .docs-search-results {
6797 position: absolute;
6798 top: 100%;
6799 left: 0;
6800 right: 0;
6801 max-width: 320px;
6802 background: var(--surface-page);
6803 border: 1px solid var(--border);
6804 border-top: none;
6805 max-height: 300px;
6806 overflow-y: auto;
6807 z-index: var(--z-dropdown);
6808 box-shadow: var(--shadow-2);
6809 }
6810 .docs-search-result {
6811 display: flex;
6812 justify-content: space-between;
6813 align-items: baseline;
6814 padding: var(--gap-peer) var(--gap-section);
6815 text-decoration: none;
6816 color: var(--content);
6817 font-size: var(--text-note);
6818 border-bottom: 1px solid var(--border);
6819 }
6820 .docs-search-result:last-child { border-bottom: none; }
6821 .docs-search-result:hover { background: var(--surface-sunken); }
6822 .docs-search-result-title { font-family: var(--font-body); }
6823 .docs-search-result-section { font-family: var(--font-mono); font-size: var(--text-fine); opacity: 0.5; }.alert .alert-title {
6824 font-family: var(--font-mono);
6825 font-size: var(--text-note);
6826 font-weight: bold;
6827 text-transform: uppercase;
6828 letter-spacing: 0.04em;
6829 margin-bottom: var(--gap-bound);
6830 }
6831 .alert p:last-child { margin-bottom: 0; }
6832 .alert-note { border-left-color: var(--focus-ring); }
6833 .alert-note .alert-title { color: var(--focus-ring); }
6834 .alert-tip { border-left-color: var(--success); }
6835 .alert-tip .alert-title { color: var(--success); }
6836 .alert-important { border-left-color: var(--action); }
6837 .alert-important .alert-title { color: var(--action); }
6838 .alert-warning { border-left-color: var(--warning); }
6839 .alert-warning .alert-title { color: var(--warning); }
6840 .alert-caution { border-left-color: var(--danger); }
6841 .alert-caution .alert-title { color: var(--danger); }
6842
6843 /* ===========================================
6844 USE CASES PAGE
6845 =========================================== */
6846
6847 .use-cases-page {
6848 max-width: 900px;
6849 margin: 0 auto;
6850 }
6851
6852 .use-cases-intro {
6853 font-size: var(--text-lead);
6854 margin-bottom: var(--gap-page);
6855 line-height: 1.6;
6856 }
6857
6858 .use-case-grid {
6859 display: grid;
6860 grid-template-columns: 1fr 1fr 1fr;
6861 gap: var(--gap-section);
6862 text-align: left;
6863 }
6864
6865 .use-case-grid-2col {
6866 grid-template-columns: 1fr 1fr;
6867 }
6868
6869 .use-case-title {
6870 font-family: var(--font-heading);
6871 font-size: var(--text-lead);
6872 margin-bottom: var(--gap-bound);
6873 }
6874
6875 .use-case-who {
6876 font-family: var(--font-mono);
6877 font-size: var(--text-fine);
6878 opacity: 0.6;
6879 margin-bottom: var(--gap-peer);
6880 }
6881
6882 .use-case-desc {
6883 font-size: var(--text-note);
6884 line-height: 1.5;
6885 margin-bottom: var(--gap-peer);
6886 }
6887
6888 .use-case-features {
6889 list-style: none;
6890 margin: 0 0 var(--gap-section);
6891 padding: 0;
6892 }
6893
6894 .use-case-features li {
6895 font-size: var(--text-note);
6896 padding-left: var(--gap-section);
6897 position: relative;
6898 margin-bottom: var(--gap-bound);
6899 }
6900
6901 .use-case-features li::before {
6902 content: "-";
6903 position: absolute;
6904 left: 0;
6905 opacity: 0.5;
6906 }
6907
6908 .use-case-tier {
6909 font-family: var(--font-mono);
6910 font-size: var(--text-fine);
6911 opacity: 0.5;
6912 }
6913
6914 /* ===========================================
6915 RESPONSIVE, 768px (tablet)
6916 =========================================== */
6917
6918 /* ==========================================================================
6919 Git Source Browser
6920 ========================================================================== */
6921
6922 .git-repo-header { margin-bottom: var(--gap-pane); }
6923 .git-repo-name { font-size: var(--text-head); margin: 0 0 var(--gap-bound); }
6924 .git-repo-name a { color: var(--content); text-decoration: none; }
6925 .git-repo-name a:hover { opacity: 0.6; }
6926 .git-repo-name .sep { opacity: 0.3; margin: 0 var(--gap-bound); }
6927 .git-repo-desc { opacity: 0.6; margin: 0 0 var(--gap-section); font-size: var(--text-note); }
6928 .git-clone-url {
6929 font-family: var(--font-mono);
6930 font-size: var(--text-fine);
6931 padding: var(--gap-peer) var(--gap-group);
6932 background: var(--surface-overlay);
6933 border: 1px solid var(--border);
6934 border-radius: var(--radius-control);
6935 display: inline-block;
6936 user-select: all;
6937 margin-bottom: var(--gap-section);
6938 }
6939 .git-linked-project { margin-top: var(--gap-peer); font-size: var(--text-note); }
6940 .git-linked-project a { color: var(--content); }
6941
6942 /* Ref bar (branch/tag selector + nav links) */
6943 .git-ref-bar {
6944 display: flex;
6945 align-items: center;
6946 gap: var(--gap-section);
6947 margin-bottom: var(--gap-section);
6948 font-size: var(--text-note);
6949 }
6950 .git-ref-select {
6951 font-family: var(--font-mono);
6952 font-size: var(--text-fine);
6953 padding: var(--gap-bound) var(--gap-peer);
6954 background: var(--surface-overlay);
6955 border: 1px solid var(--border);
6956 border-radius: var(--radius-control);
6957 }
6958 .git-nav-links { display: flex; gap: var(--gap-section); }
6959 .git-nav-links a { text-decoration: none; color: var(--content); opacity: 0.6; }
6960 .git-nav-links a:hover,
6961 .git-nav-links a.is-selected { opacity: 1; }
6962
6963 /* Breadcrumb navigation */
6964 .git-breadcrumb {
6965 font-family: var(--font-mono);
6966 font-size: var(--text-note);
6967 margin-bottom: var(--gap-section);
6968 }
6969 .git-breadcrumb a { color: var(--content); text-decoration: none; }
6970 .git-breadcrumb a:hover { text-decoration: underline; }
6971 .git-breadcrumb .sep { opacity: 0.3; margin: 0 var(--gap-bound); }
6972
6973 /* File tree table */
6974 .git-tree { width: 100%; border-collapse: collapse; font-size: var(--text-note); }
6975 .git-tree th {
6976 text-align: left;
6977 padding: var(--gap-peer) var(--gap-group);
6978 border-bottom: 2px solid var(--border);
6979 font-family: var(--font-mono);
6980 font-size: var(--text-fine);
6981 opacity: 0.5;
6982 text-transform: uppercase;
6983 }
6984 .git-tree td { padding: var(--gap-peer) var(--gap-group); border-bottom: 1px solid var(--border); }
6985 .git-tree tr:last-child td { border-bottom: none; }
6986 .git-tree .icon { width: 1.5rem; text-align: center; opacity: 0.4; font-family: var(--font-mono); }
6987 .tree-icon-dir { font-weight: 700; color: var(--content); }
6988 .tree-icon-file { font-size: var(--text-note); }
6989 .git-tree .name a { text-decoration: none; color: var(--content); }
6990 .git-tree .name a:hover { text-decoration: underline; }
6991 .git-tree .size {
6992 text-align: right;
6993 opacity: 0.5;
6994 font-family: var(--font-mono);
6995 font-size: var(--text-fine);
6996 }
6997
6998 /* README rendering */
6999 .git-readme { margin-top: var(--gap-page); padding-top: var(--gap-pane); border-top: 1px solid var(--border); }
7000 .git-readme h2 { font-size: var(--text-body); opacity: 0.5; margin-bottom: var(--gap-section); }
7001 .git-readme-body { line-height: 1.6; }
7002 .git-readme-body h1,
7003 .git-readme-body h2,
7004 .git-readme-body h3 { margin-top: var(--gap-pane); margin-bottom: var(--gap-peer); }
7005 .git-readme-body pre {
7006 background: var(--surface-overlay);
7007 padding: var(--gap-section);
7008 border-radius: var(--radius-control);
7009 overflow-x: auto;
7010 font-family: var(--font-mono);
7011 font-size: var(--text-fine);
7012 }
7013 .git-readme-body code {
7014 font-family: var(--font-mono);
7015 font-size: var(--text-note);
7016 background: var(--surface-overlay);
7017 padding: var(--gap-bound);
7018 border-radius: var(--radius-fine);
7019 }
7020 .git-readme-body pre code { background: none; padding: 0; }
7021 .git-readme-body table { border-collapse: collapse; margin: var(--gap-section) 0; }
7022 .git-readme-body th,
7023 .git-readme-body td { border: 1px solid var(--border); padding: var(--gap-peer) var(--gap-group); }
7024
7025 /* File viewer */
7026 .git-file-header {
7027 display: flex;
7028 justify-content: space-between;
7029 align-items: center;
7030 padding: var(--gap-peer) var(--gap-group);
7031 background: var(--surface-overlay);
7032 border: 1px solid var(--border);
7033 border-bottom: none;
7034 border-radius: var(--radius-control) var(--radius-control) 0 0;
7035 font-size: var(--text-fine);
7036 }
7037 .git-file-meta {
7038 font-family: var(--font-mono);
7039 opacity: 0.6;
7040 }
7041 .git-file-actions a {
7042 font-family: var(--font-mono);
7043 text-decoration: none;
7044 color: var(--content);
7045 opacity: 0.6;
7046 font-size: var(--text-fine);
7047 }
7048 .git-file-actions a:hover { opacity: 1; }
7049 .git-file-content {
7050 border: 1px solid var(--border);
7051 border-radius: 0 0 var(--radius-control) var(--radius-control);
7052 overflow-x: auto;
7053 }
7054 .git-file-content table { border-collapse: collapse; width: 100%; }
7055 .git-file-content .line-number {
7056 width: 1px;
7057 white-space: nowrap;
7058 padding: 0 var(--gap-section);
7059 text-align: right;
7060 user-select: none;
7061 opacity: 0.3;
7062 font-family: var(--font-mono);
7063 font-size: var(--text-fine);
7064 border-right: 1px solid var(--border);
7065 vertical-align: top;
7066 }
7067 .git-file-content .line-code {
7068 padding: 0 var(--gap-section);
7069 white-space: pre;
7070 font-family: var(--font-mono);
7071 font-size: var(--text-fine);
7072 line-height: 1.5;
7073 }
7074 .git-file-content .line-code span {
7075 font-family: var(--font-mono);
7076 }
7077 .git-binary-notice {
7078 padding: var(--gap-page);
7079 text-align: center;
7080 opacity: 0.5;
7081 font-family: var(--font-mono);
7082 font-size: var(--text-note);
7083 border: 1px solid var(--border);
7084 border-radius: 0 0 var(--radius-control) var(--radius-control);
7085 }
7086
7087 /* Syntax highlighting (syntect class-based) */
7088 .git-file-content .source { color: var(--content); }
7089 .git-file-content .keyword,
7090 .git-file-content .storage { color: var(--syntax-keyword); }
7091 .git-file-content .string { color: var(--syntax-string); }
7092 .git-file-content .comment { color: var(--syntax-comment); font-style: italic; }
7093 .git-file-content .constant,
7094 .git-file-content .constant.numeric { color: var(--syntax-constant); }
7095 .git-file-content .entity.name { color: var(--syntax-entity); }
7096 .git-file-content .variable { color: var(--syntax-variable); }
7097 .git-file-content .support { color: var(--syntax-support); }
7098 .git-file-content .meta.preprocessor { color: var(--syntax-keyword); }
7099
7100 /* Commit list */
7101 .git-commit-list { list-style: none; }
7102 .git-commit { padding: var(--gap-section) 0; border-bottom: 1px solid var(--border); }
7103 .git-commit:last-child { border-bottom: none; }
7104 .git-commit-message { font-size: var(--text-note); margin: 0 0 var(--gap-bound); }
7105 .git-commit-message .summary { font-weight: 600; }
7106 .git-commit-meta {
7107 font-family: var(--font-mono);
7108 font-size: var(--text-fine);
7109 opacity: 0.5;
7110 display: flex;
7111 gap: var(--gap-section);
7112 flex-wrap: wrap;
7113 }
7114 .git-commit-oid { font-family: var(--font-mono); font-size: var(--text-fine); }
7115 .git-commit-oid a { color: var(--content); text-decoration: none; }
7116 .git-commit-oid a:hover { text-decoration: underline; }
7117 .git-pagination {
7118 display: flex;
7119 justify-content: center;
7120 gap: var(--gap-section);
7121 margin-top: var(--gap-pane);
7122 padding-top: var(--gap-section);
7123 font-size: var(--text-note);
7124 }
7125 .git-pagination a { color: var(--content); text-decoration: none; }
7126 .git-pagination a:hover { text-decoration: underline; }
7127
7128 /* Release items on repo page */
7129 .git-release { margin-bottom: var(--gap-pane); padding: var(--gap-section); background: var(--surface-overlay); }
7130 .git-release-title { margin: 0 0 var(--gap-bound); font-size: var(--text-lead); }
7131 .git-release-title a { color: var(--content); text-decoration: none; }
7132 .git-release-meta { font-size: var(--text-note); opacity: 0.6; margin-bottom: var(--gap-peer); }
7133 .git-release-desc { font-size: var(--text-note); opacity: 0.8; margin-bottom: var(--gap-section); }
7134 .git-release-versions { font-size: var(--text-fine); }
7135 .git-release-version {
7136 display: flex;
7137 gap: var(--gap-section);
7138 align-items: center;
7139 padding: var(--gap-bound) 0;
7140 border-top: 1px solid var(--border);
7141 }
7142 .git-release-version .version-number { font-family: var(--font-mono); }
7143 .git-release-version .version-badge {
7144 font-size: var(--text-fine);
7145 padding: var(--gap-bound) var(--gap-peer);
7146 background: var(--content);
7147 color: var(--surface-page);
7148 border-radius: var(--radius-fine);
7149 }
7150 .git-release-version .version-meta { opacity: 0.5; }
7151
7152 /* Git commit detail + diff */
7153 .git-commit-detail { margin-bottom: var(--gap-pane); }
7154 .git-commit-full-message {
7155 font-family: var(--font-mono);
7156 font-size: var(--text-note);
7157 white-space: pre-wrap;
7158 line-height: 1.5;
7159 padding: var(--gap-section);
7160 background: var(--surface-overlay);
7161 border: 1px solid var(--border);
7162 margin-bottom: var(--gap-section);
7163 }
7164 .git-commit-detail-meta { font-size: var(--text-note); opacity: 0.7; line-height: 1.8; }
7165 .git-commit-detail-meta .git-commit-oid { font-family: var(--font-mono); font-size: var(--text-fine); word-break: break-all; }
7166 .git-commit-parents a { font-family: var(--font-mono); color: var(--content); text-decoration: none; }
7167 .git-commit-parents a:hover { text-decoration: underline; }
7168 /* Commit trailers, lifted out of the message body and shown as what they are */
7169 .git-commit-trailers {
7170 display: grid;
7171 grid-template-columns: max-content 1fr;
7172 gap: var(--gap-bound) var(--gap-section);
7173 font-size: var(--text-note);
7174 margin-bottom: var(--gap-section);
7175 }
7176 .git-commit-trailers dt { opacity: 0.6; }
7177 .git-commit-trailers dd { margin: 0; font-family: var(--font-mono); font-size: var(--text-fine); }
7178 /* Tags, with the annotation an annotated tag carries */
7179 .git-tag-list { list-style: none; }
7180 .git-tag { padding: var(--gap-section) 0; border-bottom: 1px solid var(--border); }
7181 .git-tag:last-child { border-bottom: none; }
7182 .git-tag-name { font-size: var(--text-note); font-weight: 600; margin: 0 0 var(--gap-bound); }
7183 .git-tag-name a { color: var(--content); text-decoration: none; }
7184 .git-tag-name a:hover { text-decoration: underline; }
7185 .git-tag-lightweight { font-weight: 400; font-size: var(--text-fine); opacity: 0.5; }
7186 .git-tag-message {
7187 font-family: var(--font-mono);
7188 font-size: var(--text-note);
7189 white-space: pre-wrap;
7190 line-height: 1.5;
7191 padding: var(--gap-peer) var(--gap-section);
7192 border-left: 2px solid var(--border);
7193 margin-bottom: var(--gap-bound);
7194 }
7195 .git-tag-meta {
7196 font-family: var(--font-mono);
7197 font-size: var(--text-fine);
7198 opacity: 0.5;
7199 display: flex;
7200 gap: var(--gap-section);
7201 flex-wrap: wrap;
7202 }
7203 .git-tag-meta a { color: var(--content); text-decoration: none; }
7204 .git-tag-meta a:hover { text-decoration: underline; }
7205 /* Signature state. Neutral by default: only a bad signature gets a colour. */
7206 .git-signature { font-size: var(--text-fine); opacity: 0.7; }
7207 .git-signature a { color: var(--content); }
7208 .git-signature.is-verified { opacity: 1; }
7209 .git-signature.is-bad { color: var(--danger); opacity: 1; }
7210 /* refs/replace/*: object substitutions, listed but not applied while browsing */
7211 .git-replace-intro { padding-bottom: var(--gap-section); font-size: var(--text-note); }
7212 .git-replace-intro p { margin: 0; }
7213 .git-replace-notice {
7214 font-size: var(--text-note);
7215 border-left: 2px solid var(--border);
7216 padding: var(--gap-peer) var(--gap-section);
7217 margin: 0 0 var(--gap-section);
7218 }
7219 .git-replace-table { width: 100%; border-collapse: collapse; font-size: var(--text-note); }
7220 .git-replace-table th {
7221 text-align: left;
7222 font-weight: 400;
7223 opacity: 0.5;
7224 font-size: var(--text-fine);
7225 padding: var(--gap-bound) var(--gap-section) var(--gap-bound) 0;
7226 border-bottom: 1px solid var(--border);
7227 }
7228 .git-replace-table td { padding: var(--gap-peer) var(--gap-section) var(--gap-peer) 0; border-bottom: 1px solid var(--border); }
7229 .git-replace-table a { color: var(--content); text-decoration: none; }
7230 .git-replace-table a:hover { text-decoration: underline; }
7231 /* Git notes (refs/notes/*) shown against the object they annotate */
7232 .git-notes { margin-bottom: var(--gap-pane); }
7233 .git-note {
7234 border: 1px solid var(--border);
7235 border-left: 2px solid var(--content);
7236 margin-bottom: var(--gap-section);
7237 }
7238 .git-note-header {
7239 display: flex;
7240 align-items: baseline;
7241 justify-content: space-between;
7242 gap: var(--gap-group);
7243 padding: var(--gap-peer) var(--gap-section);
7244 border-bottom: 1px solid var(--border);
7245 font-size: var(--text-fine);
7246 }
7247 .git-note-namespace { font-family: var(--font-mono); }
7248 /* Sits beside the namespace rather than being spread across the header: the
7249 auto margin absorbs the free space `space-between` would otherwise put
7250 between the two, leaving the attribution on the right where it was. */
7251 .git-note-owned { margin-right: auto; opacity: 0.6; }
7252 .git-note-attribution { opacity: 0.6; }
7253 .git-note-namespaces {
7254 display: flex;
7255 flex-wrap: wrap;
7256 gap: var(--gap-group);
7257 padding: var(--gap-section) 0;
7258 border-bottom: 1px solid var(--border);
7259 margin-bottom: var(--gap-section);
7260 font-size: var(--text-note);
7261 }
7262 .git-note-namespaces a { color: var(--content); text-decoration: none; opacity: 0.6; }
7263 .git-note-namespaces a:hover { opacity: 1; }
7264 .git-note-namespaces a.is-selected { opacity: 1; text-decoration: underline; }
7265 .git-note-target-kind { opacity: 0.6; }
7266 .git-explore-intro { margin: 0 0 var(--gap-section); font-size: var(--text-note); opacity: 0.8; }
7267 .git-notes-fetch { padding-bottom: var(--gap-section); font-size: var(--text-note); }
7268 .git-notes-fetch p { margin: 0 0 var(--gap-peer); }
7269 .git-note-feed-cap { font-size: var(--text-fine); opacity: 0.6; padding-top: var(--gap-section); }
7270 .git-note-search {
7271 display: flex;
7272 flex-wrap: wrap;
7273 align-items: center;
7274 gap: var(--gap-group);
7275 padding-bottom: var(--gap-section);
7276 font-size: var(--text-note);
7277 }
7278 .git-note-search input[type="search"] { flex: 1 1 16rem; }
7279 .git-note-search label { display: flex; align-items: center; gap: var(--gap-bound); opacity: 0.8; }
7280 .git-note-excerpt {
7281 margin: var(--gap-bound) 0 0;
7282 font-size: var(--text-note);
7283 opacity: 0.7;
7284 }
7285 .git-note-paging {
7286 display: flex;
7287 gap: var(--gap-group);
7288 align-items: center;
7289 padding-top: var(--gap-section);
7290 font-size: var(--text-note);
7291 }
7292 .git-note-paging span { font-size: var(--text-fine); opacity: 0.6; }
7293 .git-note-feed-link { padding-top: var(--gap-peer); font-size: var(--text-fine); }
7294 .git-note-badge {
7295 font-size: var(--text-fine);
7296 padding: var(--gap-bound) var(--gap-peer);
7297 border: 1px solid var(--border);
7298 border-radius: var(--radius-fine);
7299 color: var(--content);
7300 text-decoration: none;
7301 opacity: 0.7;
7302 }
7303 .git-note-badge:hover { opacity: 1; }
7304 .git-note-body { padding: var(--gap-section); font-size: var(--text-note); }
7305 .git-note-body > :first-child { margin-top: 0; }
7306 .git-note-body > :last-child { margin-bottom: 0; }
7307 /* Writing a note. Only rendered for a reader who can push. */
7308 .git-notes-edit {
7309 border-top: 1px solid var(--border);
7310 padding-top: var(--gap-section);
7311 margin-bottom: var(--gap-pane);
7312 }
7313 .git-notes-edit textarea { width: 100%; font-family: var(--font-mono); font-size: var(--text-note); }
7314 .git-note-form { margin-bottom: var(--gap-section); }
7315 .git-note-delete { margin-bottom: var(--gap-pane); }
7316 .git-note-merged {
7317 border-left: 2px solid var(--content);
7318 padding: var(--gap-peer) var(--gap-section);
7319 margin-bottom: var(--gap-section);
7320 font-size: var(--text-note);
7321 }
7322 .git-diff-stats {
7323 font-size: var(--text-note);
7324 padding: var(--gap-section) 0;
7325 margin-bottom: var(--gap-section);
7326 border-bottom: 1px solid var(--border);
7327 }
7328 .git-diff-stats .additions { color: var(--diff-add); }
7329 .git-diff-stats .deletions { color: var(--diff-del); }
7330 .git-diff-file { margin-bottom: var(--gap-pane); border: 1px solid var(--border); }
7331 .git-diff-file-header {
7332 display: flex;
7333 align-items: center;
7334 gap: var(--gap-peer);
7335 padding: var(--gap-peer) var(--gap-section);
7336 background: var(--surface-overlay);
7337 border-bottom: 1px solid var(--border);
7338 font-size: var(--text-note);
7339 flex-wrap: wrap;
7340 }
7341 .git-diff-file-header a { color: var(--content); text-decoration: none; }
7342 .git-diff-file-header a:hover { text-decoration: underline; }
7343 .git-diff-file-stats { margin-left: auto; font-size: var(--text-fine); }
7344 .git-diff-file-stats .additions { color: var(--diff-add); }
7345 .git-diff-file-stats .deletions { color: var(--diff-del); margin-left: var(--gap-bound); }
7346 .diff-status-added, .diff-status-deleted, .diff-status-modified, .diff-status-renamed {
7347 display: inline-block;
7348 width: 1.2rem;
7349 text-align: center;
7350 font-size: var(--text-fine);
7351 font-weight: 700;
7352 font-family: var(--font-mono);
7353 border-radius: var(--radius-fine);
7354 line-height: 1.4;
7355 }
7356 .diff-status-added { background: color-mix(in oklch, var(--success) 14%, var(--surface-page)); color: var(--success); }
7357 .diff-status-deleted { background: color-mix(in oklch, var(--danger) 14%, var(--surface-page)); color: var(--danger); }
7358 .diff-status-modified { background: color-mix(in oklch, var(--warning) 14%, var(--surface-page)); color: var(--warning); }
7359 .diff-status-renamed { background: color-mix(in oklch, var(--info) 14%, var(--surface-page)); color: var(--info); }
7360 .git-diff-table {
7361 width: 100%;
7362 border-collapse: collapse;
7363 font-family: var(--font-mono);
7364 font-size: var(--text-fine);
7365 table-layout: fixed;
7366 }
7367 .git-diff-hunk-header td {
7368 padding: var(--gap-bound) var(--gap-peer);
7369 background: var(--highlight-faint);
7370 color: var(--content);
7371 opacity: 0.6;
7372 font-size: var(--text-fine);
7373 }
7374 .git-diff-lineno {
7375 width: 3.5rem;
7376 padding: 0 var(--gap-peer);
7377 text-align: right;
7378 color: var(--content);
7379 opacity: 0.35;
7380 user-select: none;
7381 vertical-align: top;
7382 }
7383 .git-diff-origin {
7384 width: 1rem;
7385 text-align: center;
7386 user-select: none;
7387 vertical-align: top;
7388 }
7389 .git-diff-content {
7390 padding: 0 var(--gap-peer);
7391 white-space: pre-wrap;
7392 word-break: break-all;
7393 }
7394 .git-diff-line-add { background: var(--diff-add-bg); }
7395 .git-diff-line-add .git-diff-origin { color: var(--diff-add); }
7396 .git-diff-line-del { background: var(--diff-del-bg); }
7397 .git-diff-line-del .git-diff-origin { color: var(--diff-del); }
7398 .git-diff-line-ctx { background: transparent; }
7399 .git-diff-truncated { padding: var(--gap-peer) var(--gap-section); font-size: var(--text-fine); opacity: 0.5; font-style: italic; }
7400
7401 /* Git file view line linking */
7402 .git-file-content .line-number a {
7403 color: inherit;
7404 text-decoration: none;
7405 display: block;
7406 }
7407 .git-file-content .line-number a:hover { opacity: 0.7; }
7408 .git-file-content tr:target td,
7409 .git-file-content tr.highlighted td { background: var(--highlight-faint); }
7410
7411 /* Git blame view */
7412 .git-blame-content { overflow-x: auto; border: 1px solid var(--border); }
7413 .git-blame-content table { border-collapse: collapse; width: 100%; font-size: var(--text-fine); }
7414 .git-blame-info {
7415 padding: 0 var(--gap-peer);
7416 font-family: var(--font-mono);
7417 white-space: nowrap;
7418 width: 4.5rem;
7419 }
7420 .git-blame-info a { color: var(--content); text-decoration: none; }
7421 .git-blame-info a:hover { text-decoration: underline; }
7422 .git-blame-author {
7423 padding: 0 var(--gap-bound);
7424 font-size: var(--text-fine);
7425 opacity: 0.5;
7426 white-space: nowrap;
7427 max-width: 8rem;
7428 overflow: hidden;
7429 text-overflow: ellipsis;
7430 }
7431 .git-blame-date {
7432 padding: 0 var(--gap-bound);
7433 font-size: var(--text-fine);
7434 opacity: 0.35;
7435 white-space: nowrap;
7436 width: 5.5rem;
7437 }
7438 .git-blame-content .line-number {
7439 padding: 0 var(--gap-peer);
7440 text-align: right;
7441 opacity: 0.35;
7442 user-select: none;
7443 width: 3rem;
7444 font-family: var(--font-mono);
7445 }
7446 .git-blame-content .line-code {
7447 padding: 0 var(--gap-peer);
7448 white-space: pre;
7449 font-family: var(--font-mono);
7450 }
7451 .git-blame-boundary td { border-top: 1px solid var(--border); }
7452
7453 /* Git user repos listing */
7454 .git-repos-list { list-style: none; }
7455 .git-repos-item { padding: var(--gap-section) 0; border-bottom: 1px solid var(--border); }
7456 .git-repos-item:last-child { border-bottom: none; }
7457 .git-repos-item-header { display: flex; align-items: center; gap: var(--gap-peer); }
7458 .git-repos-item-name {
7459 font-size: var(--text-lead);
7460 font-weight: 600;
7461 color: var(--content);
7462 text-decoration: none;
7463 }
7464 .git-repos-item-name:hover { text-decoration: underline; }
7465 .git-repos-visibility {
7466 font-size: var(--text-fine);
7467 padding: var(--gap-bound) var(--gap-peer);
7468 border: 1px solid var(--border);
7469 border-radius: var(--radius-fine);
7470 opacity: 0.5;
7471 }
7472 .git-repos-item-desc { font-size: var(--text-note); opacity: 0.6; margin: var(--gap-bound) 0 0; }.git-repos-instructions p { margin-bottom: var(--gap-peer); }
7473 .git-repos-owner { opacity: 0.6; }
7474 .git-repos-item-name .sep { opacity: 0.4; }
7475 .git-file-log-label {
7476 font-family: var(--font-mono);
7477 font-size: var(--text-fine);
7478 opacity: 0.5;
7479 margin-right: var(--gap-bound);
7480 }
7481
7482 /* Site-wide footer */
7483 .site-footer {
7484 margin-top: var(--gap-page);
7485 padding: var(--gap-page) 0;
7486 text-align: center;
7487 font-family: var(--font-mono);
7488 font-size: var(--text-fine);
7489 opacity: 0.5;
7490 }
7491 .site-footer-links { margin-bottom: var(--gap-peer); }
7492 .site-footer-links a { margin: 0 var(--gap-section); color: inherit; text-decoration: none; }
7493 .site-footer-links a:hover { text-decoration: underline; }
7494
7495 @media (max-width: 839px) {
7496 /* Git browser mobile */
7497 .git-ref-bar { flex-wrap: wrap; gap: var(--gap-peer); }
7498 .git-repo-name { font-size: var(--text-subhead); }
7499 .git-clone-url { font-size: var(--text-fine); word-break: break-all; }
7500 .git-tree .icon { display: none; }
7501 .git-tree td, .git-tree th { padding: var(--gap-bound) var(--gap-peer); }
7502 .git-file-header { flex-direction: column; align-items: flex-start; gap: var(--gap-bound); }
7503 .git-file-content .line-number { padding: 0 var(--gap-peer); }
7504 .git-file-content .line-code { padding: 0 var(--gap-peer); font-size: var(--text-fine); }
7505 .git-commit-meta { flex-direction: column; gap: var(--gap-bound); }
7506 .git-release-version { flex-wrap: wrap; gap: var(--gap-peer); }
7507 .git-diff-file-header { font-size: var(--text-fine); }
7508 .git-diff-table { font-size: var(--text-fine); }
7509 .git-diff-lineno { width: 2.5rem; font-size: var(--text-fine); }
7510 .git-blame-author { display: none; }
7511 .git-blame-date { display: none; }
7512
7513 .container {
7514 padding: var(--gap-section);
7515 }
7516
7517 .padded-page {
7518 padding: var(--gap-section);
7519 }
7520
7521 .form-row {
7522 grid-template-columns: 1fr;
7523 }
7524
7525 .tab {
7526 padding: var(--gap-group) var(--gap-section);
7527 font-size: var(--text-note);
7528 }
7529
7530 .tab-content {
7531 padding: var(--gap-section);
7532 }
7533
7534 .data-table th,
7535 .data-table td {
7536 padding: var(--gap-peer) var(--gap-group);
7537 font-size: var(--text-note);
7538 }
7539
7540 .modal {
7541 max-width: 95%;
7542 }
7543
7544 .stats-grid {
7545 grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
7546 }
7547
7548 .content-section {
7549 padding: var(--gap-section);
7550 }
7551
7552 .action-buttons {
7553 flex-direction: column;
7554 }
7555
7556 .action-buttons button {
7557 width: 100%;
7558 }
7559
7560 .analytics-grid {
7561 grid-template-columns: 1fr;
7562 }
7563
7564 /* Hamburger menu */
7565 .nav-toggle-label {
7566 display: block;
7567 }
7568
7569 .header-search {
7570 display: none;
7571 flex: none;
7572 margin: 0;
7573 width: 100%;
7574 }
7575
7576 .nav-toggle-checkbox:checked ~ .header-search {
7577 display: block;
7578 position: absolute;
7579 top: 100%;
7580 left: 0;
7581 right: 0;
7582 padding: var(--gap-section) var(--gap-pane);
7583 background: var(--surface-page);
7584 border-bottom: 1px solid var(--border);
7585 z-index: var(--z-header);
7586 }
7587
7588 .site-header nav {
7589 display: none;
7590 position: absolute;
7591 top: 100%;
7592 left: 0;
7593 right: 0;
7594 background: var(--surface-page);
7595 border-bottom: 1px solid var(--border);
7596 padding: var(--gap-section) var(--gap-pane);
7597 z-index: var(--z-nav);
7598 }
7599
7600 .site-header {
7601 position: relative;
7602 }
7603
7604 .nav-toggle-checkbox:checked ~ nav {
7605 display: block;
7606 }
7607
7608 .site-header .nav-links {
7609 flex-direction: column;
7610 gap: var(--gap-section);
7611 }
7612
7613 /* Animate hamburger to X when open */
7614 .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(1) {
7615 transform: rotate(45deg) translate(5px, 5px);
7616 }
7617
7618 .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(2) {
7619 opacity: 0;
7620 }
7621
7622 .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(3) {
7623 transform: rotate(-45deg) translate(5px, -5px);
7624 }
7625 }
7626
7627 /* Doc pages, mobile */
7628 @media (max-width: 599px) {
7629 .doc-title { font-size: var(--text-title); }
7630 .doc-body { font-size: var(--text-body); }
7631 .docs-index-title { font-size: var(--text-title); }
7632 }
7633
7634 /* ===========================================
7635 RESPONSIVE, 480px (phone)
7636 =========================================== */
7637
7638 @media (max-width: 599px) {
7639 .container {
7640 padding: var(--gap-section);
7641 }
7642
7643 .padded-page {
7644 padding: var(--gap-section);
7645 }
7646
7647 h1 {
7648 font-size: var(--text-title);
7649 }
7650
7651 .centered-page h1 {
7652 font-size: var(--text-hero);
7653 }
7654
7655 .login-container,
7656 .form-container {
7657 max-width: 100%;
7658 padding: var(--gap-section);
7659 }
7660
7661 .big-button {
7662 width: 100%;
7663 }
7664
7665 .stats-grid {
7666 grid-template-columns: repeat(2, 1fr);
7667 }
7668
7669 .tab {
7670 padding: var(--gap-peer) var(--gap-section);
7671 font-size: var(--text-note);
7672 }
7673
7674 .tab-content {
7675 padding: var(--gap-section);
7676 }
7677
7678 .content-section {
7679 padding: var(--gap-section);
7680 }
7681
7682 .section-header {
7683 flex-direction: column;
7684 align-items: flex-start;
7685 gap: var(--gap-section);
7686 }
7687
7688 /* Landing page (merged from 500px) */
7689 .landing-fork,
7690 .tier-selector,
7691 .feature-grid,
7692 .use-case-grid,
7693 .use-case-grid-2col {
7694 grid-template-columns: 1fr;
7695 }
7696
7697 .explore-links {
7698 flex-direction: column;
7699 align-items: center;
7700 gap: var(--gap-section);
7701 }
7702
7703 .pricing-input {
7704 font-size: var(--text-title);
7705 }
7706
7707 .pricing-currency {
7708 font-size: var(--text-title);
7709 }
7710
7711 .calc-verdict {
7712 font-size: var(--text-body);
7713 }
7714
7715 .calc-dials {
7716 grid-template-columns: 1fr;
7717 }
7718
7719 .secondary-links {
7720 flex-direction: column;
7721 align-items: center;
7722 gap: var(--gap-section);
7723 }
7724
7725 .landing-cta {
7726 flex-direction: column;
7727 gap: var(--gap-section);
7728 }
7729
7730 .toast-container {
7731 left: 0.75rem;
7732 right: 0.75rem;
7733 bottom: 0.75rem;
7734 }
7735
7736 .toast {
7737 max-width: 100%;
7738 }
7739 }
7740
7741 /* ===========================================
7742 DISCOVER PAGE
7743 =========================================== */
7744
7745 .discover-layout { display: grid; grid-template-columns: 200px 1fr; gap: var(--gap-pane); }
7746 .discover-layout.no-sidebar { grid-template-columns: 1fr; }
7747 .discover-layout.no-sidebar .discover-sidebar { display: none; }
7748 .discover-sidebar { font-size: var(--text-note); }
7749 .filter-section { margin-bottom: var(--gap-section); }
7750 .filter-title { font-family: var(--font-heading); font-weight: bold; font-size: var(--text-note); margin-bottom: var(--gap-peer); color: var(--content); }
7751 .filter-list { list-style: none; }
7752 /* The row carries selection (`.is-selected` recipe) and hover; the filter itself
7753 is a real <button> child so Enter/Space work without scripting. */
7754 .filter-item { display: flex; align-items: center; gap: var(--gap-bound); transition: opacity 0.1s ease; }
7755 .filter-item:hover { opacity: 0.6; }
7756 .filter-item.is-selected { font-weight: bold; }
7757 .filter-item .count { opacity: 0.4; font-size: var(--text-fine); }
7758 .tag-follow-btn { background: none; border: 1px solid var(--border); cursor: pointer; font-family: var(--font-mono); font-size: var(--text-fine); opacity: 0.55; padding: var(--gap-bound) var(--gap-peer); color: var(--content); }
7759 .tag-follow-btn:hover { opacity: 1; }
7760 .tag-follow-btn.is-selected { opacity: 0.85; border-color: var(--focus-ring); background: var(--highlight-faint); }
7761 .filter-checkbox { display: flex; align-items: center; gap: var(--gap-peer); padding: var(--gap-bound) 0; cursor: pointer; }
7762 .filter-checkbox input { width: auto; margin: 0; }
7763 .price-inputs { display: flex; gap: var(--gap-bound); align-items: center; margin-bottom: var(--gap-peer); }
7764 .price-inputs input { width: 60px; padding: var(--gap-bound); font-size: var(--text-fine); background: var(--surface-sunken); border: none; }
7765 .price-inputs span { opacity: 0.4; }
7766 .discover-main { min-width: 0; }
7767
7768 /* ---- Discover sidebar: tag spine ----------------------------------------
7769 Tags are the sidebar's structure, not one facet among several, so the spine
7770 reads as the primary surface and .filter-refine below it is subordinate.
7771 Everything here composes existing primitives: .filter-item carries the row
7772 and its .is-selected recipe, .search-suggestions carries the dropdown shell,
7773 .breadcrumb carries the crumb idiom. */
7774 .tag-spine { padding-bottom: var(--gap-section); border-bottom: 1px solid var(--border); }
7775
7776 /* Selected tags. Also the active-filter summary, so they use the canonical
7777 selected tint rather than a new colour. */
7778 .tag-chips { list-style: none; display: flex; flex-wrap: wrap; gap: var(--gap-bound); margin-bottom: var(--gap-section); }
7779 .tag-chip { display: flex; align-items: center; gap: var(--gap-bound); padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine); background: var(--highlight-faint); border: 1px solid var(--focus-ring); }
7780 .tag-chip-label { overflow-wrap: anywhere; }
7781 .tag-chip-remove { text-decoration: none; color: var(--content-secondary); font-size: var(--text-body); line-height: 1; padding: 0 var(--gap-bound); }
7782 .tag-chip-remove:hover { color: var(--content); }
7783 .tag-chip-clear { background: none; border: 1px dashed var(--border-strong); }
7784 .tag-chip-clear a { text-decoration: none; color: var(--content-secondary); font-size: var(--text-fine); }
7785 .tag-chip-clear a:hover { color: var(--content); }
7786
7787 /* Typeahead. Shares the .search-suggestions shell; it toggles with the
7788 `hidden` attribute rather than an inline display, so the shared rule's
7789 display:none has to be overridden when open. */
7790 .tag-combobox { position: relative; margin-bottom: var(--gap-section); }
7791 .tag-combobox-input { width: 100%; padding: var(--gap-bound); font-size: var(--text-fine); background: var(--surface-sunken); border: none; }
7792 .tag-suggest-list { list-style: none; }
7793 .tag-suggest-list:not([hidden]) { display: block; }
7794 .tag-suggest-item { gap: var(--gap-peer); }
7795
7796 /* Drill-down. A rung either selects (assignable leaf) or navigates (category);
7797 rows with both put the chevron after the label. */
7798 .tag-crumbs { list-style: none; display: flex; flex-wrap: wrap; font-family: var(--font-mono); font-size: var(--text-fine); color: var(--content-muted); margin-bottom: var(--gap-peer); }
7799 .tag-crumbs li + li::before { content: "/"; opacity: 0.4; margin: 0 var(--gap-bound); }
7800 .tag-crumbs a { color: var(--content); text-decoration: none; }
7801 .tag-crumbs a:hover { text-decoration: underline; }
7802 .tag-drill-select { flex: 1; display: flex; align-items: center; gap: var(--gap-peer); cursor: pointer; padding: var(--gap-bound) 0; }
7803 .tag-drill-select input { width: auto; margin: 0; }
7804 .tag-drill-select span:first-of-type { flex: 1; }
7805 .tag-drill-into { display: flex; align-items: center; gap: var(--gap-peer); flex: 1; text-decoration: none; color: inherit; padding: var(--gap-bound) 0; }
7806 .tag-drill-into span:first-child { flex: 1; }
7807 .tag-drill-chevron { opacity: 0.4; }
7808
7809 /* ---- Discover sidebar: refine block -------------------------------------
7810 Subordinate to the spine: same controls, quieter. */
7811 .filter-refine { margin-top: var(--gap-section); }
7812 .filter-fieldset { border: none; padding: 0; margin: 0; min-width: 0; }
7813 .filter-check { flex: 1; display: flex; align-items: center; gap: var(--gap-peer); cursor: pointer; padding: var(--gap-bound) 0; }
7814 .filter-check input { width: auto; margin: 0; }
7815 .filter-check span:first-of-type { flex: 1; }
7816
7817 /* A zero-count facet stays visible but inert, so the shape of the catalog
7818 stays legible as filters narrow. No existing rule covers a disabled input
7819 inside a label, so this is the checkbox counterpart of the button rule.
7820 `.is-empty` covers the drill rungs, which are links and carry no input at
7821 all; they were rendering identically to rungs with items. */
7822 .filter-item:has(input:disabled), .filter-item.is-empty { opacity: 0.5; }
7823 .filter-item:has(input:disabled) .filter-check { cursor: not-allowed; }
7824
7825 /* Price buckets are links carrying a range, not form state. */
7826 .price-buckets { margin-top: var(--gap-peer); }
7827 .price-bucket { flex: 1; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-peer); text-decoration: none; color: inherit; padding: var(--gap-bound) 0; }
7828
7829 .mode-toggle { display: flex; gap: var(--gap-peer); margin-bottom: var(--gap-section); }
7830 .toggle-btn { background: var(--surface-sunken); border: none; padding: var(--gap-peer) var(--gap-section); font-size: var(--text-note); font-family: inherit; cursor: pointer; transition: background 0.1s ease, opacity 0.1s ease; }
7831 .toggle-btn:hover { opacity: 0.7; }
7832 .toggle-btn.is-selected { background: var(--primary-dark); color: var(--primary-light); }
7833 .table-controls { background: var(--surface-sunken); display: flex; align-items: center; gap: var(--gap-section); padding: var(--gap-peer) var(--gap-section); }
7834 .search-field { flex: 1 1 0; background: var(--surface-page); border: none; padding: var(--gap-peer) var(--gap-section); font-family: inherit; font-size: var(--text-note); color: var(--content); min-width: 0; }
7835 .search-field:focus { outline: 2px solid var(--action); }
7836 .search-field::placeholder { opacity: 0.5; }
7837 .table-meta { font-size: var(--text-fine); opacity: 0.6; white-space: nowrap; flex-shrink: 0; }
7838 .sort-select { background: var(--surface-page); border: none; padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine); font-family: inherit; cursor: pointer; width: auto; flex-shrink: 0; }
7839 .table-header { display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); background: var(--surface-overlay); font-size: var(--text-fine); opacity: 0.7; text-transform: uppercase; letter-spacing: 0.03em; }
7840 .table-header.projects-header { grid-template-columns: 1fr 100px 80px 70px; }
7841 /* Column headers describe the list layout only; hidden when the grid view is active. */
7842 .table-header.is-hidden { display: none; }
7843 .col-right { text-align: right; }
7844 .results-table { border: 1px solid var(--border); border-top: none; }
7845 .table-row { display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); align-items: center; text-decoration: none; color: var(--content); font-size: var(--text-note); border-bottom: 1px solid var(--border); transition: background 0.1s ease; }
7846 .table-row-item { grid-template-columns: 1fr 30px; padding: 0; gap: 0; }
7847 .table-row-item .table-row-link { display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); align-items: center; text-decoration: none; color: inherit; flex: 1; }
7848 .table-row-item .row-save { display: flex; align-items: center; justify-content: center; }
7849 .table-row.project-row { grid-template-columns: 1fr 100px 80px 70px; }
7850 .table-row:last-child { border-bottom: none; }
7851 .table-row:nth-child(odd) { background: var(--surface-overlay); }
7852 .table-row:nth-child(even) { background: var(--surface-overlay); }
7853 .table-row:hover { background: var(--surface-sunken); text-decoration: none; }
7854 .row-type { font-size: var(--text-fine); background: var(--surface-sunken); padding: var(--gap-bound) var(--gap-peer); text-align: center; opacity: 0.7; }
7855 .row-info { min-width: 0; display: flex; flex-direction: column; gap: var(--gap-bound); }
7856 .row-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
7857 .row-creator { font-size: var(--text-fine); opacity: 0.5; }.row-category { font-size: var(--text-fine); opacity: 0.6; }
7858 .row-price { text-align: right; }
7859 .row-items { text-align: right; opacity: 0.6; font-size: var(--text-fine); }
7860 .row-date { text-align: right; opacity: 0.5; font-size: var(--text-fine); }.pagination { display: flex; gap: var(--gap-bound); }
7861 .pagination button { background: var(--surface-page); border: none; padding: var(--gap-bound) var(--gap-group); font-size: var(--text-fine); cursor: pointer; }
7862 .pagination button:disabled { opacity: 0.3; cursor: not-allowed; }
7863 .pagination button.is-selected { background: var(--primary-dark); color: var(--primary-light); }
7864
7865 /* Issue tabs (open / closed) inside the git browser */
7866 .issue-tabs { display: flex; gap: var(--gap-peer); margin-bottom: var(--gap-section); }
7867 .issue-tab {
7868 padding: var(--gap-peer) var(--gap-section);
7869 border: 1px solid var(--border);
7870 background: var(--surface-overlay);
7871 font-family: var(--font-mono);
7872 font-size: var(--text-note);
7873 color: var(--content);
7874 text-decoration: none;
7875 opacity: 0.7;
7876 }
7877 .issue-tab:hover { opacity: 1; }
7878 .issue-tab.is-selected {
7879 opacity: 1;
7880 background: var(--highlight-faint);
7881 border-color: var(--focus-ring);
7882 }
7883 .page-info { opacity: 0.6; }
7884 .padded-page .page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--gap-section); padding-bottom: var(--gap-section); border-bottom: 1px solid var(--border); }
7885 .padded-page .page-header h1 { font-size: var(--text-head); text-align: center; position: absolute; left: 50%; transform: translateX(-50%); }
7886 .padded-page .page-header nav { margin: 0; }
7887 .padded-page .page-header .nav-links { gap: var(--gap-pane); }
7888 .discover-filter-toggle {
7889 display: none;
7890 font-family: var(--font-mono);
7891 font-size: var(--text-note);
7892 padding: var(--gap-peer) var(--gap-section);
7893 background: var(--surface-overlay);
7894 border: 1px solid var(--border);
7895 cursor: pointer;
7896 margin-bottom: var(--gap-section);
7897 }
7898
7899 .discover-filter-toggle.active { background: var(--surface-sunken); border-color: var(--border-strong); }
7900
7901 .discover-filter-toggle .filter-count {
7902 background: var(--action);
7903 color: var(--primary-light);
7904 font-size: var(--text-fine);
7905 padding: var(--gap-bound) var(--gap-peer);
7906 border-radius: var(--radius-control);
7907 margin-left: var(--gap-bound);
7908 }
7909
7910 @media (max-width: 839px) {
7911 .discover-filter-toggle { display: inline-block; }
7912 .discover-layout { grid-template-columns: 1fr; }
7913 .discover-sidebar { display: none; }
7914 /* Open state lives on the layout, not the sidebar: the sidebar is replaced
7915 by an out-of-band swap on every filter change and would lose the class. */
7916 .discover-layout.filters-open .discover-sidebar { display: block; margin-bottom: var(--gap-section); }
7917 .table-header, .table-row { grid-template-columns: 40px 1fr 60px; }
7918 .table-row-item { grid-template-columns: 1fr 30px; }
7919 .table-row-item .table-row-link { grid-template-columns: 40px 1fr 60px; }
7920 .table-header.projects-header, .table-row.project-row { grid-template-columns: 1fr 70px; }
7921 .row-date, .row-items, .row-category { display: none; }
7922 .table-header span:nth-child(3), .table-header span:nth-child(4), .table-header span:nth-child(5) { display: none; }
7923 .table-header.projects-header span:nth-child(3), .table-header.projects-header span:nth-child(4) { display: none; }
7924 }
7925
7926 @media (max-width: 599px) {
7927 .table-header, .table-row { grid-template-columns: 1fr 60px; }
7928 .table-row-item { grid-template-columns: 1fr 30px; }
7929 .table-row-item .table-row-link { grid-template-columns: 1fr 60px; }
7930 .row-type { display: none; }
7931 .table-header span:first-child { display: none; }
7932 .table-header.projects-header, .table-row.project-row { grid-template-columns: 1fr; }
7933 .table-header.projects-header span:nth-child(2) { display: none; }
7934 .row-price { font-size: var(--text-fine); }
7935 .search-field { min-width: 0; width: 100%; }
7936 .table-controls { flex-wrap: wrap; }
7937 .mode-toggle { flex-wrap: wrap; }
7938 .table-footer { flex-direction: column; gap: var(--gap-peer); align-items: flex-start; }
7939 }
7940
7941 /* ── Feature Cards & Suggestion Input (shared by wizard + settings) ── */
7942
7943 .type-grid {
7944 display: grid;
7945 grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
7946 gap: var(--gap-section);
7947 margin-bottom: var(--gap-section);
7948 }
7949
7950 /* Canonical selectable-card primitive. One recipe; per-context size modifiers.
7951 Used by wizard type pickers, pricing-model pickers, content-choice pickers,
7952 monetization-mode pickers, anywhere a radio-like choice is rendered as a card. */
7953 .card--selectable {
7954 cursor: pointer;
7955 display: block;
7956 }
7957 .card--selectable input[type="radio"],
7958 .card--selectable input[type="checkbox"] {
7959 position: absolute;
7960 opacity: 0;
7961 pointer-events: none;
7962 }
7963
7964 .card--selectable-inner {
7965 display: flex;
7966 flex-direction: column;
7967 align-items: center;
7968 justify-content: center;
7969 padding: var(--gap-section);
7970 border: 2px solid var(--border);
7971 border-radius: var(--radius-square);
7972 background: var(--surface-page);
7973 text-align: center;
7974 transition: border-color 0.15s, background 0.15s;
7975 min-height: 60px;
7976 }
7977 /* Variant: column-aligned content (text-heavy pricing cards). */
7978 .card--selectable.is-text-heavy .card--selectable-inner {
7979 align-items: stretch;
7980 text-align: left;
7981 padding: var(--gap-section);
7982 }
7983
7984 .card--selectable input:checked + .card--selectable-inner,
7985 .card--selectable.is-selected .card--selectable-inner {
7986 border-color: var(--action);
7987 background: var(--highlight-faint);
7988 }
7989
7990 .card--selectable:hover .card--selectable-inner {
7991 background: var(--surface-sunken);
7992 }
7993
7994 /* Focus. The card is a <label> wrapping a visually hidden radio, so the focus
7995 lands on the input and the ring has to be drawn on the inner surface. The
7996 <a> form of the card takes focus itself. */
7997 .card--selectable:focus-visible,
7998 .card--selectable input:focus-visible + .card--selectable-inner {
7999 outline: 2px solid var(--focus-ring);
8000 outline-offset: 2px;
8001 }
8002
8003 .card--selectable-label {
8004 font-weight: bold;
8005 font-size: var(--text-note);
8006 }
8007
8008 .card--selectable-desc {
8009 font-size: var(--text-fine);
8010 color: var(--content-muted);
8011 margin-top: var(--gap-bound);
8012 }
8013
8014 /* Disabled selectable card, visually dimmed, ignores pointer events. */
8015 .card--selectable.is-disabled {
8016 opacity: 0.7;
8017 pointer-events: none;
8018 }
8019
8020 .suggestion-input {
8021 position: relative;
8022 }
8023
8024 .suggestion-dropdown {
8025 display: none;
8026 position: absolute;
8027 top: 100%;
8028 left: 0;
8029 right: 0;
8030 background: var(--surface-page);
8031 border: 1px solid var(--border);
8032 border-top: none;
8033 border-radius: 0 0 var(--radius-square) var(--radius-square);
8034 max-height: 200px;
8035 overflow-y: auto;
8036 z-index: var(--z-dropdown);
8037 box-shadow: var(--shadow-2);
8038 }
8039
8040 .suggestion-dropdown.open {
8041 display: block;
8042 }
8043
8044 .suggestion-item,
8045 .tag-suggest-item {
8046 padding: var(--gap-peer) var(--gap-section);
8047 cursor: pointer;
8048 font-size: var(--text-note);
8049 }
8050
8051 .suggestion-item:hover {
8052 background: var(--surface-overlay);
8053 }
8054
8055 .suggestion-create {
8056 font-style: italic;
8057 opacity: 0.8;
8058 border-top: 1px solid var(--border);
8059 }
8060
8061 /* ── Tips ── */
8062
8063 .tip-section {
8064 margin-top: var(--gap-section);
8065 text-align: center;
8066 }
8067
8068 .tip-toggle {
8069 font-family: var(--font-mono);
8070 font-size: var(--text-note);
8071 padding: var(--gap-peer) var(--gap-pane);
8072 background: none;
8073 border: 1px solid var(--content);
8074 color: var(--content);
8075 cursor: pointer;
8076 text-decoration: none;
8077 display: inline-block;
8078 }
8079
8080 .tip-toggle:hover {
8081 background: var(--content);
8082 color: var(--surface-page);
8083 }
8084
8085 .tip-form {
8086 margin-top: var(--gap-section);
8087 display: flex;
8088 flex-direction: column;
8089 gap: var(--gap-section);
8090 max-width: 300px;
8091 margin-left: auto;
8092 margin-right: auto;
8093 text-align: left;
8094 }
8095
8096 .tip-form.hidden {
8097 display: none;
8098 }
8099
8100 .tip-amount-row {
8101 display: flex;
8102 align-items: center;
8103 gap: var(--gap-bound);
8104 }
8105
8106 .tip-amount-row .pricing-currency {
8107 font-size: var(--text-subhead);
8108 opacity: 0.7;
8109 }
8110
8111 .tip-amount-input {
8112 width: 80px;
8113 font-size: var(--text-subhead);
8114 padding: var(--gap-peer);
8115 border: 1px solid var(--border);
8116 background: var(--surface-overlay);
8117 font-family: var(--font-mono);
8118 }
8119
8120 .tip-message-input {
8121 width: 100%;
8122 font-size: var(--text-note);
8123 padding: var(--gap-peer);
8124 border: 1px solid var(--border);
8125 background: var(--surface-overlay);
8126 font-family: var(--font-body);
8127 resize: vertical;
8128 }
8129
8130 .tip-submit {
8131 font-family: var(--font-mono);
8132 font-size: var(--text-note);
8133 padding: var(--gap-peer) var(--gap-section);
8134 background: var(--content);
8135 color: var(--surface-page);
8136 border: none;
8137 cursor: pointer;
8138 }
8139
8140 .tip-submit:hover {
8141 opacity: 0.85;
8142 }
8143
8144 /* ===========================================
8145 COLLECTION PICKER (shared dropdown)
8146 =========================================== */
8147
8148 .collection-picker-anchor { position: relative; }
8149 .collection-picker {
8150 position: absolute; left: 0; right: 0; top: 100%; z-index: var(--z-picker);
8151 background: var(--surface-page); border: 1px solid var(--border);
8152 box-shadow: var(--shadow-2); min-width: 220px;
8153 }
8154 .collection-picker-list { max-height: 200px; overflow-y: auto; padding: var(--gap-bound) 0; }
8155 .collection-picker-item {
8156 display: flex; align-items: center; gap: var(--gap-peer);
8157 padding: var(--gap-peer) var(--gap-section); cursor: pointer; font-size: var(--text-note);
8158 }
8159 .collection-picker-item:hover { background: var(--surface-sunken); }
8160 .collection-picker-loading,
8161 .collection-picker-create { border-top: 1px solid var(--border); padding: var(--gap-peer) var(--gap-section); }
8162 .collection-picker-create form { display: flex; gap: var(--gap-peer); }
8163 .collection-picker-create input { flex: 1; padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-note); min-width: 0; }
8164 .collection-picker-create button { font-size: var(--text-note); white-space: nowrap; }
8165
8166 /* Save button on discover cards */
8167 .row-save, .grid-card-save {
8168 background: none; border: none; cursor: pointer;
8169 font-size: var(--text-note); padding: var(--gap-bound) var(--gap-peer); opacity: 0.4;
8170 color: var(--content); transition: opacity 0.15s;
8171 }
8172 .row-save:hover, .grid-card-save:hover { opacity: 1; }
8173 .grid-card-save {
8174 position: absolute; top: 0.4rem; right: 0.4rem; z-index: var(--z-raised);
8175 background: var(--surface-page); border-radius: var(--radius-control); padding: var(--gap-bound) var(--gap-peer);
8176 box-shadow: var(--shadow-1); opacity: 0;
8177 }
8178 .grid-card:hover .grid-card-save { opacity: 0.7; }
8179 .grid-card-save:hover { opacity: 1; }
8180
8181 /* Item page save button saved state */
8182 button.saved { border-color: var(--action); color: var(--action); }
8183
8184 /* ===========================================
8185 SEARCH SUGGESTIONS
8186 =========================================== */
8187
8188 .search-wrapper { position: relative; flex: 1; min-width: 0; }
8189 .search-suggestions,
8190 .tag-suggest-list {
8191 display: none; position: absolute; left: 0; right: 0; top: 100%; z-index: var(--z-picker);
8192 background: var(--surface-page); border: 1px solid var(--border); border-top: none;
8193 box-shadow: var(--shadow-3); max-height: 280px; overflow-y: auto;
8194 }
8195 .suggestion-item {
8196 display: flex; justify-content: space-between; align-items: center;
8197 padding: var(--gap-peer) var(--gap-section); text-decoration: none; color: var(--content);
8198 font-size: var(--text-note); cursor: pointer;
8199 }
8200 .suggestion-item:hover, .suggestion-item.highlighted,
8201 .tag-suggest-item:hover, .tag-suggest-item.highlighted { background: var(--surface-sunken); }
8202 .suggestion-category { font-size: var(--text-fine); opacity: 0.5; text-transform: uppercase; letter-spacing: 0.05em; }
8203
8204 /* ===========================================
8205 DOC UI EXAMPLES (embedded live UI in docs)
8206 =========================================== */
8207
8208 .doc-ui {
8209 margin: var(--gap-pane) 0; border: 1px solid var(--border); border-radius: var(--radius-control); overflow: hidden;
8210 }
8211 .doc-ui-frame {
8212 padding: var(--gap-section); background: var(--surface-overlay); pointer-events: none; user-select: none;
8213 }
8214 .doc-ui figcaption {
8215 padding: var(--gap-peer) var(--gap-section); font-size: var(--text-fine); opacity: 0.6;
8216 border-top: 1px solid var(--border); background: var(--surface-overlay);
8217 font-family: var(--font-mono);
8218 }
8219 .doc-ui-missing {
8220 padding: var(--gap-section); opacity: 0.4; font-style: italic;
8221 }
8222
8223 /* Library "New" badge for items with undownloaded versions */
8224 .badge-new {
8225 background: var(--action); color: var(--primary-light); font-size: var(--text-fine);
8226 padding: var(--gap-bound) var(--gap-peer); border-radius: var(--radius-control); margin-left: var(--gap-peer);
8227 vertical-align: middle; font-weight: bold;
8228 }
8229
8230 /* ===========================================
8231 BENEFIT LIST (bulleted feature lists, e.g. Stripe Connect prompt)
8232 =========================================== */
8233 .benefit-list {
8234 list-style: none;
8235 padding: 0;
8236 margin: 0 auto var(--gap-pane);
8237 max-width: 280px;
8238 text-align: left;
8239 display: flex;
8240 flex-direction: column;
8241 gap: var(--gap-peer);
8242 }
8243 .benefit-list li {
8244 position: relative;
8245 padding-left: var(--gap-section);
8246 font-size: var(--text-note);
8247 }
8248 .benefit-list li::before {
8249 content: "";
8250 position: absolute;
8251 left: 0.15rem;
8252 top: 0.55rem;
8253 width: 6px;
8254 height: 6px;
8255 border-radius: var(--radius-round);
8256 background: var(--success);
8257 }
8258
8259 /* ===========================================
8260 CHECKLIST (setup / onboarding step lists)
8261 =========================================== */
8262 .checklist {
8263 margin-bottom: var(--gap-pane);
8264 padding: var(--gap-section) var(--gap-pane);
8265 background: var(--surface-overlay);
8266 border: 1px solid var(--border);
8267 }
8268 .checklist-header {
8269 display: flex;
8270 justify-content: space-between;
8271 align-items: center;
8272 margin-bottom: var(--gap-section);
8273 }
8274 .checklist-title {
8275 margin: 0;
8276 font-family: var(--font-heading);
8277 font-weight: bold;
8278 }
8279 .checklist-mark {
8280 width: 1.1rem;
8281 height: 1.1rem;
8282 border-radius: var(--radius-round);
8283 border: 2px solid var(--border);
8284 flex-shrink: 0;
8285 box-sizing: border-box;
8286 }
8287 .checklist-mark--done {
8288 border-color: var(--success);
8289 background: var(--success);
8290 }
8291 .checklist-label {
8292 flex: 1;
8293 font-size: var(--text-note);
8294 }
8295 .checklist-label--done {
8296 opacity: 0.5;
8297 text-decoration: line-through;
8298 }
8299
8300 /* ===========================================
8301 PAYMENTS TAB (replaces inline styles in user_payments.html)
8302 =========================================== */
8303 /* Canonical "stack row", title-on-left + actions-on-right header bar.
8304 One primitive; per-context modifiers handle gap, wrap, alignment, margins.
8305 Aliases below cover existing per-tab class names (kept as JS hooks). */
8306 .stack-row,
8307 .content-header,
8308 .user-media-header,
8309 .transactions-header,
8310 .analytics-range-bar,
8311 .cart-multi-bar {
8312 display: flex;
8313 justify-content: space-between;
8314 align-items: center;
8315 flex-wrap: wrap;
8316 gap: var(--gap-section);
8317 }
8318 .stack-row--tight { gap: var(--gap-section); flex-wrap: nowrap; }
8319 .stack-row--top { align-items: flex-start; }
8320 .stack-row--bordered,
8321 .transactions-header {
8322 margin-bottom: var(--gap-section);
8323 padding-bottom: var(--gap-section);
8324 border-bottom: 1px solid var(--border);
8325 }
8326 .content-header,
8327 .user-media-header,
8328 .analytics-range-bar { margin-bottom: var(--gap-section); }
8329 .cart-multi-bar {
8330 margin-bottom: var(--gap-pane);
8331 padding: var(--gap-section);
8332 background: var(--surface-sunken);
8333 border: 1px solid var(--border);
8334 }
8335
8336 /* Canonical list row (flex, gap, padding, bottom border). Aliases below cover
8337 the per-tab variants. They exist as JS hooks but share the same look. */
8338 .list-row,
8339 .psection-row,
8340 .section-mgmt-row,
8341 .bundle-picker-row,
8342 .checklist-row {
8343 display: flex;
8344 align-items: center;
8345 gap: var(--gap-section);
8346 padding: var(--gap-group) 0;
8347 border-bottom: 1px solid var(--border);
8348 }
8349 .list-row:last-child,
8350 .psection-row:last-child,
8351 .section-mgmt-row:last-child,
8352 .bundle-picker-row:last-child,
8353 .checklist-row:last-child { border-bottom: none; }
8354 .warn-glyph {
8355 font-size: var(--text-subhead);
8356 color: var(--warning);
8357 }
8358
8359 .card-h {
8360 margin: 0 0 var(--gap-section) 0;
8361 font-size: var(--text-body);
8362 }
8363
8364 .amount-big {
8365 font-size: var(--text-subhead);
8366 font-weight: bold;
8367 }
8368
8369 .account-details {
8370 margin-top: var(--gap-section);
8371 padding-top: var(--gap-section);
8372 border-top: 1px solid var(--border);
8373 }
8374 .account-details summary { cursor: pointer; }
8375 .account-details-id {
8376 font-size: var(--text-fine);
8377 opacity: 0.5;
8378 font-family: var(--font-mono);
8379 margin-top: var(--gap-bound);
8380 }
8381
8382 .stripe-actions {
8383 display: flex;
8384 gap: var(--gap-section);
8385 flex-wrap: wrap;
8386 }
8387 .stripe-actions-disconnect { margin-left: auto; }
8388
8389 .payout-stats-grid {
8390 display: grid;
8391 grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
8392 gap: var(--gap-section);
8393 margin-top: var(--gap-section);
8394 padding-top: var(--gap-section);
8395 border-top: 1px solid var(--border);
8396 }
8397
8398 .payouts-disabled-notice {
8399 margin-top: var(--gap-section);
8400 padding: var(--gap-section);
8401 background: var(--warning);
8402 color: var(--primary-light);
8403 font-size: var(--text-note);
8404 }
8405
8406 .fee-breakdown {
8407 display: grid;
8408 grid-template-columns: 1fr 1fr;
8409 gap: var(--gap-peer);
8410 font-size: var(--text-note);
8411 }
8412 .fee-row {
8413 display: flex;
8414 justify-content: space-between;
8415 padding: var(--gap-peer) 0;
8416 border-bottom: 1px solid var(--border);
8417 }
8418
8419 .checkbox-row {
8420 display: flex;
8421 align-items: flex-start;
8422 gap: var(--gap-peer);
8423 cursor: pointer;
8424 font-size: var(--text-note);
8425 }
8426 .check-inline {
8427 width: auto;
8428 margin-top: var(--gap-bound);
8429 }
8430
8431 .stripe-connect-prompt { text-align: center; padding: var(--gap-pane); }
8432 .stripe-logo { opacity: 0.8; }
8433 .stripe-connect-btn {
8434 background: var(--stripe);
8435 padding: var(--gap-section) var(--gap-page);
8436 }
8437
8438 .export-row {
8439 display: flex;
8440 justify-content: flex-end;
8441 margin-bottom: var(--gap-section);
8442 }
8443
8444 .splits-grid {
8445 display: grid;
8446 grid-template-columns: 1fr 1fr;
8447 gap: var(--gap-section);
8448 margin-bottom: var(--gap-section);
8449 }
8450
8451 .tip-message {
8452 max-width: 300px;
8453 overflow: hidden;
8454 text-overflow: ellipsis;
8455 }
8456
8457 .transactions-title { font-size: var(--text-subhead); }
8458
8459 /* ===========================================
8460 CREATOR TAB (replaces inline styles in user_creator.html)
8461 =========================================== */
8462 .creator-tab-section {
8463 padding: var(--gap-page);
8464 }
8465 .creator-tab-section--centered { text-align: center; }
8466 .creator-tab-section--no-top { padding-top: 0; }
8467
8468 .creator-h2 { font-size: var(--text-subhead); margin-bottom: var(--gap-section); }
8469 .creator-h2--sm { font-size: var(--text-subhead); }
8470
8471 .creator-section-label {
8472 margin-left: var(--gap-page);
8473 margin-right: var(--gap-page);
8474 }
8475 .creator-form-section { padding: 0 var(--gap-page); }
8476
8477 .creator-plan-box {
8478 background: var(--surface-overlay);
8479 padding: var(--gap-section) var(--gap-pane);
8480 }
8481 .creator-plan-head {
8482 display: flex;
8483 align-items: center;
8484 gap: var(--gap-section);
8485 margin-bottom: var(--gap-peer);
8486 flex-wrap: wrap;
8487 }
8488 .creator-plan-name {
8489 font-family: var(--font-heading);
8490 font-weight: bold;
8491 font-size: var(--text-lead);
8492 }
8493
8494 .badge--founder-locked {
8495 background: var(--content);
8496 color: var(--surface-overlay);
8497 }
8498 .badge--founder-pending {
8499 background: var(--surface-sunken);
8500 }
8501
8502 .founder-callout {
8503 background: var(--surface-overlay);
8504 border-left: 3px solid var(--content);
8505 padding: var(--gap-section);
8506 }
8507
8508 .creator-tier-grid {
8509 display: grid;
8510 grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
8511 gap: var(--gap-section);
8512 }
8513 .creator-tier-card {
8514 background: var(--surface-overlay);
8515 padding: var(--gap-section);
8516 text-align: center;
8517 }
8518 .creator-tier-name {
8519 font-family: var(--font-heading);
8520 font-weight: bold;
8521 font-size: var(--text-body);
8522 margin-bottom: var(--gap-bound);
8523 }
8524 .creator-tier-link {
8525 color: inherit;
8526 text-decoration: none;
8527 border-bottom: 1px solid var(--border);
8528 }
8529 .creator-tier-sub {
8530 font-size: var(--text-fine);
8531 opacity: 0.7;
8532 }
8533 .creator-tier-storage { font-size: var(--text-fine); }
8534 .creator-tier-buttons {
8535 display: flex;
8536 flex-direction: column;
8537 gap: var(--gap-peer);
8538 }
8539 .creator-tier-btn {
8540 font-size: var(--text-note);
8541 width: 100%;
8542 }
8543
8544 .strike-dim {
8545 opacity: 0.5;
8546 text-decoration: line-through;
8547 }
8548
8549 .storage-box {
8550 background: var(--surface-overlay);
8551 padding: var(--gap-section) var(--gap-pane);
8552 }
8553 .storage-row {
8554 display: flex;
8555 justify-content: space-between;
8556 margin-bottom: var(--gap-peer);
8557 font-size: var(--text-note);
8558 }
8559 .storage-breakdown {
8560 display: grid;
8561 grid-template-columns: 1fr 1fr;
8562 gap: var(--gap-bound) var(--gap-pane);
8563 margin-top: var(--gap-section);
8564 font-size: var(--text-note);
8565 }
8566
8567 .broadcast-head {
8568 display: flex;
8569 justify-content: space-between;
8570 align-items: flex-start;
8571 }
8572 .broadcast-desc { flex: 1; }
8573 .broadcast-export { margin-left: var(--gap-section); }
8574
8575 .creator-divider {
8576 margin: 0 var(--gap-page);
8577 border-color: var(--border);
8578 }
8579
8580 .creator-intro { line-height: 1.6; }
8581 .creator-pitch-box {
8582 background: var(--surface-overlay);
8583 padding: var(--gap-pane);
8584 }
8585
8586 .mono-sm {
8587 font-family: var(--font-mono);
8588 font-size: var(--text-note);
8589 }
8590
8591 /* ===========================================
8592 PROJECT CONTENT TAB (replaces inline styles in project_content.html)
8593 =========================================== */
8594
8595 .empty-state--lg { padding: var(--gap-page) var(--gap-section); }
8596
8597 .content-filters {
8598 display: flex;
8599 gap: var(--gap-section);
8600 margin-bottom: var(--gap-section);
8601 align-items: center;
8602 flex-wrap: wrap;
8603 }
8604 .content-filter-search {
8605 flex: 1;
8606 min-width: 180px;
8607 padding: var(--gap-peer) var(--gap-group);
8608 font-size: var(--text-note);
8609 }
8610 .content-filter-select {
8611 padding: var(--gap-peer);
8612 font-size: var(--text-note);
8613 }
8614
8615 .bulk-action-bar {
8616 padding: var(--gap-section);
8617 margin-bottom: var(--gap-section);
8618 background: var(--surface-raised);
8619 border: 1px solid var(--border);
8620 border-radius: var(--radius-square);
8621 display: flex;
8622 align-items: center;
8623 gap: var(--gap-peer);
8624 flex-wrap: wrap;
8625 opacity: 0.4;
8626 }
8627 .bulk-action-bar--active { opacity: 1; }
8628 .bulk-count { font-weight: bold; min-width: 5rem; }
8629 .bulk-deselect { margin-left: auto; }
8630 .danger-text { color: var(--danger); }
8631
8632 .bulk-form {
8633 margin-bottom: var(--gap-section);
8634 padding: var(--gap-section);
8635 background: var(--surface-sunken);
8636 border: 1px solid var(--border);
8637 border-radius: var(--radius-square);
8638 }
8639 .bulk-form-row {
8640 display: flex;
8641 gap: var(--gap-section);
8642 align-items: end;
8643 }
8644 .bulk-form-label {
8645 font-size: var(--text-note);
8646 display: block;
8647 margin-bottom: var(--gap-bound);
8648 }
8649
8650 /* Sortable table column header */
8651 .sortable-th { cursor: pointer; }
8652 .sortable-th:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
8653 .sort-arrow { opacity: 0.4; }
8654 .sort-arrow--active { opacity: 1; }
8655
8656 /* Data-table column-width utilities (numeric width hints,
8657 kept as classes to avoid inline `style="width: NN%"`) */
8658 .col-3 { width: 3%; }
8659 .col-5 { width: 5%; }
8660 .col-8 { width: 8%; }
8661 .col-10 { width: 10%; }
8662 .col-12 { width: 12%; }
8663 .col-15 { width: 15%; }
8664 .col-20 { width: 20%; }
8665 .col-32 { width: 32%; }
8666 .col-45 { width: 45%; }
8667
8668 /* Pixel-width utilities for inputs and narrow fixed-width controls.
8669 Use on form inputs that can't be sized by their parent column. */
8670 .w-80 { width: 80px; }
8671 .w-100 { width: 100px; }
8672 .w-120 { width: 120px; }
8673 .w-180 { width: 180px; }
8674 .w-220 { width: 220px; }
8675 .w-260 { width: 260px; }
8676 .w-full { width: 100%; }
8677 .maxw-320 { max-width: 320px; }
8678
8679 /* Canonical input shape modifiers. One base form-input look (defined globally
8680 at line ~365 input[type], textarea, select) plus these size + feature variants.
8681 Apply alongside the per-tab class which acts as a JS hook. */
8682 .input--xs {
8683 padding: var(--gap-bound) var(--gap-peer);
8684 font-size: var(--text-note);
8685 }
8686 .input--sm {
8687 padding: var(--gap-bound) var(--gap-peer);
8688 font-size: var(--text-note);
8689 }
8690 .input--mono { font-family: var(--font-mono); }
8691 .input--upper { text-transform: uppercase; }
8692 .input--numeric { text-align: right; }
8693
8694 .order-arrow-btn {
8695 padding: var(--gap-bound);
8696 font-size: var(--text-fine);
8697 line-height: 1;
8698 }
8699
8700 .bundle-toggle {
8701 background: none;
8702 border: none;
8703 cursor: pointer;
8704 padding: 0 var(--gap-bound) 0 0;
8705 font-size: var(--text-fine);
8706 color: var(--content-muted);
8707 }
8708 .bundle-child-cell { padding-left: var(--gap-page); }
8709 .bundle-child-title { opacity: 0.85; }
8710 .bundle-child-arrow {
8711 color: var(--content-muted);
8712 margin-right: var(--gap-bound);
8713 }
8714
8715 .item-actions {
8716 display: flex;
8717 gap: var(--gap-peer);
8718 }
8719
8720 .badge--inline-tiny {
8721 font-size: var(--text-fine);
8722 margin-left: var(--gap-bound);
8723 opacity: 0.7;
8724 }
8725
8726 .deleted-items-details { margin-top: var(--gap-page); }
8727 .deleted-items-summary {
8728 cursor: pointer;
8729 font-size: var(--text-body);
8730 opacity: 0.7;
8731 }
8732
8733 .section-divider {
8734 margin: var(--gap-page) 0;
8735 border: none;
8736 border-top: 1px solid var(--border);
8737 }
8738
8739 .inline-rename-input {
8740 font-weight: bold;
8741 width: 100%;
8742 padding: var(--gap-bound) var(--gap-peer);
8743 font-size: inherit;
8744 }
8745
8746 /* ===========================================
8747 ITEM DETAILS TAB (replaces inline styles in item_details.html)
8748 =========================================== */
8749
8750 /* Compact button utility (small icon-side actions) */
8751 .btn-compact { padding: var(--gap-peer) var(--gap-section); }
8752
8753 .ml-2 { margin-left: var(--gap-peer); }
8754 .cursor-pointer { cursor: pointer; }
8755 .inline { display: inline; }
8756
8757 .tag-search-wrap { position: relative; }
8758 .tag-suggestions {
8759 position: absolute;
8760 z-index: var(--z-dropdown);
8761 background: var(--surface-sunken);
8762 border: 1px solid var(--border);
8763 width: 100%;
8764 max-height: 200px;
8765 overflow-y: auto;
8766 }
8767
8768 .advanced-details { margin-top: var(--gap-pane); }
8769 .advanced-summary {
8770 cursor: pointer;
8771 font-size: var(--text-body);
8772 opacity: 0.8;
8773 }
8774
8775 /* Bundle table inside item-details */
8776 .bundle-table {
8777 width: 100%;
8778 border-collapse: collapse;
8779 }
8780 .bundle-table-head {
8781 text-align: left;
8782 font-size: var(--text-fine);
8783 opacity: 0.7;
8784 text-transform: uppercase;
8785 letter-spacing: 0.03em;
8786 }
8787 .bundle-row { border-bottom: 1px solid var(--border); }
8788 .bundle-cell { padding: var(--gap-peer); }
8789 .bundle-cell--first { padding-left: 0; }
8790 .bundle-cell--actions { width: 60px; }
8791 .bundle-cell--desc { font-size: var(--text-note); opacity: 0.8; }
8792 .bundle-cell--file { font-size: var(--text-note); }
8793
8794 .bundle-remove-btn {
8795 padding: var(--gap-bound) var(--gap-peer);
8796 font-size: var(--text-fine);
8797 }
8798
8799 .bundle-add-row {
8800 margin-top: var(--gap-section);
8801 display: flex;
8802 gap: var(--gap-section);
8803 align-items: center;
8804 }
8805
8806 .bundle-existing-details { margin-top: var(--gap-section); }
8807 .bundle-existing-summary {
8808 cursor: pointer;
8809 font-size: var(--text-note);
8810 opacity: 0.7;
8811 }
8812 .bundle-existing-row {
8813 margin-top: var(--gap-peer);
8814 display: flex;
8815 gap: var(--gap-peer);
8816 align-items: center;
8817 }
8818 .bundle-existing-select {
8819 flex: 1;
8820 padding: var(--gap-peer);
8821 }
8822
8823 .sections-intro {
8824 font-size: var(--text-note);
8825 opacity: 0.7;
8826 margin: var(--gap-section) 0 var(--gap-section);
8827 }
8828
8829 .section-mgmt-title {
8830 flex: 1;
8831 font-weight: bold;
8832 }
8833 .section-edit-btn,
8834 .section-del-btn {
8835 padding: var(--gap-bound) var(--gap-group);
8836 font-size: var(--text-fine);
8837 }
8838
8839 .section-add-details { margin-top: var(--gap-section); }
8840 .section-add-summary {
8841 cursor: pointer;
8842 font-size: var(--text-body);
8843 }
8844
8845 .insert-image-btn {
8846 margin-top: var(--gap-peer);
8847 font-size: var(--text-note);
8848 }
8849
8850 .section-edit-modal {
8851 margin-top: var(--gap-section);
8852 padding: var(--gap-section);
8853 background: var(--surface-sunken);
8854 }
8855 .section-edit-actions {
8856 display: flex;
8857 gap: var(--gap-peer);
8858 }
8859
8860 .publish-note {
8861 margin: var(--gap-section) 0;
8862 opacity: 0.7;
8863 text-align: left;
8864 }
8865
8866 /* ===========================================
8867 CONTENT WIZARD STEP (replaces inline styles in wizards/steps/item/content.html)
8868 =========================================== */
8869
8870 /* (Wizard upload-status + slim progress moved into canonical .upload-status*
8871 / .progress-bar-container--slim primitives near top of file.) */
8872
8873 /* Bundle items picker, server-rendered rows and JS-appended rows share these. */
8874 .bundle-picker-row-title { flex: 1; }
8875 .bundle-picker-row-type { font-size: var(--text-fine); opacity: 0.6; }
8876 .bundle-picker-row-unlisted {
8877 font-size: var(--text-fine);
8878 display: flex;
8879 align-items: center;
8880 gap: var(--gap-bound);
8881 }
8882
8883 /* Batch upload queue table. */
8884 .batch-queue-table {
8885 width: 100%;
8886 border-collapse: collapse;
8887 }
8888 .batch-queue-table thead tr {
8889 border-bottom: 2px solid var(--border);
8890 text-align: left;
8891 }
8892 .batch-queue-table th,
8893 .batch-queue-table td {
8894 padding: var(--gap-peer);
8895 font-size: var(--text-note);
8896 }
8897 .batch-queue-table th:first-child,
8898 .batch-queue-table td:first-child { padding-left: 0; }
8899 .batch-queue-table th:last-child,
8900 .batch-queue-table td:last-child { padding-right: 0; text-align: right; }
8901 .batch-queue-table tbody tr { border-bottom: 1px solid var(--border); }
8902
8903 .batch-queue-name {
8904 max-width: 150px;
8905 overflow: hidden;
8906 text-overflow: ellipsis;
8907 white-space: nowrap;
8908 }
8909
8910 /* ===========================================
8911 ACCOUNT TAB (replaces inline styles in partials/tabs/user_account.html)
8912 =========================================== */
8913
8914 /* Canonical solid-tint callout (one of these in the codebase).
8915 Distinct from .alert (left-border) and .banner (full-bleed page notice).
8916 Variants:
8917 - .callout--danger (light bg, danger border, moderation, errors)
8918 - .callout--warning (light bg, warning border, DNS, email-not-verified)
8919 - .callout--solid-warning (full saturation warning bg, dark text, partial checkout) */
8920 .callout {
8921 padding: var(--gap-section);
8922 margin-bottom: var(--gap-section);
8923 font-size: var(--text-note);
8924 border: 1px solid;
8925 }
8926 .callout--danger {
8927 background: color-mix(in oklch, var(--danger) 10%, var(--surface-page));
8928 border-color: var(--danger);
8929 }
8930 .callout--warning {
8931 background: color-mix(in oklch, var(--warning) 14%, var(--surface-page));
8932 border-color: var(--warning);
8933 }
8934 .callout--solid-warning {
8935 background: var(--warning);
8936 color: var(--primary-dark);
8937 border-color: transparent;
8938 padding: var(--gap-section);
8939 }
8940 .callout-title { text-transform: capitalize; }
8941 .callout-meta {
8942 opacity: 0.6;
8943 margin-left: var(--gap-peer);
8944 }
8945 .callout-body { margin: var(--gap-peer) 0 0 0; }
8946 .callout-hint {
8947 font-size: var(--text-note);
8948 margin-top: var(--gap-peer);
8949 }
8950
8951 .account-mod-history { margin-top: var(--gap-section); }
8952 .account-mod-history summary {
8953 cursor: pointer;
8954 font-size: var(--text-note);
8955 opacity: 0.7;
8956 }
8957 .account-mod-history-list { margin-top: var(--gap-peer); }
8958 .account-mod-history-item {
8959 padding: var(--gap-peer) 0;
8960 border-bottom: 1px solid var(--border);
8961 font-size: var(--text-note);
8962 }
8963 .account-mod-history-reason {
8964 margin: var(--gap-bound) 0 0 0;
8965 opacity: 0.8;
8966 }
8967
8968 .account-tip-card-title {
8969 font-weight: bold;
8970 margin-bottom: var(--gap-peer);
8971 }
8972 .account-tip-list {
8973 display: flex;
8974 flex-direction: column;
8975 gap: var(--gap-bound);
8976 opacity: 0.8;
8977 }
8978
8979 /* Resend-verification button needs left spacing from preceding strong. */
8980 .account-resend-btn { margin-left: var(--gap-peer); }
8981
8982 /* Cluster of small inline forms (manage-billing / cancel / resume). */
8983 .inline-form { display: inline; }
8984
8985 /* Notification preferences, bare fieldset + monospace legend. */
8986 .pref-fieldset {
8987 border: none;
8988 padding: 0;
8989 margin: 0 0 var(--gap-section) 0;
8990 }
8991 .pref-fieldset--last { margin-bottom: var(--gap-peer); }
8992 .pref-legend {
8993 font-family: var(--font-mono);
8994 font-size: var(--text-note);
8995 font-weight: bold;
8996 margin-bottom: var(--gap-peer);
8997 }
8998
8999 /* The cannot-opt-out list, rendered from email::OperationalKind. Reads as
9000 reference material rather than as more settings: no controls, and the
9001 justification sits under its label rather than beside it. */
9002 .operational-mail {
9003 margin: var(--gap-peer) 0 0 0;
9004 }
9005 .operational-mail dt {
9006 font-weight: bold;
9007 font-size: var(--text-note);
9008 margin-top: var(--gap-peer);
9009 }
9010 .operational-mail dd {
9011 margin: 0;
9012 opacity: 0.8;
9013 font-size: var(--text-note);
9014 }
9015
9016 /* ===========================================
9017 PROJECT CODE TAB (replaces inline styles in partials/tabs/project_code.html)
9018 =========================================== */
9019
9020 .proj-code-docs-link { margin-left: var(--gap-section); }
9021
9022 .proj-code-linked-list { margin-bottom: var(--gap-pane); }
9023 .proj-code-linked-list > label { margin-bottom: var(--gap-peer); display: block; }
9024
9025 .proj-code-repo {
9026 margin-bottom: var(--gap-section);
9027 padding: var(--gap-section);
9028 background: var(--surface-overlay);
9029 }
9030 .proj-code-repo-row {
9031 display: flex;
9032 align-items: center;
9033 gap: var(--gap-section);
9034 }
9035 .proj-code-repo-link { flex: 1; }
9036 .proj-code-repo .danger { padding: var(--gap-bound) var(--gap-section); font-size: var(--text-note); }
9037
9038 .proj-code-collab-block {
9039 margin-top: var(--gap-peer);
9040 padding-top: var(--gap-peer);
9041 border-top: 1px solid var(--border);
9042 }
9043 .proj-code-collab-label { font-size: var(--text-note); margin-bottom: var(--gap-bound); display: block; }
9044 .proj-code-collab-row {
9045 display: flex;
9046 align-items: center;
9047 gap: var(--gap-peer);
9048 margin-bottom: var(--gap-bound);
9049 font-size: var(--text-note);
9050 }
9051 .proj-code-collab-name { flex: 1; }
9052 .proj-code-collab-readonly { opacity: 0.5; }
9053
9054 .proj-code-add-collab-row {
9055 display: flex;
9056 gap: var(--gap-peer);
9057 align-items: center;
9058 }
9059 .proj-code-add-collab-row .collab-username-input {
9060 flex: 1;
9061 font-size: var(--text-note);
9062 padding: var(--gap-bound) var(--gap-peer);
9063 }
9064 .proj-code-add-collab-row .collab-add-btn { white-space: nowrap; }
9065
9066 .proj-code-link-row {
9067 display: flex;
9068 gap: var(--gap-peer);
9069 align-items: center;
9070 }
9071 .proj-code-link-row #link-repo-select { flex: 1; }
9072 .proj-code-link-row #link-repo-btn { white-space: nowrap; }
9073
9074 .proj-code-create-section {
9075 margin-top: var(--gap-pane);
9076 padding-top: var(--gap-section);
9077 border-top: 1px solid var(--border);
9078 }
9079 .proj-code-create-section #create-repo-name { flex: 1; }
9080 .proj-code-create-section #create-repo-btn { white-space: nowrap; }
9081 .proj-code-create-status { margin-top: var(--gap-bound); }
9082 .proj-code-create-status.is-error { color: var(--danger); }
9083
9084 .proj-code-disabled {
9085 padding: var(--gap-page);
9086 text-align: center;
9087 opacity: 0.7;
9088 }
9089
9090 /* ===========================================
9091 ADMIN REPORT ENTRIES (replaces inline styles in partials/admin_report_entries.html)
9092 =========================================== */
9093
9094 .admin-report-table {
9095 width: 100%;
9096 border-collapse: collapse;
9097 font-size: var(--text-note);
9098 }
9099 .admin-report-table thead tr {
9100 border-bottom: 2px solid var(--border);
9101 text-align: left;
9102 }
9103 .admin-report-table tbody tr {
9104 border-bottom: 1px solid var(--border);
9105 }
9106 .admin-report-table th,
9107 .admin-report-table td {
9108 padding: var(--gap-peer);
9109 }
9110 .admin-report-table td.admin-report-reason {
9111 max-width: 200px;
9112 overflow: hidden;
9113 text-overflow: ellipsis;
9114 white-space: nowrap;
9115 }
9116 .admin-report-table td.admin-report-date {
9117 white-space: nowrap;
9118 }
9119
9120 .admin-report-target-type {
9121 opacity: 0.5;
9122 font-size: var(--text-fine);
9123 }
9124
9125 .admin-report-status-open {
9126 color: var(--action);
9127 font-weight: 600;
9128 }
9129 .admin-report-status-closed { opacity: 0.6; }
9130
9131 .admin-report-resolve-form {
9132 display: inline-flex;
9133 gap: var(--gap-bound);
9134 align-items: center;
9135 }
9136 .admin-report-resolve-form input[type="text"] {
9137 width: 120px;
9138 padding: var(--gap-bound) var(--gap-peer);
9139 font-size: var(--text-fine);
9140 }
9141 .admin-report-dismiss-form { display: inline; }
9142
9143 .admin-report-admin-notes {
9144 font-size: var(--text-fine);
9145 opacity: 0.6;
9146 }
9147
9148 /* ===========================================
9149 PROJECT PROMOTIONS TAB (replaces inline styles in partials/tabs/project_promotions.html)
9150 =========================================== */
9151
9152 .proj-promo-form {
9153 margin-bottom: var(--gap-page);
9154 }
9155
9156 .proj-promo-grid {
9157 display: grid;
9158 grid-template-columns: 1fr 1fr;
9159 gap: var(--gap-section) var(--gap-pane);
9160 max-width: 500px;
9161 }
9162
9163 .proj-promo-grid--bordered {
9164 margin-top: var(--gap-section);
9165 padding-top: var(--gap-section);
9166 border-top: 1px solid var(--border);
9167 }
9168
9169 .proj-promo-trial-fields {
9170 display: none;
9171 max-width: 500px;
9172 margin-top: var(--gap-section);
9173 }
9174
9175 .proj-promo-trial-fields.is-visible {
9176 display: block;
9177 }
9178
9179 .proj-promo-grid.is-hidden,
9180 .proj-promo-trial-fields.is-hidden {
9181 display: none;
9182 }
9183
9184 .proj-promo-form .form-group {
9185 margin-bottom: 0;
9186 }
9187
9188 .proj-promo-form .form-group label {
9189 font-size: var(--text-note);
9190 }
9191
9192 .proj-promo-trial-group {
9193 max-width: 240px;
9194 }
9195
9196 .proj-promo-scope {
9197 grid-column: 1 / -1;
9198 }
9199
9200 .proj-promo-actions {
9201 margin-top: var(--gap-section);
9202 }
9203
9204 .proj-promo-hint {
9205 margin-top: var(--gap-peer);
9206 }
9207
9208 /* ===========================================
9209 PROJECT OVERVIEW TAB (replaces inline styles in partials/tabs/project_overview.html)
9210 =========================================== */
9211
9212 .proj-overview-setup {
9213 margin-bottom: var(--gap-section);
9214 padding: var(--gap-section);
9215 background: var(--surface-overlay);
9216 border: 1px solid var(--border);
9217 }
9218 .proj-overview-setup-title {
9219 margin: 0 0 var(--gap-section) 0;
9220 font-family: var(--font-heading);
9221 font-weight: bold;
9222 }
9223 .proj-overview-setup-row {
9224 display: flex;
9225 align-items: center;
9226 gap: var(--gap-section);
9227 padding: var(--gap-peer) 0;
9228 border-bottom: 1px solid var(--border);
9229 }
9230 .proj-overview-setup-row:last-child { border-bottom: none; }
9231 .proj-overview-setup-check {
9232 color: var(--success);
9233 font-family: var(--font-mono);
9234 font-size: var(--text-fine);
9235 text-transform: uppercase;
9236 letter-spacing: 0.04em;
9237 flex-shrink: 0;
9238 }
9239 .proj-overview-setup-bullet {
9240 width: 1.1rem;
9241 height: 1.1rem;
9242 border: 2px solid var(--border);
9243 border-radius: var(--radius-round);
9244 flex-shrink: 0;
9245 }
9246 .proj-overview-setup-label {
9247 flex: 1;
9248 font-size: var(--text-note);
9249 }
9250 .proj-overview-setup-label--done {
9251 opacity: 0.5;
9252 text-decoration: line-through;
9253 }
9254 .proj-overview-setup-btn {
9255 font-size: var(--text-fine);
9256 padding: var(--gap-bound) var(--gap-group);
9257 }
9258
9259 .proj-overview-tools { margin-top: var(--gap-page); }
9260 .proj-overview-tools-summary {
9261 cursor: pointer;
9262 font-size: var(--text-body);
9263 font-family: var(--font-heading);
9264 font-weight: bold;
9265 }
9266 .proj-overview-tools-grid {
9267 display: grid;
9268 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
9269 gap: var(--gap-section);
9270 margin-top: var(--gap-section);
9271 }
9272 .proj-overview-tool {
9273 background: var(--surface-sunken);
9274 padding: var(--gap-section);
9275 }
9276 .proj-overview-tool-name {
9277 font-weight: bold;
9278 font-size: var(--text-body);
9279 margin-bottom: var(--gap-bound);
9280 }
9281 .proj-overview-tool-desc {
9282 font-size: var(--text-note);
9283 opacity: 0.7;
9284 margin: 0;
9285 }
9286
9287 /* ===========================================
9288 PROJECT SYNCKIT TAB (replaces inline styles in partials/tabs/project_synckit.html)
9289 =========================================== */
9290
9291 .proj-synckit-intro { margin-bottom: var(--gap-section); }
9292 .proj-synckit-intro-secondary { margin-bottom: var(--gap-pane); }
9293 .proj-synckit-create { margin-bottom: var(--gap-pane); }
9294 .proj-synckit-create-body {
9295 padding: var(--gap-section); background: var(--surface-overlay); border: 1px solid var(--border);
9296 }
9297 .proj-synckit-create-form {
9298 display: flex; flex-direction: row; gap: var(--gap-section); align-items: flex-end;
9299 }
9300 .proj-synckit-label {
9301 display: block; font-size: var(--text-note); margin-bottom: var(--gap-bound);
9302 }
9303 .proj-synckit-code { font-size: var(--text-fine); }
9304 .proj-synckit-code--wrap { font-size: var(--text-fine); word-break: break-all; }
9305 .proj-synckit-code--selectable { font-size: var(--text-note); word-break: break-all; user-select: all; }
9306 .proj-synckit-btn-tight {
9307 margin-left: var(--gap-peer); padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine);
9308 }
9309 .proj-synckit-btn-row {
9310 padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine);
9311 }
9312 .proj-synckit-btn-row + .proj-synckit-btn-row { margin-left: var(--gap-bound); }
9313 .proj-synckit-hint-inline {
9314 font-size: var(--text-fine); margin-left: var(--gap-bound);
9315 }
9316 .proj-synckit-hint-warn {
9317 font-size: var(--text-fine); color: var(--danger);
9318 }
9319 .proj-synckit-status-active { color: var(--success); }
9320 .proj-synckit-status-inactive { color: var(--danger); }
9321 .proj-synckit-new-key {
9322 padding: var(--gap-section); background: var(--surface-overlay);
9323 border: 1px solid var(--border); margin-top: var(--gap-peer);
9324 }
9325 .proj-synckit-new-key-heading {
9326 margin: 0 0 var(--gap-peer) 0; font-weight: 500; color: var(--danger);
9327 }
9328 .proj-synckit-create-status--error { color: var(--danger); margin-top: var(--gap-peer); }
9329 .proj-synckit-create-status--reset { margin-top: var(--gap-peer); }
9330
9331 /* ===========================================
9332 ITEM PAGE (replaces inline styles in pages/item.html)
9333 =========================================== */
9334
9335 .item-page .breadcrumb {
9336 display: flex;
9337 justify-content: space-between;
9338 align-items: center;
9339 }
9340
9341 .item-page .breadcrumb-edit-link {
9342 font-size: var(--text-note);
9343 color: var(--content);
9344 }
9345
9346 .item-page .video-player {
9347 width: 100%;
9348 max-height: 600px;
9349 background: var(--primary-dark);
9350 }
9351
9352 .item-page .item-cover-img {
9353 width: 100%;
9354 aspect-ratio: 1 / 1;
9355 object-fit: cover;
9356 display: block;
9357 }
9358
9359
9360 .item-page .bundle-notice-inline {
9361 background: var(--surface-sunken);
9362 padding: var(--gap-section);
9363 }
9364
9365 .item-page .bundle-notice-title {
9366 font-weight: bold;
9367 margin-bottom: var(--gap-peer);
9368 }
9369
9370 .item-page .bundle-notice-desc {
9371 opacity: 0.8;
9372 font-size: var(--text-note);
9373 }
9374
9375 .item-page .bundle-notice-entry {
9376 margin-top: var(--gap-section);
9377 }
9378
9379 .item-page .bundle-notice-entry a {
9380 font-weight: bold;
9381 }
9382
9383 .item-page .bundle-notice-price {
9384 opacity: 0.7;
9385 }
9386
9387 .item-page .bundle-notice-empty {
9388 margin-top: var(--gap-peer);
9389 opacity: 0.7;
9390 font-size: var(--text-note);
9391 }
9392
9393 .item-page .promo-details {
9394 margin-bottom: var(--gap-section);
9395 font-size: var(--text-note);
9396 }
9397
9398 .item-page .promo-summary {
9399 cursor: pointer;
9400 opacity: 0.7;
9401 }
9402
9403 .item-page .promo-form {
9404 display: flex;
9405 gap: var(--gap-peer);
9406 align-items: center;
9407 margin-top: var(--gap-peer);
9408 }
9409
9410 .item-page .promo-input {
9411 flex: 1;
9412 padding: var(--gap-peer) var(--gap-group);
9413 font-size: var(--text-note);
9414 }
9415
9416 .item-page .action-row {
9417 margin-top: var(--gap-section);
9418 }
9419
9420 .item-page .action-row-tight {
9421 margin-top: var(--gap-peer);
9422 }
9423
9424 .item-page .full-width-btn {
9425 width: 100%;
9426 font-size: var(--text-note);
9427 }
9428
9429 .item-page .license-preset-name {
9430 margin-bottom: var(--gap-section);
9431 }
9432
9433 .item-page .license-summary {
9434 cursor: pointer;
9435 opacity: 0.7;
9436 font-size: var(--text-note);
9437 }
9438
9439 .item-page .license-text {
9440 white-space: pre-wrap;
9441 font-family: var(--font-body);
9442 font-size: var(--text-note);
9443 margin-top: var(--gap-section);
9444 padding: var(--gap-section);
9445 background: var(--surface-sunken);
9446 }
9447
9448 .item-page .license-download {
9449 margin-top: var(--gap-section);
9450 }
9451
9452 .item-page .license-download a {
9453 font-size: var(--text-note);
9454 color: var(--content);
9455 }
9456
9457 .item-page .bundle-list-entry {
9458 margin-top: var(--gap-peer);
9459 }
9460
9461 .item-page .footer-links {
9462 margin-top: var(--gap-peer);
9463 }
9464
9465 /* ===========================================
9466 JOIN COMPLETE STEP (replaces inline styles in wizards/steps/join/complete.html)
9467 =========================================== */
9468 .join-complete-intro { margin-bottom: var(--gap-pane); }
9469 .join-complete-choices {
9470 display: grid; grid-template-columns: 1fr 1fr;
9471 gap: var(--gap-section); max-width: 500px;
9472 }
9473 .join-complete-choice { text-decoration: none; color: inherit; display: block; }
9474 .join-complete-card {
9475 background: var(--surface-sunken); padding: var(--gap-section); text-align: center;
9476 border: 2px solid transparent; transition: border-color 0.15s;
9477 }
9478 .join-complete-choice:hover .join-complete-card,
9479 .join-complete-card--active { border-color: var(--content); }
9480 .join-complete-card-title {
9481 font-family: var(--font-heading); font-weight: bold;
9482 font-size: var(--text-lead); margin-bottom: var(--gap-peer);
9483 }
9484 .join-complete-card-desc {
9485 font-size: var(--text-note); opacity: 0.7; margin: 0;
9486 }
9487 .join-complete-footnote {
9488 font-size: var(--text-note); opacity: 0.5;
9489 margin-top: var(--gap-section); text-align: center;
9490 }
9491
9492 /* ===========================================
9493 PROMO CODES LIST (replaces inline styles in partials/promo_codes_list.html)
9494 =========================================== */
9495 .promo-codes-row--expired { opacity: 0.5; }
9496 .promo-codes-row--pending { opacity: 0.7; }
9497 .promo-codes-schedule { font-size: var(--text-note); }
9498 .promo-codes-schedule-pending { color: var(--warning); }
9499 .promo-codes-schedule-expired { color: var(--danger); }
9500 .promo-codes-schedule-none { opacity: 0.5; }
9501 .promo-codes-actions { white-space: nowrap; }
9502 .promo-codes-edit-btn { font-size: var(--text-fine); }
9503 .promo-codes-edit-row { background: var(--surface-sunken); }
9504 .promo-codes-edit-cell { padding: var(--gap-section); }
9505 .promo-codes-edit-form {
9506 display: flex; gap: var(--gap-section); align-items: end; flex-wrap: wrap;
9507 }
9508 .promo-codes-edit-label {
9509 font-size: var(--text-fine); display: block; margin-bottom: var(--gap-bound);
9510 }
9511 .promo-codes-edit-save { font-size: var(--text-note); }
9512 .promo-codes-bulk-row { margin-top: var(--gap-section); }
9513 .promo-codes-bulk-btn { font-size: var(--text-note); opacity: 0.7; }
9514
9515 /* ===========================================
9516 USER MEDIA TAB (replaces inline styles in partials/tabs/user_media.html)
9517 =========================================== */
9518
9519 .user-media-intro {
9520 margin-bottom: var(--gap-section);
9521 }
9522
9523 .user-media-upload {
9524 border: 2px dashed var(--border);
9525 padding: var(--gap-pane);
9526 margin-bottom: var(--gap-pane);
9527 text-align: center;
9528 background: var(--surface-overlay);
9529 }
9530
9531 .user-media-upload--dragover {
9532 border-color: var(--action);
9533 }
9534
9535 .user-media-upload-row {
9536 margin-bottom: var(--gap-section);
9537 }
9538
9539 .user-media-upload-label {
9540 display: inline-block;
9541 font-size: var(--text-note);
9542 margin-right: var(--gap-section);
9543 }
9544
9545 .user-media-upload-folder-input {
9546 padding: var(--gap-bound);
9547 font-size: var(--text-note);
9548 width: 140px;
9549 margin-left: var(--gap-bound);
9550 }
9551
9552 .user-media-upload-progress {
9553 margin-top: var(--gap-section);
9554 }
9555
9556 .user-media-upload-status {
9557 font-size: var(--text-note);
9558 margin-bottom: var(--gap-bound);
9559 }
9560
9561 .user-media-upload-status--ok {
9562 color: var(--success);
9563 }
9564
9565 .user-media-upload-status--err {
9566 color: var(--danger);
9567 }
9568
9569 .user-media-folders {
9570 margin-bottom: var(--gap-section);
9571 display: flex;
9572 gap: var(--gap-peer);
9573 flex-wrap: wrap;
9574 align-items: center;
9575 }
9576
9577 .user-media-folders-label {
9578 font-size: var(--text-note);
9579 opacity: 0.7;
9580 }
9581
9582 .user-media-folders-btn {
9583 font-size: var(--text-fine);
9584 }
9585
9586 .user-media-grid {
9587 display: grid;
9588 grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
9589 gap: var(--gap-section);
9590 }
9591
9592 .user-media-card {
9593 background: var(--surface-overlay);
9594 border: 1px solid var(--border);
9595 padding: var(--gap-section);
9596 position: relative;
9597 }
9598
9599 .user-media-card-thumb {
9600 width: 100%;
9601 height: 120px;
9602 overflow: hidden;
9603 margin-bottom: var(--gap-peer);
9604 display: flex;
9605 align-items: center;
9606 justify-content: center;
9607 background: var(--surface-sunken);
9608 }
9609
9610 .user-media-card-thumb--video {
9611 font-size: var(--text-title);
9612 opacity: 0.4;
9613 }
9614
9615 .user-media-card-img {
9616 max-width: 100%;
9617 max-height: 120px;
9618 object-fit: contain;
9619 }
9620
9621 .user-media-card-name {
9622 font-size: var(--text-fine);
9623 word-break: break-all;
9624 margin-bottom: var(--gap-bound);
9625 }
9626
9627 .user-media-card-meta {
9628 font-size: var(--text-fine);
9629 opacity: 0.6;
9630 margin-bottom: var(--gap-peer);
9631 }
9632
9633 .user-media-card-actions {
9634 display: flex;
9635 gap: var(--gap-bound);
9636 }
9637
9638 .user-media-card-btn {
9639 font-size: var(--text-fine);
9640 padding: var(--gap-bound) var(--gap-peer);
9641 }
9642
9643 /* ===========================================
9644 ITEM VERSION UPLOAD (replaces inline styles in partials/item_version_upload.html)
9645 =========================================== */
9646
9647 .item-version-upload-cell-sm {
9648 font-size: var(--text-note);
9649 }
9650
9651 .item-version-upload-no-file {
9652 opacity: 0.5;
9653 }
9654
9655 .item-version-upload-btn {
9656 padding: var(--gap-peer) var(--gap-section);
9657 font-size: var(--text-note);
9658 }
9659
9660 .item-version-upload-add-btn {
9661 padding: var(--gap-peer) var(--gap-section);
9662 }
9663
9664 .item-version-upload-grid {
9665 display: grid;
9666 grid-template-columns: 1fr 1fr;
9667 gap: var(--gap-section);
9668 }
9669
9670 .item-version-upload-hint {
9671 margin-bottom: var(--gap-peer);
9672 }
9673
9674 .item-version-upload-file-table {
9675 width: 100%;
9676 border-collapse: collapse;
9677 }
9678
9679 .item-version-upload-file-table thead tr {
9680 text-align: left;
9681 font-size: var(--text-fine);
9682 opacity: 0.7;
9683 text-transform: uppercase;
9684 letter-spacing: 0.03em;
9685 }
9686
9687 .item-version-upload-file-table th:first-child {
9688 padding: var(--gap-peer) var(--gap-peer) var(--gap-peer) 0;
9689 }
9690
9691 .item-version-upload-file-table th + th {
9692 padding: var(--gap-peer);
9693 }
9694
9695 .item-version-upload-file-table th:last-child {
9696 width: 40px;
9697 }
9698
9699 .item-version-upload-add-row {
9700 margin-top: var(--gap-peer);
9701 }
9702
9703 .item-version-upload-queue {
9704 margin-bottom: var(--gap-section);
9705 }
9706
9707 .item-version-upload-speed {
9708 font-size: var(--text-fine);
9709 opacity: 0.6;
9710 margin-top: var(--gap-bound);
9711 }
9712
9713 /* ===========================================
9714 PROJECT PAGE (replaces inline styles in pages/project.html)
9715 =========================================== */
9716
9717 .project-page .store-cover {
9718 width: 120px;
9719 height: 120px;
9720 border-radius: var(--radius-panel);
9721 object-fit: cover;
9722 margin-bottom: var(--gap-pane);
9723 }
9724 .project-page .follow-btn.is-selected {
9725 opacity: 0.7;
9726 }
9727 .project-page .follower-count-muted {
9728 font-size: var(--text-note);
9729 opacity: 0.7;
9730 padding: var(--gap-peer) 0;
9731 }
9732 .project-page .promo-details {
9733 font-size: var(--text-note);
9734 margin-bottom: var(--gap-peer);
9735 }
9736 .project-page .promo-details summary {
9737 cursor: pointer;
9738 opacity: 0.7;
9739 }
9740 .project-page .promo-input {
9741 width: 100%;
9742 margin-top: var(--gap-peer);
9743 padding: var(--gap-bound) var(--gap-peer);
9744 font-size: var(--text-note);
9745 text-transform: uppercase;
9746 }
9747 .project-page .store-footer-links {
9748 margin-top: var(--gap-peer);
9749 }
9750
9751 /* ===========================================
9752 INSERTION LIST (replaces inline styles in partials/insertion_list.html)
9753 =========================================== */
9754
9755 .insertion-list-heading {
9756 font-family: var(--font-mono);
9757 font-size: var(--text-body);
9758 margin-bottom: var(--gap-section);
9759 }
9760 .insertion-list-toolbar {
9761 margin-bottom: var(--gap-section);
9762 }
9763 .insertion-list-upload {
9764 font-family: var(--font-mono);
9765 }
9766 .insertion-list-table {
9767 width: 100%;
9768 font-family: var(--font-body);
9769 }
9770 .insertion-list-table th {
9771 text-align: left;
9772 font-family: var(--font-mono);
9773 font-size: var(--text-note);
9774 }
9775 .insertion-list-table th.insertion-list-th-actions {
9776 text-align: right;
9777 }
9778 .insertion-list-table .insertion-list-actions {
9779 text-align: right;
9780 }
9781 .insertion-list-table .insertion-list-action-btn {
9782 font-family: var(--font-mono);
9783 }
9784
9785 /* ===========================================
9786 PROJECT MEMBERS TAB (replaces inline styles in partials/tabs/project_members.html)
9787 =========================================== */
9788
9789 .proj-members-summary {
9790 background: var(--surface-sunken);
9791 padding: var(--gap-section);
9792 margin-bottom: var(--gap-section);
9793 }
9794
9795 .proj-members-summary-row {
9796 display: flex;
9797 justify-content: space-between;
9798 align-items: center;
9799 }
9800
9801 .proj-members-stat {
9802 font-size: var(--text-head);
9803 font-weight: bold;
9804 }
9805
9806 .proj-members-add {
9807 margin-bottom: var(--gap-page);
9808 }
9809
9810 .proj-members-add-grid {
9811 display: grid;
9812 grid-template-columns: 1fr auto auto;
9813 gap: var(--gap-section);
9814 align-items: end;
9815 }
9816
9817 .proj-members-field-label {
9818 font-size: var(--text-note);
9819 display: block;
9820 margin-bottom: var(--gap-bound);
9821 }
9822
9823 .proj-members-add-result {
9824 margin-top: var(--gap-peer);
9825 }
9826
9827 .proj-members-name-link {
9828 color: inherit;
9829 }
9830
9831 .proj-members-split-cell {
9832 font-weight: bold;
9833 }
9834
9835 /* ===========================================
9836 PLACEMENT LIST (replaces inline styles in partials/placement_list.html)
9837 =========================================== */
9838
9839 .placement-list-heading {
9840 font-family: var(--font-mono);
9841 font-size: var(--text-body);
9842 margin-bottom: var(--gap-section);
9843 }
9844 .placement-list-table {
9845 width: 100%;
9846 font-family: var(--font-body);
9847 margin-bottom: var(--gap-section);
9848 }
9849 .placement-list-table th {
9850 text-align: left;
9851 font-family: var(--font-mono);
9852 font-size: var(--text-note);
9853 }
9854 .placement-list-table th.placement-list-th-actions {
9855 text-align: right;
9856 }
9857 .placement-list-table .placement-list-actions {
9858 text-align: right;
9859 }
9860 .placement-list-table .placement-list-remove {
9861 font-family: var(--font-mono);
9862 }
9863 .placement-list-form {
9864 display: flex;
9865 gap: var(--gap-peer);
9866 align-items: flex-end;
9867 flex-wrap: wrap;
9868 }
9869 .placement-list-label {
9870 display: block;
9871 font-family: var(--font-mono);
9872 font-size: var(--text-fine);
9873 margin-bottom: var(--gap-bound);
9874 }
9875 .placement-list-add {
9876 font-family: var(--font-mono);
9877 }
9878
9879 /* ===========================================
9880 USER SYNCKIT TAB (replaces inline styles in partials/tabs/user_synckit.html)
9881 =========================================== */
9882
9883 .user-synckit-create { margin-bottom: var(--gap-pane); }
9884 .user-synckit-create-body {
9885 padding: var(--gap-section);
9886 background: var(--surface-overlay);
9887 border: 1px solid var(--border);
9888 }
9889 .user-synckit-create-form {
9890 display: flex;
9891 flex-direction: row;
9892 gap: var(--gap-section);
9893 align-items: flex-end;
9894 }
9895 .user-synckit-create-form label {
9896 display: block;
9897 font-size: var(--text-note);
9898 margin-bottom: var(--gap-bound);
9899 }
9900 .user-synckit-create-form input[type="text"],
9901 .user-synckit-create-form select {
9902 padding: var(--gap-peer);
9903 font-size: var(--text-note);
9904 }
9905 .user-synckit-create-form input[type="text"] { width: 220px; }
9906 .user-synckit-create-form select { width: 180px; }
9907
9908 .user-synckit-code { font-size: var(--text-fine); }
9909 .user-synckit-hint {
9910 font-size: var(--text-fine);
9911 margin-left: var(--gap-bound);
9912 }
9913
9914 .user-synckit-inline-btn {
9915 margin-left: var(--gap-peer);
9916 padding: var(--gap-bound) var(--gap-peer);
9917 font-size: var(--text-fine);
9918 }
9919 .user-synckit-row-btn {
9920 padding: var(--gap-bound) var(--gap-peer);
9921 font-size: var(--text-fine);
9922 }
9923 .user-synckit-row-btn + .user-synckit-row-btn {
9924 margin-left: var(--gap-bound);
9925 }
9926
9927 .user-synckit-status-active { color: var(--success); }
9928 .user-synckit-status-inactive { color: var(--danger); }
9929
9930 .user-synckit-edit-btn {
9931 padding: var(--gap-bound) var(--gap-peer);
9932 font-size: var(--text-fine);
9933 }
9934
9935 .user-synckit-create-error {
9936 color: var(--danger);
9937 margin-top: var(--gap-peer);
9938 }
9939
9940 /* ===========================================
9941 TOTP (replaces inline styles in partials/totp_setup.html and totp_status.html)
9942 =========================================== */
9943
9944 /* Section wrapper between TOTP subsections (QR + backup codes + confirm,
9945 or regenerate + disable). */
9946 .totp-section { margin-bottom: var(--gap-pane); }
9947
9948 /* Confirm-setup section is separated from the backup codes by a top border. */
9949 .totp-confirm {
9950 border-top: 1px solid var(--border);
9951 padding-top: var(--gap-section);
9952 }
9953
9954 /* QR code container, centered above the manual-entry details. */
9955 .totp-qr {
9956 text-align: center;
9957 margin-bottom: var(--gap-section);
9958 }
9959 .totp-qr img {
9960 max-width: 200px;
9961 image-rendering: pixelated;
9962 }
9963
9964 /* Collapsible "Manual entry key" group. */
9965 .totp-manual { margin-bottom: var(--gap-section); }
9966 .totp-manual summary {
9967 cursor: pointer;
9968 opacity: 0.7;
9969 font-size: var(--text-note);
9970 }
9971 .totp-manual-secret {
9972 display: block;
9973 margin-top: var(--gap-peer);
9974 word-break: break-all;
9975 font-size: var(--text-note);
9976 padding: var(--gap-peer);
9977 background: var(--surface-sunken);
9978 }
9979
9980 /* Backup codes grid (rendered list of one-time codes). */
9981 .backup-codes-grid {
9982 display: grid;
9983 grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
9984 gap: var(--gap-peer);
9985 }
9986 .backup-codes-grid code {
9987 padding: var(--gap-peer);
9988 background: var(--surface-sunken);
9989 text-align: center;
9990 font-size: var(--text-note);
9991 }
9992
9993 /* Status row for the "Enabled" badge above the management forms. */
9994 .totp-status-row {
9995 display: flex;
9996 align-items: center;
9997 gap: var(--gap-peer);
9998 margin-bottom: var(--gap-section);
9999 }
10000
10001 /* Container where regenerated backup codes are swapped in via HTMX. */
10002 .totp-regen-output { margin-top: var(--gap-section); }
10003
10004 /* ===========================================
10005 DASHBOARD-USER SHELL (replaces inline styles in dashboards/dashboard-user.html)
10006 =========================================== */
10007
10008 .sandbox-banner {
10009 background: var(--action);
10010 color: var(--primary-light);
10011 padding: var(--gap-section) var(--gap-pane);
10012 text-align: center;
10013 font-family: var(--font-mono);
10014 font-size: var(--text-note);
10015 }
10016
10017 .sandbox-banner-link {
10018 color: var(--primary-light);
10019 text-decoration: underline;
10020 margin-left: var(--gap-peer);
10021 }
10022 .account-status-actions {
10023 display: flex;
10024 gap: var(--gap-section);
10025 flex-wrap: wrap;
10026 margin-top: var(--gap-section);
10027 }
10028
10029 .suspension-wrap {
10030 margin-bottom: var(--gap-section);
10031 }
10032
10033 .password-warning-banner {
10034 margin-bottom: var(--gap-section);
10035 padding: var(--gap-section);
10036 background: color-mix(in oklch, var(--warning) 14%, var(--surface-page));
10037 border: 1px solid var(--warning);
10038 font-size: var(--text-body);
10039 }
10040
10041 .dash-user-header-links {
10042 margin-bottom: var(--gap-peer);
10043 display: flex;
10044 gap: var(--gap-pane);
10045 }
10046
10047 .dash-user-header-link {
10048 font-size: var(--text-note);
10049 opacity: 0.7;
10050 }
10051
10052 .checklist-recovery {
10053 padding: var(--gap-section) 0;
10054 text-align: right;
10055 }
10056
10057 .checklist-recovery-link {
10058 font-size: var(--text-note);
10059 opacity: 0.7;
10060 }
10061
10062 .tab-spinner {
10063 margin-left: var(--gap-section);
10064 }
10065
10066 /* ===========================================
10067 USER PROFILE PAGE (replaces inline styles in pages/user.html)
10068 =========================================== */
10069
10070 .user-page .creator-paused-notice {
10071 margin-bottom: var(--gap-pane);
10072 padding: var(--gap-section);
10073 background: var(--surface-sunken);
10074 border: 1px solid var(--border);
10075 text-align: center;
10076 font-size: var(--text-body);
10077 }
10078
10079 .user-page .profile-action-row {
10080 margin-top: var(--gap-section);
10081 }
10082
10083 .user-page .profile-edit-btn {
10084 display: inline-block;
10085 padding: var(--gap-peer) var(--gap-section);
10086 text-decoration: none;
10087 font-size: var(--text-note);
10088 }
10089
10090 .user-page .follow-btn.is-selected {
10091 opacity: 0.7;
10092 }
10093
10094 .user-page .follower-count {
10095 margin-top: var(--gap-section);
10096 font-size: var(--text-note);
10097 opacity: 0.7;
10098 }
10099
10100 .user-page .profile-share-row {
10101 margin-top: var(--gap-section);
10102 display: flex;
10103 gap: var(--gap-section);
10104 justify-content: center;
10105 font-size: var(--text-note);
10106 }
10107
10108 .user-page .profile-share-link {
10109 opacity: 0.7;
10110 }
10111
10112 .user-page .collections-section {
10113 margin-bottom: var(--gap-page);
10114 }
10115
10116 /* ===========================================
10117 SUSPENSION BANNER (replaces inline styles in partials/suspension_banner.html)
10118 =========================================== */
10119
10120 .suspension-banner {
10121 background: color-mix(in oklch, var(--danger) 10%, var(--surface-page));
10122 border: 1px solid var(--danger);
10123 padding: var(--gap-pane);
10124 margin-bottom: var(--gap-pane);
10125 }
10126
10127 .suspension-banner-title {
10128 margin: 0 0 var(--gap-peer) 0;
10129 color: var(--danger);
10130 }
10131
10132 .suspension-banner-reason {
10133 font-size: var(--text-note);
10134 margin: 0 0 var(--gap-section) 0;
10135 }
10136
10137 .suspension-banner-denial {
10138 background: var(--surface-overlay);
10139 padding: var(--gap-section);
10140 margin-bottom: var(--gap-section);
10141 font-size: var(--text-note);
10142 }
10143
10144 .suspension-banner-denial-response {
10145 margin: var(--gap-bound) 0 0 0;
10146 }
10147
10148 .suspension-banner-denial-hint {
10149 margin: var(--gap-peer) 0 0 0;
10150 opacity: 0.7;
10151 }
10152
10153 .suspension-banner-pending {
10154 font-size: var(--text-note);
10155 opacity: 0.7;
10156 margin: 0;
10157 }
10158
10159 .suspension-banner-appeal {
10160 margin-top: var(--gap-peer);
10161 }
10162
10163 .suspension-banner-appeal-summary {
10164 cursor: pointer;
10165 font-size: var(--text-note);
10166 }
10167
10168 .suspension-banner-appeal-form {
10169 margin-top: var(--gap-section);
10170 }
10171
10172 .suspension-banner-appeal-text {
10173 width: 100%;
10174 margin-bottom: var(--gap-peer);
10175 font-size: var(--text-note);
10176 }
10177
10178 .suspension-banner-appeal-submit {
10179 font-size: var(--text-note);
10180 }
10181
10182 .suspension-banner-export {
10183 margin-top: var(--gap-section);
10184 }
10185
10186 .suspension-banner-export-link {
10187 font-size: var(--text-note);
10188 }
10189
10190 /* ===========================================
10191 REPORT MODAL (replaces inline styles in partials/report_modal.html)
10192 =========================================== */
10193
10194 .report-modal-content {
10195 background: var(--surface-page);
10196 padding: var(--gap-page);
10197 width: 90%;
10198 max-width: 480px;
10199 max-height: 90vh;
10200 overflow-y: auto;
10201 }
10202
10203 .report-modal-title {
10204 font-size: var(--text-subhead);
10205 margin: 0;
10206 }
10207
10208 .report-modal-fieldset {
10209 border: none;
10210 padding: 0;
10211 margin: 0 0 var(--gap-section) 0;
10212 }
10213
10214 .report-modal-legend {
10215 font-size: var(--text-note);
10216 font-weight: 600;
10217 margin-bottom: var(--gap-peer);
10218 }
10219
10220 .report-modal-option {
10221 display: block;
10222 margin-bottom: var(--gap-peer);
10223 cursor: pointer;
10224 }
10225
10226 .report-modal-details {
10227 margin-bottom: var(--gap-section);
10228 }
10229
10230 .report-modal-details-label {
10231 display: block;
10232 font-size: var(--text-note);
10233 margin-bottom: var(--gap-bound);
10234 }
10235
10236 .report-modal-textarea {
10237 width: 100%;
10238 font-size: var(--text-note);
10239 }
10240
10241 .report-modal-actions {
10242 display: flex;
10243 gap: var(--gap-peer);
10244 justify-content: flex-end;
10245 }
10246
10247 /* ===========================================
10248 LIBRARY COLLECTIONS TAB (partials/tabs/library_collections.html)
10249 =========================================== */
10250
10251 .badge.is-faded { opacity: 0.5; }
10252 .lib-coll-new { margin-top: var(--gap-pane); }
10253 .lib-coll-new-summary { cursor: pointer; font-weight: bold; }
10254 .lib-coll-new-form { margin-top: var(--gap-section); }
10255 .lib-coll-wishlist-heading {
10256 font-size: var(--text-lead);
10257 margin-top: var(--gap-page);
10258 margin-bottom: var(--gap-section);
10259 opacity: 0.8;
10260 }
10261 .lib-coll-wishlist-wrap {
10262 overflow-x: auto;
10263 -webkit-overflow-scrolling: touch;
10264 }
10265 .lib-coll-wishlist-table { min-width: 500px; }
10266 .lib-coll-row-actions { white-space: nowrap; }
10267 .lib-coll-row-actions .lib-coll-cart-btn { margin-right: var(--gap-bound); }
10268
10269 /* ===========================================
10270 USER ANALYTICS TAB (partials/tabs/user_analytics.html)
10271 =========================================== */
10272
10273 .analytics-export-link { margin-left: var(--gap-section); }
10274 .analytics-range-heading {
10275 font-size: var(--text-lead);
10276 margin: 0;
10277 }
10278 .analytics-section--comparison { margin-bottom: var(--gap-page); }
10279 .analytics-section--comparison h2 { margin-bottom: var(--gap-section); }
10280 .analytics-table-wrap {
10281 overflow-x: auto;
10282 -webkit-overflow-scrolling: touch;
10283 }
10284 .analytics-table { min-width: 500px; }
10285 .analytics-revenue-cell {
10286 display: flex;
10287 align-items: center;
10288 gap: var(--gap-peer);
10289 }
10290 .analytics-revenue-bar {
10291 width: var(--fill, 0%);
10292 height: 10px;
10293 min-width: 2px;
10294 max-width: 80px;
10295 background: var(--action);
10296 }
10297
10298 /* ===========================================
10299 ITEM EMBED TAB (templates/partials/tabs/item_embed.html)
10300 =========================================== */
10301 .embed-intro {
10302 opacity: 0.7;
10303 font-size: var(--text-note);
10304 margin-bottom: var(--gap-pane);
10305 }
10306 .embed-option {
10307 margin-bottom: var(--gap-pane);
10308 padding: var(--gap-section);
10309 border: 1px solid var(--border);
10310 border-radius: var(--radius-panel);
10311 }
10312 .embed-option h4 {
10313 margin-bottom: var(--gap-bound);
10314 font-size: var(--text-body);
10315 }
10316 .embed-option-desc {
10317 font-size: var(--text-note);
10318 opacity: 0.7;
10319 margin-bottom: var(--gap-peer);
10320 }
10321 .embed-preview {
10322 margin: var(--gap-section) 0;
10323 overflow: hidden;
10324 }
10325 .embed-iframe {
10326 border: none;
10327 border-radius: var(--radius-control);
10328 }
10329 .embed-layout-toggle {
10330 font-size: var(--text-note);
10331 margin-bottom: var(--gap-peer);
10332 display: flex;
10333 gap: var(--gap-section);
10334 }
10335 .embed-layout-toggle label {
10336 cursor: pointer;
10337 display: flex;
10338 align-items: center;
10339 gap: var(--gap-bound);
10340 }
10341 .embed-code-row {
10342 display: flex;
10343 align-items: center;
10344 gap: var(--gap-peer);
10345 }
10346 .embed-snippet {
10347 flex: 1;
10348 padding: var(--gap-peer) var(--gap-section);
10349 background: var(--surface-overlay);
10350 border-radius: var(--radius-control);
10351 font-size: var(--text-fine);
10352 overflow-x: auto;
10353 white-space: nowrap;
10354 font-family: var(--font-mono);
10355 border: 1px solid var(--border);
10356 }
10357 .copy-btn {
10358 padding: 6px 12px;
10359 font-size: var(--text-fine);
10360 background: var(--action);
10361 color: var(--primary-light);
10362 border: none;
10363 border-radius: var(--radius-control);
10364 cursor: pointer;
10365 white-space: nowrap;
10366 }
10367 .copy-btn:hover {
10368 opacity: 0.9;
10369 }
10370
10371 /* ===========================================
10372 ADMIN NAV (templates/partials/admin_nav.html)
10373 =========================================== */
10374 .admin-nav {
10375 display: flex;
10376 gap: var(--gap-peer);
10377 margin-bottom: var(--gap-pane);
10378 flex-wrap: wrap;
10379 font-family: var(--font-mono);
10380 font-size: var(--text-note);
10381 }
10382 .admin-nav a {
10383 padding: var(--gap-bound) var(--gap-section);
10384 text-decoration: none;
10385 }
10386
10387 /* Admin nav items and filter-bar toggles are .btn-primary / .btn-secondary,
10388 toggled by the templates to mark the current page or active filter. Colour
10389 and depth come from the button recipe; only the tighter admin metrics and
10390 the active weight are stated here. */
10391 .admin-nav a.btn-primary,
10392 .filter-bar button.btn-primary {
10393 font-weight: 700;
10394 }
10395 .admin-nav a.btn-secondary:hover,
10396 .filter-bar button.btn-secondary:hover {
10397 color: var(--content);
10398 }
10399
10400 /* ===========================================
10401 VIDEO PLAYER PAGE (templates/pages/video_player.html)
10402 =========================================== */
10403 .video-cover-img {
10404 width: 100%;
10405 display: block;
10406 background: var(--primary-dark);
10407 }
10408 .video-cover-placeholder {
10409 width: 100%;
10410 aspect-ratio: 16 / 9;
10411 background: var(--primary-dark);
10412 display: flex;
10413 align-items: center;
10414 justify-content: center;
10415 color: var(--content-muted);
10416 }
10417 .video-store-cta {
10418 margin: var(--gap-page) 0;
10419 padding: var(--gap-pane);
10420 background: var(--surface-sunken);
10421 text-align: center;
10422 }
10423 .video-cta-lead {
10424 font-size: var(--text-lead);
10425 margin-bottom: var(--gap-section);
10426 }
10427 .video-cta-price {
10428 font-size: var(--text-head);
10429 margin-bottom: var(--gap-section);
10430 }
10431 .video-cta-fineprint {
10432 font-size: var(--text-note);
10433 opacity: 0.7;
10434 margin-top: var(--gap-section);
10435 }
10436 .video-library-status {
10437 font-family: var(--font-mono);
10438 color: var(--content-muted);
10439 }
10440
10441 /* ===========================================
10442 LIBRARY LOCKED PAGE (templates/pages/library_locked.html)
10443 =========================================== */
10444 .library-locked-byline {
10445 opacity: 0.7;
10446 }
10447 .library-locked-cover {
10448 max-width: 320px;
10449 margin: var(--gap-section) 0;
10450 /* Reserve the space before the image loads (audit Run 17 Frontend). */
10451 aspect-ratio: 1 / 1;
10452 object-fit: cover;
10453 }
10454 .library-locked-purchase {
10455 margin-top: var(--gap-page);
10456 }
10457 .library-locked-lead {
10458 opacity: 0.85;
10459 }
10460 .library-locked-actions {
10461 margin-top: var(--gap-pane);
10462 display: flex;
10463 gap: var(--gap-section);
10464 flex-wrap: wrap;
10465 }
10466 .library-locked-bundles {
10467 margin-top: var(--gap-pane);
10468 }
10469 .library-locked-bundles-title {
10470 font-weight: bold;
10471 }
10472 .library-locked-bundle {
10473 margin-top: var(--gap-peer);
10474 }
10475 .library-locked-bundle-price {
10476 opacity: 0.7;
10477 }
10478
10479 /* ===========================================
10480 BLOG (replaces inline styles in project_blog.html
10481 and dashboard-blog-editor.html)
10482 =========================================== */
10483
10484 .blog-empty-state { padding: var(--gap-page) 0; }
10485 .blog-empty-hint {
10486 opacity: 0.7;
10487 font-size: var(--text-note);
10488 margin-top: var(--gap-peer);
10489 }
10490 .blog-post-title-link { font-weight: bold; }
10491 .blog-post-actions {
10492 display: flex;
10493 gap: var(--gap-peer);
10494 }
10495
10496 /* Blog editor dashboard (templates/dashboards/dashboard-blog-editor.html). */
10497 .blog-editor h1 {
10498 font-size: var(--text-title);
10499 margin-bottom: var(--gap-section);
10500 text-align: left;
10501 }
10502 .blog-editor .editor-form { max-width: 800px; }
10503 .blog-editor .editor-form .form-group { margin-bottom: var(--gap-section); }
10504 .blog-editor .editor-form textarea {
10505 font-family: var(--font-mono);
10506 font-size: var(--text-note);
10507 min-height: 400px;
10508 resize: vertical;
10509 }
10510 .blog-editor-slug-spinner { font-size: var(--text-note); }
10511 .blog-editor-media-btn {
10512 margin-top: var(--gap-peer);
10513 font-size: var(--text-note);
10514 }
10515 .blog-editor-actions {
10516 display: flex;
10517 gap: var(--gap-section);
10518 align-items: center;
10519 }
10520 .blog-editor-status { margin-top: var(--gap-peer); }
10521
10522 /* ===========================================
10523 AUDIO PLAYER (replaces inline styles in pages/audio_player.html)
10524 =========================================== */
10525
10526 .audio-store-cta {
10527 margin: var(--gap-page) 0;
10528 padding: var(--gap-pane);
10529 background: var(--surface-sunken);
10530 text-align: center;
10531 }
10532 .audio-store-cta .audio-access-msg {
10533 font-size: var(--text-lead);
10534 margin-bottom: var(--gap-section);
10535 }
10536 .audio-store-cta .audio-price {
10537 font-size: var(--text-head);
10538 margin-bottom: var(--gap-section);
10539 }
10540 .audio-store-cta .audio-fee-note {
10541 font-size: var(--text-note);
10542 opacity: 0.7;
10543 margin-top: var(--gap-section);
10544 }
10545 .audio-library-status {
10546 font-family: var(--font-mono);
10547 color: var(--content-muted);
10548 }
10549
10550 /* ===========================================
10551 SECTIONS WIZARD STEP (templates/wizards/steps/item/sections.html)
10552 Parallels .psection-row used by item_details + project-sections.js,
10553 but isolated so wizard markup can evolve independently.
10554 =========================================== */
10555
10556 .section-row {
10557 display: flex;
10558 align-items: center;
10559 gap: var(--gap-section);
10560 padding: var(--gap-group) 0;
10561 border-bottom: 1px solid var(--border);
10562 }
10563 .section-row-title { flex: 1; font-weight: bold; }
10564 .section-row-length { font-size: var(--text-fine); opacity: 0.6; }
10565 .section-row .section-delete-btn {
10566 padding: var(--gap-bound) var(--gap-group);
10567 font-size: var(--text-fine);
10568 }
10569
10570 .section-add-body { margin-top: var(--gap-section); }
10571
10572 .wizard-actions { margin-top: var(--gap-pane); }
10573
10574 /* ===========================================
10575 PROJECT SUBSCRIPTIONS TAB (templates/partials/tabs/project_subscriptions.html)
10576 =========================================== */
10577
10578 .subs-connect-info {
10579 margin-bottom: var(--gap-pane);
10580 padding: var(--gap-section);
10581 }
10582 .subs-new-tier { margin-bottom: var(--gap-page); }
10583
10584 .tier-row-desc {
10585 font-size: var(--text-note);
10586 opacity: 0.7;
10587 margin-top: var(--gap-bound);
10588 }
10589 .tier-action-btn {
10590 font-size: var(--text-note);
10591 padding: var(--gap-bound) var(--gap-group);
10592 }
10593
10594 .subs-members-bar {
10595 margin-top: var(--gap-page);
10596 display: flex;
10597 justify-content: space-between;
10598 align-items: center;
10599 }
10600
10601 /* ===========================================
10602 LIBRARY CONSUMPTION PAGES
10603 ===========================================
10604 Shared styles for the library-side consumption surfaces:
10605 text_reader.html, library_text.html (.article-page),
10606 library_downloads.html (.library-page),
10607 library_audio.html / library_video.html (.media-container + helpers).
10608 The .article-page and .library-page sections higher up in this file
10609 are activated by body classes set in the templates. */
10610
10611 /* Faded excerpt text on the public store-page text reader. */
10612 .article-page .article-body--excerpt { opacity: 0.85; }
10613
10614 /* Store-page call-to-action block (text_reader.html). */
10615 .article-page .store-cta {
10616 margin: var(--gap-page) 0;
10617 padding: var(--gap-pane);
10618 background: var(--surface-sunken);
10619 text-align: center;
10620 }
10621 .article-page .store-cta-lead {
10622 font-size: var(--text-lead);
10623 margin-bottom: var(--gap-section);
10624 }
10625 .article-page .store-cta-price {
10626 font-size: var(--text-head);
10627 margin-bottom: var(--gap-section);
10628 }
10629 .article-page .store-cta-note {
10630 font-size: var(--text-note);
10631 opacity: 0.7;
10632 margin-top: var(--gap-section);
10633 }
10634 .article-page .store-cta .library-status {
10635 font-family: var(--font-mono);
10636 color: var(--content-muted);
10637 }
10638
10639 /* Library back-link breadcrumb shared by the media library pages. */
10640 .media-container .library-back {
10641 font-size: var(--text-note);
10642 opacity: 0.7;
10643 margin: var(--gap-section) 0;
10644 }
10645 .media-container .library-back a { color: var(--content); }
10646
10647 /* Source-files download list under audio/video library pages.
10648 The .library-page version exists higher up; this block scopes the
10649 same look under .media-container for audio/video. */
10650 .media-container .library-downloads {
10651 background: var(--surface-overlay);
10652 padding: var(--gap-pane);
10653 margin-top: var(--gap-page);
10654 }
10655 .media-container .library-downloads h3 {
10656 font-size: var(--text-lead);
10657 margin-bottom: var(--gap-section);
10658 }
10659 .media-container .download-row {
10660 display: flex;
10661 align-items: center;
10662 gap: var(--gap-section);
10663 padding: var(--gap-peer) 0;
10664 border-bottom: 1px solid var(--border);
10665 }
10666 .media-container .download-row:last-child { border-bottom: none; }
10667 .media-container .download-version {
10668 min-width: 4em;
10669 font-family: var(--font-mono);
10670 font-size: var(--text-note);
10671 }
10672 .media-container .download-label { flex: 1; }
10673 .media-container .download-label--empty { flex: 1; opacity: 0.5; }
10674 .media-container .download-size {
10675 font-size: var(--text-note);
10676 opacity: 0.6;
10677 min-width: 5em;
10678 text-align: right;
10679 }
10680
10681 /* Compact download button used in both library_downloads.html and
10682 media library pages. */
10683 .btn-download-compact {
10684 padding: var(--gap-peer) var(--gap-section);
10685 font-size: var(--text-note);
10686 }
10687
10688 /* Faded "no label" placeholder on library_downloads.html download rows. */
10689 .library-page .download-label--empty { opacity: 0.5; }
10690
10691 /* Library-downloads description <details> summary + body. */
10692 .library-page .lib-summary {
10693 cursor: pointer;
10694 font-family: var(--font-heading);
10695 font-size: var(--text-subhead);
10696 }
10697 .library-page .lib-summary-body { margin-top: var(--gap-section); }
10698
10699 /* License section on library_downloads.html. */
10700 .library-page .lib-license-name { margin-bottom: var(--gap-section); }
10701 .library-page .lib-license-download {
10702 font-size: var(--text-note);
10703 color: var(--content);
10704 }
10705
10706 /* Footer below the item-footer (was inline on library_downloads.html). */
10707 .library-page .item-footer-sub { margin-top: var(--gap-peer); }
10708
10709 /* library_text.html footer: a terse in-page footer for the library's own
10710 text view, unrelated to base.html's site-footer. */
10711 .article-page .lib-text-footer {
10712 text-align: center;
10713 padding: var(--gap-page);
10714 font-size: var(--text-note);
10715 opacity: 0.6;
10716 border-top: 1px solid var(--border);
10717 }
10718 .article-page .lib-text-footer a { color: var(--content); }
10719
10720 /* Media player play/pause icon initial state. The JS in media-player.js
10721 toggles via element.style.display directly, so we must NOT use
10722 !important here, element.style would still beat a normal-priority
10723 declaration, but !important in the stylesheet would defeat that. */
10724 .media-icon-hidden { display: none; }
10725
10726 /* Video element fills its .video-display parent. .video-display already
10727 provides a black background. */
10728 .video-display video { width: 100%; }
10729
10730 /* Cover image inside .cover-art on library_audio.html, the parent
10731 .cover-art already sets aspect ratio and centering; the inline
10732 width/aspect-ratio/object-fit was redundant with .cover-art img. */
10733
10734 /* ===========================================
10735 USER DASHBOARD TABS (support / sessions / buyer contacts)
10736 =========================================== */
10737
10738 /* Support tab (templates/partials/tabs/user_support.html). */
10739 .support-tab .support-intro-meta {
10740 margin-bottom: var(--gap-pane);
10741 font-size: var(--text-note);
10742 opacity: 0.8;
10743 }
10744 .support-tab .support-form { max-width: 600px; }
10745 .support-tab .response-times {
10746 margin-top: var(--gap-page);
10747 font-size: var(--text-note);
10748 opacity: 0.7;
10749 }
10750 .support-tab .response-times ul {
10751 margin: var(--gap-bound) 0 0 var(--gap-pane);
10752 }
10753
10754 /* Sessions tab (templates/partials/tabs/user_sessions.html). */
10755 .sessions-tab .sessions-table { width: 100%; }
10756 .sessions-tab .device-cell {
10757 max-width: 300px;
10758 overflow: hidden;
10759 text-overflow: ellipsis;
10760 white-space: nowrap;
10761 }
10762
10763 /* Buyer contacts tab (templates/partials/tabs/buyer_contacts.html). */
10764 .buyer-contacts .contacts-table { min-width: 500px; }
10765
10766 /* Creators page extras (templates/pages/creators.html). */
10767 .creators-page .pricing-fineprint {
10768 font-size: var(--text-note);
10769 opacity: 0.8;
10770 margin-top: var(--gap-peer);
10771 }
10772 .creators-page .trial-callout {
10773 font-size: var(--text-note);
10774 margin-top: var(--gap-pane);
10775 }
10776
10777 /* ===========================================
10778 LIBRARY TABS
10779 Shared classes for the library-dashboard tab partials:
10780 library_wishlists, library_contacts, library_purchases,
10781 library_subscriptions. Migrated from inline styles.
10782 =========================================== */
10783
10784 /* Tables in library tabs need a minimum width so columns stay readable
10785 while the outer container scrolls horizontally on narrow viewports. */
10786
10787 /* Empty-state hint paragraph (smaller, slightly faded). */
10788 .library-tab-empty-hint {
10789 font-size: var(--text-note);
10790 opacity: 0.7;
10791 margin-bottom: var(--gap-section);
10792 }
10793
10794 /* Compact row-action button (wishlist Add to Cart / Remove, etc.). */
10795
10796 .library-filter-wrap { margin-bottom: var(--gap-section); }
10797
10798 /* Purchases table "Open" action link + context menu trigger group. */
10799 .library-row-actions {
10800 display: flex;
10801 align-items: center;
10802 gap: var(--gap-peer);
10803 }
10804 .library-row-open {
10805 font-size: var(--text-fine);
10806 padding: var(--gap-bound) var(--gap-section);
10807 text-decoration: none;
10808 white-space: nowrap;
10809 }
10810
10811 /* Section sub-heading inside purchases tab (e.g. "Subscriptions"). */
10812 .library-tab-subheading {
10813 font-size: var(--text-lead);
10814 margin-top: var(--gap-page);
10815 margin-bottom: var(--gap-section);
10816 opacity: 0.8;
10817 }
10818
10819 /* Inline-block button styled as a link target on empty-state CTAs. */
10820 .library-tab-cta {
10821 display: inline-block;
10822 text-decoration: none;
10823 }
10824
10825 /* Context menu (dropdown) on each purchase row. */
10826 .context-menu-wrapper { position: relative; }
10827 .context-menu-btn {
10828 background: none;
10829 border: none;
10830 cursor: pointer;
10831 padding: var(--gap-peer);
10832 font-size: var(--text-subhead);
10833 line-height: 1;
10834 color: var(--content-muted);
10835 border-radius: var(--radius-control);
10836 }
10837 .context-menu-btn:hover {
10838 background: var(--surface-sunken);
10839 color: var(--content);
10840 }
10841 .context-menu {
10842 display: none;
10843 position: absolute;
10844 right: 0;
10845 top: 100%;
10846 background: var(--surface-overlay);
10847 border: 1px solid var(--border);
10848 box-shadow: var(--shadow-2);
10849 min-width: 160px;
10850 z-index: var(--z-context);
10851 }
10852 .context-menu.open { display: block; }
10853 .context-menu-item {
10854 display: block;
10855 width: 100%;
10856 padding: var(--gap-section);
10857 text-align: left;
10858 background: none;
10859 border: none;
10860 font-family: var(--font-body);
10861 font-size: var(--text-note);
10862 color: var(--content);
10863 cursor: pointer;
10864 text-decoration: none;
10865 }
10866 .context-menu-item:hover { background: var(--surface-sunken); }
10867 .context-menu-item.danger { color: var(--danger); }
10868
10869 /* Inline license-key code chip shown beneath a purchase date. */
10870 .license-key-inline { margin-top: var(--gap-bound); }
10871 .key-code-small {
10872 font-size: var(--text-fine);
10873 padding: var(--gap-bound) var(--gap-peer);
10874 background: var(--surface-sunken);
10875 border-radius: var(--radius-control);
10876 cursor: pointer;
10877 user-select: all;
10878 }
10879 .key-code-small:hover { background: var(--border); }
10880
10881 /* Library row title link reads as plain text until hovered. */
10882 .library-title-link:hover { text-decoration: underline; }
10883 /* ===========================================
10884 ADMIN ENTRIES (replaces inline styles in partials/admin_user_entries.html,
10885 partials/admin_waitlist_entries.html,
10886 partials/admin_appeal_entries.html)
10887 =========================================== */
10888
10889 /* Status badges built ad hoc (Active / Trusted). */
10890 .badge-active,
10891 .badge-trusted {
10892 background: color-mix(in oklch, var(--success) 12%, var(--surface-page));
10893 color: var(--success);
10894 }
10895
10896 /* "Trust" toggle button sits next to a Trusted badge. */
10897 .admin-entries-trust-btn {
10898 margin-left: var(--gap-bound);
10899 }
10900
10901 /* Waitlist pitch cell, narrow body text. */
10902 .admin-entries-pitch {
10903 font-size: var(--text-note);
10904 max-width: 300px;
10905 }
10906 .admin-entries-pitch-empty {
10907 opacity: 0.5;
10908 font-style: italic;
10909 }
10910 .admin-entries-pitch-inviter {
10911 margin-top: var(--gap-bound);
10912 }
10913
10914 /* Appeal text columns. */
10915 .admin-entries-reason {
10916 font-size: var(--text-note);
10917 max-width: 200px;
10918 }
10919 .admin-entries-appeal {
10920 font-size: var(--text-note);
10921 max-width: 300px;
10922 }
10923
10924 /* Decide form textarea + stacked action buttons. */
10925 .admin-entries-decide-textarea {
10926 width: 200px;
10927 }
10928 .admin-entries-decide-actions {
10929 display: flex;
10930 flex-direction: column;
10931 gap: var(--gap-bound);
10932 }
10933
10934 /* Pagination row at the bottom of admin entry tables. */
10935 .admin-entries-pagination {
10936 display: flex;
10937 justify-content: center;
10938 align-items: center;
10939 gap: var(--gap-section);
10940 padding: var(--gap-section) 0;
10941 }
10942
10943 /* ===========================================
10944 ITEM TABS (item_settings, item_sales, item_overview partials)
10945 =========================================== */
10946
10947 /* Schedule form is hidden until "Schedule" button is clicked. JS toggles
10948 the `.hidden` utility class. */
10949 .item-settings-schedule-form {
10950 margin-top: var(--gap-section);
10951 }
10952
10953 .item-sales-tab-header {
10954 display: flex;
10955 justify-content: space-between;
10956 align-items: center;
10957 margin-bottom: var(--gap-section);
10958 }
10959 .item-sales-tab-header h2 { margin: 0; }
10960 .item-overview-embed {
10961 margin-top: var(--gap-pane);
10962 }
10963 .item-overview-embed > summary {
10964 cursor: pointer;
10965 font-weight: bold;
10966 font-size: var(--text-lead);
10967 }
10968
10969 /* ===========================================
10970 ITEM TEXT EDITOR (partials/item_text_editor.html)
10971 =========================================== */
10972
10973 .text-editor-insert-btn {
10974 margin-top: var(--gap-peer);
10975 font-size: var(--text-note);
10976 }
10977
10978 .text-editor-placeholder {
10979 opacity: 0.6;
10980 }
10981
10982 .text-editor-actions {
10983 margin-top: var(--gap-section);
10984 }
10985
10986 /* ===========================================
10987 PROJECT ANALYTICS TAB (partials/tabs/project_analytics.html)
10988 =========================================== */
10989
10990 .tab-docs-extra {
10991 margin-left: var(--gap-section);
10992 }
10993
10994 .analytics-tab-header {
10995 display: flex;
10996 justify-content: space-between;
10997 align-items: center;
10998 margin-bottom: var(--gap-section);
10999 }
11000 .analytics-tab-header h2 {
11001 font-size: var(--text-lead);
11002 margin: 0;
11003 }
11004
11005 .analytics-empty-block {
11006 text-align: center;
11007 padding: var(--gap-page) var(--gap-section);
11008 opacity: 0.6;
11009 }
11010
11011 /* ===========================================
11012 RECEIPT PAGE INLINE FRAGMENTS (pages/receipt.html)
11013 =========================================== */
11014
11015 .receipt-page .receipt-brand {
11016 opacity: 0.6;
11017 font-size: var(--text-note);
11018 }
11019 .receipt-page .receipt-meta {
11020 text-align: right;
11021 }
11022 .receipt-page .receipt-meta-line {
11023 font-family: var(--font-mono);
11024 font-size: var(--text-note);
11025 opacity: 0.6;
11026 }
11027 .receipt-page .receipt-meta-sub {
11028 font-family: var(--font-mono);
11029 font-size: var(--text-fine);
11030 opacity: 0.4;
11031 margin-top: var(--gap-bound);
11032 }
11033 .receipt-page .print-btn {
11034 margin-top: var(--gap-section);
11035 font-size: var(--text-note);
11036 }
11037
11038 /* ===========================================
11039 IMPORT PAGE INLINE FRAGMENTS (dashboards/dashboard-import.html)
11040 =========================================== */
11041
11042 .import-page .import-preview-label,
11043 .import-page .import-mapping-label,
11044 .import-page .import-progress-label {
11045 font-family: var(--font-mono);
11046 font-size: var(--text-note);
11047 margin-bottom: var(--gap-peer);
11048 }
11049 .import-page .import-mapping-label {
11050 margin-bottom: var(--gap-section);
11051 }
11052 .import-page .import-progress-label {
11053 font-size: var(--text-note);
11054 }
11055 .import-page .import-start-btn {
11056 margin-top: var(--gap-section);
11057 }
11058 .import-page .import-history-heading {
11059 font-size: var(--text-subhead);
11060 margin-bottom: var(--gap-section);
11061 }
11062
11063 /* ===========================================
11064 SLUG STATUS PARTIAL (partials/slug_status.html)
11065 =========================================== */
11066
11067 .slug-status {
11068 font-size: var(--text-note);
11069 }
11070 .slug-status.available { color: var(--success); }
11071 .slug-status.taken { color: var(--danger); }
11072
11073 .slug-suggestions {
11074 font-size: var(--text-note);
11075 display: block;
11076 margin-top: var(--gap-bound);
11077 }
11078 .slug-suggestion-link {
11079 margin-right: var(--gap-peer);
11080 }
11081
11082 /* ===========================================
11083 STRIPE DISCLAIMER PAGE (pages/stripe_disclaimer.html)
11084 =========================================== */
11085
11086 .stripe-disclaimer-page .container {
11087 max-width: 700px;
11088 margin: var(--gap-pane) auto;
11089 padding: 0 var(--gap-page);
11090 }
11091 .stripe-disclaimer-page .stripe-disclaimer-title {
11092 text-align: center;
11093 }
11094 .stripe-disclaimer-page .tagline {
11095 font-size: var(--text-note);
11096 opacity: 0.7;
11097 margin-bottom: var(--gap-page);
11098 text-align: center;
11099 }
11100 .stripe-disclaimer-page h2 {
11101 font-size: var(--text-head);
11102 margin-bottom: var(--gap-pane);
11103 }
11104 .stripe-disclaimer-page .stripe-disclaimer-lead {
11105 margin-bottom: var(--gap-pane);
11106 opacity: 0.8;
11107 }
11108 .stripe-disclaimer-page .section { margin-bottom: var(--gap-page); }
11109 .stripe-disclaimer-page .section h3 {
11110 font-size: var(--text-lead);
11111 margin-bottom: var(--gap-section);
11112 font-family: var(--font-mono);
11113 }
11114 .stripe-disclaimer-page .section ul {
11115 margin: 0;
11116 padding-left: var(--gap-section);
11117 }
11118 .stripe-disclaimer-page .section li {
11119 margin-bottom: var(--gap-peer);
11120 line-height: 1.5;
11121 }
11122 .stripe-disclaimer-page .stripe-disclaimer-fee-lead {
11123 margin-bottom: var(--gap-section);
11124 }
11125 .stripe-disclaimer-page .fee-example {
11126 background: var(--surface-page);
11127 padding: var(--gap-section);
11128 margin: var(--gap-section) 0;
11129 font-family: var(--font-mono);
11130 font-size: var(--text-note);
11131 }
11132 .stripe-disclaimer-page .fee-row {
11133 display: flex;
11134 justify-content: space-between;
11135 padding: var(--gap-bound) 0;
11136 }
11137 .stripe-disclaimer-page .fee-row.highlight {
11138 font-weight: bold;
11139 border-top: 1px solid var(--border);
11140 margin-top: var(--gap-peer);
11141 padding-top: var(--gap-peer);
11142 }
11143 .stripe-disclaimer-page .fee-row .zero { color: var(--content); }
11144 .stripe-disclaimer-page .stripe-disclaimer-note {
11145 font-size: var(--text-note);
11146 opacity: 0.8;
11147 }
11148 .stripe-disclaimer-page .acceptance {
11149 background: var(--surface-page);
11150 padding: var(--gap-section);
11151 margin: var(--gap-pane) 0;
11152 }
11153 .stripe-disclaimer-page .checkbox-group {
11154 display: flex;
11155 align-items: flex-start;
11156 gap: var(--gap-section);
11157 }
11158 .stripe-disclaimer-page .checkbox-group input[type="checkbox"] {
11159 margin-top: var(--gap-bound);
11160 flex-shrink: 0;
11161 }
11162 .stripe-disclaimer-page .checkbox-group label {
11163 line-height: 1.5;
11164 cursor: pointer;
11165 }
11166 .stripe-disclaimer-page .button-row {
11167 display: flex;
11168 gap: var(--gap-section);
11169 margin-top: var(--gap-pane);
11170 }
11171 .stripe-disclaimer-page .button-row button { flex: 1; }
11172 .stripe-disclaimer-page button:disabled {
11173 opacity: 0.5;
11174 cursor: not-allowed;
11175 }
11176 .stripe-disclaimer-page .note {
11177 font-size: var(--text-note);
11178 opacity: 0.6;
11179 margin-top: var(--gap-section);
11180 text-align: center;
11181 }
11182
11183 /* ===========================================
11184 LONG-TAIL CLEANUP, shared utilities promoted from the final inline-style sweep.
11185 Each rule replaces ≥2 inline-style instances across templates.
11186 =========================================== */
11187
11188 /* Tab-spinner loading indicator (used by 3+ dashboards). */
11189 .tab-spinner-indicator { margin-left: var(--gap-section); }
11190
11191 /* Inline-block secondary action link styled as a button. */
11192 .btn-link {
11193 display: inline-block;
11194 padding: var(--gap-peer) var(--gap-section);
11195 text-decoration: none;
11196 }
11197 .btn-link--sm { padding: var(--gap-peer) var(--gap-section); }
11198
11199 /* Username-availability spinner (wizards + standalone account form). */
11200 .username-spinner { font-size: var(--text-note); }
11201
11202 /* Centered foot-link beneath wizard cards. */
11203 .foot-link--centered { margin-top: var(--gap-section); text-align: center; }
11204
11205 /* Health-page status dots. */
11206 .health-indicator-dot { width: 8px; height: 8px; margin-right: var(--gap-peer); }
11207 .health-error-text { color: var(--danger); }
11208
11209 /* Inline error fragments (login_error, error_fragment) toggle this on. */
11210 .error-message.is-active { display: block; }
11211
11212 /* Cart count badge in nav. */
11213 .cart-badge-count { font-size: var(--text-fine); }
11214
11215 /* Empty-row inside a data table. */
11216 .table-empty-row { text-align: center; opacity: 0.6; }
11217
11218 /* Discover grid: card wrapper + body anchor. */
11219 .discover-grid-card { position: relative; }
11220 .discover-card-link {
11221 text-decoration: none;
11222 color: inherit;
11223 display: flex;
11224 flex-direction: column;
11225 flex: 1;
11226 }
11227 .discover-filter-checkbox {
11228 display: flex;
11229 align-items: center;
11230 gap: var(--gap-peer);
11231 cursor: pointer;
11232 font-size: var(--text-note);
11233 }
11234
11235 /* Tag suggestion list. */
11236 .tag-suggest-label {
11237 font-size: var(--text-fine);
11238 opacity: 0.7;
11239 margin-bottom: var(--gap-peer);
11240 overflow-wrap: anywhere;
11241 }
11242 .tag-suggest-pill {
11243 background: var(--surface-sunken);
11244 border: 1px solid var(--border);
11245 padding: var(--gap-bound) var(--gap-peer);
11246 font-size: var(--text-fine);
11247 cursor: pointer;
11248 margin: var(--gap-bound);
11249 }
11250
11251 /* Dashboard-export header layout + Export-all CTA. */
11252 .dashboard-export-header {
11253 display: flex;
11254 justify-content: space-between;
11255 align-items: flex-start;
11256 }
11257 .export-all-btn {
11258 white-space: nowrap;
11259 margin-top: var(--gap-peer);
11260 }
11261
11262 /* Tiny inline action button (admin-metrics, dashboard-item share). */
11263 .dashboard-item-share-btn {
11264 font-size: var(--text-fine);
11265 padding: var(--gap-bound) var(--gap-peer);
11266 opacity: 0.6;
11267 }
11268
11269 /* Monospaced page sub-headers. */
11270 .section-mono-meta {
11271 font-family: var(--font-mono);
11272 font-size: var(--text-note);
11273 opacity: 0.6;
11274 margin-bottom: var(--gap-pane);
11275 }
11276 /* Email-result single-card page. */
11277 .email-result-wrap {
11278 max-width: 600px;
11279 margin: 50px auto;
11280 padding: 20px;
11281 }
11282
11283 /* Admin lottery form (admin-waitlist). */
11284 .lottery-form {
11285 display: flex;
11286 gap: var(--gap-section);
11287 align-items: flex-end;
11288 }
11289
11290 /* Login screen: "or" divider + passkey button + error slot. */
11291 .login-divider {
11292 text-align: center;
11293 margin: var(--gap-pane) 0 var(--gap-section);
11294 opacity: 0.5;
11295 font-size: var(--text-note);
11296 }
11297 .login-passkey-btn { width: 100%; }
11298 .login-passkey-error { margin-top: var(--gap-peer); }
11299
11300 /* Upload-progress sibling text (item_audio_upload). */
11301 .upload-speed-text {
11302 font-size: var(--text-fine);
11303 opacity: 0.6;
11304 margin-top: var(--gap-bound);
11305 }
11306
11307 /* Faded "Following" follow-button state. */
11308 .follow-btn.is-selected { opacity: 0.7; }
11309
11310 /* Project blog footer. */
11311 .project-blog-footer {
11312 margin-top: var(--gap-page);
11313 padding-top: var(--gap-page);
11314 border-top: 1px solid var(--border);
11315 opacity: 0.6;
11316 }
11317 .project-blog-footer a { color: var(--content); }
11318
11319 /* Compact alert / paragraph margin variants used 2+ times. */
11320
11321 /* Paywall trust line. */
11322 .paywall-trust {
11323 font-size: var(--text-note);
11324 opacity: 0.7;
11325 margin-top: var(--gap-section);
11326 line-height: 1.4;
11327 }
11328
11329 /* Confirm-delete: button background already covered by .danger class.
11330 The wizard step uses `.primary` and inlines the danger background; we just
11331 change the class instead. No CSS rule needed here. */
11332
11333 /* Paywall-style centered narrow form-group used inside project_paywall. */
11334 .form-group--centered-narrow {
11335 max-width: 240px;
11336 margin: var(--gap-section) auto;
11337 }
11338
11339 /* Inline-form margin-left for adjacent pills (collection.html private badge). */
11340 .badge--inline-l { margin-left: var(--gap-peer); }
11341
11342 /* ===== Team page ===== */
11343
11344 .team-page { max-width: 56rem; margin: 0 auto; }
11345 .team-intro {
11346 font-size: var(--text-body);
11347 line-height: 1.55;
11348 margin: 0 0 var(--gap-page);
11349 opacity: 0.85;
11350 }
11351 .team-section { margin-top: var(--gap-page); }
11352 .team-section + .team-section { margin-top: var(--gap-page); }
11353 .team-card-experience {
11354 margin: var(--gap-section) 0 var(--gap-peer);
11355 line-height: 1.5;
11356 }
11357 .team-card-links {
11358 margin: var(--gap-peer) 0;
11359 font-size: var(--text-body);
11360 }
11361 .team-card-link { font-weight: 500; }
11362 .team-card-bio {
11363 margin-top: var(--gap-section);
11364 border-top: 1px solid var(--border);
11365 padding-top: var(--gap-section);
11366 }
11367 .team-card-bio > summary {
11368 cursor: pointer;
11369 font-weight: 500;
11370 font-size: var(--text-body);
11371 list-style: none;
11372 }
11373 .team-card-bio > summary::-webkit-details-marker { display: none; }
11374 .team-card-bio > summary::before {
11375 content: "+ ";
11376 display: inline-block;
11377 width: 1.2em;
11378 opacity: 0.6;
11379 }
11380 .team-card-bio[open] > summary::before { content: "− "; }
11381 .team-card-bio > p {
11382 margin-top: var(--gap-section);
11383 line-height: 1.6;
11384 }
11385
11386 /* ── SyncKit billing panel ──
11387 Per-app accordion shown inside the SyncKit dashboard tab (both user and
11388 project levels). The .synckit-billing root carries pricing constants as
11389 data-attrs that static/synckit-billing.js reads to mirror monthly_price_cents.
11390 */
11391 .synckit-billing {
11392 background: var(--surface-raised);
11393 border: 1px solid var(--border);
11394 border-radius: var(--radius-control);
11395 padding: var(--gap-peer) var(--gap-section);
11396 min-width: 220px;
11397 }
11398 .synckit-billing-summary {
11399 cursor: pointer;
11400 display: flex;
11401 align-items: center;
11402 gap: var(--gap-section);
11403 font-size: var(--text-note);
11404 }
11405 .synckit-billing-toggle { margin-left: auto; color: var(--content-muted); }
11406 .synckit-billing-status {
11407 display: inline-block;
11408 padding: 2px var(--gap-peer);
11409 border-radius: var(--radius-fine);
11410 font-size: var(--text-fine);
11411 font-weight: 600;
11412 }
11413 .synckit-billing-status--draft { background: var(--surface-sunken); }
11414 .synckit-billing-status--active { background: color-mix(in oklch, var(--success) 12%, var(--surface-page)); color: var(--success); }
11415 .synckit-billing-status--past-due { background: color-mix(in oklch, var(--warning) 14%, var(--surface-page)); color: var(--warning); }
11416 .synckit-billing-status--canceled { background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); color: var(--danger); }
11417 .synckit-billing-price { font-weight: 600; }
11418 .synckit-billing-body {
11419 padding-top: var(--gap-section);
11420 display: flex;
11421 flex-direction: column;
11422 gap: var(--gap-section);
11423 }
11424 .synckit-billing-section { display: flex; flex-direction: column; gap: var(--gap-peer); }
11425 .synckit-billing-subheading { font-size: var(--text-note); margin: 0; font-weight: 600; }
11426 .synckit-knob-row {
11427 display: flex;
11428 align-items: center;
11429 gap: var(--gap-peer);
11430 flex-wrap: wrap;
11431 }
11432 .synckit-knob-row label { min-width: 110px; }
11433 .synckit-knob-row input[type="range"] { flex: 1 1 160px; min-width: 120px; }
11434 .synckit-radio { display: inline-flex; align-items: center; gap: var(--gap-bound); min-width: 0; }
11435 .synckit-billing-summary-line {
11436 display: flex;
11437 align-items: baseline;
11438 gap: var(--gap-peer);
11439 padding-top: var(--gap-peer);
11440 border-top: 1px solid var(--border);
11441 }
11442 .synckit-price-preview { font-size: var(--text-lead); font-weight: 600; }
11443 .synckit-billing-actions { display: flex; gap: var(--gap-peer); }
11444 .synckit-billing-manage { flex-direction: row; gap: var(--gap-peer); }
11445 .synckit-billing-status-msg { font-size: var(--text-note); min-height: 1.25em; }
11446 .synckit-billing-status-msg--info { color: var(--content-muted); }
11447 .synckit-billing-status-msg--error { color: var(--danger); }
11448 .synckit-billing-period { margin: 0; }
11449 .synckit-gauge { display: flex; flex-direction: column; gap: 2px; }
11450 .synckit-gauge-label {
11451 display: flex;
11452 justify-content: space-between;
11453 font-size: var(--text-fine);
11454 }
11455 .synckit-gauge-value { color: var(--content-muted); }
11456
11457 /* ============================================================================
11458 Carousel: composable click-through widget (partials/carousel.html).
11459 One frame visible at a time; viewer-driven (prev/next, dots, arrow keys).
11460 No autoplay, no slide animation; frames swap instantly.
11461 ========================================================================= */
11462
11463 /* Public gallery carousel sections (item + project pages). */
11464 .item-gallery,
11465 .project-gallery {
11466 margin: var(--gap-pane) 0;
11467 }
11468
11469 /* Wizard gallery manager: thumbnail tiles with reorder + delete controls. */
11470 .gallery-manager-list {
11471 display: flex;
11472 flex-wrap: wrap;
11473 gap: var(--gap-peer);
11474 margin-bottom: var(--gap-peer);
11475 }
11476
11477 .gallery-tile {
11478 position: relative;
11479 width: 96px;
11480 height: 96px;
11481 border: 1px solid var(--border);
11482 border-radius: var(--radius-control);
11483 overflow: hidden;
11484 }
11485
11486 .gallery-tile-img {
11487 width: 100%;
11488 height: 100%;
11489 object-fit: cover;
11490 display: block;
11491 }
11492
11493 .gallery-tile-controls {
11494 position: absolute;
11495 inset: auto 0 0 0;
11496 display: flex;
11497 justify-content: space-between;
11498 background: var(--overlay);
11499 }
11500
11501 .gallery-tile-btn {
11502 flex: 1;
11503 border: none;
11504 background: transparent;
11505 color: var(--primary-light);
11506 cursor: pointer;
11507 font-size: var(--text-note);
11508 line-height: 1.4;
11509 padding: 2px 0;
11510 }
11511
11512 .gallery-tile-btn:disabled {
11513 opacity: 0.35;
11514 cursor: default;
11515 }
11516
11517 .gallery-tile-delete {
11518 color: var(--danger);
11519 font-weight: bold;
11520 }
11521
11522 /* <mnw-carousel> is a custom element (default display:inline); make it lay out
11523 like the block container it replaced. */
11524 mnw-carousel {
11525 display: block;
11526 }
11527
11528 .carousel {
11529 position: relative;
11530 margin: 0 auto;
11531 }
11532
11533 .carousel:focus-visible {
11534 outline: 2px solid var(--focus-ring);
11535 outline-offset: 3px;
11536 }
11537
11538 .carousel-viewport {
11539 display: grid; /* all frames share one cell; only the active one renders */
11540 }
11541
11542 .carousel-frame {
11543 grid-area: 1 / 1;
11544 margin: 0;
11545 display: none;
11546 }
11547
11548 .carousel-frame.is-active {
11549 display: block;
11550 }
11551
11552 /* JS-off fallback: no [data-ready] means stack every frame so all
11553 screenshots are still visible rather than stranded behind dead controls. */
11554 .carousel:not([data-ready]) .carousel-frame {
11555 display: block;
11556 }
11557
11558 .carousel-img {
11559 display: block;
11560 width: 100%;
11561 height: auto;
11562 border: 1px solid var(--border);
11563 border-radius: var(--radius-control);
11564 background: var(--surface-raised);
11565 }
11566
11567 .carousel-caption {
11568 margin-top: var(--gap-group);
11569 font-family: var(--font-mono);
11570 font-size: var(--text-note);
11571 color: var(--content-muted);
11572 text-align: center;
11573 }
11574
11575 /* Prev / next: brutalist offset buttons, vertically centered over the frame. */
11576 .carousel-nav {
11577 position: absolute;
11578 top: 50%;
11579 transform: translateY(-50%);
11580 display: flex;
11581 align-items: center;
11582 justify-content: center;
11583 width: 2.4rem;
11584 height: 2.4rem;
11585 /* Bias the flex-centered glyph up ~2px: the chevron characters sit low in
11586 their line box, so geometric centering reads as a few pixels too low. */
11587 padding: 0 0 4px;
11588 font-size: var(--text-head);
11589 line-height: 1;
11590 color: var(--content);
11591 background: var(--surface-raised);
11592 border: 1px solid var(--content);
11593 border-radius: var(--radius-control);
11594 box-shadow: var(--shadow-raised);
11595 cursor: pointer;
11596 }
11597
11598 .carousel-nav:hover {
11599 background: var(--hover-surface);
11600 }
11601
11602 /* Keeps the translateY that centres it vertically; the press is the bevel,
11603 so nothing is added to the transform. */
11604 .carousel-nav:active {
11605 box-shadow: var(--shadow-inset);
11606 }
11607
11608 .carousel-nav:focus-visible {
11609 outline: 2px solid var(--focus-ring);
11610 outline-offset: 2px;
11611 }
11612
11613 .carousel-prev { left: 0.6rem; }
11614 .carousel-next { right: 0.6rem; }
11615
11616 /* Hide the prev/next chrome entirely when JS hasn't taken over. */
11617 .carousel:not([data-ready]) .carousel-nav,
11618 .carousel:not([data-ready]) .carousel-dots {
11619 display: none;
11620 }
11621
11622 .carousel-dots {
11623 display: flex;
11624 justify-content: center;
11625 gap: var(--gap-peer);
11626 margin-top: var(--gap-section);
11627 }
11628
11629 .carousel-dot {
11630 width: 0.7rem;
11631 height: 0.7rem;
11632 padding: 0;
11633 border: 1px solid var(--content);
11634 border-radius: var(--radius-round);
11635 background: transparent;
11636 cursor: pointer;
11637 }
11638
11639 .carousel-dot.is-active {
11640 background: var(--action);
11641 border-color: var(--action);
11642 }
11643
11644 .carousel-dot:focus-visible {
11645 outline: 2px solid var(--focus-ring);
11646 outline-offset: 2px;
11647 }
11648
11649 @media (max-width: 599px) {
11650 .carousel-nav { width: 2rem; height: 2rem; font-size: var(--text-subhead); }
11651 }
11652
11653 /* ---------------------------------------------------------------------------
11654 Custom page editor (dashboard/custom_page_editor.html)
11655 Moved out of an inline <style> block per the extract-shared-CSS rule.
11656 --------------------------------------------------------------------------- */
11657 .cp-editor { max-width: 1200px; margin: 0 auto; padding: var(--gap-section); }
11658 .cp-editor-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--gap-section); flex-wrap: wrap; }
11659 .cp-editor-actions { display: flex; gap: var(--gap-section); }
11660 .cp-intro { color: var(--content-muted); margin: var(--gap-bound) 0 var(--gap-section); }
11661 /* Form beside preview. A code textarea below about 380px is not editable, so
11662 that number is the pane's, not the viewport's, and the pair stacks itself. */
11663 .cp-editor-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: var(--gap-section); align-items: start; }
11664 .cp-editor-form { display: flex; flex-direction: column; gap: var(--gap-peer); }
11665 .cp-code { width: 100%; min-height: 220px; font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--text-note); line-height: 1.4; resize: vertical; }
11666 .cp-editor-buttons { display: flex; align-items: center; gap: var(--gap-section); margin-top: var(--gap-peer); }
11667 .cp-preview-pane { border: 1px solid var(--border); border-radius: var(--radius-control); overflow: hidden; }
11668 .cp-preview-bar { font-size: var(--text-fine); padding: var(--gap-bound) var(--gap-group); background: var(--surface-sunken); border-bottom: 1px solid var(--border); }
11669 .cp-preview { width: 100%; height: 520px; border: 0; background: var(--primary-light); display: block; }
11670 .cp-blocked { margin-top: var(--gap-section); }
11671 .cp-blocked-title { font-weight: 600; margin-bottom: var(--gap-peer); }
11672 .cp-blocked-empty { color: var(--content-muted); }
11673 .cp-blocked-list { list-style: none; padding: 0; margin: 0; }
11674 .cp-blocked-list li { padding: var(--gap-peer) 0; border-bottom: 1px solid var(--border); font-size: var(--text-note); display: flex; gap: var(--gap-peer); flex-wrap: wrap; align-items: baseline; }
11675 .cp-blocked-kind { font-weight: 600; }
11676 .cp-blocked-loc { color: var(--content-muted); }
11677 .cp-blocked-val { background: var(--surface-sunken); padding: 0 var(--gap-bound); }
11678 .cp-status.cp-ok { color: var(--success); }
11679 .cp-status.cp-err { color: var(--warning); }
11680 .cp-reset { margin-top: var(--gap-pane); }
11681 .cp-locked-banner { margin: var(--gap-peer) 0 var(--gap-section); padding: var(--gap-section); border: 1px solid var(--warning); border-radius: var(--radius-control); background: color-mix(in oklch, var(--warning) 12%, var(--surface-page)); }
11682 /* Layout-shift fix (audit Run 17 Frontend): reserve space for a cover image
11683 whose class didn't already set an aspect-ratio / fixed size. */
11684 .proj-image-preview img { width: 120px; height: 120px; object-fit: cover; }
11685
11686 /* ---- Discover search: how well a result matched -------------------------
11687 Two surfaces, doing two different jobs (db/discover.rs, migration 179).
11688
11689 .results-tier-break is the single heading, and it sits exactly where the
11690 claim it makes turns true: above it every row holds at least one word the
11691 searcher typed, below it none of them do. .row-match-note is the per-row
11692 half, "Matched 3 of 5 words", because coverage varies row to row and one
11693 page can hold a 4-of-5 beside a 1-of-5.
11694
11695 Both are deliberately quiet. They explain a result the reader is already
11696 looking at; competing with the title would make search feel like it is
11697 apologising. Muted colour and small type, no accent, no border of their own
11698 beyond the rule that separates the tiers. */
11699 .results-tier-break {
11700 display: flex;
11701 align-items: baseline;
11702 gap: var(--gap-peer);
11703 flex-wrap: wrap;
11704 padding: var(--gap-section) var(--gap-section) var(--gap-peer);
11705 border-top: 1px solid var(--border);
11706 background: var(--surface-page);
11707 }
11708
11709 /* .results-table sets border-top: none and lets .table-row draw the rules, so
11710 the break supplies its own top border. Mid-list that sits against the
11711 preceding row's border-bottom and reads as one heavier 2px rule, which is
11712 wanted here: it is a section change, not another row. At the top of an
11713 all-fuzzy page there is no preceding row, and the same border supplies the
11714 table's missing top edge. */
11715 .results-tier-label {
11716 font-family: var(--font-heading);
11717 font-weight: bold;
11718 font-size: var(--text-fine);
11719 text-transform: uppercase;
11720 letter-spacing: 0.03em;
11721 color: var(--content);
11722 }
11723
11724 .results-tier-hint {
11725 font-size: var(--text-fine);
11726 color: var(--content-muted);
11727 }
11728
11729 /* The grid is `repeat(auto-fill, minmax(280px, 1fr))`, so without this the
11730 heading takes one card's slot and the tiers interleave visually. */
11731 .results-container.results-grid .results-tier-break {
11732 grid-column: 1 / -1;
11733 padding-left: 0;
11734 padding-right: 0;
11735 background: none;
11736 }
11737
11738 /* In list view this rides inside .row-info, under the creator line, not as
11739 another child of .table-row-link: that is a fixed five-column grid and a
11740 sixth child would invent a column and shift every row that carried one. */
11741 .row-match-note {
11742 font-family: var(--font-mono);
11743 font-size: var(--text-fine);
11744 color: var(--content-muted);
11745 white-space: nowrap;
11746 overflow: hidden;
11747 text-overflow: ellipsis;
11748 }
11749
11750 /* Grid cards have room to breathe; give the note its own line under the meta. */
11751 .grid-card .row-match-note {
11752 display: block;
11753 margin-top: var(--gap-bound);
11754 }
11755
11756 /* The narrow list layouts drop columns rather than wrap, and .row-info is the
11757 column that survives. Keeping the note there would crowd the title on a
11758 phone, where the tier heading above still carries the important half. */
11759 @media (max-width: 599px) {
11760 .table-row .row-match-note { display: none; }
11761 }
11762