altitude-design 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -6
- package/dist/adapters/codemirror.cjs +88 -0
- package/dist/adapters/codemirror.cjs.map +7 -0
- package/dist/adapters/codemirror.js +67 -0
- package/dist/adapters/codemirror.js.map +7 -0
- package/dist/adapters/react-flow.css +102 -0
- package/dist/adapters/tiptap.css +71 -0
- package/dist/altitude.css +3121 -244
- package/dist/altitude.iife.js +33 -5
- package/dist/altitude.no-fonts.css +3121 -244
- package/dist/chart-themes.cjs +195 -0
- package/dist/chart-themes.cjs.map +7 -0
- package/dist/chart-themes.js +165 -0
- package/dist/chart-themes.js.map +7 -0
- package/dist/cli/altitude.mjs +134 -0
- package/dist/guidance/README.md +32 -0
- package/dist/guidance/agent-setup.md +22 -0
- package/dist/guidance/app-skeleton.md +17 -0
- package/dist/guidance/evidence.json +95 -0
- package/dist/guidance/index.json +493 -0
- package/dist/guidance/llms.txt +20 -0
- package/dist/guidance/manifest.json +92 -0
- package/dist/guidance/pattern.schema.json +181 -0
- package/dist/guidance/patterns/agent-action.json +77 -0
- package/dist/guidance/patterns/agent-action.md +39 -0
- package/dist/guidance/patterns/assistant-conversation.json +77 -0
- package/dist/guidance/patterns/assistant-conversation.md +50 -0
- package/dist/guidance/patterns/async-collection.json +71 -0
- package/dist/guidance/patterns/async-collection.md +29 -0
- package/dist/guidance/patterns/audit-log.json +75 -0
- package/dist/guidance/patterns/audit-log.md +27 -0
- package/dist/guidance/patterns/auth.json +86 -0
- package/dist/guidance/patterns/auth.md +29 -0
- package/dist/guidance/patterns/billing.json +80 -0
- package/dist/guidance/patterns/billing.md +29 -0
- package/dist/guidance/patterns/consequential-action.json +76 -0
- package/dist/guidance/patterns/consequential-action.md +27 -0
- package/dist/guidance/patterns/developer-settings.json +79 -0
- package/dist/guidance/patterns/developer-settings.md +29 -0
- package/dist/guidance/patterns/edit-record.json +74 -0
- package/dist/guidance/patterns/edit-record.md +51 -0
- package/dist/guidance/patterns/import.json +77 -0
- package/dist/guidance/patterns/import.md +31 -0
- package/dist/guidance/patterns/members.json +77 -0
- package/dist/guidance/patterns/members.md +27 -0
- package/dist/guidance/patterns/onboarding.json +78 -0
- package/dist/guidance/patterns/onboarding.md +31 -0
- package/dist/guidance/patterns/review-submit.json +72 -0
- package/dist/guidance/patterns/review-submit.md +27 -0
- package/dist/guidance/patterns/screen-layout.json +79 -0
- package/dist/guidance/patterns/screen-layout.md +40 -0
- package/dist/guidance/patterns/settings.json +78 -0
- package/dist/guidance/patterns/settings.md +32 -0
- package/dist/guidance/patterns/sync.json +77 -0
- package/dist/guidance/patterns/sync.md +31 -0
- package/dist/guidance/principles.md +23 -0
- package/dist/guidance/references/build/checks/agent-action-pattern.mjs +57 -0
- package/dist/guidance/references/build/checks/agent-setup.mjs +78 -0
- package/dist/guidance/references/build/checks/assistant-conversation-pattern.mjs +49 -0
- package/dist/guidance/references/build/checks/audit-log-pattern.mjs +63 -0
- package/dist/guidance/references/build/checks/auth-pattern.mjs +67 -0
- package/dist/guidance/references/build/checks/billing-pattern.mjs +63 -0
- package/dist/guidance/references/build/checks/developer-settings-pattern.mjs +76 -0
- package/dist/guidance/references/build/checks/guidance.mjs +71 -0
- package/dist/guidance/references/build/checks/import-pattern.mjs +69 -0
- package/dist/guidance/references/build/checks/members-pattern.mjs +71 -0
- package/dist/guidance/references/build/checks/onboarding-pattern.mjs +54 -0
- package/dist/guidance/references/build/checks/pattern-types.mjs +13 -0
- package/dist/guidance/references/build/checks/pattern-workflows.mjs +226 -0
- package/dist/guidance/references/build/checks/primary-scope.mjs +49 -0
- package/dist/guidance/references/build/checks/render-tells.mjs +72 -0
- package/dist/guidance/references/build/checks/screen-layout-pattern.mjs +111 -0
- package/dist/guidance/references/build/checks/semantics.mjs +72 -0
- package/dist/guidance/references/build/checks/settings-pattern.mjs +73 -0
- package/dist/guidance/references/build/checks/sync-pattern.mjs +62 -0
- package/dist/guidance/references/build/checks/tells-lint.mjs +164 -0
- package/dist/guidance/references/build/fixtures/pattern-workflows.tsx +83 -0
- package/dist/guidance/references/build/fixtures/patterns/agent-action.jsx +60 -0
- package/dist/guidance/references/build/fixtures/patterns/app-skeleton.jsx +94 -0
- package/dist/guidance/references/build/fixtures/patterns/assistant-conversation.jsx +79 -0
- package/dist/guidance/references/build/fixtures/patterns/audit-log.jsx +81 -0
- package/dist/guidance/references/build/fixtures/patterns/auth.jsx +150 -0
- package/dist/guidance/references/build/fixtures/patterns/billing.jsx +96 -0
- package/dist/guidance/references/build/fixtures/patterns/developer-settings.jsx +95 -0
- package/dist/guidance/references/build/fixtures/patterns/import.jsx +102 -0
- package/dist/guidance/references/build/fixtures/patterns/members.jsx +106 -0
- package/dist/guidance/references/build/fixtures/patterns/onboarding.jsx +70 -0
- package/dist/guidance/references/build/fixtures/patterns/screen-layout-decision.jsx +74 -0
- package/dist/guidance/references/build/fixtures/patterns/screen-layout-editor.jsx +56 -0
- package/dist/guidance/references/build/fixtures/patterns/screen-layout-report.jsx +42 -0
- package/dist/guidance/references/build/fixtures/patterns/settings.jsx +73 -0
- package/dist/guidance/references/build/fixtures/patterns/sync.jsx +78 -0
- package/dist/guidance/references/project/components/src/pattern-async-collection.tsx +57 -0
- package/dist/guidance/references/project/components/src/pattern-consequential-action.tsx +77 -0
- package/dist/guidance/references/project/components/src/pattern-edit-record.tsx +127 -0
- package/dist/guidance/references/project/components/src/pattern-review-submit.tsx +47 -0
- package/dist/guidance/references/project/components/src/settings.tsx +79 -0
- package/dist/guidance/references/scripts/build-framework-fixture.mjs +20 -0
- package/dist/guidance/rules.json +653 -0
- package/dist/guidance/visual/Elevation.md +194 -0
- package/dist/guidance/visual/Fundamentals.md +83 -0
- package/dist/guidance/visual/Research.md +1620 -0
- package/dist/index.cjs +18792 -1925
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +18792 -1925
- package/dist/index.js.map +4 -4
- package/dist/tokens.css +84 -84
- package/dist/tokens.json +301 -128
- package/dist/types/adapter-codemirror.d.ts +13 -0
- package/dist/types/agent-fleet.d.ts +166 -0
- package/dist/types/ai-label.d.ts +30 -0
- package/dist/types/app-shell.d.ts +83 -0
- package/dist/types/area-chart.d.ts +80 -0
- package/dist/types/artifact.d.ts +65 -0
- package/dist/types/bar-chart.d.ts +83 -0
- package/dist/types/bar-list.d.ts +35 -0
- package/dist/types/breadcrumb.d.ts +62 -0
- package/dist/types/chart-brush.d.ts +35 -0
- package/dist/types/chart-themes.d.ts +229 -0
- package/dist/types/chart.d.ts +570 -0
- package/dist/types/checklist.d.ts +71 -0
- package/dist/types/code-block.d.ts +32 -0
- package/dist/types/collections.d.ts +111 -4
- package/dist/types/color-picker.d.ts +38 -0
- package/dist/types/command.d.ts +6 -3
- package/dist/types/comment-thread.d.ts +48 -0
- package/dist/types/confirmation.d.ts +60 -0
- package/dist/types/context-menu.d.ts +17 -0
- package/dist/types/controls.d.ts +100 -17
- package/dist/types/conversation.d.ts +130 -0
- package/dist/types/copy-button.d.ts +19 -0
- package/dist/types/core.d.ts +139 -10
- package/dist/types/data-grid.d.ts +76 -0
- package/dist/types/date-picker.d.ts +96 -0
- package/dist/types/diff-view.d.ts +54 -0
- package/dist/types/dot-plot.d.ts +54 -0
- package/dist/types/edit-region.d.ts +121 -0
- package/dist/types/feature-gate.d.ts +22 -0
- package/dist/types/filter-bar.d.ts +65 -0
- package/dist/types/formatted-number.d.ts +38 -0
- package/dist/types/heatmap.d.ts +64 -0
- package/dist/types/in-place.d.ts +24 -0
- package/dist/types/index.d.ts +158 -15
- package/dist/types/inline-citation.d.ts +35 -0
- package/dist/types/inline-edit.d.ts +71 -0
- package/dist/types/input-otp.d.ts +37 -0
- package/dist/types/item-group.d.ts +104 -0
- package/dist/types/line-chart.d.ts +116 -0
- package/dist/types/link.d.ts +11 -0
- package/dist/types/markdown.d.ts +88 -0
- package/dist/types/meter.d.ts +43 -0
- package/dist/types/modal-state.d.ts +62 -0
- package/dist/types/nav.d.ts +8 -1
- package/dist/types/notifications.d.ts +42 -0
- package/dist/types/number-input.d.ts +30 -0
- package/dist/types/overlays.d.ts +51 -17
- package/dist/types/page-header.d.ts +27 -0
- package/dist/types/pagination.d.ts +29 -0
- package/dist/types/palette.d.ts +64 -0
- package/dist/types/password-input.d.ts +22 -0
- package/dist/types/pattern-assistant-conversation.d.ts +55 -0
- package/dist/types/pattern-async-collection.d.ts +34 -0
- package/dist/types/pattern-consequential-action.d.ts +53 -0
- package/dist/types/pattern-edit-record.d.ts +72 -0
- package/dist/types/pattern-review-submit.d.ts +43 -0
- package/dist/types/permission-matrix.d.ts +34 -0
- package/dist/types/plan-picker.d.ts +40 -0
- package/dist/types/prompt-input.d.ts +106 -0
- package/dist/types/property-list.d.ts +67 -0
- package/dist/types/question.d.ts +64 -0
- package/dist/types/relative-time.d.ts +21 -0
- package/dist/types/removal-receipt.d.ts +1180 -0
- package/dist/types/resizable.d.ts +48 -0
- package/dist/types/roving.d.ts +44 -0
- package/dist/types/save-bar.d.ts +38 -0
- package/dist/types/saved-views.d.ts +72 -0
- package/dist/types/scatter-chart.d.ts +60 -0
- package/dist/types/schedule.d.ts +80 -0
- package/dist/types/secret-field.d.ts +33 -0
- package/dist/types/select.d.ts +44 -2
- package/dist/types/separator.d.ts +11 -0
- package/dist/types/settings.d.ts +39 -0
- package/dist/types/sheet.d.ts +1 -1
- package/dist/types/sparkline.d.ts +46 -0
- package/dist/types/split-button.d.ts +21 -0
- package/dist/types/stat.d.ts +54 -0
- package/dist/types/stepper.d.ts +6 -3
- package/dist/types/suggestions.d.ts +29 -0
- package/dist/types/suspense.d.ts +12 -1
- package/dist/types/sync-indicator.d.ts +24 -0
- package/dist/types/tag-input.d.ts +65 -0
- package/dist/types/terminal.d.ts +33 -0
- package/dist/types/thread-list.d.ts +58 -0
- package/dist/types/time-input.d.ts +33 -0
- package/dist/types/timeline.d.ts +38 -0
- package/dist/types/toggle.d.ts +46 -0
- package/dist/types/toolbar.d.ts +34 -0
- package/dist/types/tour.d.ts +23 -0
- package/dist/types/trace.d.ts +65 -0
- package/dist/types/tree.d.ts +71 -0
- package/dist/types-cjs/adapter-codemirror.d.cts +13 -0
- package/dist/types-cjs/agent-fleet.d.cts +166 -0
- package/dist/types-cjs/ai-label.d.cts +30 -0
- package/dist/types-cjs/app-shell.d.cts +83 -0
- package/dist/types-cjs/area-chart.d.cts +80 -0
- package/dist/types-cjs/artifact.d.cts +65 -0
- package/dist/types-cjs/bar-chart.d.cts +83 -0
- package/dist/types-cjs/bar-list.d.cts +35 -0
- package/dist/types-cjs/breadcrumb.d.cts +62 -0
- package/dist/types-cjs/chart-brush.d.cts +35 -0
- package/dist/types-cjs/chart-themes.d.cts +229 -0
- package/dist/types-cjs/chart.d.cts +570 -0
- package/dist/types-cjs/checklist.d.cts +71 -0
- package/dist/types-cjs/code-block.d.cts +32 -0
- package/dist/types-cjs/collections.d.cts +246 -0
- package/dist/types-cjs/color-picker.d.cts +38 -0
- package/dist/types-cjs/command.d.cts +66 -0
- package/dist/types-cjs/comment-thread.d.cts +48 -0
- package/dist/types-cjs/confirmation.d.cts +60 -0
- package/dist/types-cjs/context-menu.d.cts +17 -0
- package/dist/types-cjs/controls.d.cts +360 -0
- package/dist/types-cjs/conversation.d.cts +130 -0
- package/dist/types-cjs/copy-button.d.cts +19 -0
- package/dist/types-cjs/core.d.cts +256 -0
- package/dist/types-cjs/data-grid.d.cts +76 -0
- package/dist/types-cjs/date-picker.d.cts +96 -0
- package/dist/types-cjs/diff-view.d.cts +54 -0
- package/dist/types-cjs/dot-plot.d.cts +54 -0
- package/dist/types-cjs/edit-region.d.cts +121 -0
- package/dist/types-cjs/empty.d.cts +24 -0
- package/dist/types-cjs/feature-gate.d.cts +22 -0
- package/dist/types-cjs/filter-bar.d.cts +65 -0
- package/dist/types-cjs/formatted-number.d.cts +38 -0
- package/dist/types-cjs/heatmap.d.cts +64 -0
- package/dist/types-cjs/in-place.d.cts +24 -0
- package/dist/types-cjs/index.d.cts +154 -0
- package/dist/types-cjs/inline-citation.d.cts +35 -0
- package/dist/types-cjs/inline-edit.d.cts +71 -0
- package/dist/types-cjs/input-otp.d.cts +37 -0
- package/dist/types-cjs/item-group.d.cts +104 -0
- package/dist/types-cjs/line-chart.d.cts +116 -0
- package/dist/types-cjs/link.d.cts +11 -0
- package/dist/types-cjs/markdown.d.cts +88 -0
- package/dist/types-cjs/meter.d.cts +43 -0
- package/dist/types-cjs/modal-state.d.cts +62 -0
- package/dist/types-cjs/nav.d.cts +25 -0
- package/dist/types-cjs/notifications.d.cts +42 -0
- package/dist/types-cjs/number-input.d.cts +30 -0
- package/dist/types-cjs/overlays.d.cts +170 -0
- package/dist/types-cjs/page-header.d.cts +27 -0
- package/dist/types-cjs/pagination.d.cts +29 -0
- package/dist/types-cjs/palette.d.cts +64 -0
- package/dist/types-cjs/password-input.d.cts +22 -0
- package/dist/types-cjs/pattern-assistant-conversation.d.cts +55 -0
- package/dist/types-cjs/pattern-async-collection.d.cts +34 -0
- package/dist/types-cjs/pattern-consequential-action.d.cts +53 -0
- package/dist/types-cjs/pattern-edit-record.d.cts +72 -0
- package/dist/types-cjs/pattern-review-submit.d.cts +43 -0
- package/dist/types-cjs/permission-matrix.d.cts +34 -0
- package/dist/types-cjs/pile.d.cts +27 -0
- package/dist/types-cjs/plan-picker.d.cts +40 -0
- package/dist/types-cjs/prompt-input.d.cts +106 -0
- package/dist/types-cjs/property-list.d.cts +67 -0
- package/dist/types-cjs/question.d.cts +64 -0
- package/dist/types-cjs/relative-time.d.cts +21 -0
- package/dist/types-cjs/removal-receipt.d.cts +1180 -0
- package/dist/types-cjs/resizable.d.cts +48 -0
- package/dist/types-cjs/roving.d.cts +44 -0
- package/dist/types-cjs/save-bar.d.cts +38 -0
- package/dist/types-cjs/saved-views.d.cts +72 -0
- package/dist/types-cjs/scatter-chart.d.cts +60 -0
- package/dist/types-cjs/schedule.d.cts +80 -0
- package/dist/types-cjs/secret-field.d.cts +33 -0
- package/dist/types-cjs/select.d.cts +109 -0
- package/dist/types-cjs/separator.d.cts +11 -0
- package/dist/types-cjs/settings.d.cts +39 -0
- package/dist/types-cjs/sheet.d.cts +27 -0
- package/dist/types-cjs/sparkline.d.cts +46 -0
- package/dist/types-cjs/split-button.d.cts +21 -0
- package/dist/types-cjs/stat.d.cts +54 -0
- package/dist/types-cjs/stepper.d.cts +26 -0
- package/dist/types-cjs/suggestions.d.cts +29 -0
- package/dist/types-cjs/suspense.d.cts +85 -0
- package/dist/types-cjs/sync-indicator.d.cts +24 -0
- package/dist/types-cjs/tag-input.d.cts +65 -0
- package/dist/types-cjs/terminal.d.cts +33 -0
- package/dist/types-cjs/thread-list.d.cts +58 -0
- package/dist/types-cjs/time-input.d.cts +33 -0
- package/dist/types-cjs/timeline.d.cts +38 -0
- package/dist/types-cjs/toggle.d.cts +46 -0
- package/dist/types-cjs/toolbar.d.cts +34 -0
- package/dist/types-cjs/tour.d.cts +23 -0
- package/dist/types-cjs/trace.d.cts +65 -0
- package/dist/types-cjs/tree.d.cts +71 -0
- package/package.json +89 -9
package/dist/altitude.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400..700&family=JetBrains+Mono:wght@400;500&display=swap');
|
|
2
2
|
|
|
3
|
-
:root, [data-theme="daylight"] {
|
|
3
|
+
:root, [data-theme="daylight"], [data-theme="mesa"] {
|
|
4
4
|
--surface-trench: #e1e4ea;
|
|
5
5
|
--surface-well: #eaedf1;
|
|
6
6
|
--surface-ground: #f3f4f7;
|
|
@@ -29,6 +29,28 @@
|
|
|
29
29
|
--presence-2: #0b7285;
|
|
30
30
|
--presence-3: #7046c9;
|
|
31
31
|
--on-presence: #ffffff;
|
|
32
|
+
--data-1: #5d7bff;
|
|
33
|
+
--data-2: #e65000;
|
|
34
|
+
--data-3: #0498b9;
|
|
35
|
+
--data-4: #2a8f00;
|
|
36
|
+
--data-5: #643980;
|
|
37
|
+
--data-6: #c56f99;
|
|
38
|
+
--data-other: #31343c;
|
|
39
|
+
--data-context: #898a8e;
|
|
40
|
+
--data-seq-1: #e4ebff;
|
|
41
|
+
--data-seq-2: #c1cdef;
|
|
42
|
+
--data-seq-3: #a0b0de;
|
|
43
|
+
--data-seq-4: #8093cd;
|
|
44
|
+
--data-seq-5: #6176bb;
|
|
45
|
+
--data-seq-6: #4559a9;
|
|
46
|
+
--data-seq-7: #2b3c97;
|
|
47
|
+
--data-div-neg-3: #503000;
|
|
48
|
+
--data-div-neg-2: #966112;
|
|
49
|
+
--data-div-neg-1: #c6a178;
|
|
50
|
+
--data-div-mid: #e6e8ed;
|
|
51
|
+
--data-div-pos-1: #97a7d6;
|
|
52
|
+
--data-div-pos-2: #5569ad;
|
|
53
|
+
--data-div-pos-3: #1d2a84;
|
|
32
54
|
--scrim: #12161f52;
|
|
33
55
|
--trench: inset 0 1px 3px #0f17292e, 0 0 0 0 #0f172900, 0 0 0 0 #0f172900, 0 0 0 0 #0f172900;
|
|
34
56
|
--well: inset 0 1px 2px #0f17291a, 0 0 0 0 #0f172900, 0 0 0 0 #0f172900, 0 0 0 0 #0f172900;
|
|
@@ -41,7 +63,7 @@
|
|
|
41
63
|
--lip-top: inset 0 18px 18px -18px #0f17293d;
|
|
42
64
|
--lip-bottom: inset 0 -18px 18px -18px #0f17293d;
|
|
43
65
|
}
|
|
44
|
-
[data-theme="nightfall"] {
|
|
66
|
+
[data-theme="nightfall"], [data-theme="alpine"] {
|
|
45
67
|
--surface-trench: #08090c;
|
|
46
68
|
--surface-well: #0c0e12;
|
|
47
69
|
--surface-ground: #121419;
|
|
@@ -70,88 +92,28 @@
|
|
|
70
92
|
--presence-2: #3fc1d0;
|
|
71
93
|
--presence-3: #b39dff;
|
|
72
94
|
--on-presence: #0b0d12;
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
--
|
|
76
|
-
--
|
|
77
|
-
--
|
|
78
|
-
--
|
|
79
|
-
--
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
83
|
-
--
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
--
|
|
87
|
-
--
|
|
88
|
-
--
|
|
89
|
-
--
|
|
90
|
-
--
|
|
91
|
-
--
|
|
92
|
-
--
|
|
93
|
-
--
|
|
94
|
-
--
|
|
95
|
-
--ink-muted: #62564a;
|
|
96
|
-
--ink-faint: #a09484;
|
|
97
|
-
--line: #e6ded3;
|
|
98
|
-
--line-control: #807467;
|
|
99
|
-
--accent: #b4441b;
|
|
100
|
-
--on-accent: #ffffff;
|
|
101
|
-
--accent-ink: #a63e17;
|
|
102
|
-
--accent-soft: #f8e4d9;
|
|
103
|
-
--positive: #2c6e3f;
|
|
104
|
-
--positive-soft: #e3efdc;
|
|
105
|
-
--caution: #8a5000;
|
|
106
|
-
--caution-soft: #fbeccf;
|
|
107
|
-
--critical: #b3261e;
|
|
108
|
-
--critical-soft: #f9e1dc;
|
|
109
|
-
--on-critical: #ffffff;
|
|
110
|
-
--presence-1: #a8256a;
|
|
111
|
-
--presence-2: #0b7285;
|
|
112
|
-
--presence-3: #6e44c2;
|
|
113
|
-
--on-presence: #ffffff;
|
|
114
|
-
--scrim: #221b144d;
|
|
115
|
-
--trench: inset 0 1px 3px #2a1a0c2e, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00;
|
|
116
|
-
--well: inset 0 1px 2px #2a1a0c1a, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00;
|
|
117
|
-
--elev-0: inset 0 1px 0 #ffffff00, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00, 0 0 0 0 #2a1a0c00;
|
|
118
|
-
--elev-1: inset 0 1px 0 #ffffffb3, 0 0 0 1px #2a1a0c17, 0 1px 2px #2a1a0c12, 0 2px 4px -1px #2a1a0c0f;
|
|
119
|
-
--elev-2: inset 0 1px 0 #ffffffcc, 0 0 0 1px #2a1a0c12, 0 2px 4px -1px #2a1a0c14, 0 8px 16px -6px #2a1a0c24;
|
|
120
|
-
--elev-3: inset 0 1px 0 #ffffffe6, 0 0 0 1px #2a1a0c0f, 0 4px 8px -2px #2a1a0c14, 0 16px 32px -10px #2a1a0c33;
|
|
121
|
-
--elev-4: inset 0 1px 0 #ffffff, 0 0 0 1px #2a1a0c0d, 0 8px 16px -4px #2a1a0c17, 0 28px 56px -16px #2a1a0c3d;
|
|
122
|
-
--elev-5: inset 0 1px 0 #ffffff, 0 0 0 1px #2a1a0c0a, 0 12px 24px -6px #2a1a0c1a, 0 48px 96px -24px #2a1a0c4d;
|
|
123
|
-
--lip-top: inset 0 18px 18px -18px #2a1a0c3d;
|
|
124
|
-
--lip-bottom: inset 0 -18px 18px -18px #2a1a0c3d;
|
|
125
|
-
}
|
|
126
|
-
[data-theme="alpine"] {
|
|
127
|
-
--surface-trench: #060a09;
|
|
128
|
-
--surface-well: #0a0f0e;
|
|
129
|
-
--surface-ground: #0f1513;
|
|
130
|
-
--surface-raised: #151e1b;
|
|
131
|
-
--surface-lifted: #1a2522;
|
|
132
|
-
--surface-floating: #202c28;
|
|
133
|
-
--surface-overlay: #25332f;
|
|
134
|
-
--surface-modal: #2a3935;
|
|
135
|
-
--ink: #eaf3ef;
|
|
136
|
-
--ink-muted: #9db0a9;
|
|
137
|
-
--ink-faint: #5d6c67;
|
|
138
|
-
--line: #222c29;
|
|
139
|
-
--line-control: #62736d;
|
|
140
|
-
--accent: #5fd6b2;
|
|
141
|
-
--on-accent: #04140f;
|
|
142
|
-
--accent-ink: #6fdcbc;
|
|
143
|
-
--accent-soft: #13302a;
|
|
144
|
-
--positive: #8ed67a;
|
|
145
|
-
--positive-soft: #1a2e17;
|
|
146
|
-
--caution: #efbd5b;
|
|
147
|
-
--caution-soft: #2f2712;
|
|
148
|
-
--critical: #ff8a70;
|
|
149
|
-
--critical-soft: #3a1a14;
|
|
150
|
-
--on-critical: #1a0905;
|
|
151
|
-
--presence-1: #f07ab8;
|
|
152
|
-
--presence-2: #5cc8e0;
|
|
153
|
-
--presence-3: #b9a3ff;
|
|
154
|
-
--on-presence: #06100d;
|
|
95
|
+
--data-1: #455ee1;
|
|
96
|
+
--data-2: #989902;
|
|
97
|
+
--data-3: #983dbc;
|
|
98
|
+
--data-4: #ce3401;
|
|
99
|
+
--data-5: #0397d2;
|
|
100
|
+
--data-6: #227702;
|
|
101
|
+
--data-other: #676a73;
|
|
102
|
+
--data-context: #86888b;
|
|
103
|
+
--data-seq-1: #2d3242;
|
|
104
|
+
--data-seq-2: #404966;
|
|
105
|
+
--data-seq-3: #54618c;
|
|
106
|
+
--data-seq-4: #687ab3;
|
|
107
|
+
--data-seq-5: #7e94dc;
|
|
108
|
+
--data-seq-6: #97b0ff;
|
|
109
|
+
--data-seq-7: #bfd0ff;
|
|
110
|
+
--data-div-neg-3: #ffc580;
|
|
111
|
+
--data-div-neg-2: #c68e48;
|
|
112
|
+
--data-div-neg-1: #816038;
|
|
113
|
+
--data-div-mid: #36383c;
|
|
114
|
+
--data-div-pos-1: #576590;
|
|
115
|
+
--data-div-pos-2: #8097df;
|
|
116
|
+
--data-div-pos-3: #bfd0ff;
|
|
155
117
|
--scrim: #00000080;
|
|
156
118
|
--trench: inset 0 2px 4px #000000b3, 0 0 0 0 #00000000, 0 0 0 0 #00000000, 0 0 0 0 #00000000;
|
|
157
119
|
--well: inset 0 1px 2px #00000099, 0 0 0 0 #00000000, 0 0 0 0 #00000000, 0 0 0 0 #00000000;
|
|
@@ -179,6 +141,14 @@
|
|
|
179
141
|
--radius-lg: 14px;
|
|
180
142
|
--radius-xl: 20px;
|
|
181
143
|
--radius-full: 9999px;
|
|
144
|
+
--size-target-min: 24px;
|
|
145
|
+
--size-control-sm: 30px;
|
|
146
|
+
--size-control-md: 36px;
|
|
147
|
+
--size-field-sm: 34px;
|
|
148
|
+
--size-field-md: 40px;
|
|
149
|
+
--size-row-md: 32px;
|
|
150
|
+
--breakpoint-narrow: 480px;
|
|
151
|
+
--breakpoint-compact: 640px;
|
|
182
152
|
--dur-press: 90ms;
|
|
183
153
|
--dur-travel-1: 140ms;
|
|
184
154
|
--dur-travel-2: 180ms;
|
|
@@ -186,11 +156,16 @@
|
|
|
186
156
|
--dur-travel-4: 260ms;
|
|
187
157
|
--dur-travel-5: 300ms;
|
|
188
158
|
--dur-sweep: 1600ms;
|
|
159
|
+
--dur-breath: 4800ms;
|
|
160
|
+
--dur-wave: 1800ms;
|
|
189
161
|
--dur-grace: 300ms;
|
|
190
162
|
--dur-onset: 500ms;
|
|
191
163
|
--dur-hold: 1000ms;
|
|
192
164
|
--dur-hover: 1800ms;
|
|
165
|
+
--dur-linger: 2000ms;
|
|
193
166
|
--dur-settle: 8000ms;
|
|
167
|
+
--dur-arrive: 1100ms;
|
|
168
|
+
--dur-plate: 600ms;
|
|
194
169
|
--ease-rise: cubic-bezier(0.2, 0.9, 0.25, 1);
|
|
195
170
|
--ease-sink: cubic-bezier(0.45, 0, 0.8, 0.35);
|
|
196
171
|
--ease-settle: cubic-bezier(0.3, 1.3, 0.45, 1);
|
|
@@ -205,6 +180,31 @@
|
|
|
205
180
|
--font-sans: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
206
181
|
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
207
182
|
}
|
|
183
|
+
@media (pointer: coarse) {
|
|
184
|
+
:root {
|
|
185
|
+
--size-target-min: 44px;
|
|
186
|
+
--size-control-sm: 40px;
|
|
187
|
+
--size-control-md: 44px;
|
|
188
|
+
--size-field-sm: 44px;
|
|
189
|
+
--size-field-md: 48px;
|
|
190
|
+
--size-row-md: 44px;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
[data-density="comfortable"] {
|
|
194
|
+
--size-target-min: 44px;
|
|
195
|
+
--size-control-sm: 40px;
|
|
196
|
+
--size-control-md: 44px;
|
|
197
|
+
--size-field-sm: 44px;
|
|
198
|
+
--size-field-md: 48px;
|
|
199
|
+
--size-row-md: 44px;
|
|
200
|
+
}
|
|
201
|
+
[data-density="compact"] {
|
|
202
|
+
--size-control-sm: 30px;
|
|
203
|
+
--size-control-md: 36px;
|
|
204
|
+
--size-field-sm: 34px;
|
|
205
|
+
--size-field-md: 40px;
|
|
206
|
+
--size-row-md: 32px;
|
|
207
|
+
}
|
|
208
208
|
|
|
209
209
|
|
|
210
210
|
/* Altitude — component styles. Every value comes from tokens.css; the only
|
|
@@ -215,6 +215,13 @@
|
|
|
215
215
|
--alt-travel: 1;
|
|
216
216
|
/* Convexity cue: a 2–5 L* light-to-dark face gradient on things you press (measured; see Research). Pressing flattens it. */
|
|
217
217
|
--alt-sheen: linear-gradient(to bottom, rgb(255 255 255 / 0.06), rgb(0 0 0 / 0.03));
|
|
218
|
+
/* Insets that step down on a phone (Mobile, M11), so a small screen doesn't stack a desktop's padding at every level
|
|
219
|
+
of nesting. Each is full from the compact breakpoint (640px) up and eases to space-4 below it: the page's gutter and
|
|
220
|
+
an overlay's inset from space-6 (16px by 427px), a card's from space-5 (16px by 512px). They follow the viewport,
|
|
221
|
+
like a density, so a narrow card in a desktop grid keeps its padding; what changes form in a narrow slot still
|
|
222
|
+
follows its container. */
|
|
223
|
+
--alt-inset-page: clamp(var(--space-4), 3.75vw, var(--space-6));
|
|
224
|
+
--alt-inset-box: clamp(var(--space-4), 3.125vw, var(--space-5));
|
|
218
225
|
}
|
|
219
226
|
:root, [data-theme] {
|
|
220
227
|
/* The hairline that outlines a slab, face and wall together, so its edges read on light grounds. */
|
|
@@ -231,7 +238,7 @@
|
|
|
231
238
|
in use stays calm. 80% ink keeps a 3:1 change from line-control in every scheme. */
|
|
232
239
|
--alt-rim-held: color-mix(in oklab, var(--ink) 80%, var(--line-control));
|
|
233
240
|
}
|
|
234
|
-
|
|
241
|
+
/* alpine is a retired name for nightfall (#324). */
|
|
235
242
|
[data-theme="nightfall"], [data-theme="alpine"] {
|
|
236
243
|
--alt-ring: color-mix(in oklab, var(--ink) 9%, transparent); --alt-cast: rgb(0 0 0 / 0.8); --alt-cast-deep: rgb(0 0 0 / 1);
|
|
237
244
|
--alt-wall-in: color-mix(in oklab, var(--surface-trench), #000 55%); --alt-lip: rgb(255 255 255 / 0.035);
|
|
@@ -274,10 +281,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
274
281
|
cursor: pointer;
|
|
275
282
|
transition: box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
276
283
|
}
|
|
277
|
-
.alt-lift:
|
|
284
|
+
.alt-lift:focus-visible { background: var(--alt-bg-up); box-shadow: var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
285
|
+
@media (hover: hover) { .alt-lift:hover { background: var(--alt-bg-up); box-shadow: var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
278
286
|
.alt-lift:active { box-shadow: var(--elev-0); transform: translateY(calc(1px * var(--alt-travel))); transition-duration: var(--dur-press); transition-timing-function: ease-out; }
|
|
279
287
|
.alt-lift:focus-visible, .alt-tabs-trigger:focus-visible, .alt-acc-trigger:focus-visible,
|
|
280
|
-
.alt-slider-thumb:focus-visible, .alt-switch:focus-visible, .alt-tabs-content:focus-visible, .alt-toast-close:focus-visible {
|
|
288
|
+
.alt-slider-thumb:focus-visible, .alt-switch:focus-visible, .alt-tabs-content:focus-visible, .alt-toast-close:focus-visible, .alt-chart-pin-remove:focus-visible {
|
|
281
289
|
outline: 2px solid var(--accent-ink); outline-offset: 2px;
|
|
282
290
|
}
|
|
283
291
|
|
|
@@ -291,7 +299,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
291
299
|
--wall: var(--wall-1); --wall-c: var(--surface-trench);
|
|
292
300
|
--alt-bg: var(--surface-raised); --alt-bg-up: var(--surface-lifted); --alt-fg: var(--ink); --alt-sh: var(--elev-1); --alt-sh-up: var(--elev-2);
|
|
293
301
|
position: relative; display: inline-flex; align-items: center; justify-content: center; flex: none;
|
|
294
|
-
height:
|
|
302
|
+
height: var(--size-control-md); padding: 0 var(--space-4); border: 0; border-radius: var(--radius-md);
|
|
295
303
|
font: 500 14px/20px var(--font-sans); letter-spacing: -0.003em; white-space: nowrap; user-select: none; cursor: pointer;
|
|
296
304
|
background: var(--alt-bg); background-image: var(--alt-sheen); color: var(--alt-fg);
|
|
297
305
|
/* A button stays exactly where it is until the pointer is on it: no approach, no lean. */
|
|
@@ -304,14 +312,14 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
304
312
|
outline 2px off a box that covers the face and the wall, because an outline never adds to the page's scrollable
|
|
305
313
|
area: a button at the edge of a narrow screen must not give the page a sideways scroll. At rest the box is the face
|
|
306
314
|
alone, so only a keyboard-focused button's wall (the box's bottom edge, at most 5px) reaches past its own box. */
|
|
307
|
-
.alt-btn:focus-visible, .alt-crumbs
|
|
308
|
-
.alt-btn::after, .alt-crumbs
|
|
315
|
+
.alt-btn:focus-visible, .alt-crumbs .alt-crumb-link:focus-visible { outline: none; }
|
|
316
|
+
.alt-btn::after, .alt-crumbs .alt-crumb-link::after {
|
|
309
317
|
content: ""; position: absolute; pointer-events: none; opacity: 0;
|
|
310
318
|
inset: 0; outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-md);
|
|
311
319
|
transition: opacity var(--dur-travel-1) var(--ease-rise), bottom var(--dur-press) ease-out;
|
|
312
320
|
}
|
|
313
|
-
.alt-btn[data-size="sm"]::after, .alt-crumbs
|
|
314
|
-
.alt-btn:focus-visible::after, .alt-crumbs
|
|
321
|
+
.alt-btn[data-size="sm"]::after, .alt-crumbs .alt-crumb-link::after { border-radius: var(--radius-sm); }
|
|
322
|
+
.alt-btn:focus-visible::after, .alt-crumbs .alt-crumb-link:focus-visible::after { opacity: 1; bottom: calc(-1 * var(--wall, 0px) - 1px); }
|
|
315
323
|
.alt-btn:active::after, .alt-btn[data-loading]::after { bottom: 0; }
|
|
316
324
|
.alt-btn-label { display: inline-flex; align-items: center; gap: var(--space-2); transition: opacity var(--dur-travel-1) var(--ease-rise); }
|
|
317
325
|
.alt-btn-label > svg { flex: none; margin-inline: -2px; }
|
|
@@ -324,16 +332,21 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
324
332
|
--alt-bg: var(--critical); --alt-bg-up: var(--critical); --alt-fg: var(--on-critical); --alt-sh: var(--elev-2); --alt-sh-up: var(--elev-3);
|
|
325
333
|
}
|
|
326
334
|
.alt-btn[data-variant="ghost"] { --wall: 0px; --alt-bg: transparent; --alt-bg-up: var(--surface-raised); --alt-sh: var(--elev-0); --alt-sh-up: var(--elev-1); background-image: none; }
|
|
327
|
-
.alt-btn[data-variant="ghost"]:
|
|
328
|
-
.alt-btn[data-
|
|
335
|
+
.alt-btn[data-variant="ghost"]:focus-visible { --wall: var(--wall-1); background-image: var(--alt-sheen); }
|
|
336
|
+
@media (hover: hover) { .alt-btn[data-variant="ghost"]:hover:not(:disabled) { --wall: var(--wall-1); background-image: var(--alt-sheen); } }
|
|
337
|
+
.alt-btn[data-size="sm"] { height: var(--size-control-sm); padding: 0 var(--space-3); font-size: 13px; border-radius: var(--radius-sm); }
|
|
329
338
|
.alt-btn[data-size="lg"] { height: 44px; padding: 0 var(--space-5); font-size: 15px; }
|
|
330
|
-
.alt-btn[data-square] { width:
|
|
331
|
-
.alt-btn[data-square][data-size="sm"] { width:
|
|
339
|
+
.alt-btn[data-square] { width: var(--size-control-md); padding: 0; }
|
|
340
|
+
.alt-btn[data-square][data-size="sm"] { width: var(--size-control-sm); }
|
|
332
341
|
.alt-btn[data-square][data-size="lg"] { width: 44px; }
|
|
333
|
-
.alt-btn:
|
|
342
|
+
.alt-btn:focus-visible {
|
|
334
343
|
background: var(--alt-bg-up); background-image: var(--alt-sheen);
|
|
335
344
|
box-shadow: 0 calc(var(--wall) + 1px) 0 0 var(--wall-c), var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel)));
|
|
336
345
|
}
|
|
346
|
+
@media (hover: hover) { .alt-btn:hover:not(:disabled):not([data-loading]) {
|
|
347
|
+
background: var(--alt-bg-up); background-image: var(--alt-sheen);
|
|
348
|
+
box-shadow: 0 calc(var(--wall) + 1px) 0 0 var(--wall-c), var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel)));
|
|
349
|
+
} }
|
|
337
350
|
.alt-btn:active:not(:disabled):not([data-loading]) {
|
|
338
351
|
box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); background-image: none; transform: translateY(var(--wall));
|
|
339
352
|
transition-duration: var(--dur-press); transition-timing-function: ease-out;
|
|
@@ -350,13 +363,14 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
350
363
|
background: var(--alt-bg); box-shadow: var(--alt-sh); border-radius: var(--radius-lg); color: var(--ink);
|
|
351
364
|
display: flex; flex-direction: column;
|
|
352
365
|
}
|
|
353
|
-
.alt-card.alt-lift:
|
|
354
|
-
.alt-card-
|
|
366
|
+
.alt-card.alt-lift:focus-visible { transform: translateY(calc(-2px * var(--alt-travel))); }
|
|
367
|
+
@media (hover: hover) { .alt-card.alt-lift:hover { transform: translateY(calc(-2px * var(--alt-travel))); } }
|
|
368
|
+
.alt-card-header { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--alt-inset-box) var(--alt-inset-box) 0; }
|
|
355
369
|
.alt-card-title { margin: 0; font: 600 17px/24px var(--font-sans); letter-spacing: -0.01em; }
|
|
356
370
|
.alt-card-desc { margin: 0; font: 400 14px/20px var(--font-sans); color: var(--ink-muted); }
|
|
357
|
-
.alt-card-content { padding: var(--space-4) var(--
|
|
358
|
-
.alt-card-header + .alt-card-footer { padding-top: var(--
|
|
359
|
-
.alt-card-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); padding: 0 var(--
|
|
371
|
+
.alt-card-content { padding: var(--space-4) var(--alt-inset-box) var(--alt-inset-box); font: 400 15px/23px var(--font-sans); }
|
|
372
|
+
.alt-card-header + .alt-card-footer { padding-top: var(--alt-inset-box); }
|
|
373
|
+
.alt-card-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); padding: 0 var(--alt-inset-box) var(--alt-inset-box); }
|
|
360
374
|
|
|
361
375
|
/* ── Input / Textarea: a field is an opening cut into the page, and its depth says whether it still needs filling.
|
|
362
376
|
One camera for everything: under a raised face you see its front wall; inside an opening you see its back wall,
|
|
@@ -371,15 +385,15 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
371
385
|
.alt-input {
|
|
372
386
|
--w: 4px; --wc: var(--alt-wall-in); --halo: 0px; --halo-c: transparent;
|
|
373
387
|
--wd: calc(var(--w) + var(--n) * 2px);
|
|
374
|
-
position: relative; isolation: isolate; display: flex; align-items: center; gap: var(--space-2); height:
|
|
388
|
+
position: relative; isolation: isolate; display: flex; align-items: center; gap: var(--space-2); height: var(--size-field-md); padding: 0 var(--space-3);
|
|
375
389
|
border: 1px solid var(--line-control); border-radius: var(--radius-md);
|
|
376
390
|
background: var(--surface-well); color: var(--ink); cursor: text;
|
|
377
391
|
box-shadow: inset 0 var(--wd) 0 0 var(--wc), inset 0 calc(var(--wd) + 3px) 3px -2px var(--alt-cast), 0 1px 0 0 var(--alt-lip), 0 0 0 var(--halo) var(--halo-c);
|
|
378
392
|
transition: background-color var(--dur-travel-2) var(--ease-rise), box-shadow var(--dur-travel-2) var(--ease-settle), border-color var(--dur-travel-1) var(--ease-rise), outline-color var(--dur-travel-1) var(--ease-rise);
|
|
379
393
|
outline: 1px solid transparent; outline-offset: 0;
|
|
380
394
|
}
|
|
381
|
-
.alt-input[data-size="sm"] { --w: 3px; height:
|
|
382
|
-
.alt-input:hover { border-color: var(--ink-muted); }
|
|
395
|
+
.alt-input[data-size="sm"] { --w: 3px; height: var(--size-field-sm); border-radius: var(--radius-sm); }
|
|
396
|
+
@media (hover: hover) { .alt-input:hover { border-color: var(--ink-muted); } }
|
|
383
397
|
/* Filled: the floor rises flush with the page. Nothing left to receive, so approach no longer deepens it. */
|
|
384
398
|
.alt-input:has(input:not(:placeholder-shown)), .alt-input:has(textarea:not(:placeholder-shown)) { --w: 0px; --n: 0; background: var(--surface-raised); }
|
|
385
399
|
/* Focused: the floor lights up, and an empty field stays open (at the depth approach previewed) until something is put
|
|
@@ -397,6 +411,19 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
397
411
|
.alt-input[data-multiline] { height: auto; padding: var(--space-2) var(--space-3); align-items: stretch; flex-direction: column; overflow: hidden; }
|
|
398
412
|
.alt-input textarea { min-height: 84px; resize: vertical; line-height: 21px; }
|
|
399
413
|
.alt-input-adorn { display: inline-flex; color: var(--ink-muted); flex: none; transform: translateY(calc(var(--wd) * 0.5)); transition: transform var(--dur-travel-2) var(--ease-settle); }
|
|
414
|
+
/* Suggested: a value proposed for the field is a ghost of the value in ink-faint, the feedforward vocabulary (the ghost
|
|
415
|
+
of the check a box would get). It isn't the value, so an empty field with a ghost stays an opening; Tab accepts,
|
|
416
|
+
raising the floor flush with one afterglow. The ghost appears without motion: nothing has arrived in the record. */
|
|
417
|
+
.alt-input-field { position: relative; flex: 1; min-width: 0; display: flex; align-self: stretch; }
|
|
418
|
+
.alt-input-field[data-multiline] { flex-direction: column; }
|
|
419
|
+
.alt-input-ghost {
|
|
420
|
+
position: absolute; inset: 0; display: flex; align-items: center; overflow: hidden; white-space: pre; pointer-events: none;
|
|
421
|
+
font: 400 14px/20px var(--font-sans); color: var(--ink-faint);
|
|
422
|
+
transform: translateY(calc(var(--wd) * 0.5)); transition: transform var(--dur-travel-2) var(--ease-settle);
|
|
423
|
+
}
|
|
424
|
+
.alt-input-ghost[data-multiline] { display: block; white-space: pre-wrap; overflow-wrap: break-word; line-height: 21px; }
|
|
425
|
+
.alt-input-ghost-typed { color: transparent; }
|
|
426
|
+
@media (forced-colors: active) { .alt-input-ghost { color: GrayText; } }
|
|
400
427
|
/* Fill line: with a maxLength, the well fills from the floor as the text approaches the limit. */
|
|
401
428
|
.alt-input-gauge {
|
|
402
429
|
--g: var(--accent-ink);
|
|
@@ -449,18 +476,18 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
449
476
|
transition: transform var(--dur-travel-2) var(--ease-settle), filter var(--dur-travel-1) var(--ease-rise), color var(--dur-travel-1);
|
|
450
477
|
}
|
|
451
478
|
/* Hover is feedforward: an unchecked box shows a ghost of the check it would get; a checked one lifts its mark. */
|
|
452
|
-
.alt-check:not(.alt-radio):hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track .alt-check-mark { color: var(--ink-faint); filter: none; }
|
|
453
|
-
.alt-check:not(.alt-radio):hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track .alt-check-tick { stroke-dashoffset: 0; opacity: 0.55; transition: stroke-dashoffset var(--dur-travel-2) var(--ease-rise); }
|
|
454
|
-
.alt-check:not(.alt-radio):hover:not([data-disabled]) input:checked + .alt-check-track .alt-check-mark { transform: translateY(calc(-0.5px * var(--alt-travel))); filter: drop-shadow(0 1.5px 1px rgb(0 0 0 / 0.32)); }
|
|
455
|
-
.alt-check input:checked + .alt-check-track, .alt-check input:indeterminate + .alt-check-track { border-color: var(--accent); }
|
|
456
|
-
.alt-check input:checked + .alt-check-track .alt-check-fill, .alt-check input:indeterminate + .alt-check-track .alt-check-fill { opacity: 1; clip-path: circle(75% at 50% 50%); }
|
|
457
|
-
.alt-check:hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track { border-color: var(--ink-muted); }
|
|
479
|
+
@media (hover: hover) { .alt-check:not(.alt-radio):hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track .alt-check-mark { color: var(--ink-faint); filter: none; } }
|
|
480
|
+
@media (hover: hover) { .alt-check:not(.alt-radio):hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track .alt-check-tick { stroke-dashoffset: 0; opacity: 0.55; transition: stroke-dashoffset var(--dur-travel-2) var(--ease-rise); } }
|
|
481
|
+
@media (hover: hover) { .alt-check:not(.alt-radio):hover:not([data-disabled]) input:checked + .alt-check-track .alt-check-mark { transform: translateY(calc(-0.5px * var(--alt-travel))); filter: drop-shadow(0 1.5px 1px rgb(0 0 0 / 0.32)); } }
|
|
482
|
+
.alt-check input:checked + .alt-check-track, .alt-check input[type="checkbox"]:indeterminate + .alt-check-track { border-color: var(--accent); }
|
|
483
|
+
.alt-check input:checked + .alt-check-track .alt-check-fill, .alt-check input[type="checkbox"]:indeterminate + .alt-check-track .alt-check-fill { opacity: 1; clip-path: circle(75% at 50% 50%); }
|
|
484
|
+
@media (hover: hover) { .alt-check:hover:not([data-disabled]) input:not(:checked):not(:indeterminate) + .alt-check-track { border-color: var(--ink-muted); } }
|
|
458
485
|
.alt-check:not(.alt-radio):active:not([data-disabled]) .alt-check-track { box-shadow: var(--trench); }
|
|
459
486
|
.alt-check:not(.alt-radio):active:not([data-disabled]) .alt-check-mark { transform: scale(calc(1 - 0.12 * var(--alt-travel))); transition-duration: var(--dur-press); }
|
|
460
487
|
.alt-check input:focus-visible + .alt-check-track { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
461
488
|
.alt-check-mark path { stroke-dasharray: 16; stroke-dashoffset: 16; transition: stroke-dashoffset var(--dur-travel-1) var(--ease-sink); }
|
|
462
489
|
.alt-check input:checked:not(:indeterminate) + .alt-check-track .alt-check-tick,
|
|
463
|
-
.alt-check input:indeterminate + .alt-check-track .alt-check-dash { stroke-dashoffset: 0; transition: stroke-dashoffset var(--dur-travel-2) var(--ease-rise) 60ms; }
|
|
490
|
+
.alt-check input[type="checkbox"]:indeterminate + .alt-check-track .alt-check-dash { stroke-dashoffset: 0; transition: stroke-dashoffset var(--dur-travel-2) var(--ease-rise) 60ms; }
|
|
464
491
|
.alt-check-text { display: flex; flex-direction: column; gap: 2px; padding-top: 1px; }
|
|
465
492
|
.alt-check-label { font: 500 14px/20px var(--font-sans); }
|
|
466
493
|
.alt-check-desc { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
@@ -481,7 +508,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
481
508
|
transition: box-shadow var(--dur-travel-1) var(--ease-rise), scale var(--dur-travel-2) var(--ease-settle);
|
|
482
509
|
}
|
|
483
510
|
.alt-radio-thumb[data-ready] { transition: transform var(--dur-travel-3) var(--ease-settle), box-shadow var(--dur-travel-1) var(--ease-rise), scale var(--dur-travel-2) var(--ease-settle); }
|
|
484
|
-
.alt-radio-group:has(.alt-radio:hover input:checked) .alt-radio-thumb { box-shadow: var(--elev-2); }
|
|
511
|
+
@media (hover: hover) { .alt-radio-group:has(.alt-radio:hover input:checked) .alt-radio-thumb { box-shadow: var(--elev-2); } }
|
|
485
512
|
.alt-radio-group:has(.alt-radio:active input:checked) .alt-radio-thumb { scale: calc(1 + 0.25 * var(--alt-travel)) calc(1 - 0.08 * var(--alt-travel)); }
|
|
486
513
|
/* Feedforward: hovering an unchosen option shows a ghost of the thumb where it would land. */
|
|
487
514
|
.alt-radio .alt-check-track::after {
|
|
@@ -489,7 +516,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
489
516
|
background: var(--surface-raised); box-shadow: var(--elev-1); opacity: 0; scale: 0.5; pointer-events: none;
|
|
490
517
|
transition: opacity var(--dur-travel-1) var(--ease-sink), scale var(--dur-travel-2) var(--ease-settle);
|
|
491
518
|
}
|
|
492
|
-
.alt-radio:hover:not([data-disabled]) input:not(:checked) + .alt-check-track::after { opacity: 0.5; scale: 0.85; transition-timing-function: var(--ease-rise), var(--ease-settle); }
|
|
519
|
+
@media (hover: hover) { .alt-radio:hover:not([data-disabled]) input:not(:checked) + .alt-check-track::after { opacity: 0.5; scale: 0.85; transition-timing-function: var(--ease-rise), var(--ease-settle); } }
|
|
493
520
|
.alt-radio-group[data-disabled] .alt-radio-thumb { background: var(--surface-well); background-image: none; box-shadow: var(--elev-0); }
|
|
494
521
|
|
|
495
522
|
/* ── Switch: a trench with a raised thumb; the accent pours in behind the thumb. ── */
|
|
@@ -512,7 +539,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
512
539
|
.alt-switch[aria-checked="true"] { border-color: var(--accent); }
|
|
513
540
|
.alt-switch[aria-checked="true"] .alt-switch-fill { opacity: 1; clip-path: inset(0 0 0 0 round 999px); }
|
|
514
541
|
.alt-switch[aria-checked="true"] .alt-switch-thumb { transform: translateX(calc(var(--track) - var(--tw))); }
|
|
515
|
-
.alt-switch:hover:not(:disabled) .alt-switch-thumb { box-shadow: var(--elev-2); }
|
|
542
|
+
@media (hover: hover) { .alt-switch:hover:not(:disabled) .alt-switch-thumb { box-shadow: var(--elev-2); } }
|
|
516
543
|
.alt-switch:active:not(:disabled) { --tw: calc(18px + 5px * var(--alt-travel)); }
|
|
517
544
|
.alt-switch:disabled { cursor: not-allowed; border-color: var(--line); background: var(--surface-ground); box-shadow: var(--elev-0); }
|
|
518
545
|
.alt-switch:disabled .alt-switch-thumb { box-shadow: var(--elev-0); background: var(--surface-well); }
|
|
@@ -527,7 +554,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
527
554
|
variant="segmented": the options sit in a well and one raised pill glides between them. ── */
|
|
528
555
|
.alt-tabs { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
529
556
|
.alt-tabs-list {
|
|
530
|
-
position: relative; display: inline-flex; align-self: flex-start; gap: 2px; padding: var(--space-1);
|
|
557
|
+
position: relative; display: inline-flex; align-self: flex-start; max-width: 100%; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; gap: 2px; padding: var(--space-1);
|
|
531
558
|
border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-well); box-shadow: var(--well);
|
|
532
559
|
}
|
|
533
560
|
.alt-tabs-indicator {
|
|
@@ -536,21 +563,30 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
536
563
|
}
|
|
537
564
|
.alt-tabs-indicator[data-ready] { transition: transform var(--dur-travel-3) var(--ease-settle), width var(--dur-travel-3) var(--ease-settle), opacity var(--dur-travel-1); }
|
|
538
565
|
.alt-tabs-trigger {
|
|
539
|
-
position: relative; z-index: 1; height:
|
|
566
|
+
position: relative; z-index: 1; height: var(--size-control-sm); padding: 0 var(--space-3); border: 0; border-radius: calc(var(--radius-md) - var(--space-1));
|
|
540
567
|
background: none; color: var(--ink-muted); font: 500 14px/20px var(--font-sans); cursor: pointer; white-space: nowrap;
|
|
541
568
|
transition: color var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise);
|
|
542
569
|
}
|
|
543
|
-
.alt-tabs-trigger
|
|
544
|
-
|
|
570
|
+
.alt-tabs-trigger[aria-selected="true"], .alt-tabs-trigger[aria-checked="true"] { color: var(--ink); }
|
|
571
|
+
@media (hover: hover) { .alt-tabs-trigger:hover { color: var(--ink); } }
|
|
572
|
+
@media (hover: hover) { .alt-tabs[data-variant="segmented"] .alt-tabs-trigger:hover:not([aria-selected="true"]), .alt-tgroup .alt-tabs-trigger:hover:not([aria-checked="true"]):not(:disabled) { background: color-mix(in oklab, var(--surface-raised) 40%, transparent); } }
|
|
573
|
+
/* Narrow (#259): the list scrolls within itself; an edge with more tabs past it fades out, so you can see there's more. */
|
|
574
|
+
.alt-tabs-list::-webkit-scrollbar { display: none; }
|
|
575
|
+
.alt-tabs-list[data-more-end] { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
|
|
576
|
+
.alt-tabs-list[data-more-start] { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); }
|
|
577
|
+
.alt-tabs-list[data-more-start][data-more-end] { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); }
|
|
545
578
|
.alt-tabs-content { animation: alt-emerge var(--dur-travel-2) var(--ease-rise); border-radius: var(--radius-sm); }
|
|
546
579
|
|
|
547
580
|
.alt-tabs[data-variant="folder"] { gap: 0; }
|
|
581
|
+
/* The list makes no layer of its own, so each tab stacks against the panel: only the chosen tab lies over it (covering its
|
|
582
|
+
hairline); the others, and a tab dropping back, sit behind the sheet. */
|
|
548
583
|
.alt-tabs[data-variant="folder"] > .alt-tabs-list {
|
|
549
|
-
|
|
584
|
+
/* It scrolls when narrow (#259), so the tab's shadow above and its 1px reach into the panel sit in its padding. */
|
|
585
|
+
--r: 10px; align-self: stretch; z-index: auto; gap: var(--space-1); margin: -6px 0 -2px; padding: 6px var(--alt-inset-box) 1px;
|
|
550
586
|
border: 0; border-radius: 0; background: none; box-shadow: none;
|
|
551
587
|
}
|
|
552
588
|
.alt-tabs[data-variant="folder"] .alt-tabs-indicator { display: none; }
|
|
553
|
-
.alt-tabs[data-variant="folder"] .alt-tabs-trigger { isolation: isolate; height:
|
|
589
|
+
.alt-tabs[data-variant="folder"] .alt-tabs-trigger { z-index: 0; isolation: isolate; height: calc(var(--size-control-md) + 2px); padding: 0 var(--space-4); border-radius: var(--r) var(--r) 0 0; }
|
|
554
590
|
.alt-tabs[data-variant="folder"] .alt-tabs-trigger:focus-visible { outline-offset: -5px; }
|
|
555
591
|
/* The tab: the top edge of its sheet. It reaches 1px into the panel to cover the panel's top hairline. */
|
|
556
592
|
.alt-tabs[data-variant="folder"] .alt-tabs-trigger::before {
|
|
@@ -567,17 +603,18 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
567
603
|
radial-gradient(circle at 100% 0, transparent calc(var(--r) - 0.75px), var(--alt-ring) calc(var(--r) - 0.5px), var(--alt-ring) calc(var(--r) + 0.25px), var(--surface-raised) calc(var(--r) + 0.75px)) right / var(--r) var(--r) no-repeat;
|
|
568
604
|
opacity: 0; transition: opacity var(--dur-travel-1) var(--ease-sink);
|
|
569
605
|
}
|
|
606
|
+
.alt-tabs[data-variant="folder"] .alt-tabs-trigger[aria-selected="true"] { z-index: 2; }
|
|
570
607
|
.alt-tabs[data-variant="folder"] .alt-tabs-trigger[aria-selected="true"]::before {
|
|
571
608
|
opacity: 1; transform: none; transition: opacity var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-3) var(--ease-settle);
|
|
572
609
|
}
|
|
573
610
|
.alt-tabs[data-variant="folder"] .alt-tabs-trigger[aria-selected="true"]::after { opacity: 1; transition: opacity var(--dur-travel-2) var(--ease-rise) 60ms; }
|
|
574
|
-
.alt-tabs[data-variant="folder"] .alt-tabs-trigger:hover:not([aria-selected="true"])::before {
|
|
611
|
+
@media (hover: hover) { .alt-tabs[data-variant="folder"] .alt-tabs-trigger:hover:not([aria-selected="true"])::before {
|
|
575
612
|
opacity: 1; transform: translateY(calc(5px * var(--alt-travel))); clip-path: inset(-8px -8px calc(1px + 5px * var(--alt-travel)) -8px);
|
|
576
613
|
background: color-mix(in oklab, var(--surface-raised) 50%, var(--surface-ground)); box-shadow: 0 0 0 1px color-mix(in oklab, var(--ink) 6%, transparent);
|
|
577
614
|
transition: opacity var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
578
|
-
}
|
|
615
|
+
} }
|
|
579
616
|
.alt-tabs[data-variant="folder"] > .alt-tabs-content {
|
|
580
|
-
position: relative; z-index: 1; padding: var(--
|
|
617
|
+
position: relative; z-index: 1; padding: var(--alt-inset-box); border-radius: var(--radius-lg);
|
|
581
618
|
background: var(--surface-raised); box-shadow: var(--elev-2); color: var(--ink);
|
|
582
619
|
animation: alt-sheet-surface var(--dur-travel-3) var(--ease-rise);
|
|
583
620
|
}
|
|
@@ -593,9 +630,9 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
593
630
|
border: 2px solid var(--accent); border-radius: var(--radius-full); background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1);
|
|
594
631
|
transition: left var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
595
632
|
}
|
|
596
|
-
.alt-slider:hover .alt-slider-thumb { box-shadow: var(--elev-2); }
|
|
633
|
+
@media (hover: hover) { .alt-slider:hover .alt-slider-thumb { box-shadow: var(--elev-2); } }
|
|
597
634
|
.alt-slider[data-dragging] .alt-slider-fill, .alt-slider[data-dragging] .alt-slider-thumb { transition-property: box-shadow, transform; }
|
|
598
|
-
.alt-slider[data-dragging] .alt-slider-thumb { box-shadow: var(--elev-3); transform: scale(calc(1 + 0.12 * var(--alt-travel))); cursor: grabbing; }
|
|
635
|
+
.alt-slider[data-dragging] .alt-slider-thumb:not([data-rest]) { box-shadow: var(--elev-3); transform: scale(calc(1 + 0.12 * var(--alt-travel))); cursor: grabbing; }
|
|
599
636
|
.alt-slider-bubble {
|
|
600
637
|
position: absolute; left: 50%; bottom: calc(100% + 10px); translate: -50% 0; min-width: 32px; padding: 3px 8px; text-align: center;
|
|
601
638
|
border-radius: var(--radius-sm); background: var(--surface-floating); box-shadow: var(--elev-1); color: var(--ink);
|
|
@@ -603,13 +640,23 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
603
640
|
opacity: 0; transform: translateY(calc(4px * var(--alt-travel))) scale(calc(1 - 0.1 * var(--alt-travel))); transform-origin: 50% 100%;
|
|
604
641
|
transition: opacity var(--dur-travel-1) var(--ease-sink), transform var(--dur-travel-1) var(--ease-sink), box-shadow var(--dur-travel-1) var(--ease-sink);
|
|
605
642
|
}
|
|
606
|
-
.alt-slider[data-dragging] .alt-slider-bubble, .alt-slider-thumb:focus-visible .alt-slider-bubble {
|
|
643
|
+
.alt-slider[data-dragging] .alt-slider-thumb:not([data-rest]) .alt-slider-bubble, .alt-slider-thumb:focus-visible .alt-slider-bubble {
|
|
607
644
|
opacity: 1; transform: none; box-shadow: var(--elev-3);
|
|
608
645
|
transition: opacity var(--dur-travel-3) var(--ease-rise), transform var(--dur-travel-3) var(--ease-rise), box-shadow var(--dur-travel-3) var(--ease-rise);
|
|
609
646
|
}
|
|
610
647
|
.alt-slider[data-disabled] { cursor: not-allowed; }
|
|
611
648
|
.alt-slider[data-disabled] .alt-slider-fill { background: var(--line-control); box-shadow: none; }
|
|
612
649
|
.alt-slider[data-disabled] .alt-slider-thumb { border-color: var(--line-control); box-shadow: var(--elev-0); }
|
|
650
|
+
/* A range: two thumbs with the raised fill between them. Only the thumb under the pointer lifts, and the one held
|
|
651
|
+
floats; the other stays at rest. The fill lifts on hover too, because dragging it moves both thumbs. */
|
|
652
|
+
.alt-slider[data-range] .alt-slider-fill { position: absolute; top: 0; cursor: grab; transition: left var(--dur-travel-1) var(--ease-glide), width var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
653
|
+
@media (hover: hover) { .alt-slider[data-range]:not([data-disabled]):hover .alt-slider-thumb { box-shadow: var(--elev-1); } }
|
|
654
|
+
.alt-slider-fill[data-grabbed] { box-shadow: var(--elev-2); }
|
|
655
|
+
@media (hover: hover) { .alt-slider[data-range]:not([data-disabled]) :is(.alt-slider-thumb:hover, .alt-slider-fill:hover) { box-shadow: var(--elev-2); } }
|
|
656
|
+
.alt-slider[data-range][data-dragging] .alt-slider-thumb[data-grabbed] { box-shadow: var(--elev-3); }
|
|
657
|
+
.alt-slider[data-range][data-dragging] .alt-slider-fill { transition-property: box-shadow; }
|
|
658
|
+
.alt-slider-fill[data-grabbed] { cursor: grabbing; }
|
|
659
|
+
.alt-slider[data-range][data-disabled] .alt-slider-fill { cursor: not-allowed; box-shadow: none; }
|
|
613
660
|
|
|
614
661
|
/* ── Progress ── */
|
|
615
662
|
.alt-progress { position: relative; height: 8px; border-radius: var(--radius-full); background: var(--surface-trench); box-shadow: var(--trench); overflow: hidden; }
|
|
@@ -663,71 +710,76 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
663
710
|
@media (prefers-reduced-motion: reduce) { .alt-badge-lamp[data-pulse] { animation: none; } }
|
|
664
711
|
[data-motion="reduced"] .alt-badge-lamp[data-pulse] { animation: none; }
|
|
665
712
|
|
|
666
|
-
/* ── Avatar:
|
|
667
|
-
its shadow
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
713
|
+
/* ── Avatar: a person is a disc. While they're here (active or idle) it rests on the page like a card, at level 1 with
|
|
714
|
+
its contact shadow; when they're not (offline) it's part of the record, a plate set flush into the page, grey. Faces
|
|
715
|
+
are neutral: status hues never stand for a person, and colour stays in the lamp, as on a Badge (filled when active,
|
|
716
|
+
an empty ring when idle). Only someone live (speaking, typing, presenting), or an avatar you can press under the
|
|
717
|
+
pointer, leaves the ground: the face lifts and a shadow appears on the page beneath it, smaller and fainter the
|
|
718
|
+
higher it goes. A detached shadow is what makes an object read as floating rather than resting (Kersten, Mamassian
|
|
719
|
+
& Knill 1997). --hr is the height as a fraction of the face. ── */
|
|
671
720
|
@property --hr { syntax: "<number>"; inherits: true; initial-value: 0; }
|
|
672
721
|
.alt-avatar {
|
|
673
722
|
--size: 36px; --hr: 0; --lift: calc(var(--size) * var(--hr) * var(--alt-travel));
|
|
723
|
+
--av-sh: var(--elev-1); --av-plate: inset 0 1px 1.5px color-mix(in oklab, var(--ink) 16%, transparent), inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent);
|
|
674
724
|
position: relative; isolation: isolate; display: inline-grid; flex: none; width: var(--size); height: var(--size); vertical-align: middle;
|
|
675
725
|
transition: --hr var(--dur-travel-3) var(--ease-settle);
|
|
676
726
|
}
|
|
677
727
|
.alt-avatar-face {
|
|
678
|
-
position: relative; z-index: 1; display: grid; place-items: center; width: 100%; height: 100%; overflow: hidden; border-radius: var(--radius-full);
|
|
679
|
-
background
|
|
680
|
-
|
|
681
|
-
font: 600 calc(var(--size) * 0.36) / 1 var(--font-sans); letter-spacing: 0.01em; user-select: none;
|
|
728
|
+
position: relative; z-index: 1; display: grid; place-items: center; width: 100%; height: 100%; overflow: hidden; border-radius: var(--av-r, var(--radius-full));
|
|
729
|
+
background: var(--surface-raised); box-shadow: var(--av-sh); color: var(--ink);
|
|
730
|
+
font: 600 calc(var(--size) * 0.36) / 1 var(--font-sans); user-select: none;
|
|
682
731
|
transform: translateY(calc(-1 * var(--lift)));
|
|
683
|
-
transition: background-color var(--dur-travel-2), color var(--dur-travel-2),
|
|
732
|
+
transition: background-color var(--dur-travel-2), color var(--dur-travel-2), box-shadow var(--dur-travel-2) var(--ease-rise);
|
|
684
733
|
}
|
|
685
734
|
.alt-avatar-face img { width: 100%; height: 100%; object-fit: cover; }
|
|
686
|
-
/*
|
|
735
|
+
/* One letter has room to be bigger: small faces (under 28px) show only the first. */
|
|
736
|
+
.alt-avatar-face[data-one] { font-size: calc(var(--size) * 0.46); }
|
|
737
|
+
/* A team, an organisation, a bot or an agent is a square tile; its corners (--av-r) grow with its size, from
|
|
738
|
+
radius-xs to radius-lg. Its shadow, lamp and states are a person's. */
|
|
739
|
+
.alt-avatar[data-shape="square"] .alt-avatar-lamp { inset-inline-end: calc(var(--lamp) * -0.3); bottom: calc(var(--lamp) * -0.3); }
|
|
740
|
+
.alt-avatar[data-shape="square"] .alt-avatar-shadow { left: 8%; right: 8%; border-radius: var(--radius-sm); }
|
|
741
|
+
/* The shadow on the page. At rest it isn't there (the face's own level-1 shadow is its contact shadow); it fades in
|
|
742
|
+
as the face leaves the ground, then shrinks and fades the higher the face goes: that gap is what reads as height. */
|
|
687
743
|
.alt-avatar-shadow {
|
|
688
|
-
position: absolute; left:
|
|
689
|
-
border-radius: 50%;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
.alt-avatar[data-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
.alt-avatar[data-status="
|
|
697
|
-
.alt-avatar[data-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
.alt-avatar[data-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
.alt-avatar[data-
|
|
704
|
-
|
|
705
|
-
@keyframes alt-drift { from { --hr: 0.15; } to { --hr: 0.24; } }
|
|
706
|
-
@media (prefers-reduced-motion: reduce) { .alt-avatar[data-live] { animation: none; --hr: 0.2; } }
|
|
707
|
-
[data-motion="reduced"] .alt-avatar[data-live] { animation: none; --hr: 0.2; }
|
|
708
|
-
.alt-avatar[data-presence] .alt-avatar-face { outline: 2px solid var(--alt-presence); outline-offset: 1px; }
|
|
744
|
+
position: absolute; left: 14%; right: 14%; top: 100%; height: calc(var(--size) * 0.14); margin-top: calc(var(--size) * -0.07);
|
|
745
|
+
border-radius: 50%; pointer-events: none;
|
|
746
|
+
background: radial-gradient(closest-side, var(--alt-cast-deep), color-mix(in oklab, var(--alt-cast-deep) 55%, transparent) 50%, transparent);
|
|
747
|
+
opacity: calc(clamp(0, var(--hr) * 12, 1) * (1 - var(--hr) * 1.4)); scale: calc(1 - var(--hr) * 1.1);
|
|
748
|
+
}
|
|
749
|
+
.alt-avatar:is([data-status="offline"], [data-more]) .alt-avatar-face {
|
|
750
|
+
background: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%); color: var(--ink-muted); box-shadow: var(--av-plate), 0 1px 0 0 var(--alt-lip);
|
|
751
|
+
}
|
|
752
|
+
.alt-avatar[data-status="offline"] .alt-avatar-face img { filter: grayscale(1); opacity: 0.5; }
|
|
753
|
+
.alt-avatar[data-more] .alt-avatar-face { font-size: calc(var(--size) * 0.32); font-weight: 550; font-variant-numeric: tabular-nums; }
|
|
754
|
+
.alt-avatar[data-live] { --av-sh: var(--elev-2); animation: alt-drift var(--dur-hover) ease-in-out infinite alternate; }
|
|
755
|
+
@keyframes alt-drift { from { --hr: 0.14; } to { --hr: 0.22; } }
|
|
756
|
+
@media (prefers-reduced-motion: reduce) { .alt-avatar[data-live] { animation: none; --hr: 0.18; } }
|
|
757
|
+
[data-motion="reduced"] .alt-avatar[data-live] { animation: none; --hr: 0.18; }
|
|
758
|
+
/* Presence colour: a 2px ring drawn tight around the face. */
|
|
759
|
+
.alt-avatar[data-presence] .alt-avatar-face { outline: 2px solid var(--alt-presence); outline-offset: 0; }
|
|
760
|
+
/* The lamp is a Badge lamp, cut out of the face by a ring of ground: filled when active, an empty ring when idle. */
|
|
709
761
|
.alt-avatar-lamp {
|
|
710
|
-
--b-lamp: var(--positive);
|
|
711
|
-
position: absolute; z-index: 2;
|
|
762
|
+
--b-lamp: var(--positive); --lamp: clamp(8px, calc(var(--size) * 0.2), 12px);
|
|
763
|
+
position: absolute; z-index: 2; inset-inline-end: 0; bottom: 0; width: var(--lamp); height: var(--lamp); border-radius: var(--radius-full);
|
|
712
764
|
background: var(--b-lamp); box-shadow: 0 0 0 2px var(--surface-ground); transform: translateY(calc(-1 * var(--lift)));
|
|
713
765
|
}
|
|
714
|
-
.alt-avatar[data-status="idle"] .alt-avatar-lamp { --
|
|
715
|
-
.alt-avatar[data-interactive]:hover, .alt-avatar[data-interactive]:focus-visible { --hr: 0.3; }
|
|
766
|
+
.alt-avatar[data-status="idle"] .alt-avatar-lamp { background: var(--surface-raised); box-shadow: 0 0 0 2px var(--surface-ground), inset 0 0 0 clamp(2px, calc(var(--size) * 0.05), 3px) var(--caution); }
|
|
716
767
|
.alt-avatar[data-interactive] { cursor: pointer; outline: none; }
|
|
768
|
+
.alt-avatar[data-interactive]:focus-visible { --hr: 0.26; --av-sh: var(--elev-2); }
|
|
769
|
+
@media (hover: hover) { .alt-avatar[data-interactive]:hover { --hr: 0.26; --av-sh: var(--elev-2); } }
|
|
770
|
+
.alt-avatar[data-interactive]:active { --hr: 0; --av-sh: var(--elev-0); transition-duration: var(--dur-press); }
|
|
717
771
|
.alt-avatar[data-interactive]:focus-visible .alt-avatar-face { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
718
|
-
|
|
772
|
+
/* Group: every face stands on one ground line, cut out of the one before it by a ring of ground. Later faces are
|
|
773
|
+
laid on top, overlapping by 15% of a face, which keeps the initials beneath whole. */
|
|
774
|
+
.alt-avatar-group { isolation: isolate; display: inline-flex; align-items: flex-end; padding: calc(var(--size, 36px) * 0.26) 0 calc(var(--size, 36px) * 0.08); }
|
|
719
775
|
.alt-avatar-group .alt-avatar { isolation: auto; }
|
|
720
|
-
/*
|
|
721
|
-
|
|
776
|
+
/* An avatar that waited for its data sits inside its loading wrapper (display: contents at rest), so the overlap is
|
|
777
|
+
counted from the group's own children either way, imprints included. */
|
|
722
778
|
.alt-avatar-group > .alt-avatar:not(:first-child),
|
|
723
|
-
.alt-avatar-group > :is(.alt-await, .alt-await-wait):not(:first-child) :is(.alt-avatar, .alt-avatar-imprint) { margin-
|
|
724
|
-
|
|
725
|
-
.alt-avatar-group .alt-avatar .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground),
|
|
726
|
-
.alt-avatar-group .alt-avatar[data-
|
|
727
|
-
.alt-avatar-group .alt-avatar[data-status="active"] .alt-avatar-face { z-index: 3; }
|
|
728
|
-
.alt-avatar-group .alt-avatar[data-status="idle"] .alt-avatar-face { z-index: 2; }
|
|
729
|
-
.alt-avatar-group .alt-avatar:not([data-tint="more"]):hover { --hr: 0.32; }
|
|
730
|
-
.alt-avatar-group .alt-avatar:not([data-tint="more"]):hover .alt-avatar-face { z-index: 5; }
|
|
779
|
+
.alt-avatar-group > :is(.alt-await, .alt-await-wait):not(:first-child) :is(.alt-avatar, .alt-avatar-imprint) { margin-inline-start: calc(var(--size, 36px) * -0.12); }
|
|
780
|
+
.alt-avatar-group .alt-avatar .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), var(--av-sh); }
|
|
781
|
+
.alt-avatar-group .alt-avatar:is([data-status="offline"], [data-more]) .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), var(--av-plate); }
|
|
782
|
+
@media (hover: hover) { .alt-avatar-group .alt-avatar:is([data-live], [data-interactive]:hover, [data-interactive]:focus-visible) .alt-avatar-face { z-index: 5; } }
|
|
731
783
|
.alt-avatar-group .alt-avatar-lamp { display: none; }
|
|
732
784
|
|
|
733
785
|
/* ── Skeleton: an imprint of the content to come, pressed into the page. Light passes over it while it waits; when the
|
|
@@ -775,8 +827,77 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
775
827
|
/* Text imprints are as tall as the text they stand for: one line-height of the surrounding type per line. */
|
|
776
828
|
.alt-await-lines { display: flex; flex-direction: column; justify-content: space-around; height: calc(var(--lines, 1) * 1lh); max-width: 100%; }
|
|
777
829
|
.alt-await-lines > .alt-skeleton { flex: none; height: 0.6em; }
|
|
778
|
-
.alt-await-card { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--
|
|
830
|
+
.alt-await-card { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--alt-inset-box); }
|
|
779
831
|
.alt-await-card-body { font: 400 15px/23px var(--font-sans); }
|
|
832
|
+
|
|
833
|
+
/* ── Breath (AltitudeProvider loading="breath", #343). Instead of light sweeping over it, the imprint breathes: its floor
|
|
834
|
+
deepens from well to trench over 40% of dur-breath and releases over the rest, slower and less even than a pending
|
|
835
|
+
write's breath. In Nightfall, where well and trench are nearly one tone, the back wall (lip-top) breathes instead.
|
|
836
|
+
Every imprint takes its phase from one page clock, lagged by its height in the view (--alt-breath-delay, set by
|
|
837
|
+
useBreath), so they change in step and a swell crosses the view in dur-wave. Ten seconds in, it calms to a shallow,
|
|
838
|
+
slower breath. Depth and tone only: nothing travels. Charts, stats and meters hold still (their imprints stand for
|
|
839
|
+
amounts). When content lands: raised content (settle="raised") rises 8px to level 2 and settles to its rest over
|
|
840
|
+
dur-arrive; flush content fills past the ground (its floor passes just above it, then fills in flush); content that
|
|
841
|
+
arrives into a view already showing gets a plate that dissolves over dur-plate, no tint, so it isn't an afterglow. ── */
|
|
842
|
+
[data-loading="breath"] .alt-skeleton::after { content: none; }
|
|
843
|
+
[data-loading="breath"] .alt-skeleton { animation: alt-breath var(--dur-breath) linear var(--alt-breath-delay, 0ms) infinite; }
|
|
844
|
+
[data-loading="breath"][data-theme="nightfall"] .alt-skeleton, [data-theme="nightfall"] [data-loading="breath"] .alt-skeleton { animation-name: alt-breath-wall; }
|
|
845
|
+
[data-loading="breath"] :is([data-calm] .alt-skeleton, .alt-skeleton[data-calm]) { animation: alt-breath-calm calc(var(--dur-breath) * 0.75) ease-in-out infinite alternate; }
|
|
846
|
+
[data-loading="breath"] :is(.alt-chart-imprint .alt-skeleton, .alt-stat-imprint, .alt-sparkline-imprint, .alt-meter-imprint) { animation: none; }
|
|
847
|
+
@keyframes alt-breath {
|
|
848
|
+
0% { background-color: var(--surface-well); box-shadow: var(--well); animation-timing-function: ease-in-out; }
|
|
849
|
+
40% { background-color: var(--surface-trench); box-shadow: var(--trench); animation-timing-function: ease-in-out; }
|
|
850
|
+
100% { background-color: var(--surface-well); box-shadow: var(--well); }
|
|
851
|
+
}
|
|
852
|
+
@keyframes alt-breath-wall {
|
|
853
|
+
0% { box-shadow: var(--well), inset 0 0 0 0 transparent; animation-timing-function: ease-in-out; }
|
|
854
|
+
40% { box-shadow: var(--well), var(--lip-top); animation-timing-function: ease-in-out; }
|
|
855
|
+
100% { box-shadow: var(--well), inset 0 0 0 0 transparent; }
|
|
856
|
+
}
|
|
857
|
+
@keyframes alt-breath-calm { to { background-color: color-mix(in oklab, var(--surface-well), var(--surface-trench) 45%); } }
|
|
858
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"] > .alt-await-face { position: relative; animation: alt-fill-ride calc(var(--dur-travel-3) * 2) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
859
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"] > .alt-await-imprint { animation: alt-fill-past calc(var(--dur-travel-3) * 2) linear var(--alt-rise-delay, 0ms) forwards; }
|
|
860
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"][data-arrival="late"] > .alt-await-imprint { animation: alt-await-fill var(--dur-travel-1) linear var(--alt-rise-delay, 0ms) forwards; }
|
|
861
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"][data-arrival="late"]:not([data-rest="raised"]) > .alt-await-face::before {
|
|
862
|
+
content: ""; position: absolute; inset: -3px -6px; z-index: -1; border-radius: var(--radius-sm); pointer-events: none;
|
|
863
|
+
background: var(--surface-raised); box-shadow: var(--elev-1); animation: alt-plate var(--dur-plate) linear var(--alt-rise-delay, 0ms) both;
|
|
864
|
+
}
|
|
865
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"][data-rest="raised"] > .alt-await-face { animation: alt-arrive var(--dur-arrive) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
866
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"][data-rest="raised"] > .alt-await-face::before {
|
|
867
|
+
content: ""; position: absolute; inset: 0; z-index: -1; border-radius: var(--alt-await-radius, var(--radius-lg)); pointer-events: none;
|
|
868
|
+
box-shadow: var(--elev-2); animation: alt-plate var(--dur-arrive) linear var(--alt-rise-delay, 0ms) both;
|
|
869
|
+
}
|
|
870
|
+
[data-loading="breath"] .alt-await[data-phase="reveal"][data-rest="raised"] > .alt-await-imprint { animation: alt-await-fill var(--dur-travel-3) linear var(--alt-rise-delay, 0ms) forwards; }
|
|
871
|
+
[data-loading="breath"] .alt-reveal[data-reveal] { animation: alt-reveal-past calc(var(--dur-travel-3) * 2) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
872
|
+
/* A chart's marks still fade in place (Charts: "fade, don't rise"), under breath too. */
|
|
873
|
+
[data-loading="breath"] .alt-await.alt-chart-await[data-phase="reveal"] > .alt-await-face { animation: alt-emerge var(--dur-travel-3) var(--ease-rise) var(--alt-rise-delay, 0ms) backwards; }
|
|
874
|
+
[data-loading="breath"] .alt-await.alt-chart-await[data-phase="reveal"] > .alt-await-imprint { animation: alt-await-fill var(--dur-travel-3) linear var(--alt-rise-delay, 0ms) forwards; }
|
|
875
|
+
[data-loading="breath"] .alt-reveal[data-reveal] > .alt-reveal-inner { animation: alt-fill-ride calc(var(--dur-travel-3) * 2) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
876
|
+
@keyframes alt-fill-ride {
|
|
877
|
+
0% { opacity: 0; transform: translateY(calc(3px * var(--alt-travel))); animation-timing-function: var(--ease-rise); }
|
|
878
|
+
50% { opacity: 1; transform: translateY(calc(-1.5px * var(--alt-travel))); animation-timing-function: var(--ease-decay); }
|
|
879
|
+
100% { transform: none; }
|
|
880
|
+
}
|
|
881
|
+
@keyframes alt-fill-past {
|
|
882
|
+
0% { background-color: var(--surface-well); box-shadow: var(--well); animation-timing-function: var(--ease-rise); }
|
|
883
|
+
50% { opacity: 1; background-color: var(--surface-raised); box-shadow: var(--elev-1); animation-timing-function: var(--ease-decay); }
|
|
884
|
+
100% { opacity: 0; background-color: var(--surface-ground); box-shadow: var(--elev-0); }
|
|
885
|
+
}
|
|
886
|
+
@keyframes alt-reveal-past {
|
|
887
|
+
0% { background-color: var(--surface-well); box-shadow: var(--well); animation-timing-function: var(--ease-rise); }
|
|
888
|
+
50% { background-color: var(--surface-raised); box-shadow: var(--elev-1); animation-timing-function: var(--ease-decay); }
|
|
889
|
+
100% { background-color: transparent; box-shadow: var(--elev-0); }
|
|
890
|
+
}
|
|
891
|
+
@keyframes alt-arrive {
|
|
892
|
+
0% { opacity: 0; transform: translateY(calc(8px * var(--alt-travel))); animation-timing-function: var(--ease-rise); }
|
|
893
|
+
20% { opacity: 1; transform: translateY(calc(-3px * var(--alt-travel))); animation-timing-function: var(--ease-decay); }
|
|
894
|
+
100% { transform: none; }
|
|
895
|
+
}
|
|
896
|
+
@keyframes alt-plate { 0% { opacity: 0; } 20% { opacity: 1; animation-timing-function: var(--ease-decay); } 100% { opacity: 0; } }
|
|
897
|
+
/* Reduced motion and forced colours: the breath stops, as a pending write's does; the imprint holds still at well depth. */
|
|
898
|
+
@media (prefers-reduced-motion: reduce) { [data-loading="breath"] :is(.alt-skeleton, [data-calm] .alt-skeleton, .alt-skeleton[data-calm]) { animation: none; } }
|
|
899
|
+
[data-motion="reduced"] :is(.alt-skeleton, [data-calm] .alt-skeleton, .alt-skeleton[data-calm]) { animation: none; }
|
|
900
|
+
@media (forced-colors: active) { [data-loading="breath"] :is(.alt-skeleton, [data-calm] .alt-skeleton, .alt-skeleton[data-calm]) { animation: none; } }
|
|
780
901
|
.alt-avatar-imprint { display: inline-block; vertical-align: middle; }
|
|
781
902
|
.alt-badge-imprint { display: inline-block; vertical-align: middle; width: 64px; height: 22px; }
|
|
782
903
|
.alt-badge-imprint[data-variant="count"] { width: 22px; border-radius: var(--radius-full); }
|
|
@@ -822,8 +943,38 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
822
943
|
flex: none; width: 24px; height: 24px; margin: -2px -6px 0 0; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--radius-xs);
|
|
823
944
|
background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), color var(--dur-travel-1);
|
|
824
945
|
}
|
|
825
|
-
.alt-alert-close:hover { background: color-mix(in oklab, var(--ink) 8%, transparent); color: var(--ink); }
|
|
946
|
+
@media (hover: hover) { .alt-alert-close:hover { background: color-mix(in oklab, var(--ink) 8%, transparent); color: var(--ink); } }
|
|
826
947
|
.alt-alert-close:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
948
|
+
.alt-alert-close[data-labelled] { width: auto; height: auto; padding: 4px 8px; font: 500 13px/18px var(--font-sans); white-space: nowrap; align-self: center; }
|
|
949
|
+
/* A page banner: the same sheet, laid edge to edge across the top of the app or page, so it has no side rounding and
|
|
950
|
+
no side edges, only its bottom edge and contact shadow. It informs (flat, ground) or asks for action (raised), as
|
|
951
|
+
any Alert does. Sticky, it stays at the top while the page scrolls. Its text wraps; its padding follows the page's. */
|
|
952
|
+
.alt-banner[data-sticky] { position: sticky; top: 0; z-index: 30; }
|
|
953
|
+
.alt-alert[data-placement="page"] { border-radius: 0; box-shadow: 0 1px 0 0 color-mix(in oklab, var(--a-fg) 22%, transparent), 0 1.5px 3px -0.5px var(--alt-cast); }
|
|
954
|
+
.alt-alert[data-placement="page"][data-variant="raised"] { box-shadow: var(--elev-1); }
|
|
955
|
+
.alt-alert[data-placement="page"] .alt-alert-inner { flex-wrap: wrap; padding: var(--space-3) var(--alt-page-pad, var(--space-4)); }
|
|
956
|
+
.alt-alert[data-placement="page"] .alt-alert-body { min-width: min(100%, 240px); }
|
|
957
|
+
.alt-alert-stack[data-placement="page"] .alt-alert-sheet { border-radius: 0; left: 0; right: 0; }
|
|
958
|
+
|
|
959
|
+
/* ── RemovalReceipt. Left in place, then closed: the result shows in the item's slot, at its height, on the ground
|
|
960
|
+
(a trace of what happened, not a live surface). It lifts off as an Alert does, and only then does the gap glide shut. ── */
|
|
961
|
+
.alt-receipt-item { display: contents; }
|
|
962
|
+
/* The receipt lays itself out, whatever the item's own layout was (a row stacked on a phone included); at the item's
|
|
963
|
+
height, its line sits in the middle of the slot. */
|
|
964
|
+
.alt-receipt[data-removed] { box-sizing: border-box; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; align-content: center; justify-content: flex-start; gap: var(--space-2) var(--space-3); }
|
|
965
|
+
.alt-receipt-note:empty { display: none; }
|
|
966
|
+
.alt-receipt-note { flex: 1 1 12rem; min-width: 0; display: flex; align-items: center; gap: var(--space-2); color: var(--ink-muted); font: 400 14px/20px var(--font-sans);
|
|
967
|
+
animation: alt-receipt-in var(--dur-travel-2) ease-out both; }
|
|
968
|
+
.alt-receipt-icon { display: inline-flex; flex: none; }
|
|
969
|
+
.alt-receipt[data-tone="critical"] .alt-receipt-note { color: var(--ink); }
|
|
970
|
+
.alt-receipt[data-tone="critical"] .alt-receipt-icon { color: var(--critical); }
|
|
971
|
+
.alt-receipt-action { display: flex; flex-wrap: wrap; gap: var(--space-2); animation: alt-receipt-in var(--dur-travel-2) ease-out both; }
|
|
972
|
+
@keyframes alt-receipt-in { from { opacity: 0; } }
|
|
973
|
+
/* Like every pending surface, it only shows as pending after dur-grace, so a fast write never flickers. */
|
|
974
|
+
.alt-receipt[data-status="pending"] { background: var(--surface-lifted); border-radius: var(--radius-md); transition: background-color var(--dur-travel-1) var(--ease-rise) var(--dur-grace); animation-fill-mode: forwards; }
|
|
975
|
+
.alt-receipt[data-leaving] { pointer-events: none; }
|
|
976
|
+
.alt-receipt[data-leaving] > .alt-receipt-note, .alt-receipt[data-leaving] > .alt-receipt-action { animation: alt-lifted-off calc(var(--dur-travel-3) * 0.7) var(--ease-sink) both; }
|
|
977
|
+
@media (forced-colors: active) { .alt-receipt[data-removed] { outline: 1px solid CanvasText; outline-offset: -1px; } }
|
|
827
978
|
|
|
828
979
|
.alt-alert-stack { position: relative; display: grid; gap: var(--space-2); }
|
|
829
980
|
.alt-alert-stack[data-sheets="1"] { --sheets: 1; } .alt-alert-stack[data-sheets="2"] { --sheets: 2; }
|
|
@@ -845,11 +996,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
845
996
|
background: var(--surface-floating); color: var(--ink); box-shadow: var(--elev-2); font: 600 12px/1 var(--font-sans); cursor: pointer;
|
|
846
997
|
transition: box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
847
998
|
}
|
|
848
|
-
.alt-alert-more:hover { box-shadow: var(--elev-3); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
999
|
+
@media (hover: hover) { .alt-alert-more:hover { box-shadow: var(--elev-3); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
849
1000
|
.alt-alert-more:active { box-shadow: var(--elev-1); transform: none; transition-duration: var(--dur-press); }
|
|
850
1001
|
.alt-alert-more:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
851
1002
|
.alt-alert-stack[data-expanded] .alt-alert-more { position: static; justify-self: end; height: 26px; background: none; color: var(--ink-muted); box-shadow: none; transform: none; }
|
|
852
|
-
.alt-alert-stack[data-expanded] .alt-alert-more:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 6%, transparent); }
|
|
1003
|
+
@media (hover: hover) { .alt-alert-stack[data-expanded] .alt-alert-more:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 6%, transparent); } }
|
|
853
1004
|
.alt-alert-stack-item { animation: alt-fan var(--dur-travel-3) var(--ease-settle) calc(var(--i) * 30ms) backwards; }
|
|
854
1005
|
.alt-alert-stack-item:first-child { animation: none; }
|
|
855
1006
|
@keyframes alt-fan { from { opacity: 0; transform: translateY(calc(var(--i) * -12px * var(--alt-travel))) scale(calc(1 - 0.02 * var(--alt-travel))); } }
|
|
@@ -869,8 +1020,8 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
869
1020
|
.alt-acc-item[data-state="open"] { background: var(--surface-raised); box-shadow: var(--elev-2); margin: var(--space-2) 0; }
|
|
870
1021
|
.alt-acc-item[data-state="open"]:first-child { margin-top: 0; }
|
|
871
1022
|
.alt-acc-item[data-state="open"]:last-child { margin-bottom: 0; }
|
|
872
|
-
.alt-acc-item[data-state="open"]::before, .alt-acc-item[data-state="open"] + .alt-acc-item::before
|
|
873
|
-
.alt-acc-item:has(> .alt-acc-heading .alt-acc-trigger:hover)::before, .alt-acc-item:has(> .alt-acc-heading .alt-acc-trigger:hover) + .alt-acc-item::before { opacity: 0; }
|
|
1023
|
+
.alt-acc-item[data-state="open"]::before, .alt-acc-item[data-state="open"] + .alt-acc-item::before { opacity: 0; }
|
|
1024
|
+
@media (hover: hover) { .alt-acc-item:has(> .alt-acc-heading .alt-acc-trigger:hover)::before, .alt-acc-item:has(> .alt-acc-heading .alt-acc-trigger:hover) + .alt-acc-item::before { opacity: 0; } }
|
|
874
1025
|
.alt-acc-heading { margin: 0; }
|
|
875
1026
|
.alt-acc-trigger {
|
|
876
1027
|
display: flex; width: 100%; align-items: center; justify-content: space-between; gap: var(--space-3);
|
|
@@ -878,11 +1029,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
878
1029
|
font: 500 15px/22px var(--font-sans); text-align: left; cursor: pointer;
|
|
879
1030
|
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise);
|
|
880
1031
|
}
|
|
881
|
-
.alt-acc-item[data-state="closed"] .alt-acc-trigger:hover {
|
|
1032
|
+
@media (hover: hover) { .alt-acc-item[data-state="closed"] .alt-acc-trigger:hover {
|
|
882
1033
|
background: color-mix(in oklab, var(--surface-raised) 55%, transparent); box-shadow: 0 0 0 1px color-mix(in oklab, var(--ink) 5%, transparent), 0 1px 2px var(--alt-cast);
|
|
883
|
-
}
|
|
1034
|
+
} }
|
|
884
1035
|
.alt-acc-chevron { flex: none; color: var(--ink-muted); transition: transform var(--dur-travel-3) var(--ease-settle); }
|
|
885
|
-
.alt-acc-trigger:hover .alt-acc-chevron { transform: translateY(calc(2px * var(--alt-travel))); }
|
|
1036
|
+
@media (hover: hover) { .alt-acc-trigger:hover .alt-acc-chevron { transform: translateY(calc(2px * var(--alt-travel))); } }
|
|
886
1037
|
.alt-acc-item[data-state="open"] .alt-acc-chevron { transform: rotate(180deg); }
|
|
887
1038
|
.alt-acc-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-travel-3) var(--ease-glide); }
|
|
888
1039
|
.alt-acc-item[data-state="open"] .alt-acc-panel { grid-template-rows: 1fr; }
|
|
@@ -920,15 +1071,19 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
920
1071
|
.alt-tip {
|
|
921
1072
|
left: 50%; bottom: calc(100% + 8px); translate: -50% 0; transform-origin: 50% 100%;
|
|
922
1073
|
padding: 5px 10px; border-radius: var(--radius-sm); background: var(--surface-floating); box-shadow: var(--elev-3);
|
|
923
|
-
font: 500 12.5px/17px var(--font-sans);
|
|
1074
|
+
font: 500 12.5px/17px var(--font-sans); width: max-content; max-width: min(320px, calc(100vw - 16px)); pointer-events: none;
|
|
924
1075
|
}
|
|
925
1076
|
.alt-tip[data-side="bottom"] { bottom: auto; top: calc(100% + 8px); transform-origin: 50% 0; }
|
|
926
1077
|
.alt-tip[data-state="closed"] { transform: translateY(calc(4px * var(--alt-travel))) scale(calc(1 - 0.04 * var(--alt-travel))); }
|
|
927
1078
|
.alt-tip[data-side="bottom"][data-state="closed"] { transform: translateY(calc(-4px * var(--alt-travel))) scale(calc(1 - 0.04 * var(--alt-travel))); }
|
|
1079
|
+
.alt-tip[data-side="right"] { left: calc(100% + 8px); bottom: auto; top: 50%; translate: 0 -50%; transform-origin: 0 50%; }
|
|
1080
|
+
.alt-tip[data-side="right"][data-state="closed"] { transform: translateX(calc(-4px * var(--alt-travel))) scale(calc(1 - 0.04 * var(--alt-travel))); }
|
|
928
1081
|
.alt-tip[data-warm] { transition-duration: var(--dur-travel-1); }
|
|
929
1082
|
|
|
1083
|
+
/* On screen at any width (#248): a panel is capped at the viewport's width, and useFitFloating shifts it sideways to
|
|
1084
|
+
stay 8px clear of the edges when it opens. */
|
|
930
1085
|
.alt-pop {
|
|
931
|
-
top: calc(100% + 8px); left: 0; transform-origin: 0 0; min-width: 220px; padding: var(--space-4);
|
|
1086
|
+
top: calc(100% + 8px); left: 0; transform-origin: 0 0; min-width: min(220px, calc(100vw - 16px)); max-width: calc(100vw - 16px); box-sizing: border-box; padding: var(--space-4);
|
|
932
1087
|
border-radius: var(--radius-lg); background: var(--surface-floating); box-shadow: var(--elev-3); outline: none;
|
|
933
1088
|
}
|
|
934
1089
|
.alt-pop[data-align="end"] { left: auto; right: 0; transform-origin: 100% 0; }
|
|
@@ -938,14 +1093,14 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
938
1093
|
.alt-pop[data-state="closed"] { transform: translateY(calc(-6px * var(--alt-travel))) scale(calc(1 - 0.04 * var(--alt-travel))); }
|
|
939
1094
|
.alt-pop[data-side="top"][data-state="closed"] { transform: translateY(calc(6px * var(--alt-travel))) scale(calc(1 - 0.04 * var(--alt-travel))); }
|
|
940
1095
|
|
|
941
|
-
.alt-menu { padding: var(--space-1); min-width: 228px; }
|
|
1096
|
+
.alt-menu { padding: var(--space-1); min-width: min(228px, calc(100vw - 16px)); }
|
|
942
1097
|
.alt-menu-hl {
|
|
943
1098
|
position: absolute; top: 0; left: var(--space-1); right: var(--space-1); border-radius: var(--radius-sm); background: var(--accent-soft);
|
|
944
1099
|
pointer-events: none; transition: transform var(--dur-travel-2) var(--ease-settle), height var(--dur-travel-2) var(--ease-settle), opacity var(--dur-travel-1), background-color var(--dur-travel-1);
|
|
945
1100
|
}
|
|
946
1101
|
.alt-menu-hl[data-tone="critical"] { background: var(--critical-soft); }
|
|
947
1102
|
.alt-menu-item, .alt-menu-label {
|
|
948
|
-
position: relative; display: flex; align-items: center; gap: 10px; min-height:
|
|
1103
|
+
position: relative; display: flex; align-items: center; gap: 10px; min-height: calc(var(--size-control-md) - 2px); padding: 0 10px; border-radius: var(--radius-sm);
|
|
949
1104
|
font: 500 14px/20px var(--font-sans); color: var(--ink); cursor: pointer; user-select: none;
|
|
950
1105
|
opacity: 0; transform: translateY(calc(-3px * var(--alt-travel)));
|
|
951
1106
|
transition: opacity var(--dur-travel-2) var(--ease-rise), transform var(--dur-travel-2) var(--ease-rise);
|
|
@@ -961,14 +1116,15 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
961
1116
|
.alt-menu-sep { height: 1px; margin: var(--space-1) 6px; background: var(--line); }
|
|
962
1117
|
|
|
963
1118
|
/* ── Dialog: level 5. The page recedes, the scrim veils it, the dialog rises with its shadow growing. ── */
|
|
964
|
-
|
|
1119
|
+
/* The modal roots cover the visible viewport (#251), not the largest one, so a footer is never under the keyboard. */
|
|
1120
|
+
.alt-dialog-root { position: fixed; inset: 0; top: var(--alt-vv-top, 0px); bottom: auto; height: var(--alt-vvh, 100dvh); z-index: var(--z-modal); display: grid; place-items: center; padding: var(--alt-inset-page); padding: calc(var(--alt-inset-page) + env(safe-area-inset-top, 0px)) calc(var(--alt-inset-page) + env(safe-area-inset-right, 0px)) calc(var(--alt-inset-page) + env(safe-area-inset-bottom, 0px)) calc(var(--alt-inset-page) + env(safe-area-inset-left, 0px)); }
|
|
965
1121
|
.alt-scrim {
|
|
966
|
-
position: absolute; inset: 0; background: var(--scrim); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
|
|
1122
|
+
position: absolute; inset: 0; background: var(--scrim); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); /* tells-ignore: CM2 the scrim recedes the page behind a dialog (Elevation), not decoration */
|
|
967
1123
|
transition: opacity var(--dur-travel-5) var(--ease-rise);
|
|
968
1124
|
}
|
|
969
1125
|
.alt-dialog {
|
|
970
|
-
position: relative; width: min(100%, 460px); max-height: calc(100vh - 48px); overflow: auto; outline: none;
|
|
971
|
-
padding: var(--
|
|
1126
|
+
position: relative; width: min(100%, 460px); max-height: calc(100vh - 48px); max-height: calc(var(--alt-vvh, 100dvh) - 2 * var(--alt-inset-page)); overflow: auto; outline: none;
|
|
1127
|
+
padding: var(--alt-inset-page); border-radius: var(--radius-xl); background: var(--surface-modal); box-shadow: var(--elev-5); color: var(--ink);
|
|
972
1128
|
transition: opacity var(--dur-travel-5) var(--ease-rise), transform var(--dur-travel-5) var(--ease-rise), box-shadow var(--dur-travel-5) var(--ease-rise);
|
|
973
1129
|
}
|
|
974
1130
|
.alt-dialog[data-size="sm"] { width: min(100%, 380px); }
|
|
@@ -989,10 +1145,16 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
989
1145
|
.alt-dialog-close { position: absolute; top: var(--space-4); right: var(--space-4); color: var(--ink-muted); }
|
|
990
1146
|
|
|
991
1147
|
/* ── Toast: the stack is a depth stack. Newest in front at level 4; older ones sink behind it. ── */
|
|
1148
|
+
/* Safe areas (#252): every layer fixed to the screen's edge adds env(safe-area-inset-*) to its offset, so in an
|
|
1149
|
+
edge-to-edge page (viewport-fit=cover) it clears the notch, the rounded corners and the home indicator. Without
|
|
1150
|
+
viewport-fit=cover env() is 0 and nothing moves. The mode frame stays at the physical edge: it frames the screen. */
|
|
992
1151
|
.alt-toaster {
|
|
993
|
-
position: fixed; z-index: var(--z-toast); right: var(--space-6); bottom: var(--space-6); width: min(360px, calc(100vw - 32px));
|
|
1152
|
+
position: fixed; z-index: var(--z-toast); right: calc(var(--space-6) + env(safe-area-inset-right, 0px)); bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px)); width: min(360px, calc(100vw - 32px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)));
|
|
994
1153
|
transition: height var(--dur-travel-4) var(--ease-rise);
|
|
995
1154
|
}
|
|
1155
|
+
/* On touch (#253): a tap opens the stack and holds it (timers paused) until a tap outside; a horizontal swipe dismisses
|
|
1156
|
+
a toast, so the toast lets vertical scrolling through. */
|
|
1157
|
+
.alt-toast { touch-action: pan-y; }
|
|
996
1158
|
.alt-toast {
|
|
997
1159
|
position: absolute; right: 0; bottom: 0; width: 100%; display: flex; align-items: flex-start; gap: var(--space-3);
|
|
998
1160
|
padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4); border-radius: var(--radius-lg);
|
|
@@ -1013,7 +1175,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1013
1175
|
display: grid; place-items: center; width: 24px; height: 24px; margin: -2px -2px 0 0; padding: 0; flex: none; border: 0; border-radius: var(--radius-xs);
|
|
1014
1176
|
background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), color var(--dur-travel-1);
|
|
1015
1177
|
}
|
|
1016
|
-
.alt-toast-close:hover { background: var(--surface-well); color: var(--ink); }
|
|
1178
|
+
@media (hover: hover) { .alt-toast-close:hover { background: var(--surface-well); color: var(--ink); } }
|
|
1017
1179
|
|
|
1018
1180
|
|
|
1019
1181
|
/* ════════════════════ Depth signals: what height can tell you ════════════════════ */
|
|
@@ -1077,7 +1239,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1077
1239
|
/* ── HoldButton: holding pushes it through the ground; the fuse along the bottom shows how far. ── */
|
|
1078
1240
|
.alt-hold { overflow: visible; }
|
|
1079
1241
|
.alt-hold .alt-btn-label { position: static; }
|
|
1080
|
-
|
|
1242
|
+
/* All of its labels share one grid cell and only the current one is visible, so the button keeps the width of its
|
|
1243
|
+
longest label: holding and completing never change its size or move what's beside it. */
|
|
1244
|
+
.alt-hold-text { position: relative; display: inline-grid; justify-items: center; }
|
|
1245
|
+
.alt-hold-label { grid-area: 1 / 1; display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
|
|
1246
|
+
.alt-hold-label:not([data-shown]) { visibility: hidden; }
|
|
1081
1247
|
.alt-hold-fill {
|
|
1082
1248
|
position: absolute; left: 8px; right: 8px; bottom: 3px; height: 2px; border-radius: var(--radius-full); background: currentColor; opacity: 0.9;
|
|
1083
1249
|
transform: scaleX(0); transform-origin: left; transition: transform var(--dur-travel-2) var(--ease-sink);
|
|
@@ -1089,13 +1255,1366 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1089
1255
|
.alt-hold[data-hold="holding"] .alt-hold-fill { transform: scaleX(1); transition: transform var(--alt-hold-dur) linear; }
|
|
1090
1256
|
.alt-hold[data-hold="done"]:not([data-final]) { animation: alt-land-a calc(var(--dur-travel-3) * 1.6) var(--ease-settle); }
|
|
1091
1257
|
/* A completed hold is final by default: the action is now part of the record, so the button lies flat and can't be pressed again. */
|
|
1092
|
-
.alt-btn.alt-hold[data-final], .alt-btn.alt-hold[data-final]:
|
|
1258
|
+
.alt-btn.alt-hold[data-final], .alt-btn.alt-hold[data-final]:active, .alt-btn.alt-hold[data-final]:focus-visible {
|
|
1093
1259
|
background: var(--surface-well); background-image: none; color: var(--ink-muted); box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); transform: none; cursor: default;
|
|
1094
1260
|
transition: box-shadow var(--dur-travel-2) var(--ease-settle), transform var(--dur-travel-2) var(--ease-settle), background-color var(--dur-travel-2), color var(--dur-travel-2);
|
|
1095
1261
|
}
|
|
1262
|
+
@media (hover: hover) { .alt-btn.alt-hold[data-final]:hover {
|
|
1263
|
+
background: var(--surface-well); background-image: none; color: var(--ink-muted); box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); transform: none; cursor: default;
|
|
1264
|
+
transition: box-shadow var(--dur-travel-2) var(--ease-settle), transform var(--dur-travel-2) var(--ease-settle), background-color var(--dur-travel-2), color var(--dur-travel-2);
|
|
1265
|
+
} }
|
|
1096
1266
|
|
|
1097
1267
|
.alt-hold[data-final] .alt-hold-fill { opacity: 0; }
|
|
1098
1268
|
|
|
1269
|
+
/* ── Separator: a divider is part of the page, so it's 1px of line on the ground: no shadow, no wall, never
|
|
1270
|
+
line-control (that's for the edges of controls). A vertical one stretches to its row. A labelled one ("or") is the
|
|
1271
|
+
word in ink-muted between two lines; its lines are drawn, so only the word is read. ── */
|
|
1272
|
+
.alt-sep { flex: none; margin: 0; border: 0; background: var(--line); }
|
|
1273
|
+
.alt-sep[data-orientation="horizontal"] { width: 100%; height: 1px; }
|
|
1274
|
+
.alt-sep[data-orientation="vertical"] { display: inline-block; width: 1px; min-height: 1em; align-self: stretch; vertical-align: middle; }
|
|
1275
|
+
/* A card's header has no bottom padding (its content brings the gap), so a separator under it brings the same gap. */
|
|
1276
|
+
.alt-card-header + .alt-sep { margin-top: var(--space-4); }
|
|
1277
|
+
.alt-sep-labelled, .alt-sep-labelled[data-orientation] { display: flex; align-items: center; gap: var(--space-3); width: auto; height: auto; background: none; font: 500 12.5px/17px var(--font-sans); letter-spacing: 0.005em; color: var(--ink-muted); }
|
|
1278
|
+
.alt-sep-labelled[data-orientation="vertical"] { flex-direction: column; min-height: 4em; }
|
|
1279
|
+
.alt-sep-labelled::before, .alt-sep-labelled::after { content: ""; flex: 1; min-width: 1px; min-height: 1px; background: var(--line); }
|
|
1280
|
+
@media (prefers-contrast: more) { .alt-sep:not(.alt-sep-labelled), .alt-sep-labelled::before, .alt-sep-labelled::after { background: var(--line-control); } }
|
|
1281
|
+
[data-contrast="more"] .alt-sep:not(.alt-sep-labelled), [data-contrast="more"] .alt-sep-labelled::before, [data-contrast="more"] .alt-sep-labelled::after { background: var(--line-control); }
|
|
1282
|
+
@media (forced-colors: active) { .alt-sep:not(.alt-sep-labelled), .alt-sep-labelled::before, .alt-sep-labelled::after { background: CanvasText; forced-color-adjust: none; } }
|
|
1283
|
+
|
|
1284
|
+
/* ── Link: navigation is text in the record, so it never lifts: lifting a word would break its line. It's accent-ink
|
|
1285
|
+
with an underline (never colour alone); hover thickens the underline, focus is the 2px accent-ink ring. An external
|
|
1286
|
+
link ends with an icon and says it opens a new tab. ── */
|
|
1287
|
+
.alt-link {
|
|
1288
|
+
color: var(--accent-ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em;
|
|
1289
|
+
border-radius: var(--radius-xs); cursor: pointer; transition: text-decoration-thickness var(--dur-travel-1) var(--ease-rise);
|
|
1290
|
+
}
|
|
1291
|
+
@media (hover: hover) { .alt-link:hover { text-decoration-thickness: 2px; } }
|
|
1292
|
+
.alt-link:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1293
|
+
.alt-link[data-visited]:visited { text-decoration-color: var(--ink-muted); }
|
|
1294
|
+
.alt-link-tail { white-space: nowrap; }
|
|
1295
|
+
.alt-link-ext { display: inline-block; margin-left: 0.2em; vertical-align: -0.1em; }
|
|
1296
|
+
@media (prefers-contrast: more) { .alt-link { text-decoration-thickness: 2px; } }
|
|
1297
|
+
[data-contrast="more"] .alt-link { text-decoration-thickness: 2px; }
|
|
1298
|
+
@media (forced-colors: active) { .alt-link { color: LinkText; } .alt-link:focus-visible { outline-color: Highlight; } }
|
|
1299
|
+
|
|
1300
|
+
/* ── FormattedNumber: a number is the record, so it lies flush in its sentence or cell, in tabular figures. A changed
|
|
1301
|
+
value cross-fades (the old figure fading out over the new) and takes a ground-level tint that decays over dur-settle:
|
|
1302
|
+
the afterglow without the lift, because a word in a line can't rise. It never counts. ── */
|
|
1303
|
+
.alt-num { display: inline-grid; padding: 0 3px; margin: 0 -3px; border-radius: var(--radius-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1304
|
+
.alt-num-text { grid-area: 1 / 1; }
|
|
1305
|
+
.alt-num-text[data-in] { animation: alt-stat-in var(--dur-travel-2) var(--ease-glide) both; }
|
|
1306
|
+
.alt-num-text[data-out] { animation: alt-stat-out var(--dur-travel-2) var(--ease-glide) both; }
|
|
1307
|
+
.alt-num[data-settle]::before { animation-name: alt-chart-tint-a; }
|
|
1308
|
+
.alt-num[data-settle="b"]::before { animation-name: alt-chart-tint-b; }
|
|
1309
|
+
@media (prefers-reduced-motion: reduce) { .alt-num-text[data-in] { animation: none; } .alt-num-text[data-out] { display: none; } }
|
|
1310
|
+
[data-motion="reduced"] .alt-num-text[data-in] { animation: none; }
|
|
1311
|
+
[data-motion="reduced"] .alt-num-text[data-out] { display: none; }
|
|
1312
|
+
@media (forced-colors: active) { .alt-num[data-settle]::before { border: 1px solid Highlight; } }
|
|
1313
|
+
|
|
1314
|
+
/* ── RelativeTime: a timestamp is part of the record, so it's plain text on the ground. Ticking forward ("3 min" to
|
|
1315
|
+
"4 min") isn't a change to the record and gets nothing; a new moment (the record edited again) takes the same
|
|
1316
|
+
ground-level tint as a changed number. It's focusable for its Tooltip, so it carries the focus ring. ── */
|
|
1317
|
+
.alt-reltime { padding: 0 2px; border-radius: var(--radius-xs); font-variant-numeric: tabular-nums; }
|
|
1318
|
+
.alt-reltime:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 0; }
|
|
1319
|
+
.alt-reltime[data-settle]::before { animation-name: alt-chart-tint-a; }
|
|
1320
|
+
.alt-reltime[data-settle="b"]::before { animation-name: alt-chart-tint-b; }
|
|
1321
|
+
@media (forced-colors: active) { .alt-reltime[data-settle]::before { border: 1px solid Highlight; } .alt-reltime:focus-visible { outline-color: Highlight; } }
|
|
1322
|
+
|
|
1323
|
+
/* ── CopyButton: copying is a press that confirms in words. It's a Button (ghost by default); once copied it stays
|
|
1324
|
+
pressed to the ground for dur-linger, its icon cross-fades from copy to a check and its words to "Copied" (all its
|
|
1325
|
+
words share one cell, so its width never changes). No rise and no afterglow: nothing new arrived on the page. A
|
|
1326
|
+
failure says so in words, with the way to copy by hand announced. ── */
|
|
1327
|
+
.alt-copy-icons, .alt-copy-words { display: inline-grid; place-items: center; }
|
|
1328
|
+
.alt-copy-icons > svg, .alt-copy-words > span { grid-area: 1 / 1; transition: opacity var(--dur-travel-1) var(--ease-rise); }
|
|
1329
|
+
.alt-copy-icons > svg:not([data-shown]), .alt-copy-words > span:not([data-shown]) { opacity: 0; visibility: hidden; }
|
|
1330
|
+
.alt-copy-words > span { white-space: nowrap; }
|
|
1331
|
+
.alt-btn.alt-copy[data-copy="copied"], .alt-btn.alt-copy[data-copy="copied"]:focus-visible {
|
|
1332
|
+
box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); background-image: none; transform: translateY(var(--wall));
|
|
1333
|
+
}
|
|
1334
|
+
@media (hover: hover) { .alt-btn.alt-copy[data-copy="copied"]:hover {
|
|
1335
|
+
box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); background-image: none; transform: translateY(var(--wall));
|
|
1336
|
+
} }
|
|
1337
|
+
.alt-btn.alt-copy[data-variant="ghost"][data-copy="copied"] { background: transparent; }
|
|
1338
|
+
.alt-btn.alt-copy[data-copy="failed"] .alt-copy-icons { color: var(--critical); }
|
|
1339
|
+
@media (forced-colors: active) { .alt-btn.alt-copy:hover:not([data-copy="copied"]) { border: 1px solid ButtonText; } }
|
|
1340
|
+
|
|
1341
|
+
/* ── Toggle and ToggleGroup: a button that stays on. On is a choice, so it's built like the Switch, not like a held
|
|
1342
|
+
press (which would read as loading): the face is cut into a shallow well with accent-soft poured in and the label in
|
|
1343
|
+
accent-ink. Off is the ghost (ground) or secondary (level 1) slab it started as. Pointing at an on toggle previews
|
|
1344
|
+
off: the well lightens toward the raised face it will become. Sinking uses ease-sink, rising ease-rise.
|
|
1345
|
+
type="single" is segmented Tabs without panels: options in a well, one raised pill that glides (the same classes).
|
|
1346
|
+
type="multiple" is a row of Toggles. ── */
|
|
1347
|
+
.alt-btn.alt-toggle[aria-pressed="true"], .alt-btn.alt-toggle[aria-pressed="true"]:focus-visible, .alt-btn.alt-toggle[aria-pressed="true"]:active:not(:disabled) {
|
|
1348
|
+
--wall: 0px; background: var(--accent-soft); background-image: none; color: var(--accent-ink); box-shadow: var(--well); transform: none;
|
|
1349
|
+
transition: background-color var(--dur-travel-2) var(--ease-sink), box-shadow var(--dur-travel-2) var(--ease-sink), color var(--dur-travel-2) var(--ease-sink);
|
|
1350
|
+
}
|
|
1351
|
+
@media (hover: hover) { .alt-btn.alt-toggle[aria-pressed="true"]:hover:not(:disabled):not([data-loading]) {
|
|
1352
|
+
--wall: 0px; background: var(--accent-soft); background-image: none; color: var(--accent-ink); box-shadow: var(--well); transform: none;
|
|
1353
|
+
transition: background-color var(--dur-travel-2) var(--ease-sink), box-shadow var(--dur-travel-2) var(--ease-sink), color var(--dur-travel-2) var(--ease-sink);
|
|
1354
|
+
} }
|
|
1355
|
+
@media (hover: hover) { .alt-btn.alt-toggle[aria-pressed="true"]:hover:not(:disabled):not([data-loading]) { background: color-mix(in oklab, var(--accent-soft) 55%, var(--surface-raised)); } }
|
|
1356
|
+
.alt-btn.alt-toggle[aria-pressed="true"]:disabled { background: var(--surface-well); color: var(--ink-faint); }
|
|
1357
|
+
.alt-tgroup[data-type="multiple"] { display: inline-flex; align-items: center; gap: 2px; }
|
|
1358
|
+
.alt-tgroup[data-orientation="vertical"] { flex-direction: column; align-items: stretch; }
|
|
1359
|
+
.alt-tgroup[data-type="single"] .alt-tabs-indicator { transition: none; }
|
|
1360
|
+
.alt-tgroup[data-type="single"] .alt-tabs-indicator[data-ready] { transition: transform var(--dur-travel-3) var(--ease-glide), top var(--dur-travel-3) var(--ease-glide), width var(--dur-travel-3) var(--ease-glide), height var(--dur-travel-3) var(--ease-glide), opacity var(--dur-travel-1); }
|
|
1361
|
+
@media (prefers-reduced-motion: reduce) { .alt-tgroup[data-type="single"] .alt-tabs-indicator[data-ready] { transition: opacity var(--dur-travel-1); } }
|
|
1362
|
+
[data-motion="reduced"] .alt-tgroup[data-type="single"] .alt-tabs-indicator[data-ready] { transition: opacity var(--dur-travel-1); }
|
|
1363
|
+
.alt-tgroup .alt-tabs-trigger:disabled { color: var(--ink-faint); cursor: not-allowed; }
|
|
1364
|
+
@media (forced-colors: active) {
|
|
1365
|
+
.alt-btn.alt-toggle[aria-pressed="true"] { border: 1px dashed CanvasText; background: Highlight; color: HighlightText; forced-color-adjust: none; }
|
|
1366
|
+
.alt-btn.alt-toggle[data-variant="secondary"][aria-pressed="false"] { border: 1px solid CanvasText; }
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/* ── Toolbar: the bar lies on the ground (a line rim when bordered); its tools are ghost buttons that lift one level
|
|
1370
|
+
when pointed at, and latched tools are Toggles. Groups are parted by decorative separators. With overflow="menu" the
|
|
1371
|
+
tools that don't fit are hidden from the end and offered in a "More tools" menu; they don't animate as they go. ── */
|
|
1372
|
+
.alt-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); min-width: 0; }
|
|
1373
|
+
.alt-toolbar[data-overflow="menu"] { flex-wrap: nowrap; }
|
|
1374
|
+
.alt-toolbar[data-orientation="vertical"] { flex-direction: column; align-items: stretch; }
|
|
1375
|
+
.alt-toolbar[data-bordered] { padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-ground); }
|
|
1376
|
+
.alt-toolbar > [data-overflowed] { display: none; }
|
|
1377
|
+
.alt-toolbar-group { display: inline-flex; align-items: center; gap: 2px; }
|
|
1378
|
+
.alt-toolbar[data-orientation="vertical"] .alt-toolbar-group { flex-direction: column; align-items: stretch; }
|
|
1379
|
+
.alt-toolbar .alt-sep[data-orientation="vertical"] { align-self: center; height: 20px; min-height: 0; margin-inline: var(--space-1); }
|
|
1380
|
+
.alt-toolbar[data-orientation="vertical"] .alt-sep[data-orientation="vertical"] { width: auto; height: 1px; margin: var(--space-1) 0; }
|
|
1381
|
+
.alt-toolbar-more { flex: none; margin-left: auto; }
|
|
1382
|
+
@media (forced-colors: active) { .alt-toolbar-btn:hover:not([aria-pressed="true"]), .alt-toolbar-btn:focus-visible:not([aria-pressed="true"]) { border: 1px solid CanvasText; } }
|
|
1383
|
+
|
|
1384
|
+
/* ── SplitButton: one action with its alternatives. Two faces on one slab, sharing one wall: the action and a chevron,
|
|
1385
|
+
parted by a 1px notch (line-control). Each face lifts on its own when pointed at, because each does something
|
|
1386
|
+
different, and each has its own focus ring around its face and wall. A loading action is held pressed while the
|
|
1387
|
+
chevron stays usable. ── */
|
|
1388
|
+
.alt-split { display: inline-flex; align-items: stretch; isolation: isolate; }
|
|
1389
|
+
.alt-split .alt-pop-anchor { display: inline-flex; }
|
|
1390
|
+
.alt-split .alt-split-action { border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
1391
|
+
.alt-split .alt-split-menu { border-top-left-radius: 0; border-bottom-left-radius: 0; width: 32px; }
|
|
1392
|
+
.alt-split .alt-split-menu[data-size="sm"] { width: 26px; }
|
|
1393
|
+
.alt-split .alt-split-action::after { border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
1394
|
+
.alt-split .alt-split-menu::after { border-top-left-radius: 0; border-bottom-left-radius: 0; }
|
|
1395
|
+
.alt-split .alt-split-menu::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 1px; background: var(--line-control); pointer-events: none; }
|
|
1396
|
+
.alt-split[data-variant="primary"] .alt-split-menu::before { background: color-mix(in oklab, var(--on-accent) 45%, var(--accent)); }
|
|
1397
|
+
.alt-split .alt-split-action:focus-visible, .alt-split .alt-split-menu:focus-visible { z-index: 1; }
|
|
1398
|
+
/* One slab, so it moves as one: pointing at either face lifts both, and pressing either presses both, so the faces
|
|
1399
|
+
never stand at two heights with a step between them. Which face you're on (and so what a click does) is said by a
|
|
1400
|
+
tint on that face alone, not by height. */
|
|
1401
|
+
@media (hover: hover) {
|
|
1402
|
+
.alt-split:has(:is(.alt-split-action, .alt-split-menu):hover:not(:disabled)) :is(.alt-split-action, .alt-split-menu):not(:disabled) {
|
|
1403
|
+
background: var(--alt-bg-up); background-image: var(--alt-sheen);
|
|
1404
|
+
box-shadow: 0 calc(var(--wall) + 1px) 0 0 var(--wall-c), var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel)));
|
|
1405
|
+
}
|
|
1406
|
+
.alt-split:has(:is(.alt-split-action, .alt-split-menu):hover:not(:disabled)) :is(.alt-split-action, .alt-split-menu):hover:not(:disabled) { background-color: color-mix(in oklab, var(--alt-bg-up), var(--alt-fg) 12%); }
|
|
1407
|
+
}
|
|
1408
|
+
.alt-split:has(:is(.alt-split-action, .alt-split-menu):focus-visible) :is(.alt-split-action, .alt-split-menu):not(:disabled) {
|
|
1409
|
+
background-image: var(--alt-sheen); box-shadow: 0 calc(var(--wall) + 1px) 0 0 var(--wall-c), var(--alt-sh-up); transform: translateY(calc(-1px * var(--alt-travel)));
|
|
1410
|
+
}
|
|
1411
|
+
.alt-split:has(:is(.alt-split-action, .alt-split-menu):active:not(:disabled)) :is(.alt-split-action, .alt-split-menu):not(:disabled) {
|
|
1412
|
+
box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); background-image: none; transform: translateY(var(--wall));
|
|
1413
|
+
transition-duration: var(--dur-press); transition-timing-function: ease-out;
|
|
1414
|
+
}
|
|
1415
|
+
@media (forced-colors: active) { .alt-split .alt-split-menu::before { background: CanvasText; } }
|
|
1416
|
+
|
|
1417
|
+
/* ── NumberInput: an opening that fills, like Input, with two small raised steppers in its end cap: you press them,
|
|
1418
|
+
so they're slabs (push up, fill down), level 1 with a 1px wall, lifting to 2 when pointed at, flush and faint at a
|
|
1419
|
+
limit. The unit sits beside the number in ink-muted. Editing isn't a change to the record, so nothing glows. ── */
|
|
1420
|
+
.alt-numfield { padding-right: 3px; }
|
|
1421
|
+
.alt-numfield input { font-variant-numeric: tabular-nums; }
|
|
1422
|
+
.alt-numfield-unit { font: 400 14px/20px var(--font-sans); }
|
|
1423
|
+
/* Side by side, 24×24 each (#261): stacked, two 15px steppers could never each reach the 24px minimum. Down first. */
|
|
1424
|
+
.alt-numfield-steps { display: flex; flex-direction: row-reverse; gap: 2px; flex: none; align-self: center; }
|
|
1425
|
+
.alt-numfield-step {
|
|
1426
|
+
display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: var(--radius-xs); cursor: pointer;
|
|
1427
|
+
background: var(--surface-raised); background-image: var(--alt-sheen); color: var(--ink-muted);
|
|
1428
|
+
box-shadow: 0 1px 0 0 var(--surface-trench), var(--elev-1);
|
|
1429
|
+
transition: box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle), color var(--dur-travel-1);
|
|
1430
|
+
}
|
|
1431
|
+
.alt-numfield[data-size="sm"] .alt-numfield-step { height: 24px; }
|
|
1432
|
+
@media (hover: hover) { .alt-numfield-step:hover:not(:disabled) { background-color: var(--surface-lifted); color: var(--ink); box-shadow: 0 2px 0 0 var(--surface-trench), var(--elev-2); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
1433
|
+
.alt-numfield-step:active:not(:disabled) { box-shadow: 0 0 0 0 var(--surface-trench), var(--elev-0); background-image: none; transform: translateY(1px); transition-duration: var(--dur-press); }
|
|
1434
|
+
.alt-numfield-step:disabled { background: transparent; background-image: none; box-shadow: none; color: var(--ink-faint); cursor: default; }
|
|
1435
|
+
[data-contrast="more"] .alt-numfield-step:not(:disabled) { box-shadow: 0 1px 0 0 var(--ink), var(--elev-1); }
|
|
1436
|
+
@media (forced-colors: active) { .alt-numfield-step:not(:disabled) { border: 1px solid ButtonText; } }
|
|
1437
|
+
|
|
1438
|
+
/* ── InputOTP: a one-time code as a row of openings. One real input lies invisible over the row and drives it; each
|
|
1439
|
+
slot is built like an Input (the same well, wall and floor): empty, its back wall shows; the slot the next character
|
|
1440
|
+
goes into is held (floor lit, the depth approach would preview, the neutral rim when reached by keyboard) with a
|
|
1441
|
+
caret; once its character lands its floor rises flush. A pasted or autofilled code fills left to right, staggered
|
|
1442
|
+
35ms a slot. A rejected code drops the whole row to a trench on critical-soft. ── */
|
|
1443
|
+
.alt-otp { position: relative; display: inline-flex; align-items: center; gap: clamp(4px, 2vw, var(--space-2)); max-width: 100%; min-width: 0; }
|
|
1444
|
+
.alt-otp-input {
|
|
1445
|
+
position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; outline: 0; opacity: 0;
|
|
1446
|
+
background: transparent; color: transparent; caret-color: transparent; font-size: 16px; cursor: text;
|
|
1447
|
+
}
|
|
1448
|
+
.alt-otp-input:disabled { cursor: not-allowed; }
|
|
1449
|
+
/* Narrow (#266): slots give way from 40px to 28px, and gaps tighten, so an eight-character code fits a 320px screen. */
|
|
1450
|
+
.alt-otp-group { display: inline-flex; gap: clamp(3px, 1.6vw, 6px); min-width: 0; }
|
|
1451
|
+
.alt-otp-slot { justify-content: center; flex: 0 1 40px; width: 40px; min-width: 28px; height: 48px; padding: 0; font: 600 20px/1 var(--font-mono); cursor: text; }
|
|
1452
|
+
.alt-otp-slot[data-filled]:not([data-invalid]):not([data-disabled]) { --w: 0px; --n: 0; background: var(--surface-raised); }
|
|
1453
|
+
.alt-otp-slot[data-active]:not([data-invalid]) { background: var(--surface-raised); --n: var(--alt-travel); }
|
|
1454
|
+
.alt-otp[data-key-focus] .alt-otp-slot[data-active] { border-color: var(--alt-rim-held); outline-color: var(--alt-rim-held); }
|
|
1455
|
+
.alt-otp[data-key-focus] .alt-otp-slot[data-active][data-invalid] { border-color: var(--critical); outline-color: var(--critical); }
|
|
1456
|
+
.alt-otp-char { transform: translateY(calc(var(--wd) * 0.5)); transition: transform var(--dur-travel-2) var(--ease-settle); }
|
|
1457
|
+
.alt-otp-caret { position: absolute; left: 50%; top: 50%; width: 1.5px; height: 22px; margin: -11px 0 0 -0.75px; background: var(--ink); animation: alt-otp-blink 1.1s steps(1) infinite; }
|
|
1458
|
+
@keyframes alt-otp-blink { 50% { opacity: 0; } }
|
|
1459
|
+
@media (prefers-reduced-motion: reduce) { .alt-otp-caret { animation: none; } }
|
|
1460
|
+
[data-motion="reduced"] .alt-otp-caret { animation: none; }
|
|
1461
|
+
.alt-otp[data-burst] .alt-otp-slot { transition-delay: calc(var(--i) * 35ms); }
|
|
1462
|
+
.alt-otp-sep { flex: none; width: 10px; height: 2px; border-radius: var(--radius-full); background: var(--line-control); }
|
|
1463
|
+
@media (forced-colors: active) { .alt-otp-slot[data-filled]:not([data-invalid]) { border-style: solid !important; } .alt-otp-caret { background: CanvasText; } }
|
|
1464
|
+
|
|
1465
|
+
/* ── TimeInput: a time of day typed by segment, built like DatePicker's field (the same segments and well). It's an
|
|
1466
|
+
opening that fills only when every segment holds a value: a partial time ("09:––") is still a hole. A time zone's
|
|
1467
|
+
short name sits after it in ink-muted, and is spoken with it. ── */
|
|
1468
|
+
.alt-time { width: fit-content; min-width: 132px; max-width: 100%; }
|
|
1469
|
+
.alt-time-icon { margin-left: -2px; }
|
|
1470
|
+
.alt-time-gap { display: inline-block; width: 0.35em; }
|
|
1471
|
+
.alt-time-zone { font: 400 13px/20px var(--font-sans); }
|
|
1472
|
+
|
|
1473
|
+
/* ── PasswordInput: an Input with a reveal Toggle in its end cap (sunk and filled while the password shows). Creating
|
|
1474
|
+
a password, its strength is a reading, so it's a Meter (a flush track with flat ink), never Progress's raised fill,
|
|
1475
|
+
with the word beside its label; each requirement is stamped as met, like a done Checklist item, muted with a check.
|
|
1476
|
+
Caps Lock is a caption with an icon. ── */
|
|
1477
|
+
.alt-pw { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
|
|
1478
|
+
.alt-pw .alt-input { padding-right: 4px; }
|
|
1479
|
+
.alt-pw-reveal.alt-btn[data-square][data-size="sm"] { width: var(--size-control-sm); height: var(--size-control-sm); color: var(--ink-muted); }
|
|
1480
|
+
.alt-pw-caps { display: flex; align-items: center; gap: 6px; margin: 0; font: 500 12.5px/17px var(--font-sans); color: var(--ink); }
|
|
1481
|
+
.alt-pw-caps svg { flex: none; color: var(--caution); }
|
|
1482
|
+
.alt-pw-strength .alt-meter-label { display: inline-flex; gap: 8px; }
|
|
1483
|
+
.alt-pw-word { font-weight: 600; color: var(--ink); }
|
|
1484
|
+
.alt-pw-reqs { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; font: 400 13px/18px var(--font-sans); color: var(--ink); }
|
|
1485
|
+
.alt-pw-reqs li { display: flex; align-items: center; gap: 8px; transition: color var(--dur-travel-1) var(--ease-rise); }
|
|
1486
|
+
.alt-pw-reqs li[data-met] { color: var(--ink-muted); }
|
|
1487
|
+
.alt-pw-stamp { display: grid; place-items: center; flex: none; width: 16px; height: 16px; border-radius: var(--radius-full); border: 1px solid var(--line-control); color: var(--ink-muted); }
|
|
1488
|
+
.alt-pw-reqs li[data-met] .alt-pw-stamp { border-color: transparent; background: var(--surface-well); box-shadow: var(--well); }
|
|
1489
|
+
@media (forced-colors: active) { .alt-pw-stamp { border-color: CanvasText; } .alt-pw-caps svg { color: CanvasText; } }
|
|
1490
|
+
|
|
1491
|
+
/* ── SecretField: two readings. A new secret is an opening, something to take away: a sunk well (its back wall
|
|
1492
|
+
showing, though it holds text) in mono, with a raised Copy beside it and a caution warning, until you confirm you've
|
|
1493
|
+
saved it; then its floor rises flush and it masks to its tail. A stored secret is settled: flush, masked, with a
|
|
1494
|
+
reveal Toggle (if allowed), Rotate and a held revoke. ── */
|
|
1495
|
+
.alt-secret { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
|
|
1496
|
+
.alt-secret-row { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
1497
|
+
.alt-secret-field { flex: 1; min-width: 0; cursor: default; }
|
|
1498
|
+
.alt-input.alt-secret-field[data-open] { --w: 4px; background: var(--surface-well); }
|
|
1499
|
+
.alt-input.alt-secret-field[data-filled] { border-color: var(--line); }
|
|
1500
|
+
.alt-secret-value { flex: 1; min-width: 0; font: 400 13.5px/20px var(--font-mono); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; animation: alt-emerge var(--dur-travel-2) var(--ease-rise); --alt-from-y: 0px; }
|
|
1501
|
+
.alt-input input.alt-secret-value { font: 400 13.5px/20px var(--font-mono); cursor: text; }
|
|
1502
|
+
.alt-secret-dots { letter-spacing: 0.1em; color: var(--ink-muted); }
|
|
1503
|
+
.alt-secret-warning { display: flex; align-items: flex-start; gap: 6px; margin: 0; font: 500 12.5px/17px var(--font-sans); color: var(--ink); }
|
|
1504
|
+
.alt-secret-warning svg { flex: none; margin-top: 1.5px; color: var(--caution); }
|
|
1505
|
+
.alt-secret-saved:focus { outline: none; }
|
|
1506
|
+
.alt-secret-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
1507
|
+
@media (forced-colors: active) { .alt-input.alt-secret-field[data-filled] { border-style: solid !important; } .alt-secret-warning svg { color: CanvasText; } }
|
|
1508
|
+
|
|
1509
|
+
/* ── CodeBlock: code shown to read and copy is the record, so it lies flush on the ground: a 1px line rim, mono type,
|
|
1510
|
+
no shadow. The header carries its name, a segmented switch between forms (npm, pnpm) and a ghost CopyButton.
|
|
1511
|
+
Highlighted lines are accent-soft bands behind the text; line numbers sit in an ink-faint gutter. Code still
|
|
1512
|
+
arriving has a dashed line-control rule down its left and says "Writing…". Syntax colours are yours (ink, ink-muted
|
|
1513
|
+
and accent-ink by default, never the data palette) and carry nothing on their own. ── */
|
|
1514
|
+
.alt-code { --alt-code-lh: 20px; margin: 0; min-width: 0; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-ground); color: var(--ink); overflow: hidden; }
|
|
1515
|
+
.alt-code[data-streaming] { border-left: 2px dashed var(--line-control); }
|
|
1516
|
+
.alt-code-tabs { gap: 0; }
|
|
1517
|
+
.alt-code-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); min-height: 44px; padding: 6px 6px 6px var(--space-4); border-bottom: 1px solid var(--line); }
|
|
1518
|
+
.alt-code-head .alt-tabs-list { padding: 2px; }
|
|
1519
|
+
.alt-code-head .alt-tabs-trigger { height: 26px; padding: 0 var(--space-2); font-size: 13px; }
|
|
1520
|
+
.alt-code-title { font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
1521
|
+
.alt-code-title[data-language] { font-family: var(--font-mono); color: var(--ink-muted); }
|
|
1522
|
+
.alt-code-tools { display: inline-flex; align-items: center; gap: var(--space-2); margin-left: auto; }
|
|
1523
|
+
.alt-code-writing { font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
1524
|
+
.alt-code-panel { border-radius: 0; animation-duration: var(--dur-travel-1); --alt-from-y: 0px; }
|
|
1525
|
+
/* Scrolling in the page chains: a region on the ground (CodeBlock, ScrollArea, Table, Conversation, Terminal,
|
|
1526
|
+
streaming reasoning) hands the wheel or swipe to the page once it reaches its edge, so it never traps you. Only layers
|
|
1527
|
+
above the page (the CommandPalette list, Sheet, a Select's list, a menu as a sheet) contain their overscroll, since
|
|
1528
|
+
the page under them shouldn't move. */
|
|
1529
|
+
.alt-code-scroll { overflow: auto; }
|
|
1530
|
+
.alt-code-scroll:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1531
|
+
.alt-code-grid { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); min-width: max-content; padding: var(--space-3) 0; }
|
|
1532
|
+
.alt-code-grid[data-numbers] { grid-template-columns: auto minmax(0, 1fr); }
|
|
1533
|
+
.alt-code-band { position: absolute; left: 0; right: 0; top: calc(var(--space-3) + var(--l) * var(--alt-code-lh)); height: var(--alt-code-lh); background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent-ink); }
|
|
1534
|
+
.alt-code-gutter { position: relative; display: flex; flex-direction: column; padding: 0 var(--space-3) 0 var(--space-4); text-align: right; font: 400 12.5px/var(--alt-code-lh) var(--font-mono); color: var(--ink-faint); user-select: none; }
|
|
1535
|
+
.alt-code-pre { position: relative; margin: 0; padding: 0 var(--space-4); font: 400 13px/var(--alt-code-lh) var(--font-mono); tab-size: 2; white-space: pre; }
|
|
1536
|
+
.alt-code-grid[data-numbers] .alt-code-pre { padding-left: 0; }
|
|
1537
|
+
.alt-code-pre code { font: inherit; }
|
|
1538
|
+
.alt-code .tok-comment, .alt-code .token.comment, .alt-code .hljs-comment { color: var(--ink-muted); font-style: italic; }
|
|
1539
|
+
.alt-code .tok-keyword, .alt-code .token.keyword, .alt-code .hljs-keyword { color: var(--accent-ink); }
|
|
1540
|
+
.alt-code .tok-string, .alt-code .token.string, .alt-code .hljs-string { color: var(--ink); }
|
|
1541
|
+
@media (prefers-contrast: more) { .alt-code { border-color: var(--line-control); } }
|
|
1542
|
+
[data-contrast="more"] .alt-code { border-color: var(--line-control); }
|
|
1543
|
+
@media (forced-colors: active) { .alt-code { border: 1px solid CanvasText; } .alt-code-band { background: Mark; forced-color-adjust: none; box-shadow: none; } }
|
|
1544
|
+
|
|
1545
|
+
/* ── ColorPicker: colours as choices, built like RadioGroup. Each swatch is a trench (line-control rim) the colour
|
|
1546
|
+
pours into; the chosen one carries the raised white mark, one mark that travels between swatches on ease-glide.
|
|
1547
|
+
Pointing at another swatch shows a ghost of the mark (feedforward). The hex field is an Input that fills only when
|
|
1548
|
+
it holds a whole colour. The full picker floats in a Popover: the area and the hue strip are wells, their handles
|
|
1549
|
+
raised. A contrast check is a Badge plate that says in words whether text on the colour passes. ── */
|
|
1550
|
+
.alt-color { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
|
|
1551
|
+
.alt-swatches { position: relative; display: flex; flex-wrap: wrap; gap: 10px; }
|
|
1552
|
+
.alt-swatch {
|
|
1553
|
+
position: relative; flex: none; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line-control); border-radius: var(--radius-full);
|
|
1554
|
+
background: var(--surface-trench); box-shadow: var(--trench); cursor: pointer; -webkit-tap-highlight-color: transparent;
|
|
1555
|
+
}
|
|
1556
|
+
.alt-swatch-fill { position: absolute; inset: -1px; border-radius: inherit; background: var(--swatch); box-shadow: inset 0 3px 3px -2px var(--alt-cast-deep, rgb(0 0 0 / 0.35)), inset 0 0 0 1px rgb(0 0 0 / 0.12); }
|
|
1557
|
+
.alt-swatch:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1558
|
+
.alt-swatch::after {
|
|
1559
|
+
content: ""; position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: var(--radius-full);
|
|
1560
|
+
background: var(--surface-raised); box-shadow: var(--elev-1); opacity: 0; scale: 0.5; pointer-events: none;
|
|
1561
|
+
transition: opacity var(--dur-travel-1) var(--ease-sink), scale var(--dur-travel-2) var(--ease-settle);
|
|
1562
|
+
}
|
|
1563
|
+
@media (hover: hover) { .alt-swatch:hover:not([aria-checked="true"])::after { opacity: 0.5; scale: 0.85; } }
|
|
1564
|
+
.alt-swatch-mark {
|
|
1565
|
+
position: absolute; left: 0; top: 0; z-index: 1; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: var(--radius-full); pointer-events: none;
|
|
1566
|
+
background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1);
|
|
1567
|
+
}
|
|
1568
|
+
.alt-swatch-mark[data-ready] { transition: transform var(--dur-travel-3) var(--ease-glide), opacity var(--dur-travel-1); }
|
|
1569
|
+
@media (prefers-reduced-motion: reduce) { .alt-swatch-mark[data-ready] { transition: opacity var(--dur-travel-1); } }
|
|
1570
|
+
[data-motion="reduced"] .alt-swatch-mark[data-ready] { transition: opacity var(--dur-travel-1); }
|
|
1571
|
+
.alt-color-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
|
|
1572
|
+
.alt-color-hex { width: 150px; }
|
|
1573
|
+
.alt-color-hex input { font-family: var(--font-mono); text-transform: lowercase; }
|
|
1574
|
+
.alt-input.alt-color-partial:has(input:not(:placeholder-shown)):not(:focus-within) { --w: 4px; background: var(--surface-well); }
|
|
1575
|
+
.alt-color-chip { display: block; width: 16px; height: 16px; border-radius: var(--radius-xs); box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.15); }
|
|
1576
|
+
.alt-color-checks { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
1577
|
+
.alt-color-custom { display: flex; flex-direction: column; gap: 12px; width: 240px; max-width: 100%; padding: var(--space-3); }
|
|
1578
|
+
.alt-color-area, .alt-color-hue { position: relative; touch-action: none; cursor: crosshair; box-shadow: var(--well); }
|
|
1579
|
+
.alt-color-area { height: 150px; border-radius: var(--radius-md); background: linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, hsl(var(--hue) 100% 50%)); }
|
|
1580
|
+
/* tells-ignore: CO1 the hue track is the whole spectrum, purple included */
|
|
1581
|
+
.alt-color-hue { height: 14px; border-radius: var(--radius-full); background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
|
|
1582
|
+
.alt-color-area:focus-visible, .alt-color-hue:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1583
|
+
.alt-color-handle { position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px; border: 2px solid var(--surface-raised); border-radius: var(--radius-full); box-shadow: var(--elev-2); pointer-events: none; }
|
|
1584
|
+
[data-contrast="more"] .alt-swatch[aria-checked="true"] { outline: 2px solid var(--ink); outline-offset: 2px; }
|
|
1585
|
+
@media (forced-colors: active) {
|
|
1586
|
+
.alt-swatch-fill, .alt-color-area, .alt-color-hue, .alt-color-chip { forced-color-adjust: none; }
|
|
1587
|
+
.alt-swatch { border: 1px solid CanvasText; } .alt-swatch[aria-checked="true"] { border-width: 2px; } .alt-swatch-mark { border: 1px solid CanvasText; }
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
/* ── Pagination: the page numbers sit in a trench (surface-well) and the current page is one raised thumb that
|
|
1591
|
+
travels to it on ease-glide, as RadioGroup's does. Pointing at another page shows a ghost of the thumb there. While
|
|
1592
|
+
the next page loads the thumb is held pressed. Previous and Next are ghost buttons, flush and faint at the ends. ── */
|
|
1593
|
+
.alt-pager { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
|
|
1594
|
+
.alt-pager-pages { display: inline-flex; align-items: center; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
1595
|
+
.alt-pager-track { position: relative; display: inline-flex; align-items: center; gap: 2px; padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-well); box-shadow: var(--well); }
|
|
1596
|
+
.alt-pager-track li { display: flex; }
|
|
1597
|
+
.alt-pager-page {
|
|
1598
|
+
position: relative; z-index: 1; display: inline-grid; place-items: center; min-width: 30px; height: 28px; padding: 0 var(--space-2); border: 0; border-radius: calc(var(--radius-md) - var(--space-1));
|
|
1599
|
+
background: none; color: var(--ink-muted); font: 500 13px/18px var(--font-sans); font-variant-numeric: tabular-nums; text-decoration: none; cursor: pointer;
|
|
1600
|
+
transition: color var(--dur-travel-1) var(--ease-rise);
|
|
1601
|
+
}
|
|
1602
|
+
.alt-pager-page::after { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: var(--surface-raised); box-shadow: var(--elev-1); opacity: 0; transition: opacity var(--dur-travel-1) var(--ease-sink); }
|
|
1603
|
+
@media (hover: hover) { .alt-pager-page:hover:not([aria-current])::after { opacity: 0.45; transition-timing-function: var(--ease-rise); } }
|
|
1604
|
+
.alt-pager-page[aria-current] { color: var(--ink); }
|
|
1605
|
+
@media (hover: hover) { .alt-pager-page:hover { color: var(--ink); } }
|
|
1606
|
+
.alt-pager-page[aria-current] { font-weight: 600; cursor: default; }
|
|
1607
|
+
.alt-pager-page:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1608
|
+
.alt-pager-thumb { position: absolute; top: var(--space-1); bottom: var(--space-1); left: 0; border-radius: calc(var(--radius-md) - var(--space-1)); background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); pointer-events: none; }
|
|
1609
|
+
.alt-pager-thumb[data-ready] { transition: transform var(--dur-travel-1) var(--ease-glide), width var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1), opacity var(--dur-travel-1); }
|
|
1610
|
+
.alt-pager-thumb[data-loading] { box-shadow: var(--elev-0); background-image: none; }
|
|
1611
|
+
.alt-pager-gap { display: inline-grid; place-items: center; min-width: 24px; color: var(--ink-faint); font: 500 13px/18px var(--font-sans); }
|
|
1612
|
+
.alt-pager-step { gap: 4px; text-decoration: none; }
|
|
1613
|
+
.alt-pager-step[aria-disabled] { color: var(--ink-faint); cursor: default; pointer-events: none; }
|
|
1614
|
+
.alt-pager-summary { font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
1615
|
+
@media (max-width: 480px) { .alt-pager-step-text { display: none; } }
|
|
1616
|
+
@media (prefers-reduced-motion: reduce) { .alt-pager-thumb[data-ready] { transition: opacity var(--dur-travel-1); } }
|
|
1617
|
+
[data-motion="reduced"] .alt-pager-thumb[data-ready] { transition: opacity var(--dur-travel-1); }
|
|
1618
|
+
[data-contrast="more"] .alt-pager-thumb { box-shadow: 0 2px 0 0 var(--ink), var(--elev-1); }
|
|
1619
|
+
@media (forced-colors: active) { .alt-pager-track { border: 1px dashed CanvasText; } .alt-pager-thumb { border: 1px solid CanvasText; } }
|
|
1620
|
+
|
|
1621
|
+
/* ── PageHeader: the page's name is the record, so the block lies on the ground; only its actions are live, with one
|
|
1622
|
+
slot for the primary (level 2) and the rest secondary or ghost. Sticky, it rises one level (surface-raised, elev-1)
|
|
1623
|
+
when content passes under it, as ScrollArea's header does. ── */
|
|
1624
|
+
/* --alt-page-pad is the page's side padding (AppShell sets it): the header reaches through it, so a sticky header's
|
|
1625
|
+
rise spans the page edge to edge. */
|
|
1626
|
+
.alt-page-head { display: flex; flex-direction: column; gap: var(--space-3); margin-inline: calc(-1 * var(--alt-page-pad, 0px)); padding: var(--space-4) var(--alt-page-pad, 0px); background: var(--surface-ground); }
|
|
1627
|
+
.alt-page-head[data-sticky] { position: sticky; top: 0; z-index: 10; transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
1628
|
+
.alt-page-head[data-under] { background: var(--surface-raised); box-shadow: var(--elev-1); }
|
|
1629
|
+
.alt-page-head-sentinel { display: block; height: 1px; margin-bottom: -1px; }
|
|
1630
|
+
.alt-page-head-crumbs .alt-crumbs ol { padding-top: 6px; padding-bottom: 0; }
|
|
1631
|
+
.alt-page-head-main { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-3) var(--space-5); }
|
|
1632
|
+
.alt-page-head-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: min(100%, 240px); flex: 1; }
|
|
1633
|
+
.alt-page-head-title { margin: 0; font: 600 24px/30px var(--font-sans); letter-spacing: -0.015em; color: var(--ink); }
|
|
1634
|
+
.alt-page-head-desc { margin: 0; font: 400 14px/21px var(--font-sans); color: var(--ink-muted); max-width: 70ch; }
|
|
1635
|
+
.alt-page-head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
|
|
1636
|
+
.alt-page-head-tabs .alt-tabs { gap: 0; }
|
|
1637
|
+
@media (forced-colors: active) { .alt-page-head[data-under] { border-bottom: 1px solid CanvasText; } }
|
|
1638
|
+
|
|
1639
|
+
/* ── AppShell and Sidebar: where you are is part of the record, so the frame lies on the ground: sidebar, top bar
|
|
1640
|
+
and page on surface-ground, divided by line. Only what acts is live: a destination lifts one level when pointed at,
|
|
1641
|
+
and the current one sits on a raised pill (segmented Tabs' build) that glides between destinations. Only a count
|
|
1642
|
+
floats. Collapsing to a rail changes width on ease-glide, not level. The top bar rises one level while content
|
|
1643
|
+
passes under it. On narrow screens the sidebar is a Sheet drawer. ── */
|
|
1644
|
+
.alt-shell { --alt-sidebar-w: 248px; --alt-rail-w: 56px; position: relative; display: flex; height: 100%; min-height: 0; background: var(--surface-ground); color: var(--ink); }
|
|
1645
|
+
.alt-skip { position: absolute; left: var(--space-2); top: var(--space-2); z-index: 50; padding: 6px 12px; border-radius: var(--radius-sm); background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--ink); font: 500 13px/18px var(--font-sans); transform: translateY(-200%); }
|
|
1646
|
+
.alt-skip:focus { transform: none; outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1647
|
+
.alt-sidebar { display: flex; flex-direction: column; flex: none; width: var(--alt-sidebar-w); min-height: 0; border-right: 1px solid var(--line); background: var(--surface-ground); overflow: hidden; transition: width var(--dur-travel-3) var(--ease-glide); }
|
|
1648
|
+
.alt-sidebar[data-rail] { width: var(--alt-rail-w); }
|
|
1649
|
+
.alt-sidebar-drawer .alt-sidebar { width: auto; border: 0; background: none; margin-inline: calc(-1 * var(--space-2)); }
|
|
1650
|
+
.alt-sidebar-header, .alt-sidebar-footer { flex: none; padding: var(--space-2); }
|
|
1651
|
+
.alt-sidebar-header { border-bottom: 1px solid var(--line); }
|
|
1652
|
+
.alt-sidebar-footer { border-top: 1px solid var(--line); }
|
|
1653
|
+
.alt-sidebar-content { position: relative; flex: 1; min-height: 0; overflow: auto; padding: var(--space-2); }
|
|
1654
|
+
.alt-sidebar-pill { position: absolute; left: 0; top: 0; border-radius: var(--radius-sm); background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); pointer-events: none; }
|
|
1655
|
+
.alt-sidebar-pill[data-ready] { transition: transform var(--dur-travel-3) var(--ease-glide), width var(--dur-travel-3) var(--ease-glide), height var(--dur-travel-3) var(--ease-glide), opacity var(--dur-travel-1); }
|
|
1656
|
+
.alt-sidebar-group + .alt-sidebar-group { margin-top: var(--space-3); }
|
|
1657
|
+
.alt-sidebar-label { display: flex; align-items: center; gap: 4px; width: 100%; height: 28px; padding: 0 var(--space-2); border: 0; background: none; font: 500 12px/16px var(--font-sans); letter-spacing: 0.01em; color: var(--ink-muted); text-align: left; white-space: nowrap; }
|
|
1658
|
+
button.alt-sidebar-label { cursor: pointer; border-radius: var(--radius-sm); }
|
|
1659
|
+
button.alt-sidebar-label:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1660
|
+
.alt-sidebar-chev { margin-left: auto; transition: transform var(--dur-travel-2) var(--ease-settle); }
|
|
1661
|
+
.alt-sidebar-label[aria-expanded="false"] .alt-sidebar-chev { transform: rotate(-90deg); }
|
|
1662
|
+
.alt-sidebar[data-rail] .alt-sidebar-label { visibility: hidden; height: 12px; }
|
|
1663
|
+
.alt-sidebar-menu { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
1664
|
+
.alt-sidebar-item { position: relative; display: flex; align-items: center; }
|
|
1665
|
+
.alt-sidebar-button {
|
|
1666
|
+
position: relative; z-index: 1; display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; height: calc(var(--size-control-md) - 2px); padding: 0 var(--space-2); border-radius: var(--radius-sm);
|
|
1667
|
+
font: 500 14px/20px var(--font-sans); color: var(--ink-muted); text-decoration: none; white-space: nowrap; cursor: pointer;
|
|
1668
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), color var(--dur-travel-1);
|
|
1669
|
+
}
|
|
1670
|
+
@media (hover: hover) { .alt-sidebar-button:hover:not([aria-current]) { background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink); } }
|
|
1671
|
+
.alt-sidebar-button[aria-current] { color: var(--ink); font-weight: 600; }
|
|
1672
|
+
.alt-sidebar-button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1673
|
+
.alt-sidebar-icon { display: inline-flex; flex: none; width: 20px; justify-content: center; }
|
|
1674
|
+
.alt-sidebar-text { overflow: hidden; text-overflow: ellipsis; transition: opacity var(--dur-travel-2) var(--ease-rise); }
|
|
1675
|
+
/* Artifact (#271): what an agent is making, in a pane on the ground beside the conversation. Being written isn't the
|
|
1676
|
+
record yet: a dashed line-control rule at the pane's edge; complete goes solid with one afterglow; failed, critical.
|
|
1677
|
+
Viewing an earlier version is a mode, so it's a flush strip with a line-control border, not a lift. The reference in
|
|
1678
|
+
a message is a plate that acts. */
|
|
1679
|
+
.alt-artifact { display: flex; flex-direction: column; min-width: 0; min-height: 0; height: 100%; background: var(--surface-ground); border-left: 2px solid var(--line); outline: none; }
|
|
1680
|
+
.alt-artifact[data-status="writing"] { border-left-style: dashed; border-left-color: var(--line-control); }
|
|
1681
|
+
.alt-artifact-head { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-3) var(--space-2) var(--space-4); border-bottom: 1px solid var(--line); }
|
|
1682
|
+
.alt-artifact-titles { flex: 1; min-width: 0; }
|
|
1683
|
+
.alt-artifact-title { margin: 0; font: 600 15px/21px var(--font-sans); color: var(--ink); overflow-wrap: anywhere; outline: none; }
|
|
1684
|
+
.alt-artifact-title:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-sm); }
|
|
1685
|
+
.alt-artifact-state { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
1686
|
+
.alt-artifact-state[data-tone="critical"] { color: var(--critical); }
|
|
1687
|
+
.alt-artifact-tools { display: inline-flex; gap: 2px; }
|
|
1688
|
+
.alt-artifact-versions { display: flex; align-items: center; gap: 2px; padding: 4px var(--space-3); }
|
|
1689
|
+
.alt-artifact-version { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
1690
|
+
.alt-artifact-earlier { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding: 6px var(--space-3) 6px var(--space-4); border-block: 1px solid var(--line-control); font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
1691
|
+
.alt-artifact-earlier > span { margin-right: auto; }
|
|
1692
|
+
.alt-artifact-views { display: flex; flex-direction: column; flex: 1; min-height: 0; }
|
|
1693
|
+
.alt-artifact-views > .alt-tabs-list { align-self: flex-start; margin: var(--space-2) var(--space-3); }
|
|
1694
|
+
.alt-artifact-body { flex: 1; min-height: 0; overflow: auto; padding: var(--space-3); animation: alt-fade-in var(--dur-travel-1) var(--ease-rise) both; }
|
|
1695
|
+
.alt-artifact-body iframe { display: block; width: 100%; height: 100%; min-height: 280px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-ground); }
|
|
1696
|
+
.alt-artifact-ref { display: inline-flex; align-items: center; gap: 10px; max-width: 100%; padding: 8px 12px 8px 10px; border: 0; border-radius: var(--radius-md); text-align: left; cursor: pointer;
|
|
1697
|
+
background: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent); color: var(--ink);
|
|
1698
|
+
transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise); }
|
|
1699
|
+
@media (hover: hover) { .alt-artifact-ref:hover { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
1700
|
+
.alt-artifact-ref:active { transform: none; box-shadow: none; transition-duration: var(--dur-press); }
|
|
1701
|
+
.alt-artifact-ref:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1702
|
+
.alt-artifact-ref-icon { display: grid; place-items: center; width: 28px; height: 28px; border-radius: var(--radius-sm); background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink-muted); }
|
|
1703
|
+
.alt-artifact-ref-text { display: flex; flex-direction: column; min-width: 0; }
|
|
1704
|
+
.alt-artifact-ref-title { font: 600 13.5px/19px var(--font-sans); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1705
|
+
.alt-artifact-ref-meta { font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1706
|
+
@media (prefers-reduced-motion: reduce) { .alt-artifact-body { animation: none; } }
|
|
1707
|
+
[data-contrast="more"] .alt-artifact { border-left-color: var(--line-control); }
|
|
1708
|
+
@media (forced-colors: active) {
|
|
1709
|
+
.alt-artifact { border-left-color: CanvasText; }
|
|
1710
|
+
.alt-artifact[data-status="writing"] { border-left-style: dotted; }
|
|
1711
|
+
.alt-artifact-ref { border: 1px solid CanvasText; }
|
|
1712
|
+
}
|
|
1713
|
+
/* ThreadList (#269): conversations as Sidebar destinations, each with its state in words. Working isn't pending, so it
|
|
1714
|
+
never hovers: a plate with a steady lamp. Needs-you is a caution plate (a row can't lift into a card inside a nav).
|
|
1715
|
+
Unread is a dot and weight 600. Renaming opens a well in the row; deleting leaves a receipt in its slot. */
|
|
1716
|
+
.alt-threads { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
|
|
1717
|
+
.alt-threads-search { margin: 0 var(--space-2); }
|
|
1718
|
+
.alt-thread .alt-thread-link { height: auto; min-height: 44px; padding-block: 5px; align-items: flex-start; }
|
|
1719
|
+
.alt-thread .alt-sidebar-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; width: 100%; }
|
|
1720
|
+
.alt-thread-main { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
1721
|
+
.alt-thread-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1722
|
+
.alt-thread[data-unread] .alt-thread-title { font-weight: 600; color: var(--ink); }
|
|
1723
|
+
.alt-thread-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
|
|
1724
|
+
.alt-thread-meta { display: flex; align-items: center; gap: 6px; min-width: 0; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1725
|
+
.alt-thread-plate { flex: none; }
|
|
1726
|
+
.alt-thread-time { white-space: nowrap; }
|
|
1727
|
+
.alt-thread-more { position: absolute; right: 4px; top: 6px; opacity: 0; transition: opacity var(--dur-travel-1); }
|
|
1728
|
+
.alt-thread:focus-within .alt-thread-more, .alt-thread-more[aria-expanded="true"] { opacity: 1; }
|
|
1729
|
+
@media (hover: hover) { .alt-thread:hover .alt-thread-more { opacity: 1; } }
|
|
1730
|
+
@media (hover: none) { .alt-thread-more { opacity: 1; } }
|
|
1731
|
+
.alt-thread:has(.alt-thread-more) .alt-thread-main { padding-right: 28px; }
|
|
1732
|
+
.alt-thread-rename { display: block; width: 100%; padding: 4px; }
|
|
1733
|
+
.alt-threads-new { width: 100%; justify-content: center; }
|
|
1734
|
+
/* AgentFleet: every agent in a run of work, grouped by what they need from you. Rows are the record, flush, with their
|
|
1735
|
+
state in words on a plate (the lamp only repeats it); working never hovers. The open agent is selected: tinted and
|
|
1736
|
+
raised one level, as a table's selected row. Finished agents are stamped: a check and muted ink. Agents an agent
|
|
1737
|
+
started hang under it in a 1px line-control bracket, as TraceParallel's subagents do. */
|
|
1738
|
+
.alt-fleet { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
|
|
1739
|
+
.alt-fleet-summary { margin: 0; font: 500 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1740
|
+
.alt-fleet-group { display: flex; flex-direction: column; gap: 2px; }
|
|
1741
|
+
.alt-fleet-group-head { display: flex; align-items: center; gap: 6px; min-height: 28px; margin: 0; padding: 0 var(--space-2); border: 0; background: none; color: var(--ink-muted); font: 600 13px/18px var(--font-sans); text-align: start; }
|
|
1742
|
+
button.alt-fleet-group-head { cursor: pointer; border-radius: var(--radius-sm); }
|
|
1743
|
+
button.alt-fleet-group-head:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1744
|
+
.alt-fleet-chevron { transform: rotate(-90deg); transition: transform var(--dur-travel-1) var(--ease-glide); }
|
|
1745
|
+
.alt-fleet-group[data-open] .alt-fleet-chevron { transform: none; }
|
|
1746
|
+
.alt-fleet-group-count { font-variant-numeric: tabular-nums; font-weight: 500; }
|
|
1747
|
+
.alt-fleet-empty { margin: 0; padding: var(--space-1) var(--space-2); font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1748
|
+
.alt-fleet-list, .alt-agent-children { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
1749
|
+
.alt-fleet-list[hidden] { display: none; }
|
|
1750
|
+
.alt-agent-children { margin: 2px 0 2px 18px; padding-left: 8px; border-left: 1px solid var(--line-control); }
|
|
1751
|
+
.alt-agent-line { position: relative; }
|
|
1752
|
+
.alt-agent-row { --row-bg: transparent; --row-sh: var(--elev-0);
|
|
1753
|
+
display: flex; flex-direction: column; gap: 3px; width: 100%; min-width: 0; padding: 8px var(--space-2); border: 0; border-radius: var(--radius-md);
|
|
1754
|
+
background: var(--row-bg); box-shadow: var(--row-sh); color: var(--ink); font: 400 13px/18px var(--font-sans); text-align: start;
|
|
1755
|
+
transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise); }
|
|
1756
|
+
button.alt-agent-row { cursor: pointer; }
|
|
1757
|
+
@media (hover: hover) { button.alt-agent-row:hover { --row-bg: var(--surface-raised); --row-sh: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
1758
|
+
button.alt-agent-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1759
|
+
.alt-agent[data-selected] > .alt-agent-line > .alt-agent-row { --row-bg: var(--accent-soft); --row-sh: var(--elev-1); }
|
|
1760
|
+
.alt-agent-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
1761
|
+
.alt-agent-line:has(.alt-agent-output) .alt-agent-head { padding-right: 64px; }
|
|
1762
|
+
.alt-agent-mark { display: inline-flex; flex: none; width: 14px; justify-content: center; color: var(--ink-muted); }
|
|
1763
|
+
.alt-agent-mark i { width: 7px; height: 7px; border-radius: 50%; border: 1.5px solid currentColor; }
|
|
1764
|
+
.alt-agent[data-agent-state="working"] > .alt-agent-line .alt-agent-mark { color: var(--accent); }
|
|
1765
|
+
.alt-agent[data-agent-state="working"] > .alt-agent-line .alt-agent-mark i { background: currentColor; }
|
|
1766
|
+
.alt-agent[data-agent-state="needs-input"] > .alt-agent-line .alt-agent-mark, .alt-agent[data-agent-state="stalled"] > .alt-agent-line .alt-agent-mark { color: var(--caution); }
|
|
1767
|
+
.alt-agent[data-agent-state="needs-input"] > .alt-agent-line .alt-agent-mark i { background: currentColor; }
|
|
1768
|
+
.alt-agent[data-agent-state="failed"] > .alt-agent-line .alt-agent-mark { color: var(--critical); }
|
|
1769
|
+
.alt-agent[data-agent-state="done"] > .alt-agent-line .alt-agent-mark { color: var(--positive); }
|
|
1770
|
+
.alt-agent-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
1771
|
+
.alt-agent-time { flex: none; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1772
|
+
.alt-agent-activity { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; padding-left: 22px; color: var(--ink-muted); }
|
|
1773
|
+
.alt-agent-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; padding-left: 22px; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
1774
|
+
.alt-agent-conflict { display: flex; align-items: flex-start; gap: 6px; margin: 3px 0 0 22px; padding: 4px 8px; border-radius: var(--radius-sm); background: var(--caution-soft); color: var(--ink); font: 400 12px/16px var(--font-sans); }
|
|
1775
|
+
.alt-agent-conflict svg { flex: none; margin-top: 1px; color: var(--caution); }
|
|
1776
|
+
.alt-agent-conflict code { font: 400 11.5px/16px var(--font-code); }
|
|
1777
|
+
.alt-agent-output { position: absolute; top: 7px; right: var(--space-2); }
|
|
1778
|
+
.alt-agent:is([data-agent-state="done"], [data-agent-state="stopped"]) > .alt-agent-line .alt-agent-name { font-weight: 500; color: var(--ink-muted); }
|
|
1779
|
+
@media (forced-colors: active) {
|
|
1780
|
+
.alt-agent[data-selected] > .alt-agent-line > .alt-agent-row { outline: 1px solid Highlight; }
|
|
1781
|
+
.alt-agent-children { border-left-color: CanvasText; }
|
|
1782
|
+
.alt-agent-conflict { border: 1px dashed CanvasText; }
|
|
1783
|
+
}
|
|
1784
|
+
/* AgentRequests: the decisions agents are waiting on, oldest first. Each asks for action, so it's a raised card (level
|
|
1785
|
+
1) that arrives lifted and settles; nothing in it has been dispatched, so nothing hovers. Its answers are secondary
|
|
1786
|
+
and ghost: the view's one primary stays where it is. With nothing waiting it says so, flush. */
|
|
1787
|
+
.alt-requests { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
|
|
1788
|
+
.alt-requests-head { display: flex; align-items: center; gap: 8px; min-height: 28px; }
|
|
1789
|
+
.alt-requests-title { margin: 0; font: 600 14px/20px var(--font-sans); color: var(--ink); }
|
|
1790
|
+
.alt-requests-oldest { margin-left: auto; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1791
|
+
.alt-requests-empty { display: flex; align-items: center; gap: 8px; margin: 0; padding: var(--space-2) 0; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1792
|
+
.alt-requests-empty svg { flex: none; color: var(--positive); }
|
|
1793
|
+
.alt-requests-list { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
|
|
1794
|
+
.alt-request { display: flex; flex-direction: column; gap: 6px; padding: var(--space-3); border-radius: var(--radius-md); }
|
|
1795
|
+
.alt-request-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1796
|
+
.alt-request-agent { font-weight: 600; color: var(--ink); }
|
|
1797
|
+
.alt-request-time { margin-left: auto; font-variant-numeric: tabular-nums; }
|
|
1798
|
+
.alt-request-title { margin: 0; font: 600 14px/20px var(--font-sans); color: var(--ink); }
|
|
1799
|
+
.alt-request-detail { margin: 0; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1800
|
+
.alt-request-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: 2px; }
|
|
1801
|
+
.alt-request-actions > .alt-btn[data-variant="ghost"]:last-child { margin-left: auto; }
|
|
1802
|
+
/* AgentLanes: where each agent's time went, on one clock. A chart, so nothing here has height (Charts): stretches are
|
|
1803
|
+
equal bars lying on each lane, told apart by fill and pattern and named in the key. Working is data-1; waiting on you
|
|
1804
|
+
is caution hatched at 45°; waiting on another agent is a 1px dashed outline; failed is critical. "Now" is a 1px
|
|
1805
|
+
line-control rule. The selected lane's name is accent-ink with aria-pressed; lanes never lift. */
|
|
1806
|
+
.alt-lanes { --lane-name: 132px; display: flex; flex-direction: column; gap: var(--space-2); margin: 0; min-width: 0; }
|
|
1807
|
+
.alt-lanes-takeaway { font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
1808
|
+
.alt-lanes-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 0; padding: 0; list-style: none; font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1809
|
+
.alt-lanes-legend li { display: inline-flex; align-items: center; gap: 6px; }
|
|
1810
|
+
.alt-lane-swatch { display: inline-block; width: 14px; height: 10px; border-radius: 2px; }
|
|
1811
|
+
.alt-lanes-plot { position: relative; display: flex; flex-direction: column; gap: 6px; }
|
|
1812
|
+
.alt-lane { display: grid; grid-template-columns: var(--lane-name) 1fr; align-items: center; gap: var(--space-2); }
|
|
1813
|
+
.alt-lane-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0; border: 0; background: none; color: var(--ink); font: 500 12.5px/24px var(--font-sans); text-align: start; }
|
|
1814
|
+
button.alt-lane-name { cursor: pointer; text-decoration: underline transparent; text-underline-offset: 3px; border-radius: var(--radius-xs); }
|
|
1815
|
+
@media (hover: hover) { button.alt-lane-name:hover { text-decoration-color: currentColor; } }
|
|
1816
|
+
button.alt-lane-name:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
1817
|
+
.alt-lane[data-selected] .alt-lane-name { color: var(--accent-ink); font-weight: 650; }
|
|
1818
|
+
.alt-lane-track { position: relative; height: 14px; border-bottom: 1px solid var(--line); }
|
|
1819
|
+
.alt-lane-bar { position: absolute; top: 1px; bottom: 1px; min-width: 2px; border-radius: 2px; }
|
|
1820
|
+
.alt-lane-swatch[data-lane="working"], .alt-lane-bar[data-lane="working"] { background: var(--data-1); }
|
|
1821
|
+
.alt-lane-swatch[data-lane="waiting"], .alt-lane-bar[data-lane="waiting"] { background: repeating-linear-gradient(45deg, var(--caution) 0 2px, var(--caution-soft) 2px 5px); box-shadow: inset 0 0 0 1px var(--caution); }
|
|
1822
|
+
.alt-lane-swatch[data-lane="blocked"], .alt-lane-bar[data-lane="blocked"] { background: transparent; box-shadow: none; border: 1px dashed var(--ink-muted); }
|
|
1823
|
+
.alt-lane-swatch[data-lane="failed"], .alt-lane-bar[data-lane="failed"] { background: var(--critical); }
|
|
1824
|
+
.alt-lanes-axis { display: grid; grid-template-columns: var(--lane-name) 1fr; gap: var(--space-2); }
|
|
1825
|
+
.alt-lanes-ticks { position: relative; height: 16px; font: 400 11.5px/16px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
1826
|
+
.alt-lanes-ticks > span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
|
|
1827
|
+
.alt-lanes-ticks > span[data-edge="start"] { transform: none; }
|
|
1828
|
+
.alt-lanes-ticks > span[data-edge="end"] { transform: translateX(-100%); color: var(--ink); }
|
|
1829
|
+
.alt-lanes-now { position: absolute; top: 0; bottom: 18px; right: 0; width: 1px; background: var(--line-control); }
|
|
1830
|
+
@media (forced-colors: active) {
|
|
1831
|
+
.alt-lane-bar, .alt-lane-swatch { forced-color-adjust: none; }
|
|
1832
|
+
.alt-lane-bar[data-lane="working"], .alt-lane-swatch[data-lane="working"] { background: CanvasText; }
|
|
1833
|
+
.alt-lane-bar[data-lane="waiting"], .alt-lane-swatch[data-lane="waiting"] { background: repeating-linear-gradient(45deg, CanvasText 0 2px, Canvas 2px 5px); box-shadow: inset 0 0 0 1px CanvasText; }
|
|
1834
|
+
.alt-lane-bar[data-lane="blocked"], .alt-lane-swatch[data-lane="blocked"] { border-color: CanvasText; }
|
|
1835
|
+
.alt-lane-bar[data-lane="failed"], .alt-lane-swatch[data-lane="failed"] { background: repeating-linear-gradient(-45deg, CanvasText 0 1px, Canvas 1px 3px); border: 2px solid CanvasText; }
|
|
1836
|
+
.alt-lanes-now { background: CanvasText; }
|
|
1837
|
+
}
|
|
1838
|
+
@media (max-width: 480px) { .alt-lanes { --lane-name: 88px; } }
|
|
1839
|
+
@media (pointer: coarse) { button.alt-fleet-group-head, button.alt-lane-name { min-height: 44px; } }
|
|
1840
|
+
/* In the rail only icons show: each destination is a square, and the rail lets its tooltips out to the right. */
|
|
1841
|
+
.alt-sidebar[data-rail], .alt-sidebar[data-rail] .alt-sidebar-content { overflow: visible; }
|
|
1842
|
+
.alt-sidebar[data-rail] .alt-sidebar-item .alt-tip-anchor { display: flex; flex: 1; }
|
|
1843
|
+
.alt-sidebar[data-rail] .alt-sidebar-button { justify-content: center; padding: 0; }
|
|
1844
|
+
.alt-sidebar[data-rail] .alt-sidebar-text { display: none; }
|
|
1845
|
+
.alt-sidebar[data-rail] .alt-sidebar-header .alt-sidebar-text { display: none; }
|
|
1846
|
+
.alt-sidebar-badge { position: absolute; right: var(--space-2); z-index: 2; pointer-events: none; }
|
|
1847
|
+
.alt-sidebar[data-rail] .alt-sidebar-badge { right: 2px; top: 0; transform: scale(0.8); }
|
|
1848
|
+
.alt-shell-page { position: relative; flex: 1; min-width: 0; min-height: 0; overflow: auto; }
|
|
1849
|
+
.alt-topbar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: var(--space-2); height: 52px; padding: 0 var(--space-4); border-bottom: 1px solid var(--line); background: var(--surface-ground); transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
1850
|
+
.alt-topbar[data-under] { background: var(--surface-raised); box-shadow: var(--elev-1); }
|
|
1851
|
+
.alt-shell-sentinel { display: block; height: 1px; margin-bottom: -1px; }
|
|
1852
|
+
.alt-inset { --alt-page-pad: var(--alt-inset-page); display: block; padding: 0 var(--alt-page-pad) var(--alt-page-pad); outline: none; }
|
|
1853
|
+
@media (prefers-reduced-motion: reduce) { .alt-sidebar { transition: none; } .alt-sidebar-pill[data-ready] { transition: opacity var(--dur-travel-1); } }
|
|
1854
|
+
[data-motion="reduced"] .alt-sidebar { transition: none; }
|
|
1855
|
+
[data-motion="reduced"] .alt-sidebar-pill[data-ready] { transition: opacity var(--dur-travel-1); }
|
|
1856
|
+
@media (prefers-contrast: more) { .alt-sidebar, .alt-sidebar-header, .alt-sidebar-footer, .alt-topbar { border-color: var(--line-control); } }
|
|
1857
|
+
[data-contrast="more"] .alt-sidebar, [data-contrast="more"] .alt-sidebar-header, [data-contrast="more"] .alt-sidebar-footer, [data-contrast="more"] .alt-topbar { border-color: var(--line-control); }
|
|
1858
|
+
[data-contrast="more"] .alt-sidebar-pill { box-shadow: 0 2px 0 0 var(--ink), var(--elev-1); }
|
|
1859
|
+
@media (forced-colors: active) { .alt-sidebar-pill { border: 1px solid CanvasText; } .alt-sidebar-button:hover:not([aria-current]) { border: 1px solid CanvasText; } }
|
|
1860
|
+
|
|
1861
|
+
/* ── Resizable: panes beside each other on the ground, parted by 1px of line. The panes never lift; only the grip is
|
|
1862
|
+
live: a small line-control plate, flush at rest, lifting one level when pointed at or focused, floating at level 3
|
|
1863
|
+
while held (whatever is in a hand floats). Collapsing a pane changes its size on ease-glide, not its level. ── */
|
|
1864
|
+
.alt-resizable { display: flex; width: 100%; height: 100%; min-width: 0; min-height: 0; }
|
|
1865
|
+
.alt-resizable[data-direction="vertical"] { flex-direction: column; }
|
|
1866
|
+
.alt-resizable-panel { flex: 0 0 auto; min-width: 0; min-height: 0; overflow: auto; background: var(--surface-ground); }
|
|
1867
|
+
.alt-resizable-panel[data-collapsed] { overflow: hidden; }
|
|
1868
|
+
.alt-resizable[data-animate] .alt-resizable-panel { transition: flex-basis var(--dur-travel-3) var(--ease-glide); }
|
|
1869
|
+
.alt-resizable-handle { position: relative; z-index: 1; flex: none; display: grid; place-items: center; width: 1px; background: var(--line); cursor: col-resize; touch-action: none; outline: none; }
|
|
1870
|
+
.alt-resizable[data-direction="vertical"] > .alt-resizable-handle { width: auto; height: 1px; cursor: row-resize; }
|
|
1871
|
+
.alt-resizable-handle::before { content: ""; position: absolute; inset: 0 calc((var(--size-target-min) - 1px) / -2); }
|
|
1872
|
+
.alt-resizable[data-direction="vertical"] > .alt-resizable-handle::before { inset: calc((var(--size-target-min) - 1px) / -2) 0; }
|
|
1873
|
+
.alt-resizable-grip {
|
|
1874
|
+
position: relative; width: 8px; height: 28px; border-radius: var(--radius-full); border: 1px solid var(--line-control); background: var(--surface-ground);
|
|
1875
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-2) var(--ease-settle), border-color var(--dur-travel-1);
|
|
1876
|
+
}
|
|
1877
|
+
.alt-resizable[data-direction="vertical"] .alt-resizable-grip { width: 28px; height: 8px; }
|
|
1878
|
+
.alt-resizable-handle:focus-visible .alt-resizable-grip { background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); border-color: transparent; }
|
|
1879
|
+
@media (hover: hover) { .alt-resizable-handle:hover .alt-resizable-grip { background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); border-color: transparent; } }
|
|
1880
|
+
.alt-resizable-handle:focus-visible .alt-resizable-grip { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1881
|
+
.alt-resizable-handle[data-held] .alt-resizable-grip { background: var(--surface-floating); box-shadow: var(--elev-3); border-color: transparent; transition-timing-function: var(--ease-rise); }
|
|
1882
|
+
@media (prefers-reduced-motion: reduce) { .alt-resizable[data-animate] .alt-resizable-panel { transition: none; } }
|
|
1883
|
+
[data-motion="reduced"] .alt-resizable[data-animate] .alt-resizable-panel { transition: none; }
|
|
1884
|
+
@media (prefers-contrast: more) { .alt-resizable-handle { background: var(--line-control); } }
|
|
1885
|
+
[data-contrast="more"] .alt-resizable-handle { background: var(--line-control); }
|
|
1886
|
+
[data-contrast="more"] .alt-resizable-handle[data-held] .alt-resizable-grip { box-shadow: 0 2px 0 0 var(--ink), var(--elev-3); }
|
|
1887
|
+
@media (forced-colors: active) { .alt-resizable-handle { background: CanvasText; } .alt-resizable-grip { border: 1px solid CanvasText; } .alt-resizable-handle[data-held] .alt-resizable-grip { border-width: 2px; } }
|
|
1888
|
+
|
|
1889
|
+
/* ── ContextMenu: DropdownMenu's menu (level 3, the gliding highlight), opened where you right-click or long-press, or
|
|
1890
|
+
at the thing with Shift+F10. The thing shows it's the subject with a 2px accent-ink outline: a ring, never a height,
|
|
1891
|
+
so a flush row doesn't lift out of its table. ── */
|
|
1892
|
+
.alt-context { position: fixed; z-index: var(--z-floating); }
|
|
1893
|
+
.alt-context .alt-context-menu { position: relative; top: auto; left: auto; right: auto; bottom: auto; margin: 0; }
|
|
1894
|
+
[data-context-open] { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1895
|
+
@media (forced-colors: active) { [data-context-open] { outline-color: Highlight; } }
|
|
1896
|
+
|
|
1897
|
+
/* ── App mode (AltitudeProvider mode): test mode, viewing as a customer, read-only, a preview. The page is the ground,
|
|
1898
|
+
so height can't say "this whole ground is different"; the mode is said in words and a border (law 7 as the primary
|
|
1899
|
+
channel): a flush strip on the tone's soft colour naming it at the top, a 2px inset frame around the viewport in its
|
|
1900
|
+
tone that stays while scrolling and above dialogs and sheets, and a plate in their headers. Nothing moves. ── */
|
|
1901
|
+
.alt-mode-strip { --m: var(--caution); --m-soft: var(--caution-soft); display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--space-3); padding: 6px var(--space-4); background: var(--m-soft); border-bottom: 1px solid color-mix(in oklab, var(--m) 35%, transparent); font: 400 13px/18px var(--font-sans); color: var(--ink); }
|
|
1902
|
+
.alt-mode-strip[data-tone="accent"], .alt-mode-frame[data-tone="accent"] { --m: var(--accent); --m-soft: var(--accent-soft); }
|
|
1903
|
+
.alt-mode-strip[data-tone="critical"], .alt-mode-frame[data-tone="critical"] { --m: var(--critical); --m-soft: var(--critical-soft); }
|
|
1904
|
+
.alt-mode-strip[data-tone="neutral"], .alt-mode-frame[data-tone="neutral"] { --m: var(--ink-muted); --m-soft: var(--surface-well); }
|
|
1905
|
+
.alt-mode-label { font-weight: 600; }
|
|
1906
|
+
.alt-mode-desc { color: var(--ink); }
|
|
1907
|
+
.alt-mode-action { margin-left: auto; }
|
|
1908
|
+
.alt-mode-frame { --m: var(--caution); position: fixed; inset: 0; z-index: 2147483000; border: 2px solid var(--m); pointer-events: none; }
|
|
1909
|
+
.alt-mode-plate { margin-bottom: var(--space-1); align-self: flex-start; }
|
|
1910
|
+
@media (prefers-contrast: more) { .alt-mode-frame { border-width: 3px; } }
|
|
1911
|
+
[data-contrast="more"] .alt-mode-frame { border-width: 3px; }
|
|
1912
|
+
@media (forced-colors: active) { .alt-mode-frame { border: 4px double CanvasText; } .alt-mode-strip { border-bottom: 1px solid CanvasText; } }
|
|
1913
|
+
|
|
1914
|
+
/* ── DropZone's queue: each file is a slat on the ground under the zone. Uploading is a task, so a Progress fill rises
|
|
1915
|
+
out of its trench; processing is a write the server hasn't confirmed, so the slat hovers as pending; done lands and
|
|
1916
|
+
settles with the afterglow; failed drops to a trench with its reason and Retry. ── */
|
|
1917
|
+
.alt-drop-wrap { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
1918
|
+
.alt-upload-list { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
|
|
1919
|
+
.alt-upload { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--surface-ground); box-shadow: 0 0 0 1px var(--line); }
|
|
1920
|
+
.alt-upload-text { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
|
|
1921
|
+
.alt-upload-name { font: 500 14px/20px var(--font-sans); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1922
|
+
.alt-upload-state { display: inline-flex; align-items: center; gap: 4px; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
1923
|
+
.alt-upload[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench); }
|
|
1924
|
+
.alt-upload[data-state="failed"] .alt-upload-state { color: var(--critical); }
|
|
1925
|
+
.alt-upload .alt-progress { margin-top: 2px; }
|
|
1926
|
+
.alt-upload-actions { display: flex; flex: none; align-items: center; gap: var(--space-1); }
|
|
1927
|
+
|
|
1928
|
+
/* ── FilterBar groups: nested logic drawn as brackets on the ground. A group is a flush 1px line-control rule around its
|
|
1929
|
+
own conditions and its own all/any switch: part of the record of the query, not a live surface. ── */
|
|
1930
|
+
.alt-filter-tree { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
1931
|
+
.alt-filter-group { position: relative; display: flex; align-items: flex-start; gap: var(--space-2); margin-left: var(--space-4); padding: var(--space-2); padding-right: 36px; border: 1px solid var(--line-control); border-radius: var(--radius-md); }
|
|
1932
|
+
.alt-filter-group-remove { position: absolute; top: 6px; right: 6px; }
|
|
1933
|
+
.alt-filter-add-group { align-self: flex-start; margin-left: var(--space-4); }
|
|
1934
|
+
@media (forced-colors: active) { .alt-filter-group { border-color: CanvasText; } }
|
|
1935
|
+
|
|
1936
|
+
/* ── Timeline: the ground is the record, literally. Events stand on a ground line turned upright (1px ink-muted, the
|
|
1937
|
+
rule Stepper and the chart baseline stand on) as flush entries stamped onto it, each with a flush plate on the line.
|
|
1938
|
+
A new event arrives lifted and settles (the afterglow, staggered); one you caused hovers until it lands, or drops to a
|
|
1939
|
+
trench with Retry; an event that links lifts when pointed at; its detail opens as a raised card, as an Accordion lid
|
|
1940
|
+
does. ── */
|
|
1941
|
+
.alt-timeline-list { position: relative; display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0 0 0 36px; list-style: none; }
|
|
1942
|
+
.alt-timeline-list::before { content: ""; position: absolute; left: 13px; top: 6px; bottom: 6px; width: 1px; background: var(--ink-muted); }
|
|
1943
|
+
.alt-timeline-day { padding: var(--space-3) 0 var(--space-1); }
|
|
1944
|
+
.alt-timeline-day:first-child { padding-top: 0; }
|
|
1945
|
+
.alt-timeline-day-label { margin: 0 0 0 -36px; padding-left: 36px; font: 600 12.5px/17px var(--font-sans); color: var(--ink-muted); background: var(--surface-ground); position: relative; }
|
|
1946
|
+
.alt-timeline-item { position: relative; border-radius: var(--radius-md); }
|
|
1947
|
+
.alt-timeline-marker { position: absolute; left: -36px; top: 7px; display: grid; place-items: center; width: 27px; height: 27px; border-radius: var(--radius-full); background: var(--surface-ground); box-shadow: inset 0 0 0 1px var(--line); color: var(--ink-muted); }
|
|
1948
|
+
.alt-timeline-marker svg { width: 14px; height: 14px; }
|
|
1949
|
+
.alt-timeline-dot { width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--ink-muted); }
|
|
1950
|
+
.alt-timeline-body { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; }
|
|
1951
|
+
.alt-timeline-entry, .alt-timeline-link { display: flex; flex-direction: column; gap: 2px; font: 400 14px/20px var(--font-sans); color: var(--ink); }
|
|
1952
|
+
.alt-timeline-link { margin: -6px -8px; padding: 6px 8px; border-radius: var(--radius-sm); text-decoration: none; transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
1953
|
+
@media (hover: hover) { .alt-timeline-link:hover { background: var(--surface-raised); box-shadow: var(--elev-1); } }
|
|
1954
|
+
.alt-timeline-link:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1955
|
+
.alt-timeline-meta { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
1956
|
+
.alt-timeline-state { display: inline-flex; align-items: center; gap: 4px; }
|
|
1957
|
+
.alt-timeline-state[data-failed] { color: var(--critical); }
|
|
1958
|
+
/* A failed event isn't new to the record: it drops to its trench without the afterglow's tint. */
|
|
1959
|
+
.alt-timeline-item[data-status="failed"]::before { animation: none; }
|
|
1960
|
+
.alt-timeline-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
1961
|
+
.alt-timeline-detail { padding: var(--space-3); border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--elev-1); animation: alt-emerge var(--dur-travel-2) var(--ease-rise); --alt-from-y: -4px; }
|
|
1962
|
+
@media (prefers-contrast: more) { .alt-timeline-list::before { width: 2px; } }
|
|
1963
|
+
[data-contrast="more"] .alt-timeline-list::before { width: 2px; }
|
|
1964
|
+
@media (forced-colors: active) { .alt-timeline-list::before { background: CanvasText; } .alt-timeline-marker { box-shadow: none; border: 1px solid CanvasText; } }
|
|
1965
|
+
|
|
1966
|
+
/* ── NotificationCenter: the bell is live (a ghost button), only its count floats, the panel floats at level 3 (a
|
|
1967
|
+
Sheet at 4 on narrow screens), and the list in it lies on the ground: a record of what happened. Unread is a mark,
|
|
1968
|
+
not a height: an accent dot and weight 600, with "Unread" in the name. A new notification arrives lifted and
|
|
1969
|
+
settles; an item that links lifts when pointed at. Mark all as read removes the dots with a cross-fade. ── */
|
|
1970
|
+
.alt-notes-bell { position: relative; }
|
|
1971
|
+
.alt-notes-badge { position: absolute; top: -4px; right: -6px; pointer-events: none; }
|
|
1972
|
+
.alt-notes-pop { width: min(380px, calc(100vw - 32px)); padding: var(--space-3); }
|
|
1973
|
+
.alt-notes-title-bar { margin: 0 0 var(--space-1); font: 600 15px/20px var(--font-sans); color: var(--ink); }
|
|
1974
|
+
.alt-notes { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
1975
|
+
.alt-notes-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: 30px; }
|
|
1976
|
+
.alt-notes-count { font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
1977
|
+
.alt-notes-list { display: flex; flex-direction: column; gap: 2px; max-height: 420px; overflow: auto; margin: 0 -4px; padding: 0 4px; list-style: none; }
|
|
1978
|
+
.alt-notes-day { padding: var(--space-2) var(--space-2) 2px; font: 600 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1979
|
+
.alt-notes-item { border-radius: var(--radius-md); }
|
|
1980
|
+
.alt-notes-link { display: flex; gap: 10px; padding: 8px; border-radius: var(--radius-md); color: var(--ink); text-decoration: none; transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
1981
|
+
@media (hover: hover) { a.alt-notes-link:hover { background: var(--surface-raised); box-shadow: var(--elev-1); } }
|
|
1982
|
+
a.alt-notes-link:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1983
|
+
.alt-notes-dot { flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: var(--radius-full); background: var(--accent); opacity: 0; transition: opacity var(--dur-travel-2) var(--ease-rise); }
|
|
1984
|
+
.alt-notes-item[data-unread] .alt-notes-dot { opacity: 1; }
|
|
1985
|
+
.alt-notes-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
1986
|
+
.alt-notes-title { font: 400 14px/20px var(--font-sans); }
|
|
1987
|
+
.alt-notes-item[data-unread] .alt-notes-title { font-weight: 600; }
|
|
1988
|
+
.alt-notes-body { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1989
|
+
.alt-notes-time { font: 400 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
1990
|
+
.alt-notes-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: 0 8px 8px 26px; }
|
|
1991
|
+
.alt-notes-empty { margin: 0; padding: var(--space-4) var(--space-2); font: 400 14px/20px var(--font-sans); color: var(--ink-muted); text-align: center; }
|
|
1992
|
+
.alt-notes-foot { padding-top: var(--space-2); border-top: 1px solid var(--line); font: 400 13px/18px var(--font-sans); }
|
|
1993
|
+
@media (forced-colors: active) { .alt-notes-dot { forced-color-adjust: none; background: CanvasText; } }
|
|
1994
|
+
|
|
1995
|
+
/* ── SyncIndicator: whether work has reached the server, said on a flush Badge plate whose lamp relights when the
|
|
1996
|
+
state changes (saving pulses). A label never lifts, so a change of state cross-fades; only when the plate opens the
|
|
1997
|
+
queue of waiting changes does it lift when pointed at, because then it acts. ── */
|
|
1998
|
+
.alt-sync { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
|
|
1999
|
+
.alt-sync-button { padding: 0; border: 0; border-radius: var(--radius-sm); background: none; cursor: pointer; transition: box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise); }
|
|
2000
|
+
@media (hover: hover) { .alt-sync-button:hover { box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
2001
|
+
.alt-sync-button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
2002
|
+
.alt-sync-time { font-weight: 400; color: var(--ink-muted); }
|
|
2003
|
+
.alt-sync-queue { display: flex; flex-direction: column; gap: var(--space-2); width: 280px; max-width: 100%; }
|
|
2004
|
+
.alt-sync-queue-title { margin: 0; font: 600 13px/18px var(--font-sans); color: var(--ink); }
|
|
2005
|
+
[data-contrast="more"] .alt-sync-plate { box-shadow: inset 0 0 0 1px var(--line-control); }
|
|
2006
|
+
|
|
2007
|
+
/* ── DiffView: both versions are the record, so it lies flush with a line rim (as CodeBlock). Additions are positive-
|
|
2008
|
+
soft with a + sign, removals critical-soft and struck through with a − sign, and words that changed inside a line take
|
|
2009
|
+
a stronger tint; a changed property shows the old value struck through beside the new. Signs and words carry it, never
|
|
2010
|
+
colour alone. Long unchanged runs fold behind lids. ── */
|
|
2011
|
+
.alt-diff { margin: 0; min-width: 0; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-ground); overflow: hidden; }
|
|
2012
|
+
.alt-diff-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: 6px 6px 6px var(--space-4); border-bottom: 1px solid var(--line); }
|
|
2013
|
+
.alt-diff-caption { font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
2014
|
+
.alt-diff-summary { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2015
|
+
/* The buttons wrap rather than run past the frame, which clips them (a review's four don't fit a phone on one line). */
|
|
2016
|
+
.alt-diff-nav { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: 2px; margin-left: auto; min-width: 0; }
|
|
2017
|
+
.alt-diff-body { overflow: auto; max-height: 420px; padding: var(--space-2) 0; }
|
|
2018
|
+
.alt-diff-lines { min-width: max-content; font: 400 13px/20px var(--font-mono); }
|
|
2019
|
+
.alt-diff-line { display: flex; padding-right: var(--space-4); outline: none; }
|
|
2020
|
+
.alt-diff-line:focus-visible { box-shadow: inset 2px 0 0 var(--accent-ink); }
|
|
2021
|
+
.alt-diff-sign { flex: none; width: 28px; text-align: center; color: var(--ink-muted); user-select: none; }
|
|
2022
|
+
.alt-diff-text { white-space: pre; text-decoration: none; color: var(--ink); }
|
|
2023
|
+
.alt-diff-line[data-kind="add"] { background: var(--positive-soft); }
|
|
2024
|
+
.alt-diff-line[data-kind="del"] { background: var(--critical-soft); }
|
|
2025
|
+
.alt-diff-line[data-kind="del"] .alt-diff-text { text-decoration: line-through; text-decoration-color: color-mix(in oklab, var(--critical) 60%, transparent); }
|
|
2026
|
+
.alt-diff-line[data-kind="add"] .alt-diff-sign { color: var(--positive); }
|
|
2027
|
+
.alt-diff-line[data-kind="del"] .alt-diff-sign { color: var(--critical); }
|
|
2028
|
+
.alt-diff-word { color: inherit; border-radius: 2px; }
|
|
2029
|
+
.alt-diff-line[data-kind="add"] .alt-diff-word { background: color-mix(in oklab, var(--positive) 22%, transparent); }
|
|
2030
|
+
.alt-diff-line[data-kind="del"] .alt-diff-word { background: color-mix(in oklab, var(--critical) 18%, transparent); }
|
|
2031
|
+
.alt-diff-fold button { display: flex; align-items: center; gap: 6px; width: 100%; padding: 4px var(--space-4) 4px 28px; border: 0; border-block: 1px solid var(--line); background: var(--surface-ground); font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); cursor: pointer; text-align: left; }
|
|
2032
|
+
/* Reviewing (#282): a change awaiting a decision is a proposal, raised at level 1 around its lines (as PropertyList's
|
|
2033
|
+
proposed value); deciding lowers it to the record: accepted flush with one afterglow, rejected muted and collapsed. */
|
|
2034
|
+
.alt-diff-hunk { position: relative; margin: 4px 6px; border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--elev-1); overflow: hidden;
|
|
2035
|
+
transition: box-shadow var(--dur-travel-1) var(--ease-sink), background-color var(--dur-travel-1) var(--ease-sink), margin var(--dur-travel-1) var(--ease-sink); }
|
|
2036
|
+
.alt-diff-hunk[data-decision] { margin-inline: 0; border-radius: 0; background: none; box-shadow: none; }
|
|
2037
|
+
.alt-diff-hunk[data-decision="reject"] { color: var(--ink-muted); }
|
|
2038
|
+
.alt-diff-hunk[data-decision="reject"] .alt-diff-line { opacity: 0.6; }
|
|
2039
|
+
.alt-diff-hunk-bar { position: sticky; left: 0; display: flex; align-items: center; justify-content: flex-end; gap: 2px; width: min(100%, 100cqw); padding: 2px 6px 2px 28px; font: 500 12.5px/17px var(--font-sans); }
|
|
2040
|
+
.alt-diff-hunk-state { display: inline-flex; align-items: center; gap: 5px; margin-right: auto; color: var(--ink-muted); }
|
|
2041
|
+
.alt-diff-hunk-folded { display: block; width: 100%; padding: 4px var(--space-4) 4px 28px; border: 0; background: none; color: var(--ink-muted); font: 500 12.5px/17px var(--font-sans); text-align: left; cursor: pointer; }
|
|
2042
|
+
.alt-diff-hunk-folded:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
2043
|
+
.alt-diff-body { container-type: inline-size; }
|
|
2044
|
+
@media (prefers-reduced-motion: reduce) { .alt-diff-hunk { transition: none; } }
|
|
2045
|
+
@media (forced-colors: active) { .alt-diff-hunk:not([data-decision]) { border: 1px solid CanvasText; } }
|
|
2046
|
+
@media (hover: hover) { .alt-diff-fold button:hover { background: var(--surface-raised); color: var(--ink); } }
|
|
2047
|
+
.alt-diff-fold button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
2048
|
+
.alt-diff-props { display: flex; flex-direction: column; margin: 0; padding: 0 var(--space-4); }
|
|
2049
|
+
.alt-diff-prop { display: grid; grid-template-columns: minmax(96px, 30%) 1fr; gap: var(--space-3); padding: 8px 0; border-bottom: 1px solid var(--line); font: 400 14px/20px var(--font-sans); outline: none; }
|
|
2050
|
+
.alt-diff-prop:last-child { border-bottom: 0; }
|
|
2051
|
+
.alt-diff-prop:focus-visible { box-shadow: inset 2px 0 0 var(--accent-ink); }
|
|
2052
|
+
.alt-diff-prop dt { color: var(--ink-muted); }
|
|
2053
|
+
.alt-diff-prop dd { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; }
|
|
2054
|
+
.alt-diff-old { padding: 0 4px; border-radius: var(--radius-xs); background: var(--critical-soft); text-decoration: line-through; color: var(--ink); }
|
|
2055
|
+
.alt-diff-new { padding: 0 4px; border-radius: var(--radius-xs); background: var(--positive-soft); text-decoration: none; color: var(--ink); font-weight: 500; }
|
|
2056
|
+
.alt-diff-arrow { transform: rotate(-90deg); color: var(--ink-muted); }
|
|
2057
|
+
[data-contrast="more"] .alt-diff-line[data-kind="add"] { box-shadow: inset 2px 0 0 var(--positive); }
|
|
2058
|
+
[data-contrast="more"] .alt-diff-line[data-kind="del"] { box-shadow: inset 2px 0 0 var(--critical); }
|
|
2059
|
+
@media (forced-colors: active) { .alt-diff-line[data-kind], .alt-diff-old, .alt-diff-new, .alt-diff-word { background: none; } .alt-diff { border: 1px solid CanvasText; } }
|
|
2060
|
+
|
|
2061
|
+
/* ── SaveBar: an unsaved draft at page scope is something in your hand, so the bar floats at level 3, docked to the
|
|
2062
|
+
bottom edge (below a Sheet or a Dialog, never competing with them). It rises from the edge once a change has settled
|
|
2063
|
+
into the draft; saving hovers as a pending write; saved, it lowers away while the fields settle; a failed save drops
|
|
2064
|
+
it to a trench, keeping the draft. ── */
|
|
2065
|
+
.alt-savebar {
|
|
2066
|
+
position: fixed; left: 50%; bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); z-index: var(--z-floating); translate: -50% 0;
|
|
2067
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); width: max-content; max-width: calc(100vw - 32px);
|
|
2068
|
+
padding: 8px 8px 8px var(--space-4); border-radius: var(--radius-lg); background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--ink);
|
|
2069
|
+
transition: opacity var(--dur-travel-3) var(--ease-rise), transform var(--dur-travel-3) var(--ease-rise), background-color var(--dur-travel-2), box-shadow var(--dur-travel-2);
|
|
2070
|
+
}
|
|
2071
|
+
.alt-savebar[data-state="closed"] { opacity: 0; transform: translateY(calc(16px * var(--alt-travel))); transition-timing-function: var(--ease-settle); }
|
|
2072
|
+
.alt-savebar[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench); outline: 1px solid var(--critical); animation-duration: var(--dur-travel-5); }
|
|
2073
|
+
.alt-savebar-text { display: inline-flex; align-items: center; gap: 6px; font: 500 14px/20px var(--font-sans); }
|
|
2074
|
+
.alt-savebar[data-status="failed"] .alt-savebar-text svg { color: var(--critical); }
|
|
2075
|
+
.alt-savebar-actions { display: inline-flex; gap: var(--space-2); margin-left: auto; }
|
|
2076
|
+
[data-contrast="more"] .alt-savebar { box-shadow: 0 3px 0 0 var(--ink), var(--elev-3); }
|
|
2077
|
+
@media (forced-colors: active) { .alt-savebar { border: 2px solid CanvasText; } .alt-savebar[data-status="pending"] { border-style: dotted; } .alt-savebar[data-status="failed"] { border: 3px dashed CanvasText; } }
|
|
2078
|
+
|
|
2079
|
+
/* ── Settings: a section is a raised Card (level 1) of rows; the danger zone is the same card with a critical rim,
|
|
2080
|
+
never higher. A row is flush, label and description on the start side, the control on the end side, stacked when
|
|
2081
|
+
the section is narrow. An apply-now control's write is the row's: pending hovers it, committed lands, failed drops
|
|
2082
|
+
it into a trench, with the state in words beside the control. ── */
|
|
2083
|
+
.alt-settings-section { container-type: inline-size; padding: var(--alt-inset-box); gap: var(--space-3); }
|
|
2084
|
+
.alt-settings-section[data-tone="critical"] { box-shadow: var(--elev-1), inset 0 0 0 1px var(--critical); }
|
|
2085
|
+
.alt-settings-head { display: flex; flex-direction: column; gap: var(--space-1); }
|
|
2086
|
+
.alt-settings-title { margin: 0; font: 600 17px/24px var(--font-sans); letter-spacing: -0.01em; }
|
|
2087
|
+
.alt-settings-desc, .alt-settings-readonly { margin: 0; font: 400 14px/20px var(--font-sans); color: var(--ink-muted); }
|
|
2088
|
+
.alt-settings-readonly { display: flex; gap: 6px; align-items: flex-start; }
|
|
2089
|
+
.alt-settings-readonly svg { flex: none; margin-top: 3px; }
|
|
2090
|
+
.alt-settings-rows { margin: 0; padding: 0; border: 0; min-width: 0; display: flex; flex-direction: column; }
|
|
2091
|
+
.alt-settings-row {
|
|
2092
|
+
display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-2) var(--space-5);
|
|
2093
|
+
margin-inline: calc(-1 * var(--space-3)); padding: var(--space-3); border-radius: var(--radius-md);
|
|
2094
|
+
}
|
|
2095
|
+
.alt-settings-row + .alt-settings-row { box-shadow: inset 0 1px 0 var(--line); }
|
|
2096
|
+
.alt-settings-row[data-status="pending"] { background: var(--surface-raised); }
|
|
2097
|
+
.alt-settings-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
2098
|
+
.alt-settings-label { font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
2099
|
+
.alt-settings-rowdesc, .alt-settings-error { margin: 0; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2100
|
+
.alt-settings-error { display: flex; gap: 6px; align-items: flex-start; color: var(--critical); }
|
|
2101
|
+
.alt-settings-error svg { flex: none; margin-top: 2px; }
|
|
2102
|
+
.alt-settings-control { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; min-width: 0; }
|
|
2103
|
+
.alt-settings-control > .alt-input, .alt-settings-control > .alt-select { width: min(280px, 100%); }
|
|
2104
|
+
.alt-settings-status { font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2105
|
+
.alt-settings-status:empty { display: none; }
|
|
2106
|
+
.alt-settings-row[data-status="failed"] .alt-settings-status { color: var(--critical); }
|
|
2107
|
+
@container (max-width: 480px) {
|
|
2108
|
+
.alt-settings-row { grid-template-columns: minmax(0, 1fr); }
|
|
2109
|
+
.alt-settings-control { align-items: flex-start; }
|
|
2110
|
+
}
|
|
2111
|
+
@media (forced-colors: active) { .alt-settings-section[data-tone="critical"] { border: 2px solid CanvasText; } }
|
|
2112
|
+
|
|
2113
|
+
/* ── PermissionMatrix: roles against permissions. The grid is settled, on the ground with line dividers. An editable
|
|
2114
|
+
grant is a choice, a Checkbox's trench that the accent pours into; an inherited or fixed grant is already decided, so
|
|
2115
|
+
it's a flush stamped check (or a dash), muted, like a done Checklist item: pressed into the record, not disabled. The
|
|
2116
|
+
active cell is a 2px accent-ink ring, never a height. A saving cell hovers; a saved one settles. ── */
|
|
2117
|
+
.alt-matrix { overflow-x: auto; }
|
|
2118
|
+
.alt-matrix-grid { border-collapse: separate; border-spacing: 0; min-width: 100%; font: 400 14px/20px var(--font-sans); color: var(--ink); }
|
|
2119
|
+
.alt-matrix-grid th, .alt-matrix-grid td { padding: 8px 12px; border-bottom: 1px solid var(--line); text-align: center; }
|
|
2120
|
+
.alt-matrix-grid thead th { font: 600 13px/18px var(--font-sans); color: var(--ink); white-space: nowrap; }
|
|
2121
|
+
.alt-matrix-grid .alt-matrix-perm { text-align: left; font-weight: 400; white-space: nowrap; }
|
|
2122
|
+
.alt-matrix-group th { padding-top: var(--space-4); text-align: left; font: 600 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2123
|
+
.alt-matrix-cell { position: relative; }
|
|
2124
|
+
.alt-matrix-box { display: inline-grid; place-items: center; width: 30px; height: 30px; border-radius: var(--radius-sm); cursor: pointer; outline: none; }
|
|
2125
|
+
.alt-matrix-box[data-fixed] { cursor: help; }
|
|
2126
|
+
.alt-matrix-box:focus-visible { box-shadow: 0 0 0 2px var(--accent-ink); }
|
|
2127
|
+
.alt-matrix-box[data-on] .alt-check-fill { opacity: 1; clip-path: circle(75% at 50% 50%); }
|
|
2128
|
+
.alt-matrix-box[data-on] .alt-check-tick { stroke-dashoffset: 0; transition: stroke-dashoffset var(--dur-travel-2) var(--ease-rise) 60ms; }
|
|
2129
|
+
.alt-matrix-box[data-on] .alt-check-track { border-color: var(--accent); }
|
|
2130
|
+
.alt-matrix-stamp { display: grid; place-items: center; width: 22px; height: 22px; color: var(--ink-muted); }
|
|
2131
|
+
.alt-matrix-dash { display: block; width: 10px; height: 2px; border-radius: 1px; background: var(--ink-faint); }
|
|
2132
|
+
[data-contrast="more"] .alt-matrix-box:focus-visible { box-shadow: 0 0 0 3px var(--accent-ink); }
|
|
2133
|
+
@media (forced-colors: active) { .alt-matrix-box:focus-visible { box-shadow: none; outline: 2px solid Highlight; } .alt-matrix-dash { background: CanvasText; } }
|
|
2134
|
+
|
|
2135
|
+
/* ── PlanPicker: plans are positions in one choice, so the cards lie flush on the ground (a line rim) and one raised
|
|
2136
|
+
mark, the radio thumb's build, travels to the chosen card's corner on ease-glide; the chosen card also takes an
|
|
2137
|
+
accent rim. Pointing at another card shows a ghost of the mark there. "Current plan" and "Recommended" are flush plates,
|
|
2138
|
+
never extra height: the only level-2 slab is the Continue button beside it. A period change cross-fades the prices. ── */
|
|
2139
|
+
.alt-plans { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
2140
|
+
.alt-plans-period { align-self: flex-start; }
|
|
2141
|
+
.alt-plans-grid { position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-3); }
|
|
2142
|
+
.alt-plan {
|
|
2143
|
+
position: relative; display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); border-radius: var(--radius-lg);
|
|
2144
|
+
background: var(--surface-ground); box-shadow: inset 0 0 0 1px var(--line); cursor: pointer; outline: none;
|
|
2145
|
+
transition: box-shadow var(--dur-travel-2) var(--ease-rise);
|
|
2146
|
+
}
|
|
2147
|
+
.alt-plan[data-chosen] { box-shadow: inset 0 0 0 2px var(--accent); }
|
|
2148
|
+
.alt-plan[aria-disabled] { cursor: not-allowed; color: var(--ink-faint); }
|
|
2149
|
+
.alt-plan:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
2150
|
+
.alt-plan::after {
|
|
2151
|
+
content: ""; position: absolute; top: 15px; right: 15px; width: 14px; height: 14px; border-radius: var(--radius-full);
|
|
2152
|
+
background: var(--surface-raised); box-shadow: var(--elev-1); opacity: 0; scale: 0.5; pointer-events: none; transition: opacity var(--dur-travel-1) var(--ease-sink), scale var(--dur-travel-2) var(--ease-settle);
|
|
2153
|
+
}
|
|
2154
|
+
@media (hover: hover) { .alt-plan:hover:not([data-chosen]):not([aria-disabled])::after { opacity: 0.5; scale: 0.85; } }
|
|
2155
|
+
.alt-plans-mark { position: absolute; left: 0; top: 0; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: var(--radius-full); background: var(--accent); box-shadow: var(--elev-1), inset 0 0 0 3px var(--surface-raised); pointer-events: none; }
|
|
2156
|
+
.alt-plans-mark[data-ready] { transition: transform var(--dur-travel-3) var(--ease-glide), opacity var(--dur-travel-1); }
|
|
2157
|
+
.alt-plan-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding-right: 24px; }
|
|
2158
|
+
.alt-plan-name { font: 600 16px/22px var(--font-sans); }
|
|
2159
|
+
.alt-plan-plates { display: inline-flex; flex-wrap: wrap; gap: 4px; }
|
|
2160
|
+
.alt-plan-price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px; }
|
|
2161
|
+
.alt-plan-amount { font: 600 26px/32px var(--font-sans); letter-spacing: -0.015em; }
|
|
2162
|
+
.alt-plan-per { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2163
|
+
.alt-plan-desc { font: 400 13.5px/19px var(--font-sans); color: var(--ink-muted); }
|
|
2164
|
+
.alt-plan-desc p { margin: 0 0 var(--space-2); }
|
|
2165
|
+
.alt-plan-desc ul { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; color: var(--ink); }
|
|
2166
|
+
.alt-plan-desc li { display: flex; gap: 6px; align-items: flex-start; }
|
|
2167
|
+
.alt-plan-desc li svg { flex: none; margin-top: 2px; color: var(--positive); }
|
|
2168
|
+
.alt-plan-why { margin: 0; color: var(--ink-muted); }
|
|
2169
|
+
@media (prefers-reduced-motion: reduce) { .alt-plans-mark[data-ready] { transition: opacity var(--dur-travel-1); } }
|
|
2170
|
+
[data-motion="reduced"] .alt-plans-mark[data-ready] { transition: opacity var(--dur-travel-1); }
|
|
2171
|
+
[data-contrast="more"] .alt-plans-mark { box-shadow: 0 2px 0 0 var(--ink), inset 0 0 0 3px var(--surface-raised); }
|
|
2172
|
+
@media (forced-colors: active) { .alt-plan { border: 1px solid CanvasText; } .alt-plan[data-chosen] { border-width: 2px; } .alt-plans-mark { forced-color-adjust: none; outline: 1px solid CanvasText; } }
|
|
2173
|
+
|
|
2174
|
+
/* ── FeatureGate: locked, not disabled. Disabled means nothing happens; a locked control explains the plan and offers
|
|
2175
|
+
the upgrade, so it keeps its own build and normal ink and lifts when pointed at (hover is a promise), with a lock and
|
|
2176
|
+
a flush plan plate. Pressing it opens a Popover at level 3 with the upgrade as its one primary. ── */
|
|
2177
|
+
.alt-gate { display: inline-flex; }
|
|
2178
|
+
.alt-gate-trigger .alt-btn-label, .alt-gate-trigger { gap: 6px; }
|
|
2179
|
+
.alt-gate-lock { flex: none; color: var(--ink-muted); }
|
|
2180
|
+
.alt-gate-plan { margin-left: 2px; }
|
|
2181
|
+
.alt-gate-pop { width: min(300px, calc(100vw - 32px)); }
|
|
2182
|
+
.alt-gate-body { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2183
|
+
.alt-gate-title { display: flex; align-items: center; gap: 6px; margin: 0; font: 600 14px/20px var(--font-sans); color: var(--ink); }
|
|
2184
|
+
.alt-gate-desc, .alt-gate-admin { margin: 0; font: 400 13.5px/19px var(--font-sans); color: var(--ink-muted); }
|
|
2185
|
+
.alt-gate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-1); font: 400 13.5px/19px var(--font-sans); }
|
|
2186
|
+
|
|
2187
|
+
/* ── CommentThread: posted comments are the record, flush on the ground; the composer is an Input well that fills; a
|
|
2188
|
+
comment being posted hovers as pending, lands and settles, or drops to a trench keeping its text. Mentions are flush
|
|
2189
|
+
accent-soft plates in the text and a listbox at level 3 while writing. A resolved thread is stamped like a done
|
|
2190
|
+
Checklist item: muted, collapsed to one line, kept in place. Someone typing shows their presence colour, quietly. ── */
|
|
2191
|
+
.alt-comments { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
2192
|
+
.alt-comment-list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
2193
|
+
.alt-comment { border-radius: var(--radius-md); }
|
|
2194
|
+
.alt-comment-article { display: flex; gap: 10px; padding: 8px; }
|
|
2195
|
+
.alt-comment-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
|
|
2196
|
+
.alt-comment-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
|
|
2197
|
+
.alt-comment-author { font: 600 14px/20px var(--font-sans); color: var(--ink); }
|
|
2198
|
+
.alt-comment-time { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2199
|
+
.alt-comment-failed { display: inline-flex; align-items: center; gap: 4px; color: var(--critical); }
|
|
2200
|
+
.alt-comment-body { margin: 0; font: 400 14px/21px var(--font-sans); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2201
|
+
.alt-comment-mention { padding: 0 3px; border-radius: var(--radius-xs); background: var(--accent-soft); color: var(--accent-ink); font-weight: 500; }
|
|
2202
|
+
.alt-comment-actions { display: flex; gap: var(--space-2); margin-top: 4px; }
|
|
2203
|
+
.alt-comment[data-deleted] { display: flex; align-items: center; gap: var(--space-2); padding: 8px 8px 8px 46px; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); font-style: italic; }
|
|
2204
|
+
.alt-comments-resolved { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding: 6px 8px; border-radius: var(--radius-md); background: var(--surface-well); box-shadow: var(--well); }
|
|
2205
|
+
.alt-comments-stamp { display: inline-flex; align-items: center; gap: 6px; margin-right: auto; font: 500 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2206
|
+
.alt-comments[data-resolved] .alt-comment-list { opacity: 0.7; }
|
|
2207
|
+
.alt-comments-compose { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2208
|
+
.alt-comments-field { position: relative; }
|
|
2209
|
+
.alt-comments-field textarea { min-height: 56px; }
|
|
2210
|
+
.alt-comments-mentions { top: calc(100% + 4px); left: 0; min-width: 220px; padding: var(--space-1); }
|
|
2211
|
+
.alt-comments-option { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm); font: 400 14px/20px var(--font-sans); cursor: pointer; }
|
|
2212
|
+
.alt-comments-option[data-active] { background: var(--accent-soft); }
|
|
2213
|
+
.alt-comments-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }
|
|
2214
|
+
.alt-comments-typing { display: flex; align-items: center; gap: 6px; margin: 0; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2215
|
+
.alt-comments-dots { display: inline-flex; gap: 3px; }
|
|
2216
|
+
.alt-comments-dots i { width: 5px; height: 5px; border-radius: var(--radius-full); background: var(--presence-1); animation: alt-typing 1.2s ease-in-out infinite; }
|
|
2217
|
+
.alt-comments-dots i:nth-child(2) { animation-delay: 0.15s; } .alt-comments-dots i:nth-child(3) { animation-delay: 0.3s; }
|
|
2218
|
+
@keyframes alt-typing { 50% { opacity: 0.35; } }
|
|
2219
|
+
@media (prefers-reduced-motion: reduce) { .alt-comments-dots i { animation: none; } }
|
|
2220
|
+
[data-motion="reduced"] .alt-comments-dots i { animation: none; }
|
|
2221
|
+
@media (forced-colors: active) { .alt-comment-mention { border: 1px solid CanvasText; } }
|
|
2222
|
+
|
|
2223
|
+
/* ── Tour: a note in the air above the page. One step at a time floats at level 3 beside its target, which takes a
|
|
2224
|
+
2px accent-ink ring (a ring, not a lift: the target may not be something you press). It never dims the page,
|
|
2225
|
+
because receding is the summit's privilege, and it isn't modal. Moving on lifts the step off; the next rises at its
|
|
2226
|
+
own target. ── */
|
|
2227
|
+
.alt-tour {
|
|
2228
|
+
position: fixed; z-index: var(--z-floating); width: min(320px, calc(100vw - 16px)); padding: var(--space-4); border-radius: var(--radius-lg);
|
|
2229
|
+
background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--ink); outline: none;
|
|
2230
|
+
animation: alt-emerge var(--dur-travel-3) var(--ease-rise); --alt-from-y: 6px;
|
|
2231
|
+
}
|
|
2232
|
+
.alt-tour[data-side="above"] { --alt-from-y: -6px; }
|
|
2233
|
+
.alt-tour:focus-visible { box-shadow: var(--elev-3), 0 0 0 2px var(--accent-ink); }
|
|
2234
|
+
.alt-tour-count { margin: 0 0 2px; font: 500 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
2235
|
+
.alt-tour-title { margin: 0 24px 6px 0; font: 600 15px/21px var(--font-sans); }
|
|
2236
|
+
.alt-tour-body { font: 400 14px/20px var(--font-sans); color: var(--ink-muted); }
|
|
2237
|
+
.alt-tour-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-3); }
|
|
2238
|
+
.alt-tour-dots { display: inline-flex; gap: 4px; margin-right: auto; }
|
|
2239
|
+
.alt-tour-dots i { width: 6px; height: 6px; border-radius: var(--radius-full); background: var(--line-control); }
|
|
2240
|
+
.alt-tour-dots i[data-on] { background: var(--accent); }
|
|
2241
|
+
.alt-tour-close { position: absolute; top: 8px; right: 8px; }
|
|
2242
|
+
[data-tour-target] { outline: 2px solid var(--accent-ink) !important; outline-offset: 3px !important; border-radius: var(--radius-sm); }
|
|
2243
|
+
[data-contrast="more"] .alt-tour { box-shadow: 0 4px 0 0 var(--ink), var(--elev-3); }
|
|
2244
|
+
@media (forced-colors: active) { .alt-tour { border: 2px solid CanvasText; } [data-tour-target] { outline-color: Highlight !important; } }
|
|
2245
|
+
|
|
2246
|
+
/* ── PromptInput: where a person writes to an agent. The whole composer is the Input well (empty: its back wall shows and
|
|
2247
|
+
it deepens as the pointer nears; holding text: the floor rises flush), with attachments as Tag plates above the text
|
|
2248
|
+
and a toolbar below. Send is the level-2 primary; while generating it becomes Stop in the same slot and size, its icon
|
|
2249
|
+
cross-fading without travel. An empty composer's Send is flush and faint but stays focusable (aria-disabled).
|
|
2250
|
+
Dragging a file over drops the well to a trench, as DropZone. ── */
|
|
2251
|
+
.alt-prompt { position: relative; }
|
|
2252
|
+
.alt-prompt-well { position: relative; gap: var(--space-2); padding: var(--space-3); border-radius: var(--radius-lg); }
|
|
2253
|
+
.alt-prompt-well .alt-prompt-text { width: 100%; min-height: 24px; resize: none; transition: height var(--dur-travel-2) var(--ease-glide); }
|
|
2254
|
+
.alt-prompt-files { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
2255
|
+
/* @ mentions and / commands (#283): the list is a floating Combobox list (level 3) rising above the composer, grouped
|
|
2256
|
+
with headings; what's chosen is a flush plate in the composer's context row ("@label" stays in the text too). */
|
|
2257
|
+
.alt-prompt-suggest { position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 20; width: min(420px, 100%); max-height: 280px; overflow: auto; padding: 4px; }
|
|
2258
|
+
.alt-prompt-suggest-head { margin: 4px 8px 2px; font: 500 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
2259
|
+
.alt-prompt-option { display: flex; flex-direction: column; gap: 1px; padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; }
|
|
2260
|
+
.alt-prompt-option[data-active] { background: var(--surface-raised); box-shadow: var(--elev-1); }
|
|
2261
|
+
.alt-prompt-option-label { font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
2262
|
+
.alt-prompt-option-label[data-kind="command"], .alt-prompt-command .alt-tag-face { font-family: var(--font-mono); }
|
|
2263
|
+
.alt-prompt-option-desc { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2264
|
+
.alt-prompt-searching { display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2265
|
+
.alt-prompt-searching .alt-skeleton { width: 80px; height: 10px; }
|
|
2266
|
+
/* Dictation (#284): listening is a latched toggle (a shallow well with accent-soft poured in, a steady lamp and the
|
|
2267
|
+
word), never a pulse or a waveform; interim words are a ghost in ink-faint, like a suggestion, never in the value. */
|
|
2268
|
+
.alt-btn.alt-prompt-dictate[data-listening], .alt-btn.alt-prompt-dictate[data-listening]:focus-visible, .alt-btn.alt-prompt-dictate[data-listening]:active:not(:disabled) {
|
|
2269
|
+
--wall: 0px; background: var(--accent-soft); background-image: none; color: var(--accent-ink); box-shadow: var(--well); transform: none; }
|
|
2270
|
+
.alt-prompt-listening { display: inline-flex; align-items: center; gap: 5px; margin-left: 4px; font: 500 12.5px/17px var(--font-sans); }
|
|
2271
|
+
.alt-prompt-lamp { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 22%, transparent); }
|
|
2272
|
+
.alt-prompt-dictate[data-blocked] { color: var(--ink-faint); }
|
|
2273
|
+
.alt-prompt-interim { margin: 2px 0 0; font: 400 15px/23px var(--font-sans); color: var(--ink-faint); white-space: pre-wrap; }
|
|
2274
|
+
@media (forced-colors: active) { .alt-btn.alt-prompt-dictate[data-listening] { border: 2px solid Highlight; } }
|
|
2275
|
+
.alt-prompt-context { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 8px 0; }
|
|
2276
|
+
.alt-prompt-kind { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-muted); }
|
|
2277
|
+
.alt-prompt-kind[data-kind="file"] { background: var(--accent-ink); }
|
|
2278
|
+
.alt-prompt-kind[data-kind="person"] { background: var(--positive); }
|
|
2279
|
+
.alt-prompt-chip { display: inline-flex; border-radius: var(--radius-sm); }
|
|
2280
|
+
.alt-prompt-uploading { color: var(--ink-muted); }
|
|
2281
|
+
.alt-prompt-bar { display: flex; align-items: center; gap: var(--space-2); }
|
|
2282
|
+
.alt-prompt-send { margin-left: auto; }
|
|
2283
|
+
.alt-prompt-send svg { animation: alt-fade-in var(--dur-travel-1) var(--ease-rise); }
|
|
2284
|
+
@keyframes alt-fade-in { from { opacity: 0; } }
|
|
2285
|
+
.alt-prompt-send[aria-disabled] {
|
|
2286
|
+
background: var(--surface-well); background-image: none; color: var(--ink-faint); box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); transform: none; cursor: not-allowed;
|
|
2287
|
+
}
|
|
2288
|
+
@media (hover: hover) { .alt-prompt-send[aria-disabled]:is(:hover, :active, :focus-visible) {
|
|
2289
|
+
background: var(--surface-well); background-image: none; color: var(--ink-faint); box-shadow: 0 0 0 0 var(--wall-c), var(--elev-0); transform: none; cursor: not-allowed;
|
|
2290
|
+
} }
|
|
2291
|
+
.alt-prompt-well[data-drop="over"] { background: var(--accent-soft); box-shadow: var(--trench); border-color: var(--accent-ink); }
|
|
2292
|
+
.alt-prompt-drop { position: absolute; inset: 0; display: grid; place-items: center; font: 500 14px/20px var(--font-sans); color: var(--accent-ink); background: color-mix(in oklab, var(--accent-soft), transparent 15%); pointer-events: none; }
|
|
2293
|
+
@media (prefers-reduced-motion: reduce) { .alt-prompt-text { transition: none; } }
|
|
2294
|
+
[data-motion="reduced"] .alt-prompt-text { transition: none; }
|
|
2295
|
+
@media (forced-colors: active) { .alt-prompt-send[aria-disabled] { border: 1px solid GrayText; color: GrayText; } .alt-prompt-well[data-drop="over"] { border: 2px dashed CanvasText; } }
|
|
2296
|
+
|
|
2297
|
+
/* ── Conversation: settled messages are the record. A person's message is a flush accent-soft sheet; an assistant's is
|
|
2298
|
+
flush with no bubble. A streaming answer isn't the record yet and isn't a pending write either (nothing you sent is in
|
|
2299
|
+
flight), so it rests on the ground with charts' "not yet the record" treatment: a dashed line-control rule down its
|
|
2300
|
+
left edge, "Generating…" and a caret. Completing makes the rule solid and settles it once. "Jump to latest" floats at
|
|
2301
|
+
level 3 once you've scrolled up. ── */
|
|
2302
|
+
.alt-convo { position: relative; display: flex; flex-direction: column; min-height: 0; }
|
|
2303
|
+
.alt-convo-scroll { flex: 1; min-height: 0; overflow-y: auto; outline: none; border-radius: var(--radius-md); }
|
|
2304
|
+
.alt-convo-scroll:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
2305
|
+
/* The scrollbar sits beside the messages, never over them: a classic bar gets a gutter of its own, and the content keeps
|
|
2306
|
+
12px clear of the right edge, where an overlay bar (macOS, phones) is drawn. */
|
|
2307
|
+
.alt-convo-scroll { scrollbar-gutter: stable; scrollbar-width: thin; }
|
|
2308
|
+
.alt-convo-content { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-3) var(--space-3) var(--space-3) var(--space-1); }
|
|
2309
|
+
.alt-msg { display: flex; flex-direction: column; gap: 4px; max-width: 72ch; border-radius: var(--radius-md); }
|
|
2310
|
+
.alt-msg[data-from="user"] { align-self: flex-end; max-width: min(72ch, 85%); padding: 10px 14px; background: var(--accent-soft); color: var(--ink); }
|
|
2311
|
+
/* Attachments (#274): part of the record, on the ground. Images are 64px thumbnails in a 1px line rim; files are the
|
|
2312
|
+
composer's Tag plates, settled. One that opens lifts a level when pointed at, as any plate that acts. */
|
|
2313
|
+
.alt-msg-attachments { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; padding: 0; list-style: none; }
|
|
2314
|
+
.alt-msg[data-from="user"] .alt-msg-attachments { justify-content: flex-end; }
|
|
2315
|
+
.alt-msg-attachment { display: flex; min-width: 0; }
|
|
2316
|
+
.alt-msg-thumb { position: relative; display: block; height: 64px; padding: 0; border: 0; border-radius: var(--radius-md); background: none; overflow: hidden; cursor: default; transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
2317
|
+
.alt-msg-thumb img { display: block; height: 64px; width: auto; max-width: 160px; min-width: 48px; object-fit: cover; }
|
|
2318
|
+
.alt-msg-thumb::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--line); pointer-events: none; }
|
|
2319
|
+
:is(a, button).alt-msg-thumb { cursor: pointer; }
|
|
2320
|
+
.alt-msg-file { height: 28px; }
|
|
2321
|
+
.alt-msg-file .alt-tag-face { max-width: 36ch; text-decoration: none; }
|
|
2322
|
+
:is(a, button).alt-tag-face { cursor: pointer; }
|
|
2323
|
+
.alt-msg-file-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2324
|
+
.alt-msg-file-meta { flex: none; color: var(--ink-muted); font-weight: 450; }
|
|
2325
|
+
.alt-msg-file[data-unavailable] { color: var(--ink-muted); }
|
|
2326
|
+
@media (hover: hover) {
|
|
2327
|
+
.alt-msg-file[data-acts]:hover { --t-bg: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
2328
|
+
:is(a, button).alt-msg-thumb:hover { box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
2329
|
+
}
|
|
2330
|
+
.alt-msg-thumb:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
2331
|
+
.alt-msg-file[data-acts]:active, :is(a, button).alt-msg-thumb:active { transform: none; box-shadow: none; transition-duration: var(--dur-press); }
|
|
2332
|
+
@media (forced-colors: active) { .alt-msg-thumb { border: 1px solid CanvasText; } }
|
|
2333
|
+
.alt-msg[data-from="assistant"] { border-radius: 0; padding: 2px 0 8px 14px; box-shadow: inset 2px 0 0 0 transparent; transition: box-shadow var(--dur-travel-2) var(--ease-rise); }
|
|
2334
|
+
.alt-msg[data-stream="streaming"] { box-shadow: none; background-image: linear-gradient(var(--line-control) 50%, transparent 50%); background-size: 2px 8px; background-repeat: repeat-y; background-position: left top; }
|
|
2335
|
+
.alt-msg[data-stream="complete"], .alt-msg[data-stream="stopped"], .alt-msg[data-stream="error"] { box-shadow: inset 2px 0 0 0 var(--line); }
|
|
2336
|
+
.alt-msg[data-stream="error"] { box-shadow: inset 2px 0 0 0 var(--critical); }
|
|
2337
|
+
.alt-msg-name { margin: 0; font: 600 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2338
|
+
.alt-msg-content { font: 400 15px/23px var(--font-sans); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2339
|
+
|
|
2340
|
+
/* ---- Terminal (#273) ----
|
|
2341
|
+
A command's output in CodeBlock's frame: flush on the ground, a 1px rim, mono. Running, it isn't the record yet, so
|
|
2342
|
+
a dashed line-control rule runs down the left; finished, the rule goes solid (critical when the exit is non-zero)
|
|
2343
|
+
with one afterglow. stderr lines carry a critical mark in the gutter. "Jump to latest output" floats at level 3. */
|
|
2344
|
+
.alt-term { position: relative; border-left-width: 2px; }
|
|
2345
|
+
.alt-term[data-status="running"] { border-left-style: dashed; border-left-color: var(--line-control); }
|
|
2346
|
+
.alt-term-cmd { min-width: 0; font: 500 13px/18px var(--font-mono); color: var(--ink); overflow-wrap: anywhere; }
|
|
2347
|
+
.alt-term-prompt { color: var(--ink-muted); }
|
|
2348
|
+
.alt-term-viewport { position: relative; }
|
|
2349
|
+
.alt-term-body { max-height: 320px; overflow: auto; scrollbar-width: thin; outline: none; }
|
|
2350
|
+
.alt-term-body:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
2351
|
+
.alt-term-pre { margin: 0; padding: var(--space-3) var(--space-4); font: 400 13px/20px var(--font-mono); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 4; }
|
|
2352
|
+
.alt-term-line { position: relative; display: block; min-height: 20px; }
|
|
2353
|
+
.alt-term-line[data-stream="stderr"]::before { content: ""; position: absolute; left: calc(-1 * var(--space-4) + 6px); top: 3px; bottom: 3px; width: 3px; border-radius: 2px; background: var(--critical); }
|
|
2354
|
+
.alt-term [data-tone="critical"] { color: var(--critical); }
|
|
2355
|
+
.alt-term [data-tone="caution"] { color: var(--caution); }
|
|
2356
|
+
.alt-term [data-tone="positive"] { color: var(--positive); }
|
|
2357
|
+
.alt-term [data-tone="accent"] { color: var(--accent-ink); }
|
|
2358
|
+
.alt-term [data-tone="muted"] { color: var(--ink-muted); }
|
|
2359
|
+
.alt-term [data-bold] { font-weight: 600; }
|
|
2360
|
+
.alt-term-more { display: block; margin: var(--space-2) var(--space-4) 0; padding: 0; border: 0; background: none; color: var(--accent-ink); font: 500 12.5px/24px var(--font-sans); text-decoration: underline; cursor: pointer; }
|
|
2361
|
+
.alt-term-exit { display: flex; align-items: center; gap: 6px; padding: 8px var(--space-4); border-top: 1px solid var(--line); font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
2362
|
+
.alt-term-exit[data-tone="critical"] { color: var(--critical); }
|
|
2363
|
+
[data-contrast="more"] .alt-term { border-color: var(--line-control); }
|
|
2364
|
+
@media (forced-colors: active) {
|
|
2365
|
+
.alt-term { border-color: CanvasText; }
|
|
2366
|
+
.alt-term[data-status="running"] { border-left-style: dotted; }
|
|
2367
|
+
.alt-term-line[data-stream="stderr"]::before { background: CanvasText; }
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
/* ---- Suggestions (#272) ----
|
|
2371
|
+
Prompts to start from or follow up with are things you press: small secondary buttons, raised at level 1 with their
|
|
2372
|
+
wall, lifting to 2 when pointed at, never the view's level-2 primary (that's the composer's Send). Follow-ups merely
|
|
2373
|
+
became available, so they cross-fade in under the answer; they don't rise. */
|
|
2374
|
+
.alt-suggestions { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
2375
|
+
.alt-suggestions[data-arrive] { animation: alt-fade-in var(--dur-onset) var(--ease-rise) both; }
|
|
2376
|
+
.alt-suggestion { max-width: 100%; }
|
|
2377
|
+
.alt-suggestion-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2378
|
+
.alt-msg .alt-suggestions { margin-top: 4px; }
|
|
2379
|
+
.alt-empty .alt-suggestions { justify-content: center; }
|
|
2380
|
+
@media (prefers-reduced-motion: reduce) { .alt-suggestions[data-arrive] { animation: none; } }
|
|
2381
|
+
[data-motion="reduced"] .alt-suggestions[data-arrive] { animation: none; }
|
|
2382
|
+
|
|
2383
|
+
/* ---- Markdown (#268) ----
|
|
2384
|
+
An answer's text set as the record: everything sits on the ground at level 0 and nothing here adds height. Code is
|
|
2385
|
+
CodeBlock, links are Link, [n] markers are InlineCitation (the only part that lifts, when pointed at), tables use
|
|
2386
|
+
Table's flush rows with line dividers, and an image waits in a sunk imprint (-1) and rises out of it. Headings are
|
|
2387
|
+
the heading style, never the title (which belongs to the page). Rims and rules are line; under contrast="more"
|
|
2388
|
+
they're line-control, and in forced colours CanvasText. */
|
|
2389
|
+
.alt-md { font: 400 15px/23px var(--font-sans); color: var(--ink); white-space: normal; overflow-wrap: anywhere; }
|
|
2390
|
+
.alt-md :is(p, .alt-md-list, .alt-md-quote, .alt-md-pre, .alt-md-table, .alt-md-html) { margin: 0 0 12px; }
|
|
2391
|
+
/* After the block margins: at equal specificity the later rule wins, so the last block doesn't carry 12px out of the answer. */
|
|
2392
|
+
.alt-md > :first-child { margin-top: 0; }
|
|
2393
|
+
.alt-md > :last-child { margin-bottom: 0; }
|
|
2394
|
+
.alt-md :is(li, blockquote) > :last-child { margin-bottom: 0; }
|
|
2395
|
+
.alt-md-h { margin: 20px 0 8px; color: var(--ink); font: 600 15px/23px var(--font-sans); }
|
|
2396
|
+
.alt-md-h[data-depth="1"] { font-size: 19px; line-height: 26px; letter-spacing: -0.01em; }
|
|
2397
|
+
.alt-md-h[data-depth="2"] { font-size: 17px; line-height: 24px; }
|
|
2398
|
+
.alt-md-list { padding-inline-start: 1.5em; }
|
|
2399
|
+
.alt-md-list li + li { margin-top: 4px; }
|
|
2400
|
+
.alt-md-list .alt-md-list { margin: 4px 0 0; }
|
|
2401
|
+
.alt-md-list li::marker { color: var(--ink-muted); }
|
|
2402
|
+
.alt-md-task-item { list-style: none; display: flex; gap: 8px; align-items: flex-start; margin-inline-start: -1.5em; }
|
|
2403
|
+
.alt-md-task { flex: none; display: inline-grid; place-items: center; width: 16px; height: 16px; margin-top: 3.5px; border-radius: 4px; box-shadow: inset 0 0 0 1px var(--line-control); color: var(--on-accent); }
|
|
2404
|
+
.alt-md-task[data-done] { background: var(--accent); box-shadow: none; }
|
|
2405
|
+
.alt-md-task-body { min-width: 0; }
|
|
2406
|
+
.alt-md-code { padding: 1px 5px; border-radius: var(--radius-sm); box-shadow: inset 0 0 0 1px var(--line); font: 400 0.87em/1 var(--font-mono); overflow-wrap: anywhere; }
|
|
2407
|
+
.alt-md-quote { margin-inline: 0; padding-inline-start: 12px; border-inline-start: 2px solid var(--line); color: var(--ink-muted); }
|
|
2408
|
+
.alt-md-hr { margin: 16px 0; }
|
|
2409
|
+
.alt-md-table { overflow-x: auto; outline: none; scrollbar-width: thin; }
|
|
2410
|
+
.alt-md-table:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-md); }
|
|
2411
|
+
.alt-md-table .alt-table { width: auto; min-width: 100%; }
|
|
2412
|
+
.alt-md-table .alt-th, .alt-md-table .alt-td { padding: 8px 12px; }
|
|
2413
|
+
.alt-md del { color: var(--ink-muted); }
|
|
2414
|
+
.alt-md-img { position: relative; display: inline-block; max-width: 100%; vertical-align: top; }
|
|
2415
|
+
.alt-md-img img { display: block; max-width: 100%; height: auto; border-radius: var(--radius-md); transition: opacity var(--dur-travel-1) var(--ease-rise), translate var(--dur-travel-1) var(--ease-rise); }
|
|
2416
|
+
.alt-md-img[data-state="wait"] { width: min(100%, 320px); aspect-ratio: 16 / 9; }
|
|
2417
|
+
.alt-md-img[data-state="wait"] img { position: absolute; inset: 0; opacity: 0; translate: 0 2px; }
|
|
2418
|
+
.alt-md-img-imprint { position: absolute; inset: 0; border-radius: var(--radius-md); }
|
|
2419
|
+
.alt-md-img-failed { color: var(--ink-muted); font-style: italic; }
|
|
2420
|
+
@media (prefers-reduced-motion: reduce) { .alt-md-img img { transition: none; translate: none; } }
|
|
2421
|
+
[data-contrast="more"] .alt-md-code { box-shadow: inset 0 0 0 1px var(--line-control); }
|
|
2422
|
+
[data-contrast="more"] .alt-md-quote { border-inline-start-color: var(--line-control); }
|
|
2423
|
+
@media (forced-colors: active) {
|
|
2424
|
+
.alt-md-code, .alt-md-task { box-shadow: none; border: 1px solid CanvasText; }
|
|
2425
|
+
.alt-md-quote { border-inline-start-color: CanvasText; }
|
|
2426
|
+
.alt-md-task[data-done] { background: Highlight; color: HighlightText; }
|
|
2427
|
+
}
|
|
2428
|
+
.alt-msg-caret { display: inline-block; width: 7px; height: 1.05em; margin-left: 2px; vertical-align: text-bottom; background: var(--ink-muted); animation: alt-caret 1s steps(1) infinite; }
|
|
2429
|
+
@keyframes alt-caret { 50% { opacity: 0; } }
|
|
2430
|
+
.alt-msg-caption { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2431
|
+
.alt-msg-caption[data-tone="critical"] { color: var(--critical); }
|
|
2432
|
+
.alt-msg-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin-left: -6px; }
|
|
2433
|
+
.alt-msg-actions .alt-tgroup { gap: 2px; }
|
|
2434
|
+
.alt-msg-versions { display: inline-flex; align-items: center; gap: 2px; margin-right: 4px; }
|
|
2435
|
+
.alt-msg-version { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
2436
|
+
/* Editing a sent message (#275): the text stays where it is while a Textarea well opens around it, inside the sheet.
|
|
2437
|
+
Editing is local input, not a pending write, so nothing hovers; "Send edit" is secondary, leaving the composer's
|
|
2438
|
+
Send the view's one primary. */
|
|
2439
|
+
.alt-msg[data-from="user"][data-editing] { width: min(72ch, 85%); }
|
|
2440
|
+
.alt-msg-editor { display: flex; flex-direction: column; gap: 8px; }
|
|
2441
|
+
.alt-msg-editor .alt-textarea, .alt-msg-editor textarea { width: 100%; font: 400 15px/23px var(--font-sans); }
|
|
2442
|
+
.alt-msg-editor-actions { display: flex; justify-content: flex-end; gap: 6px; }
|
|
2443
|
+
.alt-msg[data-from="user"] .alt-msg-actions { justify-content: flex-end; margin-left: 0; margin-right: -6px; }
|
|
2444
|
+
.alt-msg[data-from="user"] .alt-msg-edited { justify-content: flex-end; }
|
|
2445
|
+
/* The queue (#276): written but not dispatched, so it doesn't hover; the person's sheet on the ground with a dashed
|
|
2446
|
+
line-control rim, the way Altitude draws what isn't in the record yet. It appears in place: it's the person's own act. */
|
|
2447
|
+
.alt-convo-queue { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; margin: 0; padding: 0; list-style: none; }
|
|
2448
|
+
.alt-queued { width: fit-content; max-width: min(72ch, 85%); padding: 8px 12px 4px; border: 1px dashed var(--line-control); border-radius: var(--radius-md); background: var(--accent-soft); color: var(--ink); }
|
|
2449
|
+
.alt-queued[data-editing] { width: min(72ch, 85%); padding-bottom: 8px; }
|
|
2450
|
+
.alt-queued-text { margin: 0; font: 400 15px/23px var(--font-sans); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2451
|
+
.alt-queued-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px; }
|
|
2452
|
+
.alt-queued-state { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
2453
|
+
.alt-queued-actions { display: inline-flex; gap: 2px; margin-right: -6px; }
|
|
2454
|
+
@media (forced-colors: active) { .alt-queued { border-color: CanvasText; } }
|
|
2455
|
+
/* Checkpoints (#277): a divider with a word between its lines, on the ground. While restoring, its label hovers as
|
|
2456
|
+
pending (after dur-grace); restored, it settles once; failed, it sinks into a trench. Restore is a ghost button. */
|
|
2457
|
+
.alt-checkpoint { display: flex; align-items: center; gap: var(--space-3); margin: 4px 0; font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2458
|
+
.alt-checkpoint-line { flex: 1; min-width: 16px; height: 1px; background: var(--line); }
|
|
2459
|
+
.alt-checkpoint-label { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0 6px; min-width: 0; padding: 2px 8px; border-radius: var(--radius-sm); }
|
|
2460
|
+
.alt-checkpoint-label > * + *::before { content: "·"; margin-right: 6px; color: var(--ink-muted); }
|
|
2461
|
+
.alt-checkpoint-word { color: var(--ink); }
|
|
2462
|
+
.alt-checkpoint-name { overflow-wrap: anywhere; }
|
|
2463
|
+
.alt-checkpoint time { font-variant-numeric: tabular-nums; }
|
|
2464
|
+
.alt-checkpoint-actions { display: inline-flex; gap: 2px; }
|
|
2465
|
+
.alt-checkpoint-actions:empty { display: none; }
|
|
2466
|
+
.alt-checkpoint[data-phase="failed"] .alt-checkpoint-word { color: var(--critical); }
|
|
2467
|
+
[data-contrast="more"] .alt-checkpoint-line { background: var(--line-control); }
|
|
2468
|
+
@media (forced-colors: active) {
|
|
2469
|
+
.alt-checkpoint-line { background: CanvasText; }
|
|
2470
|
+
.alt-checkpoint-label[data-status="pending"] { outline: 1px dotted CanvasText; }
|
|
2471
|
+
.alt-checkpoint-label[data-status="failed"] { outline: 3px dashed CanvasText; }
|
|
2472
|
+
}
|
|
2473
|
+
.alt-convo-jump, .alt-term-jump { position: absolute; left: 50%; bottom: var(--space-3); translate: -50% 0; z-index: 1; --alt-bg: var(--surface-floating); --alt-sh: var(--elev-3); --alt-sh-up: var(--elev-3); animation: alt-emerge var(--dur-travel-2) var(--ease-rise); --alt-from-y: 6px; }
|
|
2474
|
+
@media (prefers-reduced-motion: reduce) { .alt-msg-caret { animation: none; } }
|
|
2475
|
+
[data-motion="reduced"] .alt-msg-caret { animation: none; }
|
|
2476
|
+
[data-contrast="more"] .alt-msg[data-stream] { box-shadow: inset 2px 0 0 0 var(--line-control); }
|
|
2477
|
+
[data-contrast="more"] .alt-msg[data-stream="streaming"] { box-shadow: none; background-size: 2px 8px; }
|
|
2478
|
+
@media (forced-colors: active) {
|
|
2479
|
+
.alt-msg[data-stream="streaming"] { background-image: none; border-left: 2px dotted CanvasText; }
|
|
2480
|
+
.alt-msg[data-stream="complete"], .alt-msg[data-stream="stopped"] { border-left: 2px solid CanvasText; }
|
|
2481
|
+
.alt-msg[data-from="user"] { border: 1px solid CanvasText; }
|
|
2482
|
+
.alt-msg-caret { forced-color-adjust: none; background: CanvasText; }
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
/* ── Confirmation: a proposal from an agent, not yet dispatched. It's status that asks for action, so it's raised
|
|
2486
|
+
(level 1, Alert's raised rule), never pending: hovering would say the action is already in flight. Approving turns it
|
|
2487
|
+
into a pending write (hovers after dur-grace), which lands with the afterglow or drops to a trench. Rejected and
|
|
2488
|
+
expired are flush and muted, kept in the record. ── */
|
|
2489
|
+
.alt-confirm {
|
|
2490
|
+
display: flex; flex-direction: column; gap: 6px; padding: var(--space-4); border-radius: var(--radius-lg);
|
|
2491
|
+
background: var(--surface-ground); box-shadow: inset 0 0 0 1px var(--line); max-width: 560px;
|
|
2492
|
+
transition: background var(--dur-travel-2) var(--ease-rise), box-shadow var(--dur-travel-2) var(--ease-rise);
|
|
2493
|
+
}
|
|
2494
|
+
.alt-confirm[data-state="requested"] { background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); }
|
|
2495
|
+
.alt-confirm[data-state="rejected"], .alt-confirm[data-state="expired"] { color: var(--ink-muted); }
|
|
2496
|
+
.alt-confirm[data-state="rejected"] .alt-confirm-title, .alt-confirm[data-state="expired"] .alt-confirm-title { color: var(--ink-muted); }
|
|
2497
|
+
.alt-confirm-line { display: flex; align-items: center; gap: 6px; margin: 0; font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2498
|
+
.alt-confirm-line[data-tone="accent"] { color: var(--accent-ink); }
|
|
2499
|
+
.alt-confirm-line[data-tone="critical"] { color: var(--critical); }
|
|
2500
|
+
.alt-confirm-title { margin: 0; font: 600 15px/21px var(--font-sans); color: var(--ink); }
|
|
2501
|
+
.alt-confirm-consequence { margin: 0; font: 400 14px/20px var(--font-sans); color: inherit; }
|
|
2502
|
+
.alt-confirm-details { margin-top: var(--space-2); }
|
|
2503
|
+
.alt-confirm-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
|
|
2504
|
+
.alt-confirm-who { margin-right: auto; font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2505
|
+
[data-contrast="more"] .alt-confirm[data-state="requested"] { box-shadow: 0 3px 0 0 var(--ink), var(--elev-1); }
|
|
2506
|
+
/* Editing a proposal (#281): its fields open in place inside the raised card (wells), as EditRegion does; Reject and
|
|
2507
|
+
Approve stay; an edited proposal carries an "Edited" plate by its title. */
|
|
2508
|
+
.alt-confirm-edit { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
|
|
2509
|
+
.alt-confirm-edit-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }
|
|
2510
|
+
.alt-confirm-edited { display: inline-flex; align-items: center; height: 20px; margin-left: 8px; padding: 0 6px; border-radius: var(--radius-sm); vertical-align: 2px;
|
|
2511
|
+
background: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent); font: 550 11.5px/1 var(--font-sans); color: var(--ink-muted); }
|
|
2512
|
+
.alt-confirm-scope { display: flex; flex-direction: column; gap: 1px; padding-block: 6px; white-space: normal; }
|
|
2513
|
+
.alt-menu:has(.alt-confirm-scope) { min-width: min(300px, calc(100vw - 16px)); }
|
|
2514
|
+
.alt-menu-item:has(.alt-confirm-scope) { height: auto; min-height: var(--size-row-md, 32px); }
|
|
2515
|
+
.alt-confirm-scope-desc { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2516
|
+
@media (forced-colors: active) { .alt-confirm { border: 1px solid CanvasText; } }
|
|
2517
|
+
/* Question (#270): Confirmation's raised card asking for an answer rather than approval. Options are trenches (radio or
|
|
2518
|
+
checkbox), "Your own answer" a well; Answer is the one primary. Answered, it presses to the ground as one muted line. */
|
|
2519
|
+
.alt-question-options { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 0; }
|
|
2520
|
+
.alt-question-other { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
|
|
2521
|
+
.alt-question-other-label { font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
2522
|
+
.alt-question[data-state="answered"], .alt-question[data-state="expired"] { padding-block: 8px; box-shadow: none; background: none; }
|
|
2523
|
+
.alt-question-record { display: flex; align-items: flex-start; gap: 6px; margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); }
|
|
2524
|
+
.alt-question-record svg { flex: none; margin-top: 2.5px; }
|
|
2525
|
+
.alt-question-asked { color: var(--ink); }
|
|
2526
|
+
.alt-question-asked::after { content: " ·"; color: var(--ink-muted); }
|
|
2527
|
+
.alt-question-set { display: flex; flex-direction: column; gap: var(--space-3); max-width: 560px; }
|
|
2528
|
+
.alt-question-set > .alt-confirm { max-width: none; }
|
|
2529
|
+
|
|
2530
|
+
/* ── Trace: an agent's steps as a vertical Stepper. Done steps are flush with a stamped check, the running step stands
|
|
2531
|
+
raised on its wall (no spinner: a small steady lamp in its mark), planned steps are imprints. A failed step drops to a
|
|
2532
|
+
trench with a critical rim and Retry. A step waiting for approval holds a Confirmation. Tool calls and reasoning sit
|
|
2533
|
+
behind Accordion lids under their step, indented to the text. ── */
|
|
2534
|
+
.alt-trace .alt-step { width: 100%; }
|
|
2535
|
+
.alt-trace .alt-step[data-state="failed"] { background: var(--surface-well); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); color: var(--ink); }
|
|
2536
|
+
.alt-trace .alt-step[data-state="failed"] .alt-step-mark { background: var(--critical); color: var(--on-critical); }
|
|
2537
|
+
.alt-trace-live { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--accent); }
|
|
2538
|
+
.alt-trace-error { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: 4px; font: 400 13px/18px var(--font-sans); color: var(--ink); }
|
|
2539
|
+
/* Indented to the step's text where there's room; in a narrow trace (under 480px) one small step in, so tool calls and
|
|
2540
|
+
approvals keep the width (a percentage margin reads the step's width: 40px at 480px, 12px from 452px down). */
|
|
2541
|
+
.alt-trace-detail { display: flex; flex-direction: column; gap: var(--space-2); margin: 6px 0 0 clamp(var(--space-3), 100% - 440px, 40px); }
|
|
2542
|
+
/* Subagents (#278): a nested run is a nested list behind an Accordion lid that says how it's going; steps that run at
|
|
2543
|
+
once sit in a labelled group with a 1px line-control bracket, several raised at once because each is in play. */
|
|
2544
|
+
.alt-trace-nested-title { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
|
|
2545
|
+
.alt-trace-nested-sum { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2546
|
+
.alt-trace-nested .alt-trace { padding: 4px 0 0; }
|
|
2547
|
+
.alt-trace-parallel { margin: 6px 0 0; padding: 2px 0 2px 12px; border-left: 1px solid var(--line-control); }
|
|
2548
|
+
.alt-trace-parallel-head { display: flex; flex-wrap: wrap; gap: 2px 8px; margin: 0 0 6px; font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2549
|
+
.alt-trace-parallel-head > :first-child { color: var(--ink); }
|
|
2550
|
+
@media (forced-colors: active) { .alt-trace-parallel { border-left-color: CanvasText; } }
|
|
2551
|
+
.alt-trace-acc .alt-acc-trigger { padding-block: 6px; font-size: 13px; }
|
|
2552
|
+
.alt-trace-tool code { font: 500 12.5px/18px var(--font-mono); }
|
|
2553
|
+
.alt-trace-io { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2554
|
+
.alt-trace-wait, .alt-trace-reasoning { margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); white-space: pre-wrap; }
|
|
2555
|
+
/* Reasoning while it streams (#279): not yet the record, like a streaming answer: "Thinking…" on the lid, and a dashed
|
|
2556
|
+
line-control rule down the open compartment. No shimmer, no pulsing dots, no ticking counter; the lid's words
|
|
2557
|
+
cross-fade when it completes. */
|
|
2558
|
+
.alt-trace-thought-title { animation: alt-fade-in var(--dur-travel-1) var(--ease-rise) both; }
|
|
2559
|
+
.alt-trace-thought[data-status="streaming"] .alt-trace-reasoning { max-height: 240px; overflow-y: auto; padding-left: 10px; border-left: 1px dashed var(--line-control); }
|
|
2560
|
+
.alt-trace-reasoning-hidden { margin: 0; padding: 4px 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); }
|
|
2561
|
+
@media (prefers-reduced-motion: reduce) { .alt-trace-thought-title { animation: none; } }
|
|
2562
|
+
[data-motion="reduced"] .alt-trace-thought-title { animation: none; }
|
|
2563
|
+
@media (forced-colors: active) { .alt-trace-thought[data-status="streaming"] .alt-trace-reasoning { border-left: 1px dotted CanvasText; } }
|
|
2564
|
+
.alt-trace-item:last-child .alt-step-line { display: none; }
|
|
2565
|
+
@media (forced-colors: active) { .alt-step[data-state="failed"] { border: 3px dashed CanvasText; } }
|
|
2566
|
+
|
|
2567
|
+
/* ── AILabel: marks what AI made. A label is part of the record, so it's a flush Badge plate with a small static icon
|
|
2568
|
+
and a word: no gradient, no glow, no shimmer (the light never moves, and a gradient rim reads as focus). It acts, so
|
|
2569
|
+
it lifts one level when pointed at, and opens a Popover at level 3 saying how the content was produced. ── */
|
|
2570
|
+
.alt-ailabel { border: 0; cursor: pointer; font-family: var(--font-sans); transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1); }
|
|
2571
|
+
.alt-ailabel svg { flex: none; color: var(--ink-muted); }
|
|
2572
|
+
.alt-ailabel:focus-visible, .alt-ailabel[aria-expanded="true"] { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
2573
|
+
@media (hover: hover) { .alt-ailabel:hover { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
2574
|
+
.alt-ailabel:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
2575
|
+
.alt-ailabel-pop { width: min(320px, calc(100vw - 32px)); }
|
|
2576
|
+
.alt-ailabel-body { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2577
|
+
.alt-ailabel-title { margin: 0; font: 600 14px/20px var(--font-sans); }
|
|
2578
|
+
.alt-ailabel-sub { margin: var(--space-1) 0 0; font: 600 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
2579
|
+
.alt-ailabel-facts { display: grid; gap: 4px; margin: 0; font: 400 13px/18px var(--font-sans); }
|
|
2580
|
+
.alt-ailabel-facts div { display: grid; grid-template-columns: 96px 1fr; gap: var(--space-2); }
|
|
2581
|
+
.alt-ailabel-facts dt { color: var(--ink-muted); }
|
|
2582
|
+
.alt-ailabel-facts dd { margin: 0; color: var(--ink); }
|
|
2583
|
+
.alt-ailabel-sources { margin: 0; padding-left: 1.3em; font: 400 13px/19px var(--font-sans); }
|
|
2584
|
+
.alt-ailabel-more { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2585
|
+
[data-contrast="more"] .alt-ailabel { box-shadow: inset 0 0 0 1px var(--line-control); }
|
|
2586
|
+
@media (forced-colors: active) { .alt-ailabel { border: 1px solid ButtonText; } }
|
|
2587
|
+
|
|
2588
|
+
/* ── InlineCitation: a numbered source marker in generated text. A citation is part of the record, so it's a small flush
|
|
2589
|
+
plate (ChartAnnotation's build) with a mono number, raised a little off the baseline like a superscript; it acts, so it
|
|
2590
|
+
lifts one level when pointed at, and a HoverCard previews the source at level 3. Sources is a flush ordered list. ── */
|
|
2591
|
+
.alt-cite {
|
|
2592
|
+
display: inline-flex; align-items: center; gap: 2px; min-width: 16px; height: 16px; padding: 0 4px; margin: 0 1px; border-radius: var(--radius-xs);
|
|
2593
|
+
vertical-align: 0.35em; background: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%); color: var(--ink-muted);
|
|
2594
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent); font: 600 10.5px/1 var(--font-mono); text-decoration: none; justify-content: center;
|
|
2595
|
+
transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1), color var(--dur-travel-1);
|
|
2596
|
+
}
|
|
2597
|
+
.alt-cite:focus-visible { background: var(--surface-raised); color: var(--accent-ink); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
2598
|
+
@media (hover: hover) { .alt-cite:hover { background: var(--surface-raised); color: var(--accent-ink); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
2599
|
+
.alt-cite:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
2600
|
+
.alt-cite[data-unverified] svg { color: var(--caution); }
|
|
2601
|
+
.alt-cite-card { width: min(300px, calc(100vw - 32px)); }
|
|
2602
|
+
.alt-cite-preview { display: flex; flex-direction: column; gap: 4px; }
|
|
2603
|
+
.alt-cite-meta { margin: 0; font: 500 12px/16px var(--font-sans); color: var(--ink-muted); }
|
|
2604
|
+
.alt-cite-title { margin: 0; font: 600 14px/20px var(--font-sans); }
|
|
2605
|
+
.alt-cite-excerpt { margin: 4px 0 0; padding-left: 10px; box-shadow: inset 2px 0 0 var(--line-control); font: 400 13px/19px var(--font-sans); color: var(--ink-muted); }
|
|
2606
|
+
.alt-cite-warn { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; color: var(--ink); font: 500 12px/16px var(--font-sans); }
|
|
2607
|
+
.alt-cite-meta .alt-cite-warn { margin-left: 0; }
|
|
2608
|
+
.alt-cite-warn svg { color: var(--caution); }
|
|
2609
|
+
.alt-sources { margin-top: var(--space-3); }
|
|
2610
|
+
.alt-sources-heading { margin: 0 0 6px; font: 600 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
2611
|
+
.alt-sources-list { margin: 0; padding-left: 1.4em; font: 400 13.5px/20px var(--font-sans); color: var(--ink); }
|
|
2612
|
+
.alt-sources-list li { scroll-margin: 40px; border-radius: var(--radius-xs); outline: none; }
|
|
2613
|
+
.alt-sources-list li:target, .alt-sources-list li:focus { background: var(--accent-soft); }
|
|
2614
|
+
.alt-sources-domain { color: var(--ink-muted); }
|
|
2615
|
+
[data-contrast="more"] .alt-cite { box-shadow: inset 0 0 0 1px var(--line-control); }
|
|
2616
|
+
@media (forced-colors: active) { .alt-cite { border: 1px solid LinkText; } }
|
|
2617
|
+
|
|
1099
2618
|
/* ── Pile: thickness says roughly how many (ordinal), the badge says exactly. Click a pile that has items and they
|
|
1100
2619
|
slide out from under the top card, floating over the page below (level 3) without moving anything. Click the top,
|
|
1101
2620
|
press Escape or click away and they slide back under. ── */
|
|
@@ -1111,8 +2630,10 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1111
2630
|
}
|
|
1112
2631
|
.alt-pile-live .alt-pile-top { cursor: pointer; }
|
|
1113
2632
|
/* Hover: the top lifts and the sheets beneath fan a little, a preview of what's underneath. */
|
|
1114
|
-
.alt-pile-live .alt-pile-top:
|
|
1115
|
-
.alt-pile-live
|
|
2633
|
+
.alt-pile-live .alt-pile-top:focus-visible { box-shadow: var(--elev-2); transform: translateY(calc(-3px * var(--alt-travel))); }
|
|
2634
|
+
@media (hover: hover) { .alt-pile-live .alt-pile-top:hover { box-shadow: var(--elev-2); transform: translateY(calc(-3px * var(--alt-travel))); } }
|
|
2635
|
+
.alt-pile-live:has(.alt-pile-top:focus-visible) .alt-pile-sheet { --fan: var(--alt-travel); }
|
|
2636
|
+
@media (hover: hover) { .alt-pile-live:has(.alt-pile-top:hover) .alt-pile-sheet { --fan: var(--alt-travel); } }
|
|
1116
2637
|
.alt-pile-live .alt-pile-top:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1117
2638
|
.alt-pile-count {
|
|
1118
2639
|
position: absolute; z-index: 5; top: -8px; right: -8px; min-width: 22px; height: 22px; padding: 0 6px; display: grid; place-items: center;
|
|
@@ -1142,9 +2663,12 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1142
2663
|
opacity: 1; transform: none;
|
|
1143
2664
|
transition: transform var(--dur-travel-3) var(--ease-settle) calc(var(--i) * 22ms), opacity var(--dur-travel-2) var(--ease-rise) calc(var(--i) * 22ms), box-shadow var(--dur-travel-1) var(--ease-rise);
|
|
1144
2665
|
}
|
|
1145
|
-
.alt-pile-list[data-state="open"] .alt-pile-item:
|
|
2666
|
+
.alt-pile-list[data-state="open"] .alt-pile-item:focus-visible {
|
|
1146
2667
|
box-shadow: var(--elev-3); transform: translateY(calc(-2px * var(--alt-travel))); transition-delay: 0ms;
|
|
1147
2668
|
}
|
|
2669
|
+
@media (hover: hover) { .alt-pile-list[data-state="open"] .alt-pile-item:hover {
|
|
2670
|
+
box-shadow: var(--elev-3); transform: translateY(calc(-2px * var(--alt-travel))); transition-delay: 0ms;
|
|
2671
|
+
} }
|
|
1148
2672
|
.alt-pile-list[data-state="open"] .alt-pile-item:active { box-shadow: var(--elev-0); transform: translateY(calc(1px * var(--alt-travel))); transition-duration: var(--dur-press); transition-delay: 0ms; }
|
|
1149
2673
|
.alt-pile-item:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1150
2674
|
|
|
@@ -1155,7 +2679,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1155
2679
|
transition: box-shadow var(--dur-travel-2) var(--ease-rise);
|
|
1156
2680
|
}
|
|
1157
2681
|
.alt-scroll[data-top] .alt-scroll-header { box-shadow: var(--elev-2); }
|
|
1158
|
-
.alt-scroll-body { overflow: auto;
|
|
2682
|
+
.alt-scroll-body { overflow: auto; outline: none; scrollbar-width: thin; }
|
|
1159
2683
|
.alt-scroll-body:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
1160
2684
|
.alt-scroll-lip { position: absolute; left: 0; right: 0; height: 24px; pointer-events: none; opacity: 0; transition: opacity var(--dur-travel-2) var(--ease-rise); z-index: 1; }
|
|
1161
2685
|
.alt-scroll-lip[data-edge="top"] { top: 0; box-shadow: var(--lip-top); }
|
|
@@ -1165,12 +2689,12 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1165
2689
|
|
|
1166
2690
|
/* ── DropZone: flush until a file is in flight anywhere, then it opens; over it, it deepens. ── */
|
|
1167
2691
|
.alt-drop {
|
|
1168
|
-
display: grid; place-items: center; gap: var(--space-2); min-height: 132px; padding: var(--
|
|
2692
|
+
display: grid; place-items: center; gap: var(--space-2); min-height: 132px; padding: var(--alt-inset-box); text-align: center;
|
|
1169
2693
|
border: 1.5px dashed var(--line-control); border-radius: var(--radius-lg); background: var(--surface-ground); box-shadow: var(--elev-0);
|
|
1170
2694
|
color: var(--ink-muted); font: 500 14px/20px var(--font-sans); cursor: pointer;
|
|
1171
2695
|
transition: background-color var(--dur-travel-2) var(--ease-rise), box-shadow var(--dur-travel-2) var(--ease-rise), border-color var(--dur-travel-2), transform var(--dur-travel-2) var(--ease-settle);
|
|
1172
2696
|
}
|
|
1173
|
-
.alt-drop:hover { border-color: var(--ink-muted); }
|
|
2697
|
+
@media (hover: hover) { .alt-drop:hover { border-color: var(--ink-muted); } }
|
|
1174
2698
|
.alt-drop:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1175
2699
|
.alt-drop[data-drop="armed"] { background: var(--surface-well); box-shadow: var(--well); border-color: var(--ink-muted); }
|
|
1176
2700
|
.alt-drop[data-drop="over"] { background: var(--accent-soft); box-shadow: var(--trench); border-style: solid; border-color: var(--accent-ink); color: var(--ink); transform: scale(calc(1 - 0.01 * var(--alt-travel))); }
|
|
@@ -1182,7 +2706,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1182
2706
|
It sinks in when it appears (flush to well, dur-travel-1, ease-sink), the inverse of content arriving lifted,
|
|
1183
2707
|
and it never pulses. ── */
|
|
1184
2708
|
.alt-empty {
|
|
1185
|
-
display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: var(--space-8) var(--
|
|
2709
|
+
display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: var(--space-8) var(--alt-inset-page); text-align: center;
|
|
1186
2710
|
border: 1.5px dashed var(--line-control); border-radius: var(--radius-lg); background: var(--surface-well); box-shadow: var(--well);
|
|
1187
2711
|
color: var(--ink-muted); font: 400 15px/23px var(--font-sans);
|
|
1188
2712
|
animation: alt-empty-sink var(--dur-travel-1) var(--ease-sink);
|
|
@@ -1194,7 +2718,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1194
2718
|
.alt-empty-title { margin: 0; font: 600 19px/26px var(--font-sans); letter-spacing: -0.01em; color: var(--ink); text-wrap: balance; }
|
|
1195
2719
|
.alt-empty-desc { margin: 0; max-width: 46ch; color: var(--ink-muted); text-wrap: pretty; }
|
|
1196
2720
|
.alt-empty-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); margin-top: var(--space-3); }
|
|
1197
|
-
.alt-empty:has(.alt-empty-actions > :first-child:is(:hover, :focus-visible):not(:disabled)) { box-shadow: var(--trench); border-color: var(--ink-muted); }
|
|
2721
|
+
@media (hover: hover) { .alt-empty:has(.alt-empty-actions > :first-child:is(:hover, :focus-visible):not(:disabled)) { box-shadow: var(--trench); border-color: var(--ink-muted); } }
|
|
1198
2722
|
.alt-empty[data-size="sm"] { gap: var(--space-1); padding: var(--space-5) var(--space-4); border-radius: var(--radius-md); font: 400 14px/20px var(--font-sans); }
|
|
1199
2723
|
.alt-empty[data-size="sm"] .alt-empty-icon > svg { width: 18px; height: 18px; }
|
|
1200
2724
|
.alt-empty[data-size="sm"] .alt-empty-title { font: 500 14px/20px var(--font-sans); letter-spacing: 0; }
|
|
@@ -1205,21 +2729,29 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1205
2729
|
.alt-sort-item {
|
|
1206
2730
|
position: relative; z-index: 1; display: flex; align-items: center; gap: var(--space-3); padding: 10px var(--space-4) 10px var(--space-3);
|
|
1207
2731
|
border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink);
|
|
1208
|
-
font: 500 14px/20px var(--font-sans); user-select: none; touch-
|
|
2732
|
+
font: 500 14px/20px var(--font-sans); user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation; cursor: grab; outline: none;
|
|
1209
2733
|
transition: transform var(--dur-travel-2) var(--ease-settle), box-shadow var(--dur-travel-2) var(--ease-rise), background-color var(--dur-travel-1);
|
|
1210
2734
|
}
|
|
1211
|
-
.alt-sort-item:hover { box-shadow: var(--elev-2); }
|
|
2735
|
+
@media (hover: hover) { .alt-sort-item:hover { box-shadow: var(--elev-2); } }
|
|
1212
2736
|
.alt-sort-item:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1213
2737
|
.alt-sort-item[data-held] { z-index: 3; cursor: grabbing; background: var(--surface-floating); box-shadow: var(--elev-3); }
|
|
1214
2738
|
.alt-sort-item[data-held]:not([data-following]) { transform: translateY(calc(-2px * var(--alt-travel))) rotate(calc(-0.5deg * var(--alt-travel))); }
|
|
1215
2739
|
.alt-sort-item[data-following] { transition: box-shadow var(--dur-travel-2) var(--ease-rise), background-color var(--dur-travel-1); rotate: calc(-0.6deg * var(--alt-travel)); scale: calc(1 + 0.015 * var(--alt-travel)); }
|
|
1216
2740
|
.alt-sort-grip { display: inline-flex; color: var(--ink-muted); flex: none; }
|
|
2741
|
+
/* On touch a swipe on the row scrolls and a press-and-hold picks it up; the grip picks it up at once, with a finger-sized hit area (#256). */
|
|
2742
|
+
.alt-sort-grip { position: relative; touch-action: none; }
|
|
2743
|
+
@media (pointer: coarse) { .alt-sort-grip::after { content: ""; position: absolute; inset: -14px -14px; } }
|
|
1217
2744
|
.alt-sort-body { flex: 1; min-width: 0; }
|
|
1218
2745
|
.alt-sort-slot {
|
|
1219
2746
|
position: absolute; left: 0; right: 0; z-index: 0; border-radius: var(--radius-md); background: var(--surface-well); box-shadow: var(--well);
|
|
1220
2747
|
border: 1.5px dashed var(--line-control); transition: top var(--dur-travel-2) var(--ease-settle);
|
|
1221
2748
|
}
|
|
1222
2749
|
|
|
2750
|
+
/* ── Breadcrumb and Drilldown share one staircase (CrumbStep): every step stands on the ground line, each deeper one a
|
|
2751
|
+
taller slab. Breadcrumb's steps are links (or your router's), the current page a step that doesn't press, and a deep
|
|
2752
|
+
path's middle waits behind a "…" step that opens a menu. A route change re-renders it without motion. ── */
|
|
2753
|
+
.alt-breadcrumb ol { padding-top: 14px; padding-bottom: 4px; }
|
|
2754
|
+
|
|
1223
2755
|
/* ── Drilldown: hierarchy as a stack. Each place you go into is laid on top of the last: the crumbs climb a
|
|
1224
2756
|
staircase (each one step higher, its wall one pixel thicker), and the view sits on a pile as thick as you are deep. ── */
|
|
1225
2757
|
.alt-drill { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
@@ -1228,10 +2760,10 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1228
2760
|
higher, so all the contact shadows line up. Face and wall share one hairline so the top edge always reads.
|
|
1229
2761
|
Shadow list: [face ring, wall, wall ring, contact, ambient]. */
|
|
1230
2762
|
.alt-crumbs li { position: relative; display: flex; --rise: calc(var(--d) * 3px); }
|
|
1231
|
-
.alt-crumbs
|
|
2763
|
+
.alt-crumbs .alt-crumb-link {
|
|
1232
2764
|
--wd: calc(2px + var(--rise)); --wall: var(--wd); --wall-c: color-mix(in oklab, var(--surface-trench), var(--ink) 6%);
|
|
1233
|
-
position: relative; height:
|
|
1234
|
-
font: 500 13px/18px var(--font-sans); color: var(--ink-muted); white-space: nowrap; cursor: pointer;
|
|
2765
|
+
position: relative; display: inline-flex; align-items: center; height: var(--size-control-sm); padding: 0 12px; border: 0; border-radius: var(--radius-sm);
|
|
2766
|
+
font: 500 13px/18px var(--font-sans); color: var(--ink-muted); white-space: nowrap; text-decoration: none; cursor: pointer;
|
|
1235
2767
|
background: var(--surface-raised); background-image: var(--alt-sheen);
|
|
1236
2768
|
box-shadow: 0 0 0 1px var(--alt-ring), 0 var(--wd) 0 0 var(--wall-c), 0 var(--wd) 0 1px var(--alt-ring),
|
|
1237
2769
|
0 calc(var(--wd) + 1px) 2px 0 var(--alt-cast), 0 calc(var(--wd) + 3px) 8px -3px var(--alt-cast);
|
|
@@ -1239,12 +2771,15 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1239
2771
|
transition: transform var(--dur-travel-2) var(--ease-settle), box-shadow var(--dur-travel-2) var(--ease-settle),
|
|
1240
2772
|
color var(--dur-travel-1), background-color var(--dur-travel-1);
|
|
1241
2773
|
}
|
|
1242
|
-
.alt-crumbs li:not([data-current])
|
|
2774
|
+
.alt-crumbs li:not([data-current]) .alt-crumb-link:focus-visible {
|
|
1243
2775
|
--wd: calc(3px + var(--rise)); color: var(--ink); background-color: var(--surface-lifted);
|
|
1244
2776
|
}
|
|
1245
|
-
.alt-crumbs li:not([data-current])
|
|
1246
|
-
|
|
1247
|
-
|
|
2777
|
+
@media (hover: hover) { .alt-crumbs li:not([data-current]) .alt-crumb-link:hover {
|
|
2778
|
+
--wd: calc(3px + var(--rise)); color: var(--ink); background-color: var(--surface-lifted);
|
|
2779
|
+
} }
|
|
2780
|
+
.alt-crumbs li:not([data-current]) .alt-crumb-link:active { --wd: 0px; background-image: none; transition-duration: var(--dur-press); transition-timing-function: ease-out; }
|
|
2781
|
+
.alt-crumbs li[data-current] .alt-crumb-link { color: var(--ink); font-weight: 600; cursor: default; }
|
|
2782
|
+
.alt-crumbs li[data-new] .alt-crumb-link { animation: alt-crumb-rise var(--dur-travel-3) var(--ease-settle); }
|
|
1248
2783
|
/* A new step is built: it rises out of the ground to its height. */
|
|
1249
2784
|
@keyframes alt-crumb-rise {
|
|
1250
2785
|
from { transform: none; box-shadow: 0 0 0 1px var(--alt-ring), 0 0 0 0 var(--wall-c), 0 0 0 1px var(--alt-ring), 0 1px 2px 0 var(--alt-cast), 0 3px 8px -3px var(--alt-cast); }
|
|
@@ -1268,7 +2803,8 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1268
2803
|
background: transparent; color: var(--ink); text-align: left; cursor: pointer; box-shadow: var(--elev-0);
|
|
1269
2804
|
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
1270
2805
|
}
|
|
1271
|
-
.alt-drill-row:
|
|
2806
|
+
.alt-drill-row:focus-visible { background: var(--surface-lifted); box-shadow: var(--elev-2); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
2807
|
+
@media (hover: hover) { .alt-drill-row:hover { background: var(--surface-lifted); box-shadow: var(--elev-2); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
1272
2808
|
.alt-drill-row:active { box-shadow: var(--elev-0); transform: translateY(calc(1px * var(--alt-travel))); transition-duration: var(--dur-press); }
|
|
1273
2809
|
.alt-drill-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1274
2810
|
.alt-drill-row-text { flex: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
@@ -1286,7 +2822,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1286
2822
|
change, rows that survive travel to their new places (FLIP, ease-glide) and new ones rise 4px in, at most four at a
|
|
1287
2823
|
time (riseDelay) and only near the top; the rest cross-fade. On opening, predicted items are there at once and the
|
|
1288
2824
|
rest fade in over dur-onset. ── */
|
|
1289
|
-
.alt-cmd-root { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; justify-items: center; align-items: start; padding: max(var(--space-6),
|
|
2825
|
+
.alt-cmd-root { position: fixed; inset: 0; top: var(--alt-vv-top, 0px); bottom: auto; height: var(--alt-vvh, 100dvh); z-index: var(--z-modal); display: grid; justify-items: center; align-items: start; padding: max(var(--space-6), calc(var(--alt-vvh, 100dvh) * 0.12)) var(--space-4) var(--space-6); }
|
|
1290
2826
|
.alt-cmd-root[data-state="closed"] .alt-scrim { opacity: 0; transition-duration: calc(var(--dur-travel-5) * 0.7); transition-timing-function: var(--ease-sink); }
|
|
1291
2827
|
.alt-cmd {
|
|
1292
2828
|
position: relative; display: flex; flex-direction: column; width: min(100%, 600px); max-height: 100%; overflow: hidden; outline: none;
|
|
@@ -1301,7 +2837,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1301
2837
|
.alt-cmd .alt-cmd-field { height: 48px; padding: 0 var(--space-3) 0 var(--space-4); gap: var(--space-3); border-radius: calc(var(--radius-xl) - var(--space-3)); }
|
|
1302
2838
|
.alt-cmd .alt-cmd-field input { font-size: 16px; }
|
|
1303
2839
|
.alt-cmd .alt-cmd-field:focus-within, .alt-cmd .alt-cmd-field:has(input:not(:placeholder-shown)) { background: var(--surface-modal); }
|
|
1304
|
-
.alt-cmd-viewport { position: relative; flex: 0 1 auto; min-height: 0; max-height: min(380px, 52vh); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
|
|
2840
|
+
.alt-cmd-viewport { position: relative; flex: 0 1 auto; min-height: 0; max-height: min(380px, 52vh); max-height: min(380px, calc(var(--alt-vvh, 100dvh) * 0.52)); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
|
|
1305
2841
|
.alt-cmd-viewport[data-ready] { transition: height var(--dur-travel-2) var(--ease-glide); }
|
|
1306
2842
|
.alt-cmd-list { position: relative; padding: 0 var(--space-2) var(--space-2); outline: none; }
|
|
1307
2843
|
.alt-cmd-group + .alt-cmd-group { margin-top: var(--space-1); }
|
|
@@ -1312,7 +2848,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1312
2848
|
}
|
|
1313
2849
|
.alt-cmd-hl[data-tone="critical"] { background: var(--critical-soft); }
|
|
1314
2850
|
.alt-cmd-item {
|
|
1315
|
-
position: relative; display: flex; align-items: center; gap: 10px; min-height:
|
|
2851
|
+
position: relative; display: flex; align-items: center; gap: 10px; min-height: var(--size-field-md); padding: 0 10px; border-radius: var(--radius-sm);
|
|
1316
2852
|
font: 500 14px/20px var(--font-sans); color: var(--ink); cursor: pointer; user-select: none;
|
|
1317
2853
|
}
|
|
1318
2854
|
.alt-cmd-item[data-tone="critical"] { color: var(--critical); }
|
|
@@ -1340,7 +2876,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1340
2876
|
8px clear of its edge so its own edges, and the shadow it casts below them, read as height. While content scrolls
|
|
1341
2877
|
under its header the header rises; where content continues below, the footer's top edge curves away. A bottom
|
|
1342
2878
|
sheet has a finger pull: a small recess (an opening, like every place you put something into) to drag it down by. ── */
|
|
1343
|
-
.alt-sheet-root { position: fixed; inset: 0; z-index: var(--z-overlay); pointer-events: none; }
|
|
2879
|
+
.alt-sheet-root { position: fixed; inset: 0; top: var(--alt-vv-top, 0px); bottom: auto; height: var(--alt-vvh, 100dvh); z-index: var(--z-overlay); pointer-events: none; }
|
|
1344
2880
|
.alt-sheet-root > .alt-scrim { pointer-events: auto; }
|
|
1345
2881
|
.alt-sheet-root[data-state="closed"] > .alt-scrim { opacity: 0; transition-duration: calc(var(--dur-travel-4) * 0.7); transition-timing-function: var(--ease-sink); }
|
|
1346
2882
|
.alt-sheet {
|
|
@@ -1351,10 +2887,10 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1351
2887
|
}
|
|
1352
2888
|
.alt-sheet[data-size="sm"] { --w: 360px; }
|
|
1353
2889
|
.alt-sheet[data-size="lg"] { --w: 560px; }
|
|
1354
|
-
.alt-sheet[data-side="right"], .alt-sheet[data-side="left"] { top: var(--edge); bottom: var(--edge); width: min(calc(100% - 2 * var(--edge)), var(--w)); }
|
|
1355
|
-
.alt-sheet[data-side="right"] { right: var(--edge); transform-origin: 100% 50%; }
|
|
1356
|
-
.alt-sheet[data-side="left"] { left: var(--edge); transform-origin: 0 50%; }
|
|
1357
|
-
.alt-sheet[data-side="bottom"] { --w: 720px; left: 0; right: 0; bottom: var(--edge); width: min(calc(100% - 2 * var(--edge)), var(--w)); max-height: calc(100% - 2 * var(--space-8)); margin: 0 auto; transform-origin: 50% 100%; }
|
|
2890
|
+
.alt-sheet[data-side="right"], .alt-sheet[data-side="left"] { top: calc(var(--edge) + env(safe-area-inset-top, 0px)); bottom: calc(var(--edge) + env(safe-area-inset-bottom, 0px)); width: min(calc(100% - 2 * var(--edge)), var(--w)); }
|
|
2891
|
+
.alt-sheet[data-side="right"] { right: calc(var(--edge) + env(safe-area-inset-right, 0px)); transform-origin: 100% 50%; }
|
|
2892
|
+
.alt-sheet[data-side="left"] { left: calc(var(--edge) + env(safe-area-inset-left, 0px)); transform-origin: 0 50%; }
|
|
2893
|
+
.alt-sheet[data-side="bottom"] { --w: 720px; left: 0; right: 0; bottom: calc(var(--edge) + env(safe-area-inset-bottom, 0px)); width: min(calc(100% - 2 * var(--edge)), var(--w)); max-height: calc(100% - 2 * var(--space-8)); margin: 0 auto; transform-origin: 50% 100%; }
|
|
1358
2894
|
.alt-sheet[data-side="bottom"][data-size="sm"] { --w: 480px; }
|
|
1359
2895
|
.alt-sheet[data-side="bottom"][data-size="lg"] { --w: 960px; }
|
|
1360
2896
|
.alt-sheet-root[data-state="closed"] .alt-sheet { opacity: 0; box-shadow: var(--elev-1); }
|
|
@@ -1366,19 +2902,19 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1366
2902
|
transition-duration: calc(var(--dur-travel-4) * 0.7); transition-timing-function: var(--ease-sink);
|
|
1367
2903
|
}
|
|
1368
2904
|
.alt-sheet-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
|
|
1369
|
-
.alt-sheet-inner { display: flex; flex-direction: column; gap: var(--space-4); min-height: 100%; padding: 0 var(--
|
|
2905
|
+
.alt-sheet-inner { display: flex; flex-direction: column; gap: var(--space-4); min-height: 100%; padding: 0 var(--alt-inset-page); }
|
|
1370
2906
|
.alt-sheet-header {
|
|
1371
|
-
position: sticky; top: 0; z-index: 2; display: flex; flex-direction: column; gap: 6px; margin: 0 calc(-1 * var(--
|
|
1372
|
-
padding: var(--
|
|
2907
|
+
position: sticky; top: 0; z-index: 2; display: flex; flex-direction: column; gap: 6px; margin: 0 calc(-1 * var(--alt-inset-page));
|
|
2908
|
+
padding: var(--alt-inset-page) calc(var(--alt-inset-page) + 36px) var(--space-2) var(--alt-inset-page); background: var(--surface-overlay); box-shadow: var(--elev-0);
|
|
1373
2909
|
transition: box-shadow var(--dur-travel-2) var(--ease-rise);
|
|
1374
2910
|
}
|
|
1375
2911
|
.alt-sheet[data-top] .alt-sheet-header { box-shadow: var(--elev-2); }
|
|
1376
2912
|
.alt-sheet-title { margin: 0; font: 600 19px/26px var(--font-sans); letter-spacing: -0.01em; }
|
|
1377
2913
|
.alt-sheet-desc { margin: 0; font: 400 14px/21px var(--font-sans); color: var(--ink-muted); }
|
|
1378
|
-
.alt-sheet-inner > :last-child:not(.alt-sheet-footer) { margin-bottom: var(--
|
|
2914
|
+
.alt-sheet-inner > :last-child:not(.alt-sheet-footer) { margin-bottom: var(--alt-inset-page); }
|
|
1379
2915
|
.alt-sheet-footer {
|
|
1380
|
-
position: sticky; bottom: 0; z-index: 2; display: flex; justify-content: flex-end; gap: var(--space-2); margin: auto calc(-1 * var(--
|
|
1381
|
-
padding: var(--space-4) var(--
|
|
2916
|
+
position: sticky; bottom: 0; z-index: 2; display: flex; justify-content: flex-end; gap: var(--space-2); margin: auto calc(-1 * var(--alt-inset-page)) 0;
|
|
2917
|
+
padding: var(--space-4) var(--alt-inset-page) var(--alt-inset-page); background: var(--surface-overlay);
|
|
1382
2918
|
}
|
|
1383
2919
|
.alt-sheet-footer::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 24px; pointer-events: none; box-shadow: var(--lip-bottom); opacity: 0; transition: opacity var(--dur-travel-2) var(--ease-rise); }
|
|
1384
2920
|
.alt-sheet[data-bottom] .alt-sheet-footer::before { opacity: 1; }
|
|
@@ -1493,9 +3029,12 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1493
3029
|
.alt-step-desc { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
1494
3030
|
.alt-step[role="button"] { cursor: pointer; }
|
|
1495
3031
|
.alt-step[role="button"][data-state="done"] { --wall: 0px; }
|
|
1496
|
-
.alt-step[role="button"][data-state="done"]:
|
|
3032
|
+
.alt-step[role="button"][data-state="done"]:focus-visible {
|
|
1497
3033
|
--wall: var(--wall-1); --st-ring: var(--alt-ring); --st-cast: var(--alt-cast); background: color-mix(in oklab, var(--surface-raised) 60%, transparent);
|
|
1498
3034
|
}
|
|
3035
|
+
@media (hover: hover) { .alt-step[role="button"][data-state="done"]:hover {
|
|
3036
|
+
--wall: var(--wall-1); --st-ring: var(--alt-ring); --st-cast: var(--alt-cast); background: color-mix(in oklab, var(--surface-raised) 60%, transparent);
|
|
3037
|
+
} }
|
|
1499
3038
|
.alt-step[role="button"][data-state="done"]:active { --wall: 0px; transition-duration: var(--dur-press); transition-timing-function: ease-out; }
|
|
1500
3039
|
.alt-step[role="button"]:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1501
3040
|
.alt-stepper[data-orientation="vertical"] { flex-direction: column; align-items: stretch; flex-wrap: nowrap; padding: 4px 0 0; }
|
|
@@ -1513,17 +3052,31 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1513
3052
|
level 3, tilted, over a slot that opens as a well. Cells can't share one shadow, so each row's height is drawn as a
|
|
1514
3053
|
single slab by its first cell, as wide as the table (--alt-row-w, measured). ── */
|
|
1515
3054
|
.alt-data-table { min-width: 0; }
|
|
1516
|
-
.alt-table-tools { display: flex; align-items: center; gap: var(--space-3); min-height: 36px; margin-bottom: var(--space-2); font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
1517
|
-
.alt-table-selection { display: inline-flex; align-items: center; gap: var(--space-2); --alt-from-y: 4px; animation: alt-emerge var(--dur-travel-1) var(--ease-rise); }
|
|
3055
|
+
.alt-table-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); min-height: 36px; margin-bottom: var(--space-2); font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
3056
|
+
.alt-table-selection { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); --alt-from-y: 4px; animation: alt-emerge var(--dur-travel-1) var(--ease-rise); }
|
|
1518
3057
|
.alt-table-selection .alt-badge[data-variant="count"] { position: static; }
|
|
1519
|
-
.alt-table-tools-end { display: inline-flex; align-items: center; gap: var(--space-2); margin-left: auto; }
|
|
1520
|
-
|
|
3058
|
+
.alt-table-tools-end { display: inline-flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); min-width: 0; max-width: 100%; margin-left: auto; }
|
|
3059
|
+
/* At narrow widths the toolbar wraps (WCAG 1.4.10 Reflow): a field in it may shrink rather than push the row off the page. */
|
|
3060
|
+
.alt-table-tools-end > .alt-input { flex: 0 1 auto; min-width: 0; }
|
|
3061
|
+
.alt-table-tools-end > .alt-input input { min-width: 0; }
|
|
3062
|
+
.alt-table-wrap { position: relative; isolation: isolate; overflow: auto; scrollbar-width: thin; outline: none; }
|
|
1521
3063
|
.alt-table-wrap:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-md); }
|
|
1522
3064
|
.alt-table-wrap::after { content: ""; position: sticky; z-index: 4; bottom: 0; display: block; height: 24px; margin-top: -24px; pointer-events: none; box-shadow: var(--lip-bottom); opacity: 0; transition: opacity var(--dur-travel-2) var(--ease-rise); }
|
|
1523
3065
|
.alt-table-wrap[data-bottom]::after { opacity: 1; }
|
|
1524
3066
|
.alt-table { --alt-row-w: 100%; width: 100%; border-collapse: separate; border-spacing: 0; font: 400 14px/20px var(--font-sans); color: var(--ink); font-variant-numeric: tabular-nums; }
|
|
1525
3067
|
.alt-th, .alt-td { padding: 10px 12px; text-align: left; vertical-align: middle; border-bottom: 1px solid var(--line); transition: border-color var(--dur-travel-1); }
|
|
1526
3068
|
.alt-th[data-align="end"], .alt-td[data-align="end"] { text-align: right; }
|
|
3069
|
+
/* Narrow (#260): columns keep a readable width and the table scrolls inside its frame. A header never wraps, and a
|
|
3070
|
+
cell's min-width (from minWidth, or 6rem when the table is narrow) stops a column being squeezed to "Des…". */
|
|
3071
|
+
.alt-table-wrap { container-type: inline-size; }
|
|
3072
|
+
@container (max-width: 480px) { .alt-table { table-layout: auto; width: max-content; min-width: 100%; } .alt-th, .alt-td:not([colspan]) { min-width: 6rem; } .alt-table :is(.alt-td-check, .alt-td-expand, .alt-td-grip, .alt-td-edit, .alt-th-check, .alt-th-expand, .alt-th-grip, .alt-th-edit, .alt-th:empty) { min-width: 0; } }
|
|
3073
|
+
/* A sticky column stays put over a flush 1px edge, on the ground surface (no shadow: it isn't live). */
|
|
3074
|
+
.alt-table .alt-th[data-sticky], .alt-table .alt-td[data-sticky] { position: sticky; left: 0; z-index: 2; background: var(--surface-ground); box-shadow: inset -1px 0 0 var(--line); }
|
|
3075
|
+
.alt-table .alt-th[data-sticky] { z-index: 5; }
|
|
3076
|
+
/* The edge with more columns fades; the start edge only without a sticky column, which already marks it. */
|
|
3077
|
+
.alt-table-wrap[data-more-end] { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
|
|
3078
|
+
.alt-table-wrap[data-more-start]:not(:has([data-sticky])) { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); }
|
|
3079
|
+
.alt-table-wrap[data-more-start][data-more-end]:not(:has([data-sticky])) { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); }
|
|
1527
3080
|
.alt-th { position: sticky; top: 0; z-index: 3; padding-block: 9px; font: 500 12.5px/17px var(--font-sans); color: var(--ink-muted); white-space: nowrap; }
|
|
1528
3081
|
.alt-td:first-child { position: relative; }
|
|
1529
3082
|
.alt-tr { --row-bg: transparent; --row-sh: var(--elev-0); transition: transform var(--dur-travel-1) var(--ease-rise), color var(--dur-travel-2) var(--ease-rise); }
|
|
@@ -1535,17 +3088,18 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1535
3088
|
.alt-table-wrap[data-top] .alt-table-head .alt-tr { --row-bg: var(--surface-raised); --row-sh: var(--elev-2); }
|
|
1536
3089
|
.alt-table-wrap[data-top] .alt-th { border-bottom-color: transparent; }
|
|
1537
3090
|
.alt-tr[data-interactive] { cursor: pointer; }
|
|
1538
|
-
.alt-tr[data-interactive]:not([data-row-held]):
|
|
3091
|
+
.alt-tr[data-interactive]:not([data-row-held]):has(.alt-table-rowbtn:focus-visible) { --row-bg: var(--surface-raised); --row-sh: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
3092
|
+
@media (hover: hover) { .alt-tr[data-interactive]:not([data-row-held]):hover { --row-bg: var(--surface-raised); --row-sh: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
1539
3093
|
.alt-tr[data-interactive]:not([data-row-held]):active { --row-sh: var(--elev-0); transform: none; transition-duration: var(--dur-press); }
|
|
1540
3094
|
.alt-tr[data-row-relevance="match"] { --row-bg: var(--surface-raised); --row-sh: var(--elev-1); }
|
|
1541
3095
|
.alt-tr[data-row-relevance="miss"] { --row-bg: var(--surface-well); --row-sh: var(--well); color: var(--ink-muted); }
|
|
1542
3096
|
.alt-tr[data-selected] { --row-bg: var(--accent-soft); --row-sh: var(--elev-1); }
|
|
1543
|
-
.alt-tr[data-selected][data-interactive]:not([data-row-held]):hover { --row-bg: var(--accent-soft); --row-sh: var(--elev-2); }
|
|
3097
|
+
@media (hover: hover) { .alt-tr[data-selected][data-interactive]:not([data-row-held]):hover { --row-bg: var(--accent-soft); --row-sh: var(--elev-2); } }
|
|
1544
3098
|
.alt-tr[data-row-held] { position: relative; z-index: 5; --row-bg: var(--surface-floating); --row-sh: var(--elev-3); rotate: calc(-0.35deg * var(--alt-travel)); transition: none; }
|
|
1545
3099
|
.alt-tr[data-row-held][data-selected] { --row-bg: color-mix(in oklab, var(--accent-soft) 70%, var(--surface-floating)); }
|
|
1546
3100
|
.alt-tr[data-row-held]:not([data-following]) { transform: translateY(calc(-3px * var(--alt-travel))); }
|
|
1547
|
-
.alt-tr:is([data-interactive]:hover, [data-selected], [data-row-relevance], [data-row-held]) > .alt-td,
|
|
1548
3101
|
.alt-tr:has(+ .alt-tr:is([data-selected], [data-row-relevance], [data-row-held])) > .alt-td { border-bottom-color: transparent; }
|
|
3102
|
+
@media (hover: hover) { .alt-tr:is([data-interactive]:hover, [data-selected], [data-row-relevance], [data-row-held]) > .alt-td { border-bottom-color: transparent; } }
|
|
1549
3103
|
.alt-tr[data-row-settle="a"] > .alt-td:first-child::before { animation: alt-afterglow-a var(--dur-settle) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
1550
3104
|
.alt-tr[data-row-settle="b"] > .alt-td:first-child::before { animation: alt-afterglow-b var(--dur-settle) linear var(--alt-rise-delay, 0ms) backwards; }
|
|
1551
3105
|
.alt-table[data-dragging] .alt-tr:not([data-following]) { transition: transform var(--dur-travel-2) var(--ease-settle); }
|
|
@@ -1554,11 +3108,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1554
3108
|
background: none; color: inherit; font: inherit; cursor: pointer; box-shadow: var(--elev-0);
|
|
1555
3109
|
transition: background-color var(--dur-travel-1), color var(--dur-travel-1), box-shadow var(--dur-travel-1) var(--ease-rise);
|
|
1556
3110
|
}
|
|
1557
|
-
.alt-table-sort:hover { color: var(--ink); background: var(--surface-raised); box-shadow: var(--elev-1); }
|
|
3111
|
+
@media (hover: hover) { .alt-table-sort:hover { color: var(--ink); background: var(--surface-raised); box-shadow: var(--elev-1); } }
|
|
1558
3112
|
.alt-table-sort:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1559
3113
|
.alt-table-sort:not([data-sort="none"]) { color: var(--ink); }
|
|
1560
3114
|
.alt-table-sort-icon { opacity: 0; transition: transform var(--dur-travel-2) var(--ease-settle), opacity var(--dur-travel-1); }
|
|
1561
|
-
.alt-table-sort:hover .alt-table-sort-icon { opacity: 0.45; }
|
|
3115
|
+
@media (hover: hover) { .alt-table-sort:hover .alt-table-sort-icon { opacity: 0.45; } }
|
|
1562
3116
|
.alt-table-sort:not([data-sort="none"]) .alt-table-sort-icon { opacity: 1; }
|
|
1563
3117
|
.alt-table-sort[data-sort="descending"] .alt-table-sort-icon { transform: rotate(180deg); }
|
|
1564
3118
|
.alt-th[data-align="end"] .alt-table-sort { flex-direction: row-reverse; }
|
|
@@ -1566,7 +3120,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1566
3120
|
.alt-th-check .alt-check, .alt-td-check .alt-check { display: inline-flex; vertical-align: middle; }
|
|
1567
3121
|
.alt-th-grip, .alt-td-grip { width: 24px; padding-right: 0; }
|
|
1568
3122
|
.alt-table-grip { display: inline-grid; place-items: center; width: 24px; height: 28px; margin: -4px 0 -4px -6px; padding: 0; border: 0; border-radius: var(--radius-xs); background: none; color: var(--ink-muted); cursor: grab; touch-action: none; }
|
|
1569
|
-
.alt-table-grip:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 7%, transparent); }
|
|
3123
|
+
@media (hover: hover) { .alt-table-grip:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 7%, transparent); } }
|
|
1570
3124
|
.alt-table-grip:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
1571
3125
|
.alt-tr[data-row-held] .alt-table-grip { cursor: grabbing; color: var(--ink); }
|
|
1572
3126
|
.alt-table-slot { position: absolute; z-index: -2; left: 0; width: var(--alt-row-w); border-radius: var(--radius-md); background: var(--surface-well); box-shadow: var(--well); outline: 1.5px dashed var(--line-control); outline-offset: -3px; pointer-events: none; transition: top var(--dur-travel-2) var(--ease-settle); }
|
|
@@ -1602,11 +3156,11 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1602
3156
|
.alt-board-empty { display: grid; place-items: center; min-height: 72px; border-radius: var(--radius-md); font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
|
|
1603
3157
|
.alt-board-card {
|
|
1604
3158
|
position: relative; padding: 10px 12px; border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--elev-1);
|
|
1605
|
-
color: var(--ink); font: 500 14px/20px var(--font-sans); cursor: grab; user-select: none; outline: none;
|
|
3159
|
+
color: var(--ink); font: 500 14px/20px var(--font-sans); cursor: grab; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation; outline: none;
|
|
1606
3160
|
transition: box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1), transform var(--dur-travel-2) var(--ease-settle),
|
|
1607
3161
|
rotate var(--dur-travel-2) var(--ease-settle), scale var(--dur-travel-2) var(--ease-settle);
|
|
1608
3162
|
}
|
|
1609
|
-
.alt-board-card:hover:not([data-carried]):not([data-held]) { box-shadow: var(--elev-2); }
|
|
3163
|
+
@media (hover: hover) { .alt-board-card:hover:not([data-carried]):not([data-held]) { box-shadow: var(--elev-2); } }
|
|
1610
3164
|
.alt-board-card:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
1611
3165
|
.alt-board-card[data-carried] { z-index: 10; cursor: grabbing; background: var(--surface-floating); box-shadow: var(--elev-3); rotate: calc(-1.2deg * var(--alt-travel)); scale: calc(1 + 0.015 * var(--alt-travel)); }
|
|
1612
3166
|
.alt-board-card[data-carried]:not([data-following]) { transform: translateY(calc(-3px * var(--alt-travel))); }
|
|
@@ -1634,6 +3188,565 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1634
3188
|
.alt-menu[data-ephemeral][data-state="open"] .alt-menu-item:not([data-predicted]) { transition: opacity var(--dur-onset) linear, transform var(--dur-onset) var(--ease-rise); transition-delay: 0ms; }
|
|
1635
3189
|
.alt-menu[data-ephemeral][data-state="open"] .alt-menu-item[data-predicted], .alt-menu[data-ephemeral][data-state="open"] .alt-menu-label { transition-duration: var(--dur-travel-1); transition-delay: 0ms; }
|
|
1636
3190
|
|
|
3191
|
+
/* ════════════════════ Charts ════════════════════ */
|
|
3192
|
+
|
|
3193
|
+
/* ── ChartContainer: the frame every chart shares. A chart is the record drawn as a shape, so everything in the frame
|
|
3194
|
+
lies flush on whatever it sits on, the page or a Card's surface-raised: no level, no shadow, no wall, no sheen. Amounts
|
|
3195
|
+
are length and position on a flat plot standing on the ground line (1px ink-muted, as under Stepper and Drilldown),
|
|
3196
|
+
never height. Gridlines are decorative line hairlines, horizontal only; tick labels are caption text in ink-muted
|
|
3197
|
+
with tabular figures. Marks wear data-* fills and strokes and nothing else. On resize it lays out again without
|
|
3198
|
+
animating. ── */
|
|
3199
|
+
.alt-chart { margin: 0; min-width: 0; display: flex; flex-direction: column; gap: var(--space-3); color: var(--ink); }
|
|
3200
|
+
.alt-chart-head { display: flex; flex-direction: column; gap: 2px; }
|
|
3201
|
+
.alt-chart-title { font: 600 15px/22px var(--font-sans); color: var(--ink); }
|
|
3202
|
+
.alt-chart-desc { margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); }
|
|
3203
|
+
/* A takeaway (#187) is the chart's heading: text-heading in ink. The title over it becomes a label, caption-sized
|
|
3204
|
+
ink-muted, so the chart stays named. A changed takeaway cross-fades in place, like a Stat's value. */
|
|
3205
|
+
.alt-chart-takeaway { display: grid; margin: 0; font: 600 19px/26px var(--font-sans); letter-spacing: -0.01em; color: var(--ink); text-wrap: balance; }
|
|
3206
|
+
.alt-chart-head[data-takeaway] .alt-chart-title { font: 500 12.5px/17px var(--font-sans); letter-spacing: 0.005em; color: var(--ink-muted); }
|
|
3207
|
+
.alt-chart-head[data-takeaway] .alt-chart-desc { margin-top: 2px; }
|
|
3208
|
+
.alt-chart-takeaway-text { grid-area: 1 / 1; }
|
|
3209
|
+
.alt-chart-takeaway-text[data-in] { animation: alt-stat-in var(--dur-travel-2) var(--ease-glide) both; }
|
|
3210
|
+
.alt-chart-takeaway-text[data-out] { animation: alt-stat-out var(--dur-travel-2) var(--ease-glide) both; }
|
|
3211
|
+
.alt-chart-plot { position: relative; width: 100%; min-width: 0; }
|
|
3212
|
+
.alt-chart-svg { position: absolute; inset: 0; display: block; overflow: visible; }
|
|
3213
|
+
.alt-chart-grid line { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3214
|
+
.alt-chart-ground { stroke: var(--ink-muted); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3215
|
+
.alt-chart-floor { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3216
|
+
/* Axis text is a caption in weight 400 (#193): the frame's words rank below the marks, the ground line and references.
|
|
3217
|
+
Inline y labels sit just above their gridlines on a halo of the plot's colour, as reference labels do. */
|
|
3218
|
+
.alt-chart-axis text { fill: var(--ink-muted); font: 400 13px/19px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3219
|
+
.alt-chart-axis[data-inline] text { stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; }
|
|
3220
|
+
.alt-chart-summary { margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); text-wrap: pretty; }
|
|
3221
|
+
/* ── BarChart: amounts as lengths standing on the ground line. Bars are the record, so they're flat data-* fills,
|
|
3222
|
+
square where they meet the ground and rounded (radius-xs) only at the data end; neighbours and stacked segments
|
|
3223
|
+
are parted by a real gap, never a shadow. A negative bar hangs below the ground line: that's position, not depth,
|
|
3224
|
+
so it has no inner wall and doesn't read as an opening. A bar never lifts, on hover or anywhere else. Value labels
|
|
3225
|
+
are selective and wear ink, never a series colour. ── */
|
|
3226
|
+
.alt-bar-label { fill: var(--ink); font: 500 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3227
|
+
@media (forced-colors: active) { .alt-bar-label { fill: CanvasText; } }
|
|
3228
|
+
|
|
3229
|
+
/* ── Not yet the record (#67): a period still being counted or a forecast. A pending write hovers, but a pending mark
|
|
3230
|
+
can't, since lifting a bar misstates it; so it's drawn with pattern and a word instead. Bars are hatched at 45° in
|
|
3231
|
+
their colour on the plot's own, with no solid fill (a projection also keeps its outline); lines are dashed from the
|
|
3232
|
+
last settled point and their points hollow; areas are hatched. The record ends at a 1px line-control rule named in
|
|
3233
|
+
words ("Today"). A partial period that completes cross-fades from hatch to solid over dur-travel-3: it's been pressed
|
|
3234
|
+
into the record. An uncertainty band is a flat, low-opacity fill: no gradient, no error bars. ── */
|
|
3235
|
+
.alt-bar-mark, .alt-bar-pending { transition: fill-opacity var(--dur-travel-3) var(--ease-glide), stroke-opacity var(--dur-travel-3) var(--ease-glide), fill var(--dur-travel-1) var(--ease-glide); }
|
|
3236
|
+
/* Focus (#188): what a chart isn't about is drawn in data-context, one flat neutral, and its words recede to ink-muted.
|
|
3237
|
+
A change of focus cross-fades fills and strokes in place over dur-travel-1; under reduced motion it changes at once.
|
|
3238
|
+
Under forced colours context is GrayText at full strength, apart from a filter's misses, which also recede to 35%. */
|
|
3239
|
+
.alt-line-stroke, .alt-area-fill, .alt-area-line, .alt-scatter-dot { transition: fill var(--dur-travel-1) var(--ease-glide), stroke var(--dur-travel-1) var(--ease-glide); }
|
|
3240
|
+
.alt-line-label[data-context], .alt-area-label[data-context] { fill: var(--ink-muted); }
|
|
3241
|
+
/* The record before (#189): both settled, both flat on the ground, the earlier one quieter. On bars, a 2px ink tick
|
|
3242
|
+
across the bar at the earlier value, 4px past it each side (the bullet graph's comparative measure); on lines, a
|
|
3243
|
+
1.5px solid ink-muted trace under the series, named at its end. Never dotted: under forced colours dots mean pending. */
|
|
3244
|
+
.alt-bar-compare { stroke: var(--ink); stroke-width: 2; shape-rendering: crispEdges; }
|
|
3245
|
+
/* A reading set down (#190): the readout lands on the record as a plate, from level 3 to the ground over dur-travel-3 on
|
|
3246
|
+
ease-settle, then takes the afterglow once. A pin icon tells it from an annotation; its ghost remove button lifts
|
|
3247
|
+
when pointed at, as a ghost button does. Two pins say their difference under the x axis on a line-control bracket. */
|
|
3248
|
+
.alt-chart-note[data-pin] { display: inline-flex; align-items: center; gap: 4px; padding-right: 2px;
|
|
3249
|
+
animation: alt-pin-land var(--dur-travel-3) var(--ease-settle) both, alt-pin-glow var(--dur-settle) var(--ease-decay) var(--dur-travel-3) both; }
|
|
3250
|
+
.alt-chart-pin-icon { flex: none; color: var(--ink-muted); }
|
|
3251
|
+
.alt-chart-pin-remove { display: inline-grid; place-items: center; width: 18px; height: 18px; margin: 0; padding: 0; border: 0; border-radius: var(--radius-xs); background: transparent; color: var(--ink-muted); cursor: pointer; box-shadow: var(--elev-0); transition: background-color var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1) var(--ease-glide), transform var(--dur-travel-1) var(--ease-glide); }
|
|
3252
|
+
.alt-chart-pin-remove:focus-visible { background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
3253
|
+
@media (hover: hover) { .alt-chart-pin-remove:hover { background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
3254
|
+
@keyframes alt-pin-land { from { box-shadow: var(--elev-3); transform: translateY(calc(-6px * var(--alt-travel))); } }
|
|
3255
|
+
@keyframes alt-pin-glow { 0%, 12% { outline: 2px solid var(--accent-soft); outline-offset: 1px; } 100% { outline: 2px solid transparent; outline-offset: 1px; } }
|
|
3256
|
+
.alt-chart-pins-strip path { stroke: var(--line-control); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3257
|
+
.alt-chart-pins-strip text { fill: var(--ink); font: 500 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3258
|
+
@media (forced-colors: active) { .alt-chart-pins-strip path { stroke: CanvasText; } .alt-chart-pins-strip text { fill: CanvasText; } .alt-chart-pin-icon, .alt-chart-pin-remove { color: CanvasText; } }
|
|
3259
|
+
.alt-line-compare { stroke: var(--ink-muted); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
|
|
3260
|
+
@media (forced-colors: active) { .alt-bar-compare { stroke: CanvasText; } .alt-line-compare { stroke: CanvasText; stroke-width: 1; } }
|
|
3261
|
+
/* ── DotPlot (#192): values where zero isn't part of the story, as position, never height. The values are the record,
|
|
3262
|
+
so dots lie flat on the ground as a ScatterChart's do: at least 8px, a marker per series, ringed 2px in the plot's
|
|
3263
|
+
colour. With no zero there's no ground line, only the plot's foot. Each row has a 1px line guide from its label. A
|
|
3264
|
+
spread is a flat band of the series colour at 16% behind the dot, never error bars. A dumbbell joins a hollow "from"
|
|
3265
|
+
dot to a filled "to" dot with a 2px ink-muted link. A mark that acts gets a 2px ink outline, never a lift. ── */
|
|
3266
|
+
.alt-dot-guide { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3267
|
+
.alt-dot-range { fill-opacity: 0.16; }
|
|
3268
|
+
.alt-dot-link { stroke: var(--ink-muted); stroke-width: 2; stroke-linecap: round; }
|
|
3269
|
+
.alt-dot-hover { fill: none; stroke: var(--ink); stroke-width: 2; }
|
|
3270
|
+
.alt-chart-plot[data-interactive] .alt-dot-row { cursor: pointer; }
|
|
3271
|
+
@media (prefers-contrast: more) { .alt-dot-guide { stroke: var(--line-control); } }
|
|
3272
|
+
[data-contrast="more"] .alt-dot-guide { stroke: var(--line-control); }
|
|
3273
|
+
@media (forced-colors: active) {
|
|
3274
|
+
.alt-dot-guide { stroke: GrayText; }
|
|
3275
|
+
.alt-dot-range { fill: GrayText; fill-opacity: 0.4; }
|
|
3276
|
+
.alt-dot-link, .alt-dot-hover { stroke: CanvasText; }
|
|
3277
|
+
.alt-dot-mark[data-from] { stroke: CanvasText; fill: Canvas; }
|
|
3278
|
+
}
|
|
3279
|
+
/* Changed since a snapshot (#191): a changed bar's old value is the comparison's tick; a changed line point's old place
|
|
3280
|
+
is a small hollow ink-muted ring. The caption says how many changed, in a switch that recedes the rest. */
|
|
3281
|
+
.alt-line-since { fill: var(--alt-chart-bg); stroke: var(--ink-muted); stroke-width: 1.5; }
|
|
3282
|
+
.alt-chart-since { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-2); border-radius: var(--radius-sm); font: 400 13px/19px var(--font-sans); color: var(--ink-muted); }
|
|
3283
|
+
@media (forced-colors: active) { .alt-line-since { stroke: CanvasText; fill: Canvas; } }
|
|
3284
|
+
.alt-legend-key[data-context] { color: var(--ink-muted); }
|
|
3285
|
+
@media (prefers-reduced-motion: reduce) { .alt-bar-mark, .alt-line-stroke, .alt-area-fill, .alt-area-line, .alt-scatter-dot { transition-property: fill-opacity, stroke-opacity; } }
|
|
3286
|
+
[data-motion="reduced"] .alt-bar-mark, [data-motion="reduced"] .alt-line-stroke, [data-motion="reduced"] .alt-area-fill, [data-motion="reduced"] .alt-area-line, [data-motion="reduced"] .alt-scatter-dot { transition-property: fill-opacity, stroke-opacity; }
|
|
3287
|
+
.alt-chart-boundary line { stroke: var(--line-control); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3288
|
+
.alt-chart-boundary-label { fill: var(--ink-muted); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 500 12.5px/17px var(--font-sans); }
|
|
3289
|
+
.alt-line-hollow { fill: var(--alt-chart-bg); stroke-width: 2; }
|
|
3290
|
+
.alt-bar-label-status { fill: var(--ink-muted); }
|
|
3291
|
+
.alt-chart-readout-note { margin-left: 6px; color: var(--ink); font-weight: 600; }
|
|
3292
|
+
.alt-chart-readout-note::before { content: "· "; }
|
|
3293
|
+
/* ── Filtered in place (#70): misses recede to 35% and keep their place and their colour slot, so positions survive
|
|
3294
|
+
the filter. Relevance elsewhere raises matches, but marks can't rise, so in a chart it's spent as contrast only.
|
|
3295
|
+
Misses cross-fade over dur-travel-1. The frame says how many match, in words. ── */
|
|
3296
|
+
.alt-chart-rel { transition: opacity var(--dur-travel-1) var(--ease-glide); }
|
|
3297
|
+
.alt-bar-label-miss { fill: var(--ink-muted); }
|
|
3298
|
+
.alt-chart-count { margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
3299
|
+
@media (forced-colors: active) {
|
|
3300
|
+
.alt-chart-rel[data-miss] { opacity: 1 !important; }
|
|
3301
|
+
.alt-chart-rel[data-miss] .alt-bar-mark:not([fill^="url"]), .alt-chart-rel[data-miss] .alt-area-fill:not([fill^="url"]) { fill: GrayText; }
|
|
3302
|
+
.alt-chart-rel[data-miss] [fill^="url"] { opacity: 0.45; }
|
|
3303
|
+
.alt-chart-rel[data-miss] .alt-line-stroke, .alt-chart-rel[data-miss] .alt-area-line { stroke: GrayText; }
|
|
3304
|
+
.alt-chart-rel[data-miss] circle, .alt-chart-rel[data-miss] rect, .alt-chart-rel[data-miss] path:not([class]) { fill: GrayText; stroke: GrayText; }
|
|
3305
|
+
.alt-bar-label-miss { fill: GrayText; }
|
|
3306
|
+
.alt-chart-count { color: CanvasText; }
|
|
3307
|
+
}
|
|
3308
|
+
/* ── ChartBrush (#74): an overview you slide a window along. The overview is an opening, the series flat in a trench;
|
|
3309
|
+
the window is a raised lens over it (level 1, accent rim, clear face), level 2 on hover and 3 while held, whose
|
|
3310
|
+
grips are a range Slider's thumbs, whose date bubbles hang below, clear of the plot's axis. The series is a flat line spanning its own low to high, as a Sparkline's is;
|
|
3311
|
+
outside the window it recedes to 35%. ── */
|
|
3312
|
+
.alt-brush { display: flex; flex-direction: column; gap: 6px; }
|
|
3313
|
+
.alt-brush-slide { position: relative; touch-action: none; user-select: none; cursor: pointer; padding: 2px 0; }
|
|
3314
|
+
.alt-brush-track { position: relative; border-radius: var(--radius-sm); background: var(--surface-trench); box-shadow: var(--trench); overflow: hidden; }
|
|
3315
|
+
.alt-brush-svg { display: block; }
|
|
3316
|
+
.alt-brush-line { stroke-width: 1.5px; stroke-linejoin: round; stroke-linecap: round; }
|
|
3317
|
+
.alt-brush-lens {
|
|
3318
|
+
position: absolute; top: 0; bottom: 0; box-sizing: border-box; border: 2px solid var(--accent); border-radius: var(--radius-sm);
|
|
3319
|
+
box-shadow: var(--elev-1); cursor: grab;
|
|
3320
|
+
transition: left var(--dur-travel-1) var(--ease-glide), width var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1) var(--ease-rise);
|
|
3321
|
+
}
|
|
3322
|
+
@media (hover: hover) { .alt-brush-lens:hover { box-shadow: var(--elev-2); } }
|
|
3323
|
+
.alt-brush-lens[data-grabbed] { box-shadow: var(--elev-3); cursor: grabbing; }
|
|
3324
|
+
.alt-brush-lens:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3325
|
+
.alt-brush-grip {
|
|
3326
|
+
position: absolute; top: 50%; width: 10px; height: 24px; translate: -50% -50%; box-sizing: border-box;
|
|
3327
|
+
border: 2px solid var(--accent); border-radius: var(--radius-full); background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1);
|
|
3328
|
+
cursor: ew-resize; transition: left var(--dur-travel-1) var(--ease-glide), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
|
|
3329
|
+
}
|
|
3330
|
+
@media (hover: hover) { .alt-brush-grip:hover { box-shadow: var(--elev-2); } }
|
|
3331
|
+
.alt-brush-grip[data-grabbed] { box-shadow: var(--elev-3); transform: scale(calc(1 + 0.12 * var(--alt-travel))); }
|
|
3332
|
+
.alt-brush-grip:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3333
|
+
.alt-brush[data-dragging] :is(.alt-brush-lens, .alt-brush-grip) { transition-property: box-shadow, transform; }
|
|
3334
|
+
.alt-brush-bubble {
|
|
3335
|
+
position: absolute; left: 50%; top: calc(100% + 8px); translate: -50% 0; padding: 3px 8px; text-align: center;
|
|
3336
|
+
border-radius: var(--radius-sm); background: var(--surface-floating); box-shadow: var(--elev-1); color: var(--ink);
|
|
3337
|
+
font: 500 12px/16px var(--font-mono); white-space: nowrap; pointer-events: none; z-index: 1;
|
|
3338
|
+
opacity: 0; transform: translateY(calc(-4px * var(--alt-travel))) scale(calc(1 - 0.1 * var(--alt-travel))); transform-origin: 50% 0;
|
|
3339
|
+
transition: opacity var(--dur-travel-1) var(--ease-sink), transform var(--dur-travel-1) var(--ease-sink);
|
|
3340
|
+
}
|
|
3341
|
+
.alt-brush-grip[data-edge="start"] .alt-brush-bubble { left: 50%; translate: 0 0; transform-origin: 0 0; }
|
|
3342
|
+
.alt-brush-grip[data-edge="end"] .alt-brush-bubble { left: auto; right: 50%; translate: 0 0; transform-origin: 100% 0; }
|
|
3343
|
+
.alt-brush-grip[data-grabbed] .alt-brush-bubble, .alt-brush-grip:focus-visible .alt-brush-bubble {
|
|
3344
|
+
opacity: 1; transform: none; box-shadow: var(--elev-3);
|
|
3345
|
+
transition: opacity var(--dur-travel-3) var(--ease-rise), transform var(--dur-travel-3) var(--ease-rise), box-shadow var(--dur-travel-3) var(--ease-rise);
|
|
3346
|
+
}
|
|
3347
|
+
.alt-brush-range { margin: 0; font: 400 13px/19px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
3348
|
+
[data-contrast="more"] .alt-brush-lens { border-color: var(--ink); }
|
|
3349
|
+
@media (forced-colors: active) {
|
|
3350
|
+
.alt-brush-track { border: 2px dashed CanvasText; }
|
|
3351
|
+
.alt-brush-lens { border: 1px solid Highlight; box-shadow: none; }
|
|
3352
|
+
.alt-brush-lens[data-grabbed] { border-width: 2px; }
|
|
3353
|
+
.alt-brush-grip { border: 1px solid CanvasText; }
|
|
3354
|
+
.alt-brush-line { forced-color-adjust: none; stroke: CanvasText; }
|
|
3355
|
+
.alt-brush-range { color: CanvasText; }
|
|
3356
|
+
}
|
|
3357
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3358
|
+
.alt-brush-lens, .alt-brush-grip { transition-property: box-shadow; }
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
|
|
3362
|
+
/* ── Marks you can select or drill into (#69). Hover is a promise: only where a click does something does the plot take
|
|
3363
|
+
a pointer cursor and outline the record under it in 2px ink (appearing over dur-travel-1), and the readout names the
|
|
3364
|
+
action. A mark can't lift without misstating its value, so in a chart the hover cue is the outline, the cursor and
|
|
3365
|
+
the readout; height stays with the readout. The selection keeps its colour and the rest recede, as a filter's do. ── */
|
|
3366
|
+
.alt-chart-plot[data-interactive] { cursor: pointer; }
|
|
3367
|
+
.alt-chart-hover path, .alt-chart-hover circle { fill: none; stroke: var(--ink); stroke-width: 2; pointer-events: none; animation: alt-chart-hover-in var(--dur-travel-1) var(--ease-glide); }
|
|
3368
|
+
@keyframes alt-chart-hover-in { from { stroke-opacity: 0; } }
|
|
3369
|
+
.alt-chart-readout-action { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); color: var(--ink-muted); font-weight: 500; }
|
|
3370
|
+
.alt-chart-selection { display: flex; align-items: center; gap: var(--space-2); min-height: 0; font: 500 13px/19px var(--font-sans); color: var(--ink); }
|
|
3371
|
+
.alt-chart-selection:empty { display: none; }
|
|
3372
|
+
@media (forced-colors: active) {
|
|
3373
|
+
.alt-chart-hover path, .alt-chart-hover circle { stroke: Highlight; }
|
|
3374
|
+
.alt-chart-readout-action { border-top-color: CanvasText; }
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
/* ── Live charts (#66): show the new state at once and leave a fading trace (phosphor). A new or changed mark carries
|
|
3378
|
+
a 2px ink outline (an outline, not height or a glow) that holds for the first second and decays over dur-settle on
|
|
3379
|
+
ease-decay, with its value beside it for that first second. A whole refresh is one change: the "Updated" caption
|
|
3380
|
+
takes a ground-level tint instead. Afterglows keep under reduced motion: they're tint, not travel. ── */
|
|
3381
|
+
.alt-chart-glow path, .alt-chart-glow circle { fill: none; stroke: var(--ink); stroke-width: 2; animation: alt-chart-glow var(--dur-settle) linear var(--alt-rise-delay, 0ms) both; }
|
|
3382
|
+
.alt-chart-glow text { fill: var(--ink); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 600 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; animation: alt-chart-glow-label var(--dur-settle) linear var(--alt-rise-delay, 0ms) both; }
|
|
3383
|
+
@keyframes alt-chart-glow { 0%, 12% { stroke-opacity: 1; animation-timing-function: var(--ease-decay); } 100% { stroke-opacity: 0; } }
|
|
3384
|
+
@keyframes alt-chart-glow-label { 0%, 12% { opacity: 1; } 14%, 100% { opacity: 0; } }
|
|
3385
|
+
.alt-chart-live { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3); }
|
|
3386
|
+
.alt-chart-updated { padding: 1px 6px; margin-left: -6px; border-radius: var(--radius-sm); font: 400 13px/19px var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
3387
|
+
.alt-chart-updated[data-settle]::before { animation-name: alt-chart-tint-a; }
|
|
3388
|
+
.alt-chart-updated[data-settle="b"]::before { animation-name: alt-chart-tint-b; }
|
|
3389
|
+
@keyframes alt-chart-tint-a { 0%, 12% { background-color: var(--accent-soft); animation-timing-function: var(--ease-decay); } 100% { background-color: transparent; } }
|
|
3390
|
+
@keyframes alt-chart-tint-b { 0%, 12% { background-color: var(--accent-soft); animation-timing-function: var(--ease-decay); } 100% { background-color: transparent; } }
|
|
3391
|
+
@media (forced-colors: active) {
|
|
3392
|
+
.alt-chart-glow path, .alt-chart-glow circle { stroke: Highlight; }
|
|
3393
|
+
.alt-chart-glow text { fill: CanvasText; stroke: Canvas; }
|
|
3394
|
+
.alt-chart-updated[data-settle]::before { border: 1px solid Highlight; }
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
/* ── References and annotations (#68): what a chart is read against. A reference line is 1px ink-muted and solid,
|
|
3398
|
+
because dashes mean projected; a status threshold wears caution or critical, with its icon in the label. A band is a
|
|
3399
|
+
flat line-coloured fill (a decorative token, so it isn't read as data or as an opening), under the marks. Labels are
|
|
3400
|
+
caption text on a halo of the plot's colour, kept apart in rows. An annotation is a Badge plate flush at the top of
|
|
3401
|
+
the plot (a label is part of the record) with a 1px line-control leader to its x; one that acts lifts one level. ── */
|
|
3402
|
+
.alt-chart-ref { stroke: var(--ink-muted); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3403
|
+
.alt-chart-ref[data-tone="caution"] { stroke: var(--caution); }
|
|
3404
|
+
.alt-chart-ref[data-tone="critical"] { stroke: var(--critical); }
|
|
3405
|
+
.alt-chart-ref-label text { fill: var(--ink-muted); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 500 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3406
|
+
.alt-chart-ref-label[data-tone] text { fill: var(--ink); }
|
|
3407
|
+
.alt-chart-ref-icon { color: var(--critical); }
|
|
3408
|
+
.alt-chart-ref-label[data-tone="caution"] .alt-chart-ref-icon { color: var(--caution); }
|
|
3409
|
+
.alt-chart-note-leader { stroke: var(--line-control); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3410
|
+
.alt-chart-note { position: absolute; z-index: 1; justify-content: center; box-sizing: border-box; }
|
|
3411
|
+
button.alt-chart-note { border: 0; cursor: pointer; box-shadow: var(--elev-0); transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise); }
|
|
3412
|
+
@media (hover: hover) { button.alt-chart-note:hover { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
3413
|
+
button.alt-chart-note:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3414
|
+
@media (forced-colors: active) {
|
|
3415
|
+
.alt-bar-pending { stroke: CanvasText; stroke-dasharray: 2 2; }
|
|
3416
|
+
.alt-chart-ref, .alt-chart-ref[data-tone] { stroke: CanvasText; }
|
|
3417
|
+
.alt-chart-ref-label text, .alt-chart-ref-label[data-tone] text { fill: CanvasText; stroke: Canvas; }
|
|
3418
|
+
.alt-chart-ref-icon, .alt-chart-ref-label[data-tone="caution"] .alt-chart-ref-icon { color: CanvasText; }
|
|
3419
|
+
.alt-chart-note-leader { stroke: CanvasText; }
|
|
3420
|
+
.alt-chart-note, button.alt-chart-note { border: 1px solid CanvasText; }
|
|
3421
|
+
button.alt-chart-note:focus-visible { outline-color: Highlight; }
|
|
3422
|
+
.alt-chart-boundary line { stroke: CanvasText; }
|
|
3423
|
+
.alt-chart-boundary-label { fill: CanvasText; stroke: Canvas; }
|
|
3424
|
+
.alt-line-hollow { fill: Canvas; }
|
|
3425
|
+
.alt-bar-label-status { fill: CanvasText; }
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
/* ── The table view: the same record as a sortable DataTable, behind a segmented Chart | Table control (one raised
|
|
3429
|
+
pill gliding between two views of one record). Both views sit in one box, so switching doesn't move the page: the
|
|
3430
|
+
table scrolls inside the chart's size, and the two cross-fade over dur-travel-2 rather than slide. The unchosen
|
|
3431
|
+
view stays mounted but inert. ── */
|
|
3432
|
+
.alt-chart-tabs { display: contents; }
|
|
3433
|
+
.alt-chart-bar { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
|
|
3434
|
+
.alt-chart-bar .alt-tabs-list { flex: none; }
|
|
3435
|
+
.alt-chart-views { display: grid; min-width: 0; }
|
|
3436
|
+
.alt-chart-view { grid-area: 1 / 1; min-width: 0; transition: opacity var(--dur-travel-2) var(--ease-glide); }
|
|
3437
|
+
.alt-chart-view.alt-tabs-content { animation: none; }
|
|
3438
|
+
.alt-chart-view[data-state="inactive"] { opacity: 0; pointer-events: none; }
|
|
3439
|
+
.alt-chart-view[data-view="table"] { position: relative; }
|
|
3440
|
+
.alt-chart-tablebox { position: absolute; inset: 0; overflow: auto; }
|
|
3441
|
+
|
|
3442
|
+
/* ── Loading, refetch and nothing: an imprint in the chart's shape while data is on its way (surface-well bars or a
|
|
3443
|
+
band pressed into the page, no labels, after dur-grace like every imprint); data lands by fading in place, never
|
|
3444
|
+
rising, because a rising bar would leave the ground line. A refetch keeps the chart and, after dur-grace, drops its
|
|
3445
|
+
marks to half strength. Nothing to show is an opening: an EmptyState in the plot's own box, with no axes. ── */
|
|
3446
|
+
.alt-chart-imprint { display: flex; align-items: flex-end; gap: 8%; padding: 8px 4% 26px; box-sizing: border-box; }
|
|
3447
|
+
.alt-chart-imprint > .alt-skeleton { flex: 1; border-radius: var(--radius-xs) var(--radius-xs) 0 0; }
|
|
3448
|
+
.alt-chart-imprint[data-shape="line"] { padding: 0; }
|
|
3449
|
+
.alt-chart-imprint-line { fill: none; stroke: var(--surface-well); stroke-width: 10px; stroke-linecap: round; vector-effect: non-scaling-stroke; }
|
|
3450
|
+
.alt-chart-await[data-phase="reveal"] > .alt-await-face { --alt-from-y: 0px; }
|
|
3451
|
+
.alt-chart-plot[data-refreshing] .alt-chart-marks { opacity: 0.5; transition: opacity var(--dur-travel-1) var(--ease-glide) var(--dur-grace); }
|
|
3452
|
+
.alt-chart-marks { transition: opacity var(--dur-travel-1) var(--ease-glide); }
|
|
3453
|
+
.alt-chart-empty { position: absolute; inset: 0; display: grid; }
|
|
3454
|
+
.alt-chart-empty > .alt-empty { margin: 0; justify-content: center; }
|
|
3455
|
+
@media (forced-colors: active) { .alt-chart-imprint-line { stroke: GrayText; stroke-dasharray: 6 4; stroke-width: 2px; } .alt-chart-imprint ~ .alt-chart-legend rect { fill: CanvasText; } }
|
|
3456
|
+
|
|
3457
|
+
/* ── The keyboard path: a layer of focusable nodes over the aria-hidden SVG, one level at a time (summary, series,
|
|
3458
|
+
points) with a roving tabindex, so the plot is one Tab stop. A node is invisible until focused; then it's the focus
|
|
3459
|
+
ring itself, 2px accent-ink at a 2px offset, round on a point. It moves instantly, because it's focus, and the
|
|
3460
|
+
readout opens on the focused point. Nodes take no pointer events, so the plot's hover still works through them. ── */
|
|
3461
|
+
.alt-chart-nav { position: absolute; inset: 0; pointer-events: none; }
|
|
3462
|
+
.alt-chart-node { position: absolute; box-sizing: border-box; border-radius: var(--radius-sm); outline: none; }
|
|
3463
|
+
.alt-chart-node[data-level="point"] { border-radius: var(--radius-full); }
|
|
3464
|
+
.alt-chart-node:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3465
|
+
@media (forced-colors: active) { .alt-chart-node:focus-visible { outline-color: Highlight; } }
|
|
3466
|
+
|
|
3467
|
+
/* ── LineChart: change over time as 2px strokes lying on the plot, round joins, no fill, no glow. A missing value is a
|
|
3468
|
+
gap in the line, never bridged. The last point wears a marker and each line is named at its end in ink, so the
|
|
3469
|
+
lines are their own legend. ── */
|
|
3470
|
+
.alt-line-label { fill: var(--ink); font: 500 12.5px/17px var(--font-sans); }
|
|
3471
|
+
@media (forced-colors: active) { .alt-line-label { fill: CanvasText; } }
|
|
3472
|
+
.alt-line-active { stroke: var(--alt-chart-bg); stroke-width: 2; paint-order: stroke; }
|
|
3473
|
+
|
|
3474
|
+
/* ── Sparkline: a word-sized trend, the record in the row it sits in. It lies flush on the ground like every mark: a
|
|
3475
|
+
1.5px stroke (2px under contrast="more"), round joins, no fill under it, no axes, no gridlines, no readout, and a 4px
|
|
3476
|
+
marker on the last point. Bars are flat fills parted by 1px, standing on zero. A normal range is a flat band of
|
|
3477
|
+
line behind the marks. Its height is one line of the text around it (1lh) and it sits on that text's baseline. The
|
|
3478
|
+
colour is context (ink-muted) unless it's the series (data-1) or a status. While its data loads it's an imprint one
|
|
3479
|
+
line high and its own width. ── */
|
|
3480
|
+
.alt-sparkline { --spark: var(--ink-muted); display: inline-block; vertical-align: middle; height: 1.4em; height: 1lh; min-width: 0; }
|
|
3481
|
+
.alt-sparkline > svg { display: block; overflow: visible; }
|
|
3482
|
+
.alt-sparkline[data-tone="data"] { --spark: var(--data-1); }
|
|
3483
|
+
.alt-sparkline[data-tone="positive"] { --spark: var(--positive); }
|
|
3484
|
+
.alt-sparkline[data-tone="caution"] { --spark: var(--caution); }
|
|
3485
|
+
.alt-sparkline[data-tone="critical"] { --spark: var(--critical); }
|
|
3486
|
+
.alt-sparkline-line { fill: none; stroke: var(--spark); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
|
|
3487
|
+
.alt-sparkline-end, .alt-sparkline-bar { fill: var(--spark); }
|
|
3488
|
+
.alt-sparkline-band { fill: var(--line); }
|
|
3489
|
+
.alt-sparkline-imprint { display: inline-block; vertical-align: middle; height: 0.6em; border-radius: var(--radius-xs); }
|
|
3490
|
+
@media (prefers-contrast: more) { .alt-sparkline-line { stroke-width: 2; } }
|
|
3491
|
+
[data-contrast="more"] .alt-sparkline-line { stroke-width: 2; }
|
|
3492
|
+
@media (forced-colors: active) {
|
|
3493
|
+
.alt-sparkline, .alt-sparkline[data-tone] { --spark: CanvasText; }
|
|
3494
|
+
.alt-sparkline-band { fill: none; stroke: GrayText; stroke-dasharray: 2 2; }
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
/* ── Stat: a headline number. A number is the record, so a stat lies flush on what it sits on (a Card's surface, or
|
|
3498
|
+
the page): no level, no shadow. The label is text-label in ink-muted; the value is text-title (text-display when
|
|
3499
|
+
size="hero", once per view) in ink, weight 600, tabular figures. A change with no stated good direction is
|
|
3500
|
+
ink-muted text with an arrow; with one, it's a Badge plate whose lamp is positive or critical, so the word, the
|
|
3501
|
+
arrow and the colour each say it. A changed value cross-fades (dur-travel-2) and the stat takes the afterglow; it
|
|
3502
|
+
never counts up. While the value loads it's an imprint of a number's width under a label already in place. ── */
|
|
3503
|
+
.alt-stat { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; border-radius: var(--radius-md); }
|
|
3504
|
+
.alt-stat-label { font: 500 14px/20px var(--font-sans); color: var(--ink-muted); }
|
|
3505
|
+
.alt-stat-main { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
3506
|
+
.alt-stat-value { display: inline-grid; font: 600 28px/34px var(--font-sans); letter-spacing: -0.018em; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
3507
|
+
.alt-stat[data-size="hero"] .alt-stat-value { font: 600 44px/48px var(--font-sans); letter-spacing: -0.025em; }
|
|
3508
|
+
.alt-stat-text { grid-area: 1 / 1; }
|
|
3509
|
+
.alt-stat-text[data-in] { animation: alt-stat-in var(--dur-travel-2) var(--ease-glide) both; }
|
|
3510
|
+
.alt-stat-text[data-out] { animation: alt-stat-out var(--dur-travel-2) var(--ease-glide) both; }
|
|
3511
|
+
@keyframes alt-stat-in { from { opacity: 0; } }
|
|
3512
|
+
@keyframes alt-stat-out { to { opacity: 0; } }
|
|
3513
|
+
.alt-stat-trend { display: inline-flex; align-items: center; min-width: 0; font: 400 15px/23px var(--font-sans); }
|
|
3514
|
+
.alt-stat-imprint { display: inline-block; vertical-align: middle; width: 4.5em; height: 0.6em; border-radius: var(--radius-xs); }
|
|
3515
|
+
.alt-stat-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); font: 500 12.5px/17px var(--font-sans); letter-spacing: 0.005em; color: var(--ink-muted); }
|
|
3516
|
+
.alt-stat-change { gap: 4px; font-variant-numeric: tabular-nums; }
|
|
3517
|
+
.alt-stat-change[data-neutral] { display: inline-flex; align-items: center; color: var(--ink-muted); }
|
|
3518
|
+
.alt-stat-arrow { display: block; flex: none; }
|
|
3519
|
+
.alt-stat-help { flex-basis: 100%; font-weight: 400; }
|
|
3520
|
+
.alt-stat-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); gap: var(--space-5) var(--space-6); }
|
|
3521
|
+
@media (forced-colors: active) { .alt-stat-change.alt-badge { border: 1px solid CanvasText; } }
|
|
3522
|
+
|
|
3523
|
+
/* ── Meter: where a reading sits in a known range. A reading is the record, so unlike Progress (a task under way, live,
|
|
3524
|
+
a raised fill rising out of a trench) a meter lies on the ground: a flush track of line with the reading drawn
|
|
3525
|
+
across it as flat ink, data-1 by default, no shadow, wall or sheen. Thresholds are 1px ink-muted notches standing
|
|
3526
|
+
at the track's rim. Past `high` the fill turns caution and past the limit critical, cross-fading, and the state is
|
|
3527
|
+
said in a Badge whose lamp relights. Segments are flat data-* fills parted by 2px, with a legend of their values.
|
|
3528
|
+
The fill glides on ease-glide (dur-travel-3); under reduced motion it changes in place. ── */
|
|
3529
|
+
.alt-meter { --meter-fill: var(--data-1); --meter-bg: var(--surface-ground); display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
|
|
3530
|
+
.alt-card .alt-meter { --meter-bg: var(--surface-raised); }
|
|
3531
|
+
.alt-meter[data-state="high"] { --meter-fill: var(--caution); }
|
|
3532
|
+
.alt-meter[data-state="critical"] { --meter-fill: var(--critical); }
|
|
3533
|
+
.alt-meter-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
3534
|
+
.alt-meter-value { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--ink-muted); }
|
|
3535
|
+
.alt-meter-state { order: 3; }
|
|
3536
|
+
.alt-meter-track { position: relative; height: 8px; border-radius: var(--radius-full); background: var(--line); }
|
|
3537
|
+
.alt-meter-fill {
|
|
3538
|
+
position: absolute; top: 0; bottom: 0; border-radius: inherit; background: var(--meter-fill);
|
|
3539
|
+
transition: width calc(var(--dur-travel-3) * var(--alt-travel)) var(--ease-glide), left calc(var(--dur-travel-3) * var(--alt-travel)) var(--ease-glide), background-color var(--dur-travel-3) var(--ease-glide);
|
|
3540
|
+
}
|
|
3541
|
+
/* A segment after the first is parted from the one before by a 2px cut in the surface's own colour, and only the ends are rounded. */
|
|
3542
|
+
.alt-meter-fill[data-slot] { border-radius: 0; }
|
|
3543
|
+
.alt-meter-fill[data-slot] + .alt-meter-fill[data-slot] { border-left: 2px solid var(--meter-bg); background-clip: padding-box; }
|
|
3544
|
+
.alt-meter-fill[data-slot]:first-child { border-radius: var(--radius-full) 0 0 var(--radius-full); }
|
|
3545
|
+
.alt-meter-notch { position: absolute; top: -3px; bottom: -3px; width: 1px; margin-left: -0.5px; background: var(--ink-muted); }
|
|
3546
|
+
.alt-meter-legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: 0; padding: 0; list-style: none; font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
3547
|
+
.alt-meter-legend-value { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
3548
|
+
.alt-meter-imprint { height: 8px; border-radius: var(--radius-full); }
|
|
3549
|
+
@media (prefers-contrast: more) { .alt-meter-track { box-shadow: inset 0 0 0 1px var(--ink); } .alt-meter-notch { background: var(--ink); } }
|
|
3550
|
+
[data-contrast="more"] .alt-meter-track { box-shadow: inset 0 0 0 1px var(--ink); }
|
|
3551
|
+
[data-contrast="more"] .alt-meter-notch { background: var(--ink); }
|
|
3552
|
+
@media (forced-colors: active) {
|
|
3553
|
+
.alt-meter-track { background: Canvas; border: 1px solid CanvasText; }
|
|
3554
|
+
.alt-meter-fill { background: Highlight; forced-color-adjust: none; }
|
|
3555
|
+
.alt-meter-fill[data-slot] { background: Highlight !important; }
|
|
3556
|
+
.alt-meter-fill[data-slot] + .alt-meter-fill[data-slot] { border-left-color: Canvas; background-clip: border-box; }
|
|
3557
|
+
.alt-meter-notch { background: CanvasText; }
|
|
3558
|
+
.alt-meter-legend rect { fill: CanvasText; }
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
/* ── BarList: a ranked list whose rows are their own bars. Rows are the record, flush on the ground with line
|
|
3562
|
+
dividers, like a table's. Each row's bar is a flat tint of data-1 (22%, so ink holds 4.5:1 over it) behind its label,
|
|
3563
|
+
its length the row's value against the largest; the label wraps rather than being cut, and the value sits at the
|
|
3564
|
+
row's end in tabular figures. A row that does something (a link or a button) lifts one level when pointed at, as
|
|
3565
|
+
Table's rows do, and its bar stays flat on it; a row that doesn't never moves. "Other" wears data-other. Filtering
|
|
3566
|
+
in place raises matches and sinks misses. Re-ranked rows are carried to their places (FLIP, dur-travel-3,
|
|
3567
|
+
ease-glide), and bars glide to their new lengths; under reduced motion both change in place. ── */
|
|
3568
|
+
.alt-barlist-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; }
|
|
3569
|
+
.alt-barlist { align-self: stretch; margin: 0; padding: 0; list-style: none; font: 500 14px/20px var(--font-sans); color: var(--ink); }
|
|
3570
|
+
.alt-barlist-row { --bar: color-mix(in oklab, var(--data-1) 22%, transparent); position: relative; border-bottom: 1px solid var(--line); }
|
|
3571
|
+
.alt-barlist-row:last-child { border-bottom-color: transparent; }
|
|
3572
|
+
.alt-barlist-row[data-other] { --bar: color-mix(in oklab, var(--data-other) 22%, transparent); }
|
|
3573
|
+
.alt-barlist-item {
|
|
3574
|
+
position: relative; display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: 36px;
|
|
3575
|
+
margin: 0; padding: 3px var(--space-2) 3px 0; box-sizing: border-box; border: 0; border-radius: var(--radius-sm);
|
|
3576
|
+
background: transparent; font: inherit; color: inherit; text-align: start; text-decoration: none;
|
|
3577
|
+
}
|
|
3578
|
+
button.alt-barlist-item, a.alt-barlist-item {
|
|
3579
|
+
cursor: pointer; -webkit-tap-highlight-color: transparent; box-shadow: var(--elev-0);
|
|
3580
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise);
|
|
3581
|
+
}
|
|
3582
|
+
@media (hover: hover) { button.alt-barlist-item:hover, a.alt-barlist-item:hover { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
3583
|
+
button.alt-barlist-item:active, a.alt-barlist-item:active { box-shadow: var(--elev-0); transform: none; transition-duration: var(--dur-press); }
|
|
3584
|
+
button.alt-barlist-item:focus-visible, a.alt-barlist-item:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3585
|
+
.alt-barlist-zone { position: relative; isolation: isolate; flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-3); min-height: 30px; padding: 4px var(--space-2); }
|
|
3586
|
+
.alt-barlist-bar {
|
|
3587
|
+
position: absolute; z-index: -1; left: 0; top: 0; bottom: 0; border-radius: var(--radius-xs); background: var(--bar);
|
|
3588
|
+
transition: width calc(var(--dur-travel-3) * var(--alt-travel)) var(--ease-glide);
|
|
3589
|
+
}
|
|
3590
|
+
.alt-barlist-name { flex: 1; min-width: 0; overflow-wrap: break-word; }
|
|
3591
|
+
.alt-barlist-value { flex: none; font-variant-numeric: tabular-nums; }
|
|
3592
|
+
.alt-barlist-share { flex: none; min-width: 3.2em; text-align: end; font-variant-numeric: tabular-nums; color: var(--ink-muted); }
|
|
3593
|
+
.alt-barlist-row[data-other] .alt-barlist-name { color: var(--ink-muted); }
|
|
3594
|
+
.alt-barlist-imprint { display: block; height: 0.6em; margin: 14px 0; }
|
|
3595
|
+
.alt-barlist-row[data-relevance="miss"] .alt-barlist-name, .alt-barlist-row[data-relevance="miss"] .alt-barlist-value { color: var(--ink-muted); }
|
|
3596
|
+
@media (prefers-contrast: more) { .alt-barlist-bar { box-shadow: inset 0 0 0 1px var(--ink); } }
|
|
3597
|
+
[data-contrast="more"] .alt-barlist-bar { box-shadow: inset 0 0 0 1px var(--ink); }
|
|
3598
|
+
@media (forced-colors: active) {
|
|
3599
|
+
.alt-barlist-bar { forced-color-adjust: none; background: color-mix(in srgb, Highlight 25%, Canvas); border: 1px solid Highlight; }
|
|
3600
|
+
button.alt-barlist-item:hover, a.alt-barlist-item:hover { outline: 1px solid CanvasText; }
|
|
3601
|
+
.alt-barlist-row { border-bottom-color: GrayText; }
|
|
3602
|
+
.alt-barlist-row[data-relevance="match"] { outline: 1px solid CanvasText; outline-offset: -1px; }
|
|
3603
|
+
.alt-barlist-row[data-relevance="miss"] { border-bottom-style: dashed; }
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
/* ── AreaChart: a total over time and what it's made of. Bands are the record: flat data-* fills stacked on the ground
|
|
3607
|
+
line in config order, parted by a 2px line in the plot's own colour (the gap a stacked bar has), with a 2px top line
|
|
3608
|
+
on the topmost. A single area is a flat data-1 fill at reduced opacity under its 2px line. No gradient fill, ever:
|
|
3609
|
+
the only face gradient in Altitude is the sheen, and it means "press it". Bands are named inside themselves in ink,
|
|
3610
|
+
on a halo of the plot's colour, where they're tall enough. ── */
|
|
3611
|
+
.alt-area-sep { stroke: var(--alt-chart-bg); stroke-width: 2; }
|
|
3612
|
+
.alt-area-label { fill: var(--ink); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 500 12.5px/17px var(--font-sans); pointer-events: none; }
|
|
3613
|
+
.alt-chart-imprint[data-shape="area"] { padding: 0; }
|
|
3614
|
+
.alt-chart-imprint-area { fill: var(--surface-well); }
|
|
3615
|
+
@media (prefers-contrast: more) { .alt-area-sep { stroke: var(--ink); stroke-width: 3; } }
|
|
3616
|
+
[data-contrast="more"] .alt-area-sep { stroke: var(--ink); stroke-width: 3; }
|
|
3617
|
+
@media (forced-colors: active) {
|
|
3618
|
+
.alt-area-sep { stroke: Canvas; }
|
|
3619
|
+
.alt-area-label { fill: CanvasText; stroke: Canvas; }
|
|
3620
|
+
.alt-chart-imprint-area { fill: none; stroke: GrayText; stroke-dasharray: 6 4; stroke-width: 2px; vector-effect: non-scaling-stroke; }
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
/* ── Heatmap (#61): a value per cell across two dimensions. Cells are the record, so they lie flat on the plot: a
|
|
3624
|
+
data-seq step (data-div around a midpoint for polarity), parted by a 2px gap of the plot's own colour and rounded
|
|
3625
|
+
at radius-xs (less on small cells), never raised, shaded or glowing. "No data" isn't an opening, since it isn't
|
|
3626
|
+
waiting for anything: no fill and a 1px line outline. Rows and columns are named in ink-muted like an axis; the
|
|
3627
|
+
legend is the steps as swatches with their ranges in ink. With no crosshair in a grid, the cell under the readout
|
|
3628
|
+
wears a 2px ink outline, which never lifts it. A value change cross-fades the cell's fill; under reduced motion it's
|
|
3629
|
+
instant. Under forced colours and print each step adds a pattern density (none, sparse dots, dense dots, hatching,
|
|
3630
|
+
crosshatch), and contrast="more" gives every cell a 1px ink edge. ── */
|
|
3631
|
+
.alt-heat-labels text { fill: var(--ink-muted); font: 500 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3632
|
+
.alt-heat-legend text { fill: var(--ink); font: 500 12.5px/17px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
3633
|
+
.alt-heat-fill { transition: fill var(--dur-travel-1) var(--ease-glide); }
|
|
3634
|
+
.alt-heat-none { fill: none; stroke: var(--line); stroke-width: 1; }
|
|
3635
|
+
.alt-heat-pattern { pointer-events: none; }
|
|
3636
|
+
.alt-heat-active { fill: none; stroke: var(--ink); stroke-width: 2; pointer-events: none; }
|
|
3637
|
+
.alt-chart-node[data-shape="cell"] { border-radius: var(--radius-xs); }
|
|
3638
|
+
.alt-chart-imprint[data-shape="grid"] { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); grid-template-rows: repeat(7, minmax(0, 1fr)); gap: 3px; padding: 8px 0 26px; }
|
|
3639
|
+
.alt-chart-imprint[data-shape="grid"] > .alt-skeleton { border-radius: var(--radius-xs); }
|
|
3640
|
+
@media (prefers-reduced-motion: reduce) { .alt-heat-fill { transition: none; } }
|
|
3641
|
+
[data-motion="reduced"] .alt-heat-fill { transition: none; }
|
|
3642
|
+
@media (prefers-contrast: more) { .alt-heat-none { stroke: var(--line-control); } }
|
|
3643
|
+
[data-contrast="more"] .alt-heat-none { stroke: var(--line-control); }
|
|
3644
|
+
@media (forced-colors: active) {
|
|
3645
|
+
.alt-heat-labels text, .alt-heat-legend text { fill: CanvasText; }
|
|
3646
|
+
.alt-heat-none { stroke: GrayText; }
|
|
3647
|
+
.alt-heat-active { stroke: Highlight; }
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
/* ── ScatterChart (#62): two measures per record. Dots are the record, so they lie on the plot: at least 8px, a data-*
|
|
3651
|
+
fill and a marker shape per group (circle, square, triangle), with a 2px ring of the plot's own colour so dots that
|
|
3652
|
+
overlap stay apart, and never a shadow, sheen or glow. At most three groups, the palette's first three slots, since
|
|
3653
|
+
any two dots can be neighbours. The dot under the readout is drawn again on top, so an overlapped dot is seen whole;
|
|
3654
|
+
it doesn't lift. Labelled outliers are ink on a halo with a 1px ink-muted leader; the trend is a 1px ink-muted
|
|
3655
|
+
line, named in words. Under forced colours the shapes carry the groups; contrast="more" rings each dot in 1px ink. ── */
|
|
3656
|
+
.alt-scatter-label text { fill: var(--ink); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 500 12.5px/17px var(--font-sans); }
|
|
3657
|
+
.alt-scatter-label line { stroke: var(--ink-muted); stroke-width: 1; }
|
|
3658
|
+
.alt-scatter-trend line { stroke: var(--ink-muted); stroke-width: 1; }
|
|
3659
|
+
.alt-scatter-axis-name text { fill: var(--ink-muted); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 600 12.5px/17px var(--font-sans); }
|
|
3660
|
+
.alt-scatter-trend text { fill: var(--ink-muted); stroke: var(--alt-chart-bg); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; font: 500 12.5px/17px var(--font-sans); }
|
|
3661
|
+
.alt-chart-imprint[data-shape="dots"] { display: block; position: relative; padding: 0; }
|
|
3662
|
+
.alt-chart-imprint-dot { position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: var(--radius-full); }
|
|
3663
|
+
@media (forced-colors: active) {
|
|
3664
|
+
.alt-scatter-label text, .alt-scatter-trend text, .alt-scatter-axis-name text { fill: CanvasText; stroke: Canvas; }
|
|
3665
|
+
.alt-scatter-label line, .alt-scatter-trend line { stroke: CanvasText; }
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
/* ── ChartTooltip: reading exact values. The crosshair is a mark on the ground (1px line-control, snapped to the nearest
|
|
3669
|
+
x); the readout is live, a level-3 floating surface like Tooltip. It rises 4px once as its shadow grows from elev-1
|
|
3670
|
+
to elev-3, then steps between x positions with no easing (it follows the data, not each pixel of the pointer), flips
|
|
3671
|
+
sides so it never covers the point, and leaves in 70% of the time. It shares Tooltip's 400ms warm-up. It's
|
|
3672
|
+
aria-hidden: the same values are in each datum's name, the keyboard path and the table view. ── */
|
|
3673
|
+
.alt-chart { --alt-chart-bg: var(--surface-ground); }
|
|
3674
|
+
.alt-card .alt-chart { --alt-chart-bg: var(--surface-raised); }
|
|
3675
|
+
.alt-chart-plot { touch-action: pan-y; }
|
|
3676
|
+
.alt-chart-crosshair { stroke: var(--line-control); stroke-width: 1; shape-rendering: crispEdges; }
|
|
3677
|
+
.alt-chart-readout {
|
|
3678
|
+
position: absolute; z-index: var(--z-floating); pointer-events: none; display: grid; gap: 3px; min-width: 112px;
|
|
3679
|
+
padding: 8px 10px; border-radius: var(--radius-lg); background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--ink);
|
|
3680
|
+
font: 400 12.5px/17px var(--font-sans); white-space: nowrap;
|
|
3681
|
+
transition: opacity var(--dur-travel-3) var(--ease-rise), transform var(--dur-travel-3) var(--ease-rise), box-shadow var(--dur-travel-3) var(--ease-rise);
|
|
3682
|
+
}
|
|
3683
|
+
.alt-chart-readout[data-state="closed"] {
|
|
3684
|
+
opacity: 0; box-shadow: var(--elev-1); transform: translateY(calc(4px * var(--alt-travel)));
|
|
3685
|
+
transition-timing-function: var(--ease-sink); transition-duration: calc(var(--dur-travel-3) * 0.7);
|
|
3686
|
+
}
|
|
3687
|
+
.alt-chart-readout[data-warm] { transition-duration: var(--dur-travel-1); }
|
|
3688
|
+
.alt-chart-readout[data-side="right"] { translate: 14px 0; }
|
|
3689
|
+
.alt-chart-readout[data-side="left"] { translate: calc(-100% - 14px) 0; }
|
|
3690
|
+
.alt-chart-readout[data-side="below"] { translate: 0 14px; }
|
|
3691
|
+
.alt-chart-readout[data-side="above"] { translate: 0 calc(-100% - 14px); }
|
|
3692
|
+
.alt-chart-readout-label { font-weight: 500; color: var(--ink-muted); margin-bottom: 1px; }
|
|
3693
|
+
.alt-chart-readout-row { display: grid; grid-template-columns: auto auto 1fr; align-items: center; gap: 6px; }
|
|
3694
|
+
.alt-chart-readout-key { display: block; }
|
|
3695
|
+
.alt-chart-readout-value { font-weight: 500; font-variant-numeric: tabular-nums; color: var(--ink); }
|
|
3696
|
+
.alt-chart-readout-name { color: var(--ink-muted); }
|
|
3697
|
+
/* ── ChartLegend: names the series. A legend that only labels is settled: keys flush on the ground, a swatch in the
|
|
3698
|
+
series colour and its name in ink. A legend that hides and shows series is a row of choices, so each key is built
|
|
3699
|
+
like a Checkbox: a ghost button that lifts one level when pointed at (clicking it does something), holding a trench
|
|
3700
|
+
that the series colour pours into while the series is shown and drains out of when it's hidden. The checked fill
|
|
3701
|
+
is the series colour, not accent, because the swatch is the key. Hidden series keep their colour slots. ── */
|
|
3702
|
+
.alt-chart-legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
3703
|
+
.alt-chart-legend[data-toggle] { gap: var(--space-1); margin-left: calc(-1 * var(--space-2)); }
|
|
3704
|
+
.alt-legend-key { display: inline-flex; align-items: center; gap: var(--space-2); }
|
|
3705
|
+
.alt-legend-mark { display: block; flex: none; }
|
|
3706
|
+
button.alt-legend-key {
|
|
3707
|
+
appearance: none; border: 0; margin: 0; padding: 5px var(--space-2); border-radius: var(--radius-sm); font: inherit; color: var(--ink);
|
|
3708
|
+
background: transparent; box-shadow: var(--elev-0); cursor: pointer; -webkit-tap-highlight-color: transparent;
|
|
3709
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise), color var(--dur-travel-1);
|
|
3710
|
+
}
|
|
3711
|
+
button.alt-legend-key:focus-visible { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
3712
|
+
@media (hover: hover) { button.alt-legend-key:hover:not([aria-disabled]) { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
3713
|
+
button.alt-legend-key:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
3714
|
+
button.alt-legend-key[aria-pressed="false"] { color: var(--ink-muted); }
|
|
3715
|
+
button.alt-legend-key[aria-disabled] { cursor: default; }
|
|
3716
|
+
.alt-legend-swatch {
|
|
3717
|
+
display: inline-grid; place-items: center; width: 22px; height: 16px; box-sizing: border-box; border-radius: 5px;
|
|
3718
|
+
border: 1px solid var(--line-control); background: var(--surface-trench); box-shadow: var(--trench);
|
|
3719
|
+
}
|
|
3720
|
+
.alt-legend-swatch .alt-legend-mark {
|
|
3721
|
+
opacity: 0; clip-path: circle(0% at 50% 50%);
|
|
3722
|
+
transition: clip-path var(--dur-travel-1) var(--ease-sink), opacity var(--dur-travel-1) var(--ease-sink);
|
|
3723
|
+
}
|
|
3724
|
+
.alt-legend-swatch[data-shown] { border-color: transparent; }
|
|
3725
|
+
.alt-legend-swatch .alt-legend-mark { width: 22px; height: 16px; }
|
|
3726
|
+
.alt-legend-swatch[data-shown] .alt-legend-mark { opacity: 1; clip-path: circle(75% at 50% 50%); transition-timing-function: var(--ease-rise); }
|
|
3727
|
+
@media (prefers-contrast: more) { .alt-legend-swatch { box-shadow: inset 0 1px 0 0 var(--ink); } }
|
|
3728
|
+
[data-contrast="more"] .alt-legend-swatch { box-shadow: inset 0 1px 0 0 var(--ink); }
|
|
3729
|
+
@media (forced-colors: active) {
|
|
3730
|
+
.alt-chart-readout { border: 2px solid CanvasText; }
|
|
3731
|
+
button.alt-legend-key:hover:not([aria-disabled]) { border: 1px solid CanvasText; padding: 4px 7px; }
|
|
3732
|
+
.alt-legend-swatch { border-color: CanvasText; }
|
|
3733
|
+
.alt-legend-swatch:not([data-shown]) { border-style: dashed; }
|
|
3734
|
+
.alt-chart-crosshair { stroke: CanvasText; }
|
|
3735
|
+
.alt-line-active { stroke: Canvas; }
|
|
3736
|
+
}
|
|
3737
|
+
@media (prefers-contrast: more) {
|
|
3738
|
+
.alt-chart-grid line, .alt-chart-floor { stroke: var(--line-control); }
|
|
3739
|
+
.alt-chart-ground { stroke: var(--ink); }
|
|
3740
|
+
}
|
|
3741
|
+
[data-contrast="more"] .alt-chart-grid line, [data-contrast="more"] .alt-chart-floor { stroke: var(--line-control); }
|
|
3742
|
+
[data-contrast="more"] .alt-chart-ground { stroke: var(--ink); }
|
|
3743
|
+
@media (forced-colors: active) {
|
|
3744
|
+
.alt-chart-axis text { fill: CanvasText; }
|
|
3745
|
+
.alt-chart-axis[data-inline] text { stroke: Canvas; }
|
|
3746
|
+
.alt-chart-grid line, .alt-chart-floor { stroke: GrayText; }
|
|
3747
|
+
.alt-chart-ground { stroke: CanvasText; }
|
|
3748
|
+
}
|
|
3749
|
+
|
|
1637
3750
|
/* ════════════════════ Accessible elevation ════════════════════ */
|
|
1638
3751
|
|
|
1639
3752
|
/* High contrast: elevation becomes a hard slab whose thickness is the height (1 level = 1px more),
|
|
@@ -1651,7 +3764,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1651
3764
|
--line: var(--line-control); --alt-sheen: none;
|
|
1652
3765
|
--alt-ring: var(--ink); --alt-cast: transparent; --alt-cast-deep: transparent; --alt-wall-in: var(--ink-muted); --alt-lip: transparent; --alt-rim-held: var(--ink);
|
|
1653
3766
|
}
|
|
1654
|
-
.alt-btn, .alt-kbd, .alt-crumbs
|
|
3767
|
+
.alt-btn, .alt-kbd, .alt-crumbs .alt-crumb-link { --wall-c: var(--ink); }
|
|
1655
3768
|
.alt-empty { border-style: solid; }
|
|
1656
3769
|
}
|
|
1657
3770
|
[data-contrast="more"], [data-contrast="more"] [data-theme] {
|
|
@@ -1666,7 +3779,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1666
3779
|
--line: var(--line-control); --alt-sheen: none;
|
|
1667
3780
|
--alt-ring: var(--ink); --alt-cast: transparent; --alt-cast-deep: transparent; --alt-wall-in: var(--ink-muted); --alt-lip: transparent; --alt-rim-held: var(--ink);
|
|
1668
3781
|
}
|
|
1669
|
-
[data-contrast="more"] .alt-btn, [data-contrast="more"] .alt-kbd, [data-contrast="more"] .alt-crumbs
|
|
3782
|
+
[data-contrast="more"] .alt-btn, [data-contrast="more"] .alt-kbd, [data-contrast="more"] .alt-crumbs .alt-crumb-link { --wall-c: var(--ink); }
|
|
1670
3783
|
[data-contrast="more"] .alt-empty { border-style: solid; }
|
|
1671
3784
|
|
|
1672
3785
|
/* Forced colours (Windows High Contrast) drop every box-shadow, which erases elevation entirely
|
|
@@ -1690,7 +3803,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1690
3803
|
.alt-tabs[data-variant="folder"] .alt-tabs-trigger[aria-selected="true"] { border: 2px solid CanvasText; border-bottom: 0; }
|
|
1691
3804
|
.alt-tabs[data-variant="folder"] > .alt-tabs-content { border: 1px solid CanvasText; }
|
|
1692
3805
|
.alt-acc-item[data-state="open"] { border: 1px solid CanvasText; } .alt-acc-content { border: 1px dashed CanvasText; }
|
|
1693
|
-
.alt-crumbs
|
|
3806
|
+
.alt-crumbs .alt-crumb-link { border: 1px solid CanvasText; } .alt-crumbs li[data-current] .alt-crumb-link { border-width: 2px; }
|
|
1694
3807
|
.alt-cmd { border: 4px double CanvasText; } .alt-cmd-hl { display: none; } .alt-cmd-item[aria-selected="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
1695
3808
|
.alt-sheet { border: 2px solid CanvasText; } .alt-sheet-pull { border: 1px dashed CanvasText; }
|
|
1696
3809
|
.alt-input[data-filled]:not([data-invalid]) { border-style: solid !important; }
|
|
@@ -1699,5 +3812,769 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
|
|
|
1699
3812
|
.alt-board-card { border: 1px solid CanvasText; } .alt-board-card[data-carried] { border-width: 2px; } .alt-board[data-carrying] .alt-board-col { border: 1px dashed CanvasText; } .alt-board[data-carrying] .alt-board-col[data-over] { border: 2px dashed Highlight; } .alt-board-slot { outline-color: CanvasText; }
|
|
1700
3813
|
.alt-tr[data-row-held] > .alt-td { border-block: 2px solid CanvasText; } .alt-table-slot { outline-color: CanvasText; } .alt-table-wrap[data-top] .alt-th { border-bottom: 1px solid CanvasText; }
|
|
1701
3814
|
.alt-step[data-state="current"] { border: 1px solid CanvasText; } .alt-step[data-state="upcoming"] { border: 1px dashed CanvasText; } .alt-step-mark { border: 1px solid CanvasText; } .alt-step-line[data-done] { background: Highlight; }
|
|
1702
|
-
.alt-avatar[data-status="offline"] .alt-avatar-face { border:
|
|
3815
|
+
.alt-avatar:is([data-status="offline"], [data-more]) .alt-avatar-face { border-color: GrayText; } .alt-avatar[data-live] .alt-avatar-face { border-width: 2px; }
|
|
3816
|
+
.alt-avatar[data-status="active"] .alt-avatar-lamp { forced-color-adjust: none; background: CanvasText; }
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
/* Stationary dialogs keep their context still; the dimming scrim and modal tier remain. */
|
|
3820
|
+
.alt-dialog-root[data-background="stationary"] > .alt-scrim,
|
|
3821
|
+
[data-modal-stationary] > .alt-scrim {
|
|
3822
|
+
backdrop-filter: none;
|
|
3823
|
+
-webkit-backdrop-filter: none;
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
/* ── InlineEdit: one value edited where it sits. The value's text lives in a slot with the control's own inset (12px
|
|
3827
|
+
and a transparent 1px border, as .alt-input and .alt-select-trigger have) and font, at the control's height, and the
|
|
3828
|
+
line reaches 13px past its column on each side so the text itself lines up with the page. Opening puts the control in
|
|
3829
|
+
the same slot: the text stays exactly where it was and only the well (floor, walls, border) fades in around it. Save
|
|
3830
|
+
and Cancel take the Edit button's place on the same row; notes under the line (an error, the key hint, "Choose to
|
|
3831
|
+
save") arrive as the line's glide reaches them. The status is announced, never shown. ── */
|
|
3832
|
+
.alt-inline-edit { min-width: 0; }
|
|
3833
|
+
.alt-inline-edit-surface { min-width: 0; border-radius: var(--radius-md); }
|
|
3834
|
+
.alt-inline-edit-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; margin-inline: -13px; }
|
|
3835
|
+
.alt-inline-edit-slot, .alt-inline-edit-slot-box { flex: 0 1 auto; min-width: 0; }
|
|
3836
|
+
.alt-inline-edit[data-state]:not([data-state="read"]) .alt-inline-edit-slot-box { flex: 1 1 14rem; }
|
|
3837
|
+
.alt-inline-edit-slot {
|
|
3838
|
+
position: relative; box-sizing: border-box; display: flex; align-items: center; max-width: 100%; min-height: max(40px, calc(1lh + 18px));
|
|
3839
|
+
padding: 0 12px; border: 1px solid transparent; border-radius: var(--radius-md);
|
|
3840
|
+
}
|
|
3841
|
+
.alt-inline-edit-value { overflow-wrap: anywhere; min-width: 0; }
|
|
3842
|
+
.alt-inline-edit-slot-box > .alt-input:not([data-multiline]) { font: inherit; height: max(40px, calc(1lh + 18px)); }
|
|
3843
|
+
.alt-inline-edit-slot-box .alt-input input, .alt-inline-edit-slot-box .alt-input textarea, .alt-inline-edit-slot-box .alt-select-trigger { font: inherit; }
|
|
3844
|
+
.alt-inline-edit-slot-box .alt-select > .alt-input { font: inherit; height: max(40px, calc(1lh + 18px)); }
|
|
3845
|
+
.alt-inline-edit .alt-input, .alt-inline-edit .alt-select { min-width: 0; }
|
|
3846
|
+
.alt-inline-edit .alt-input input { width: 100%; min-width: 0; }
|
|
3847
|
+
/* The well opens under text that stays put: only its floor, walls and border fade in. */
|
|
3848
|
+
.alt-inline-edit-slot-box > .alt-input, .alt-inline-edit-slot-box .alt-select > .alt-input, .alt-edit-region-form[data-layout="in-place"] :is(.alt-input, .alt-select > .alt-input) { animation: alt-well-open var(--dur-travel-2) var(--ease-sink); }
|
|
3849
|
+
@keyframes alt-well-open { from { background-color: transparent; border-color: transparent; box-shadow: none; } }
|
|
3850
|
+
.alt-inline-edit-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin-inline-start: auto; }
|
|
3851
|
+
.alt-inline-edit-actions .alt-btn { max-width: 100%; white-space: normal; }
|
|
3852
|
+
.alt-inline-edit-note { margin: var(--space-2) 0 0; }
|
|
3853
|
+
.alt-inline-edit-current { color: var(--ink-muted); font: 400 12.5px/17px var(--font-sans); overflow-wrap: anywhere; }
|
|
3854
|
+
/* Multiline: the slot takes the textarea's inset and the whole width; Edit, then Save and Cancel, sit under it on the right. */
|
|
3855
|
+
.alt-inline-edit[data-multiline] .alt-inline-edit-slot, .alt-inline-edit[data-multiline] .alt-inline-edit-slot-box { flex: 1 1 100%; }
|
|
3856
|
+
.alt-inline-edit[data-multiline] .alt-inline-edit-slot { align-items: flex-start; min-height: 0; padding: var(--space-2) 12px; }
|
|
3857
|
+
.alt-inline-edit[data-multiline] .alt-inline-edit-value { white-space: pre-wrap; }
|
|
3858
|
+
.alt-inline-edit[data-multiline] textarea { flex: none; min-height: 0; resize: none; }
|
|
3859
|
+
.alt-inline-edit[data-multiline] .alt-inline-edit-actions { margin-inline-start: 0; justify-content: flex-start; }
|
|
3860
|
+
.alt-inline-edit-keys { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 2px; }
|
|
3861
|
+
|
|
3862
|
+
/* The value as its own button ('pencil', 'field'): the same slot, so its text sits where it always does. */
|
|
3863
|
+
.alt-inline-edit-slot-button { width: auto; margin: 0; font: inherit; color: inherit; text-align: start; background: transparent; cursor: pointer;
|
|
3864
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), border-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
3865
|
+
.alt-inline-edit-slot-button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
3866
|
+
.alt-inline-edit-slot-button:disabled { cursor: default; }
|
|
3867
|
+
.alt-inline-edit-slot-button[data-empty] .alt-inline-edit-value { color: var(--ink-muted); }
|
|
3868
|
+
/* Pencil: a faint well tint and the pencil, whose space is always reserved, so showing it moves nothing. Touch screens
|
|
3869
|
+
can't hover, so they always show it. */
|
|
3870
|
+
.alt-inline-edit-pencil { flex: none; margin-inline-start: var(--space-2); color: var(--ink-muted); opacity: 0; transition: opacity var(--dur-travel-1) var(--ease-rise); }
|
|
3871
|
+
@media (hover: hover) { .alt-inline-edit[data-trigger="pencil"] .alt-inline-edit-slot-button:hover:not(:disabled) { background: var(--surface-well); } }
|
|
3872
|
+
.alt-inline-edit-slot-button:focus-visible .alt-inline-edit-pencil { opacity: 1; }
|
|
3873
|
+
@media (hover: hover) { .alt-inline-edit-slot-button:hover:not(:disabled) .alt-inline-edit-pencil { opacity: 1; } }
|
|
3874
|
+
@media (hover: none) { .alt-inline-edit-pencil { opacity: 1; } }
|
|
3875
|
+
/* Field: pointing at the value draws the filled input's border and floor around it, so the click only focuses it. */
|
|
3876
|
+
.alt-inline-edit[data-trigger="field"] .alt-inline-edit-slot-button { cursor: text; }
|
|
3877
|
+
@media (hover: hover) { .alt-inline-edit[data-trigger="field"] .alt-inline-edit-slot-button:is(:hover, :focus-visible):not(:disabled) { background: var(--surface-raised); border-color: var(--line-control); box-shadow: 0 1px 0 0 var(--alt-lip); } }
|
|
3878
|
+
.alt-inline-edit[data-trigger="field"] .alt-inline-edit-slot-button:focus-visible { outline-color: var(--alt-rim-held); }
|
|
3879
|
+
/* It was drawn on hover already, so the real field doesn't fade in again. */
|
|
3880
|
+
.alt-inline-edit[data-trigger="field"] .alt-inline-edit-slot-box > .alt-input { animation: none; }
|
|
3881
|
+
/* hint="seam": at rest, a hairline groove is scored under the value where its well will open (a line-control cut with
|
|
3882
|
+
the well's light lip beneath it), so a value whose trigger only shows on hover still reads as something to point at.
|
|
3883
|
+
The field trigger's border replaces it on hover. Flat in contrast="more" and forced colours. */
|
|
3884
|
+
.alt-inline-edit [data-hint="seam"]::after {
|
|
3885
|
+
content: ""; position: absolute; left: 12px; right: 12px; bottom: 6px; height: 1px; border-radius: 1px; pointer-events: none;
|
|
3886
|
+
background: color-mix(in oklab, var(--line-control) 40%, transparent); box-shadow: 0 1px 0 0 var(--alt-lip);
|
|
3887
|
+
transition: opacity var(--dur-travel-1) var(--ease-rise);
|
|
3888
|
+
}
|
|
3889
|
+
.alt-inline-edit[data-trigger="pencil"] [data-hint="seam"]::after { right: calc(12px + 15px + var(--space-2)); }
|
|
3890
|
+
.alt-inline-edit-plate[data-hint="seam"] { position: relative; }
|
|
3891
|
+
.alt-inline-edit-plate[data-hint="seam"]::after { left: 12px; right: 12px; bottom: 3px; }
|
|
3892
|
+
@media (hover: hover) { .alt-inline-edit[data-trigger="field"] [data-hint="seam"]:is(:hover, :focus-visible)::after { opacity: 0; } }
|
|
3893
|
+
.alt-inline-edit [data-hint="seam"][data-empty]::after { display: none; }
|
|
3894
|
+
[data-contrast="more"] .alt-inline-edit [data-hint="seam"]::after { background: var(--line-control); box-shadow: none; }
|
|
3895
|
+
@media (forced-colors: active) {
|
|
3896
|
+
.alt-inline-edit [data-hint="seam"]::after { background: none; border-bottom: 1px dashed CanvasText; box-shadow: none; }
|
|
3897
|
+
.alt-inline-edit[data-trigger="field"] .alt-inline-edit-slot-button:is(:hover, :focus-visible) { border-color: CanvasText; }
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
/* ── EditRegion: several fields of one record, saved together. Read mode is the record on the ground (level 0) with a
|
|
3901
|
+
visible Edit button. Editing lifts the region into a static raised card (level 1, no hover lift: it's being worked
|
|
3902
|
+
in, not offered), as an Accordion lid lifts over its compartment; the fields inside are openings (Input, Select…),
|
|
3903
|
+
and Surface's own signals carry the write: pending hovers after dur-grace, a known failure drops to a trench, an
|
|
3904
|
+
accepted save settles back to the ground with one afterglow for the whole region. The height glides open; the
|
|
3905
|
+
fields fade in together, never staggered. Forced colours and contrast="more" come from Surface and the controls. ── */
|
|
3906
|
+
.alt-edit-region { min-width: 0; --alt-er-pad: var(--space-3); }
|
|
3907
|
+
.alt-edit-region[data-layout="in-place"] { --alt-er-pad: calc(var(--space-2) + 13px); }
|
|
3908
|
+
/* The editor's padding and border are there in read mode too, under a matching negative margin, so lifting it changes
|
|
3909
|
+
colour and shadow but never moves the record; the afterglow fills that padded box, so it never sits against the text. */
|
|
3910
|
+
.alt-edit-region-surface { min-width: 0; margin: calc(-1 * (var(--space-2) + 1px)) calc(-1 * (var(--alt-er-pad) + 1px)); padding: var(--space-2) var(--alt-er-pad); border: 1px solid transparent; border-radius: var(--radius-lg);
|
|
3911
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), border-color var(--dur-travel-1) var(--ease-rise); }
|
|
3912
|
+
.alt-edit-region:not([data-state="read"]) .alt-edit-region-surface { border-color: var(--line-control); }
|
|
3913
|
+
.alt-edit-region-view { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2) var(--space-3); flex-wrap: wrap; }
|
|
3914
|
+
.alt-edit-region-values { flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; }
|
|
3915
|
+
.alt-edit-region-form { display: grid; gap: var(--space-4); min-width: 0; --alt-from-y: 0px; animation: alt-emerge var(--dur-travel-1) var(--ease-rise); }
|
|
3916
|
+
.alt-edit-region-fields { display: grid; gap: var(--space-3); min-width: 0; }
|
|
3917
|
+
.alt-edit-region-fields .alt-field, .alt-edit-region-fields .alt-input, .alt-edit-region-fields .alt-select { min-width: 0; }
|
|
3918
|
+
.alt-edit-region-fields .alt-input input { width: 100%; min-width: 0; }
|
|
3919
|
+
.alt-edit-region-chrome { display: grid; gap: var(--space-3); }
|
|
3920
|
+
.alt-edit-region-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
3921
|
+
.alt-edit-region-actions .alt-btn { max-width: 100%; white-space: normal; }
|
|
3922
|
+
/* In place: the form doesn't fade in as a whole; fields whose text lands where their value was simply appear, the
|
|
3923
|
+
rest fade in, and nothing slides. Controls take the value's font and reach 13px past their column on each side. */
|
|
3924
|
+
.alt-edit-region-form[data-layout="in-place"] { gap: var(--space-3); animation: none; }
|
|
3925
|
+
/* The fields sit where the values were, so the form adds no spacing of its own between them. */
|
|
3926
|
+
.alt-edit-region-form[data-layout="in-place"] .alt-edit-region-fields { display: block; }
|
|
3927
|
+
.alt-edit-region-form[data-layout="in-place"] :is(.alt-input:not(.alt-select > .alt-input), .alt-select) { margin-inline: -13px; }
|
|
3928
|
+
.alt-edit-region-form[data-layout="in-place"] :is(.alt-input input, .alt-input textarea, .alt-select-trigger, .alt-switch-row) { font: inherit; }
|
|
3929
|
+
.alt-edit-region-form[data-layout="in-place"] .alt-input:not([data-multiline]), .alt-edit-region-form[data-layout="in-place"] .alt-select > .alt-input { font: inherit; height: max(40px, calc(1lh + 18px)); }
|
|
3930
|
+
.alt-edit-region-form[data-layout="in-place"] .alt-edit-region-actions { justify-content: flex-end; }
|
|
3931
|
+
/* The record around the fields names them (a row's label, the value's place), so a Field's label is announced, not shown. */
|
|
3932
|
+
.alt-edit-region-form[data-layout="in-place"] .alt-field { gap: var(--space-1); }
|
|
3933
|
+
.alt-edit-region-form[data-layout="in-place"] .alt-field-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
3934
|
+
/* A reserved value already has its control's height and inset, so in labelled rows Edit moves nothing. */
|
|
3935
|
+
.alt-edit-region-value[data-reserve] { box-sizing: border-box; display: flex; align-items: center; min-height: max(40px, calc(1lh + 18px)); margin-inline: -13px; padding: 0 12px; border: 1px solid transparent; overflow-wrap: anywhere; }
|
|
3936
|
+
/* ── TagInput: a set of short values in one field. The field is an opening, built like Input; once it holds a tag its
|
|
3937
|
+
floor rises flush (filled). Each tag is a label, part of the record, so it's a Badge plate stamped flush into the
|
|
3938
|
+
floor, and stamped again (down 3px, settling) when it arrives. A first Backspace arms the last tag: it lifts one
|
|
3939
|
+
level on a critical tint; a second lifts it off (up 8px, fading) and the gap closes. A tag waiting on onAdd or
|
|
3940
|
+
onRemove hovers at level 2 after dur-grace; a rejected one drops to a trench with a critical rim. Suggestions are the
|
|
3941
|
+
Combobox's list at level 3. ── */
|
|
3942
|
+
.alt-tag-input { position: relative; display: block; min-width: 0; }
|
|
3943
|
+
.alt-tag-input > .alt-input { height: auto; min-height: var(--size-field-md); flex-wrap: wrap; align-items: center; row-gap: 4px; padding-block: 5px; }
|
|
3944
|
+
.alt-tag-input > .alt-input[data-size="sm"] { min-height: var(--size-field-sm); padding-block: 3px; }
|
|
3945
|
+
.alt-tag-input > .alt-input > input { flex: 1 1 6ch; min-width: 6ch; height: 28px; }
|
|
3946
|
+
.alt-tag-input > .alt-input:not([data-filled]):not([data-invalid]):not([data-disabled]) { --w: 4px; --n: calc(var(--near) * var(--alt-travel)); background: var(--surface-well); }
|
|
3947
|
+
.alt-tag-list { display: contents; list-style: none; margin: 0; padding: 0; }
|
|
3948
|
+
.alt-tag {
|
|
3949
|
+
--t-bg: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%);
|
|
3950
|
+
position: relative; display: inline-flex; align-items: center; max-width: 100%; height: 24px; border-radius: var(--radius-sm);
|
|
3951
|
+
background: var(--t-bg); color: var(--ink); font: 550 12.5px/1 var(--font-sans); vertical-align: middle;
|
|
3952
|
+
box-shadow: inset 0 1px 1.5px color-mix(in oklab, var(--ink) 16%, transparent), inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent), 0 1px 0 0 var(--alt-lip);
|
|
3953
|
+
transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise);
|
|
3954
|
+
}
|
|
3955
|
+
.alt-tag-face { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 24ch; height: 100%; margin: 0; padding: 0 8px; border: 0; border-radius: inherit; background: none; color: inherit; font: inherit; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: default; }
|
|
3956
|
+
.alt-tag-face:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
3957
|
+
.alt-tag:has(.alt-tag-remove) .alt-tag-face { padding-right: 2px; }
|
|
3958
|
+
.alt-tag-remove { display: inline-grid; place-items: center; width: 20px; height: 20px; margin-right: 2px; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), box-shadow var(--dur-travel-1); }
|
|
3959
|
+
@media (hover: hover) { .alt-tag-remove:hover { background: var(--surface-raised); color: var(--ink); box-shadow: var(--elev-1); } }
|
|
3960
|
+
.alt-tag[data-stamp] { animation: alt-stamp-a var(--dur-travel-2) var(--ease-settle); }
|
|
3961
|
+
.alt-tag[data-armed] { --t-bg: var(--critical-soft); background: var(--critical-soft); transform: translateY(calc(-1px * var(--alt-travel))); box-shadow: var(--elev-1), inset 0 0 0 1px color-mix(in oklab, var(--critical) 40%, transparent); }
|
|
3962
|
+
.alt-tag[data-status="pending"] { animation: alt-tag-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
3963
|
+
@keyframes alt-tag-pending { to { background: var(--surface-lifted); box-shadow: var(--elev-2); transform: translateY(calc(-2px * var(--alt-travel))); } }
|
|
3964
|
+
.alt-tag[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
3965
|
+
.alt-tag[data-leaving] { animation: alt-lifted-off 140ms var(--ease-rise) forwards; }
|
|
3966
|
+
.alt-tag-edit { display: inline-flex; }
|
|
3967
|
+
.alt-tag-edit-input { height: 24px; padding: 0 6px; border: 1px solid var(--line-control); border-radius: var(--radius-sm); background: var(--surface-well); color: var(--ink); font: 500 12.5px/1 var(--font-sans); box-shadow: var(--well); outline: none; }
|
|
3968
|
+
.alt-tag-edit-input:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
3969
|
+
.alt-tag-edit-input[aria-invalid] { background: var(--critical-soft); border-color: var(--critical); }
|
|
3970
|
+
.alt-tag-hint { display: block; margin-top: var(--space-1); color: var(--ink-muted); font: 400 12.5px/17px var(--font-sans); }
|
|
3971
|
+
.alt-tag-error { margin: var(--space-1) 0 0; }
|
|
3972
|
+
.alt-tag-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
3973
|
+
.alt-tag-standalone { margin: 2px; }
|
|
3974
|
+
[data-contrast="more"] .alt-tag[data-armed] { box-shadow: 0 2px 0 0 var(--ink), inset 0 0 0 1px var(--critical); }
|
|
3975
|
+
@media (forced-colors: active) {
|
|
3976
|
+
.alt-tag { border: 1px solid CanvasText; } .alt-tag[data-armed] { border: 1px solid Highlight; outline: 1px solid Highlight; }
|
|
3977
|
+
.alt-tag[data-status="pending"] { border-style: dotted; } .alt-tag[data-status="failed"] { border: 2px dashed CanvasText; }
|
|
3978
|
+
.alt-tag-input > .alt-input:not([data-filled]) { border-style: dashed; }
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
/* ── DatePicker, DateRangePicker, Calendar: the field is an opening built like Select. Day, month and year are typed
|
|
3982
|
+
segments; a partial date isn't a value, so the floor rises flush only once a whole, allowed date is held. The
|
|
3983
|
+
calendar floats at level 3 like a Popover. Days are flush (at this density a trench per day would be noise); the
|
|
3984
|
+
chosen day carries the one raised accent mark, the RadioGroup thumb's rule, and the mark is carried to a new day in
|
|
3985
|
+
the same month. Hovering a day shows the mark's ghost (feedforward). A range is the Slider's build: a sunk
|
|
3986
|
+
accent-soft track between two raised ends. A month change is lateral, so it cross-fades. Calendar on its own lies
|
|
3987
|
+
on the page at level 0. ── */
|
|
3988
|
+
.alt-date-picker { position: relative; display: block; min-width: 0; }
|
|
3989
|
+
.alt-date-picker > .alt-input { gap: 0; padding-right: 4px; cursor: text; }
|
|
3990
|
+
.alt-date-segs { display: inline-flex; align-items: center; min-width: 0; font: 400 14px/20px var(--font-sans); font-variant-numeric: tabular-nums; transform: translateY(calc(var(--wd) * 0.5)); transition: transform var(--dur-travel-2) var(--ease-settle); }
|
|
3991
|
+
.alt-date-seg { padding: 1px 2px; border-radius: 3px; outline: none; caret-color: transparent; }
|
|
3992
|
+
.alt-date-seg[data-placeholder] { color: var(--ink-muted); }
|
|
3993
|
+
.alt-date-seg:focus { background: var(--accent); color: var(--on-accent); }
|
|
3994
|
+
.alt-date-sep { padding: 0 1px; color: var(--ink-muted); }
|
|
3995
|
+
.alt-date-to { padding: 0 var(--space-2); color: var(--ink-muted); transform: translateY(calc(var(--wd) * 0.5)); }
|
|
3996
|
+
.alt-date-button { display: inline-grid; place-items: center; width: var(--size-control-sm); height: var(--size-control-sm); margin-left: auto; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), box-shadow var(--dur-travel-1), color var(--dur-travel-1); }
|
|
3997
|
+
@media (hover: hover) { .alt-date-button:hover:not(:disabled) { background: var(--surface-raised); color: var(--ink); box-shadow: var(--elev-1); } }
|
|
3998
|
+
.alt-date-button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
3999
|
+
.alt-date-picker > .alt-input:not([data-filled]):not([data-invalid]):not([data-disabled]) { --w: 4px; --n: calc(var(--near) * var(--alt-travel)); background: var(--surface-well); }
|
|
4000
|
+
.alt-date-picker > .alt-input:not([data-filled]):not([data-invalid]):not([data-disabled]):focus-within { --n: var(--alt-travel); }
|
|
4001
|
+
.alt-date-error { margin: var(--space-1) 0 0; }
|
|
4002
|
+
.alt-date-panel { padding: var(--space-3); min-width: 0; }
|
|
4003
|
+
.alt-date-step { margin: 0 0 var(--space-2); color: var(--ink-muted); font: 500 12.5px/17px var(--font-sans); }
|
|
4004
|
+
.alt-cal { width: max-content; color: var(--ink); font: 400 13.5px/1 var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
4005
|
+
.alt-cal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }
|
|
4006
|
+
.alt-cal-title { margin: 0; font: 600 14px/20px var(--font-sans); }
|
|
4007
|
+
.alt-cal-nav { display: inline-grid; place-items: center; width: var(--size-control-sm); height: var(--size-control-sm); padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), box-shadow var(--dur-travel-1); }
|
|
4008
|
+
@media (hover: hover) { .alt-cal-nav:hover:not(:disabled) { background: var(--surface-raised); color: var(--ink); box-shadow: var(--elev-1); } }
|
|
4009
|
+
.alt-cal-nav:disabled { opacity: 0.4; cursor: default; }
|
|
4010
|
+
.alt-cal-nav:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4011
|
+
[dir="rtl"] .alt-cal-chevron { scale: -1 1; }
|
|
4012
|
+
.alt-cal-body { position: relative; }
|
|
4013
|
+
.alt-cal-grid { border-collapse: separate; border-spacing: 0 2px; animation: alt-cal-fade var(--dur-travel-1) ease-out; }
|
|
4014
|
+
@keyframes alt-cal-fade { from { opacity: 0; } }
|
|
4015
|
+
.alt-cal-grid th { width: 36px; height: 24px; padding: 0; color: var(--ink-muted); font: 500 11.5px/1 var(--font-sans); text-align: center; }
|
|
4016
|
+
.alt-cal-grid td { padding: 0; text-align: center; }
|
|
4017
|
+
.alt-cal-day {
|
|
4018
|
+
position: relative; z-index: 1; display: inline-grid; place-items: center; width: 36px; height: 34px; padding: 0; border: 0; border-radius: var(--radius-sm);
|
|
4019
|
+
background: none; color: inherit; font: inherit; cursor: pointer; transition: color var(--dur-travel-2) var(--ease-settle);
|
|
4020
|
+
}
|
|
4021
|
+
.alt-cal-day::before { content: ""; position: absolute; inset: 2px; z-index: -1; border-radius: var(--radius-sm); background: var(--accent); opacity: 0; scale: 0.8; transition: opacity var(--dur-travel-1) var(--ease-sink), scale var(--dur-travel-2) var(--ease-settle); }
|
|
4022
|
+
@media (hover: hover) { .alt-cal-day:hover:not([aria-disabled]):not([data-chosen])::before { opacity: 0.16; scale: 1; } }
|
|
4023
|
+
.alt-cal-day:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
4024
|
+
.alt-cal-day[data-today]::after { content: ""; position: absolute; bottom: 4px; left: 50%; width: 4px; height: 4px; margin-left: -2px; border-radius: 50%; background: var(--accent-ink); }
|
|
4025
|
+
.alt-cal-day[aria-disabled] { color: var(--ink-faint); cursor: default; }
|
|
4026
|
+
.alt-cal-day[data-chosen] { color: var(--on-accent); font-weight: 600; }
|
|
4027
|
+
.alt-cal-day[data-chosen][data-today]::after { background: var(--on-accent); }
|
|
4028
|
+
.alt-cal-mark {
|
|
4029
|
+
position: absolute; left: 0; top: 0; z-index: 0; border-radius: var(--radius-sm); pointer-events: none; opacity: 0;
|
|
4030
|
+
background: var(--accent); background-image: var(--alt-sheen); box-shadow: var(--elev-1), inset 0 -2px 0 0 color-mix(in oklab, var(--accent-ink) 55%, transparent);
|
|
4031
|
+
transition: transform var(--dur-travel-2) var(--ease-settle), width var(--dur-travel-2) var(--ease-settle), height var(--dur-travel-2) var(--ease-settle);
|
|
4032
|
+
}
|
|
4033
|
+
/* Presets: a column of rows beside the calendar (above it when narrow), built like Select's options at level 3 inside
|
|
4034
|
+
the popover: one gliding highlight, the chosen row wearing the tick. A chosen preset fills the field with its name in
|
|
4035
|
+
ink and its dates in ink-muted, and settles with the value's afterglow. */
|
|
4036
|
+
.alt-date-panes { display: flex; align-items: flex-start; gap: var(--space-3); }
|
|
4037
|
+
.alt-date-presets { position: relative; flex: none; min-width: 168px; padding: 0 var(--space-3) 0 0; border-right: 1px solid var(--line); }
|
|
4038
|
+
.alt-date-presets .alt-menu-hl { left: 0; right: var(--space-3); }
|
|
4039
|
+
.alt-date-presets:focus { outline: none; }
|
|
4040
|
+
.alt-date-custom { min-width: 0; }
|
|
4041
|
+
@media (max-width: 480px) {
|
|
4042
|
+
.alt-date-panes { flex-direction: column; }
|
|
4043
|
+
.alt-date-presets { align-self: stretch; padding: 0 0 var(--space-2); border-right: 0; border-bottom: 1px solid var(--line); }
|
|
4044
|
+
.alt-date-presets .alt-menu-hl { right: 0; }
|
|
4045
|
+
}
|
|
4046
|
+
.alt-date-preset {
|
|
4047
|
+
display: flex; align-items: baseline; gap: 0.35em; flex: 1; min-width: 0; margin: 0; padding: 1px 2px; border: 0; border-radius: 3px; background: none;
|
|
4048
|
+
font: 400 14px/20px var(--font-sans); color: var(--ink); text-align: start; white-space: nowrap; cursor: pointer;
|
|
4049
|
+
transform: translateY(calc(var(--wd) * 0.5)); transition: transform var(--dur-travel-2) var(--ease-settle);
|
|
4050
|
+
}
|
|
4051
|
+
.alt-date-preset:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4052
|
+
.alt-date-preset-name { flex: none; font-weight: 500; }
|
|
4053
|
+
.alt-date-preset-dates { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
4054
|
+
.alt-date-preset[data-glow="a"] { animation: alt-value-a calc(var(--dur-settle) / 5) var(--ease-decay); }
|
|
4055
|
+
.alt-date-preset[data-glow="b"] { animation: alt-value-b calc(var(--dur-settle) / 5) var(--ease-decay); }
|
|
4056
|
+
@media (forced-colors: active) { .alt-date-presets { border-color: CanvasText; } .alt-date-presets .alt-select-option[data-active] { outline: 2px solid Highlight; outline-offset: -2px; } }
|
|
4057
|
+
/* A range: the sunk track, then the two raised ends. */
|
|
4058
|
+
.alt-cal[data-range] .alt-cal-day[data-span]:not([data-chosen])::before { opacity: 1; scale: 1; inset: 2px -1px; border-radius: 0; background: var(--accent-soft); box-shadow: var(--well); }
|
|
4059
|
+
.alt-cal[data-range] .alt-cal-day[data-span-start]:not([data-chosen])::before { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
|
|
4060
|
+
.alt-cal[data-range] .alt-cal-day[data-span-end]:not([data-chosen])::before { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
|
|
4061
|
+
.alt-cal[data-range] .alt-cal-day[data-chosen]::before { opacity: 1; scale: 1; background: var(--accent); background-image: var(--alt-sheen); box-shadow: var(--elev-1); }
|
|
4062
|
+
.alt-calendar { display: inline-block; }
|
|
4063
|
+
[data-contrast="more"] .alt-cal-mark, [data-contrast="more"] .alt-cal[data-range] .alt-cal-day[data-chosen]::before { box-shadow: 0 2px 0 0 var(--ink); }
|
|
4064
|
+
@media (forced-colors: active) {
|
|
4065
|
+
.alt-date-panel { border: 2px solid CanvasText; }
|
|
4066
|
+
.alt-date-picker > .alt-input:not([data-filled]) { border-style: dashed; }
|
|
4067
|
+
.alt-cal-mark, .alt-cal[data-range] .alt-cal-day[data-chosen]::before { forced-color-adjust: none; background: Highlight; }
|
|
4068
|
+
.alt-cal-day[data-chosen] { forced-color-adjust: none; color: HighlightText; }
|
|
4069
|
+
.alt-cal[data-range] .alt-cal-day[data-span]:not([data-chosen])::before { border: 1px dashed CanvasText; background: Canvas; }
|
|
4070
|
+
.alt-cal-day[data-today] { text-decoration: underline; }
|
|
4071
|
+
.alt-date-seg:focus { forced-color-adjust: none; background: Highlight; color: HighlightText; }
|
|
4072
|
+
}
|
|
4073
|
+
@media (prefers-reduced-motion: reduce) { .alt-cal-mark { transition: none; } }
|
|
4074
|
+
|
|
4075
|
+
/* ── Tree: a hierarchy seen all at once. Nodes are the record: flush rows on the ground, indented space-4 per level,
|
|
4076
|
+
like Table rows. Only a node that acts lifts on hover; the selected node is tinted accent-soft and raised one level.
|
|
4077
|
+
Expanding is disclosure of existing records, so it's a chevron turn, not a rise. Renaming sinks the label into a
|
|
4078
|
+
well (InlineEdit's field); a write hovers while pending and drops to a trench when it fails. A carried node floats
|
|
4079
|
+
at level 3, tilted, with a count of what it carries; the slot it would land in is a thin well at the target's
|
|
4080
|
+
depth, and a node it would go into opens to a trench with an accent rim (a dashed critical one if it can't). ── */
|
|
4081
|
+
.alt-tree-wrap { position: relative; min-width: 0; }
|
|
4082
|
+
.alt-tree { margin: 0; padding: 0; list-style: none; color: var(--ink); font: 400 14px/20px var(--font-sans); }
|
|
4083
|
+
.alt-tree-row {
|
|
4084
|
+
--indent: calc((var(--level, 1) - 1) * var(--space-4));
|
|
4085
|
+
position: relative; display: flex; align-items: center; gap: 4px; min-height: var(--size-row-md); padding: 2px var(--space-2) 2px 4px; border-radius: var(--radius-sm);
|
|
4086
|
+
outline: none; cursor: default; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: pan-y;
|
|
4087
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise);
|
|
4088
|
+
}
|
|
4089
|
+
.alt-tree-indent { flex: none; width: var(--indent); }
|
|
4090
|
+
.alt-tree-chevron { display: inline-grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: var(--radius-sm); color: var(--ink-muted); cursor: pointer; }
|
|
4091
|
+
.alt-tree-chevron svg { rotate: -90deg; transition: rotate var(--dur-travel-2) var(--ease-settle); }
|
|
4092
|
+
.alt-tree-chevron[data-open] svg { rotate: 0deg; }
|
|
4093
|
+
[dir="rtl"] .alt-tree-chevron:not([data-open]) svg { rotate: 90deg; }
|
|
4094
|
+
.alt-tree-leaf { flex: none; width: 20px; }
|
|
4095
|
+
.alt-tree-label { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4096
|
+
@media (hover: hover) { .alt-tree-row:hover:not([data-carried]) { background: var(--surface-raised); box-shadow: var(--elev-1); } }
|
|
4097
|
+
.alt-tree-row[aria-selected="true"] { background: var(--accent-soft); box-shadow: var(--elev-1); }
|
|
4098
|
+
.alt-tree-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
4099
|
+
.alt-tree-row[data-status="pending"] { animation: alt-tree-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
4100
|
+
@keyframes alt-tree-pending { to { background: var(--surface-lifted); box-shadow: var(--elev-2); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
4101
|
+
.alt-tree-row[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4102
|
+
.alt-tree-row[data-carried="keys"] { z-index: 2; background: var(--surface-floating); box-shadow: var(--elev-3); rotate: calc(-1.2deg * var(--alt-travel)); }
|
|
4103
|
+
.alt-tree-row[data-carried="pointer"] { opacity: 0.4; }
|
|
4104
|
+
.alt-tree-row[data-into] { background: var(--surface-trench); box-shadow: var(--trench), inset 0 0 0 2px var(--accent); }
|
|
4105
|
+
.alt-tree-row[data-into="blocked"] { box-shadow: var(--trench), inset 0 0 0 2px var(--critical); }
|
|
4106
|
+
.alt-tree-slot { height: 6px; margin: -3px 0 -3px calc((var(--level, 1) - 1) * var(--space-4) + 28px); border-radius: var(--radius-full); background: var(--surface-well); box-shadow: var(--well), inset 0 0 0 1px var(--accent); list-style: none; }
|
|
4107
|
+
.alt-tree-count { flex: none; margin-left: auto; padding: 1px 6px; border-radius: var(--radius-full); background: var(--surface-floating); box-shadow: var(--elev-2); font: 650 11px/16px var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
4108
|
+
.alt-tree-ghost {
|
|
4109
|
+
position: fixed; z-index: var(--z-floating); display: flex; align-items: center; gap: var(--space-2); min-height: 32px; padding: 2px var(--space-3); border-radius: var(--radius-sm);
|
|
4110
|
+
background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--ink); font: 400 14px/20px var(--font-sans); pointer-events: none;
|
|
4111
|
+
rotate: calc(-1.2deg * var(--alt-travel)); scale: calc(1 + 0.015 * var(--alt-travel));
|
|
4112
|
+
}
|
|
4113
|
+
.alt-tree-wrap[data-carrying="pointer"] { cursor: grabbing; }
|
|
4114
|
+
.alt-tree-name { position: relative; display: inline-flex; flex-direction: column; flex: 1; min-width: 0; }
|
|
4115
|
+
.alt-tree-name-input {
|
|
4116
|
+
width: 100%; min-width: 8ch; height: 26px; padding: 0 6px; border: 1px solid var(--line-control); border-radius: var(--radius-sm); outline: none;
|
|
4117
|
+
background: var(--surface-well); box-shadow: var(--well); color: var(--ink); font: inherit; animation: alt-tree-sink var(--dur-travel-1) var(--ease-sink);
|
|
4118
|
+
}
|
|
4119
|
+
@keyframes alt-tree-sink { from { background: var(--surface-ground); box-shadow: none; } }
|
|
4120
|
+
.alt-tree-name-input:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4121
|
+
.alt-tree-name[data-invalid] .alt-tree-name-input { background: var(--critical-soft); border-color: var(--critical); box-shadow: var(--trench); }
|
|
4122
|
+
.alt-tree-error { display: inline-flex; align-items: center; gap: 4px; margin-top: 2px; color: var(--critical); font: 400 12px/16px var(--font-sans); white-space: normal; }
|
|
4123
|
+
.alt-tree-new { cursor: default; }
|
|
4124
|
+
.alt-tree-imprint { width: 40%; height: 12px; border-radius: var(--radius-sm); }
|
|
4125
|
+
.alt-tree-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4126
|
+
[data-contrast="more"] .alt-tree-row[data-carried="keys"], [data-contrast="more"] .alt-tree-ghost { box-shadow: var(--elev-3), 0 3px 0 0 var(--ink); }
|
|
4127
|
+
@media (forced-colors: active) {
|
|
4128
|
+
.alt-tree-row[aria-selected="true"] { border: 1px solid Highlight; } .alt-tree-row[data-carried="keys"], .alt-tree-ghost { border: 2px solid CanvasText; }
|
|
4129
|
+
.alt-tree-slot { background: none; border-top: 2px dashed CanvasText; height: 0; } .alt-tree-row[data-into] { border: 3px dashed Highlight; }
|
|
4130
|
+
.alt-tree-row[data-status="pending"] { border: 1px dotted CanvasText; } .alt-tree-row[data-status="failed"] { border: 2px dashed CanvasText; }
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
/* ── Checklist: a list you write into. Each item is the record, flush on the ground, and its line is the editor: no
|
|
4134
|
+
raised editor and no Save, the line commits when you leave it. The empty line at the end is an opening (a well
|
|
4135
|
+
while it's focused or holds text). The checkbox is the Checkbox's choice; a done item is pressed into the record,
|
|
4136
|
+
muted with a strike drawn left to right, and stays in place. A write hovers after dur-grace and a failure drops to
|
|
4137
|
+
a trench with Retry on the line. Nesting indents by space-6 and glides sideways, a change of hierarchy. ── */
|
|
4138
|
+
.alt-checklist { position: relative; min-width: 0; color: var(--ink); font: 400 14px/20px var(--font-sans); }
|
|
4139
|
+
.alt-checklist-list { margin: 0; padding: 0; list-style: none; }
|
|
4140
|
+
.alt-checklist-list .alt-checklist-list { padding-left: var(--space-6); }
|
|
4141
|
+
.alt-checklist-item { min-width: 0; border-radius: var(--radius-sm); outline: none; }
|
|
4142
|
+
.alt-checklist-item:focus-visible > .alt-checklist-line { outline: 2px solid var(--accent-ink); outline-offset: -2px; }
|
|
4143
|
+
.alt-checklist-line { position: relative; display: flex; align-items: center; gap: var(--space-2); min-height: 34px; padding: 0 var(--space-1); border-radius: var(--radius-sm); transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise); }
|
|
4144
|
+
.alt-checklist-line > .alt-check { flex: none; }
|
|
4145
|
+
.alt-checklist-text { flex: 1; min-width: 0; height: 28px; margin: 0; padding: 0 var(--space-1); border: 0; border-radius: var(--radius-sm); outline: none; background: none; color: inherit; font: inherit; }
|
|
4146
|
+
.alt-checklist-text:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-ink); }
|
|
4147
|
+
.alt-checklist-item[data-done] > .alt-checklist-line .alt-checklist-text {
|
|
4148
|
+
color: var(--ink-muted); text-decoration: line-through; text-decoration-color: var(--ink-muted);
|
|
4149
|
+
animation: alt-checklist-strike var(--dur-travel-2) var(--ease-rise);
|
|
4150
|
+
}
|
|
4151
|
+
@keyframes alt-checklist-strike { from { text-decoration-color: transparent; color: var(--ink); } }
|
|
4152
|
+
.alt-checklist-item[data-arrived] > .alt-checklist-line { animation: alt-checklist-open var(--dur-travel-1) var(--ease-sink); }
|
|
4153
|
+
@keyframes alt-checklist-open { from { background: var(--surface-well); box-shadow: var(--well); transform: translateY(calc(-4px * var(--alt-travel))); } }
|
|
4154
|
+
.alt-checklist-item[data-status="pending"] > .alt-checklist-line { animation: alt-checklist-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
4155
|
+
@keyframes alt-checklist-pending { to { background: var(--surface-lifted); box-shadow: var(--elev-2); } }
|
|
4156
|
+
.alt-checklist-item[data-status="failed"] > .alt-checklist-line { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4157
|
+
.alt-checklist-failed { display: inline-flex; flex: none; align-items: center; gap: 4px; color: var(--critical); font: 400 12.5px/17px var(--font-sans); }
|
|
4158
|
+
.alt-checklist-new .alt-checklist-line { color: var(--ink-muted); }
|
|
4159
|
+
.alt-checklist-plus { display: inline-grid; place-items: center; flex: none; width: 20px; font: 500 16px/1 var(--font-sans); }
|
|
4160
|
+
.alt-checklist-new .alt-checklist-text::placeholder { color: var(--ink-muted); opacity: 1; }
|
|
4161
|
+
.alt-checklist-new .alt-checklist-line:focus-within, .alt-checklist-new .alt-checklist-line:has(input:not(:placeholder-shown)) { background: var(--surface-well); box-shadow: var(--well); color: var(--ink); }
|
|
4162
|
+
.alt-checklist-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4163
|
+
[data-contrast="more"] .alt-checklist-new .alt-checklist-line:focus-within { box-shadow: inset 0 2px 0 0 var(--ink); }
|
|
4164
|
+
@media (forced-colors: active) {
|
|
4165
|
+
.alt-checklist-new .alt-checklist-line { border: 1px dashed CanvasText; }
|
|
4166
|
+
.alt-checklist-item[data-status="pending"] > .alt-checklist-line { border: 1px dotted CanvasText; }
|
|
4167
|
+
.alt-checklist-item[data-status="failed"] > .alt-checklist-line { border: 2px dashed CanvasText; }
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
/* ── FilterBar: a query as a row of conditions. Applied filters are part of what you're looking at, so each condition
|
|
4171
|
+
is a plate stamped flush into the bar like a Badge, split into segments (field · condition · value · remove). Each
|
|
4172
|
+
segment opens a picker, so it lifts one level when pointed at (hover is a promise). A condition still waiting for
|
|
4173
|
+
a value keeps that segment as an opening (a dashed well), and so does Add filter, deepening under the pointer.
|
|
4174
|
+
Pickers float at level 3 as Select's list does. A changed condition is re-stamped (down 3px, settling). ── */
|
|
4175
|
+
.alt-filter-bar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; color: var(--ink); font: 400 13px/18px var(--font-sans); }
|
|
4176
|
+
.alt-filter-token {
|
|
4177
|
+
display: inline-flex; align-items: stretch; max-width: 100%; min-height: 28px; border-radius: var(--radius-sm);
|
|
4178
|
+
background: color-mix(in oklab, var(--surface-ground), var(--surface-well) 70%);
|
|
4179
|
+
box-shadow: inset 0 1px 1.5px color-mix(in oklab, var(--ink) 16%, transparent), inset 0 0 0 1px color-mix(in oklab, var(--ink) 9%, transparent), 0 1px 0 0 var(--alt-lip);
|
|
4180
|
+
}
|
|
4181
|
+
.alt-filter-token[data-stamp] { animation: alt-stamp-a var(--dur-travel-2) var(--ease-settle); }
|
|
4182
|
+
.alt-filter-anchor { position: relative; display: inline-flex; min-width: 0; }
|
|
4183
|
+
.alt-filter-seg {
|
|
4184
|
+
display: inline-flex; align-items: center; gap: 4px; min-width: 0; min-height: 28px; margin: 0; padding: 0 8px; border: 0; border-radius: var(--radius-sm);
|
|
4185
|
+
background: none; color: inherit; font: inherit; white-space: nowrap; cursor: pointer;
|
|
4186
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise);
|
|
4187
|
+
}
|
|
4188
|
+
.alt-filter-seg[aria-expanded="true"] { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); }
|
|
4189
|
+
@media (hover: hover) { .alt-filter-seg:hover { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
4190
|
+
.alt-filter-seg:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -1px; }
|
|
4191
|
+
.alt-filter-field { font-weight: 600; }
|
|
4192
|
+
.alt-filter-operator { color: var(--ink-muted); }
|
|
4193
|
+
.alt-filter-value { font-weight: 550; overflow: hidden; text-overflow: ellipsis; max-width: 24ch; }
|
|
4194
|
+
.alt-filter-value[data-empty] { margin: 3px; min-height: 22px; border: 1px dashed var(--line-control); background: var(--surface-well); box-shadow: var(--well); color: var(--ink-muted); font-weight: 400; }
|
|
4195
|
+
.alt-filter-remove { width: 28px; justify-content: center; padding: 0; color: var(--ink-muted); }
|
|
4196
|
+
.alt-filter-add { border: 1px dashed var(--line-control); background: var(--surface-well); box-shadow: var(--well); color: var(--ink-muted); }
|
|
4197
|
+
@media (hover: hover) { .alt-filter-add:hover { background: var(--surface-trench); box-shadow: var(--trench); transform: none; color: var(--ink); } }
|
|
4198
|
+
.alt-filter-plus { font: 500 15px/1 var(--font-sans); }
|
|
4199
|
+
.alt-filter-match { color: var(--ink-muted); }
|
|
4200
|
+
.alt-filter-match strong { color: var(--ink); font-weight: 600; }
|
|
4201
|
+
.alt-filter-count { color: var(--ink-muted); font: 400 12.5px/17px var(--font-sans); }
|
|
4202
|
+
.alt-filter-picker { min-width: 200px; max-width: min(320px, calc(100vw - 32px)); padding: var(--space-1); }
|
|
4203
|
+
.alt-filter-list { max-height: 260px; overflow-y: auto; outline: none; }
|
|
4204
|
+
.alt-filter-option { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: var(--size-row-md); padding: 0 var(--space-2); border-radius: var(--radius-sm); cursor: pointer; font: 400 13.5px/18px var(--font-sans); }
|
|
4205
|
+
.alt-filter-option[data-active] { background: var(--accent-soft); }
|
|
4206
|
+
.alt-filter-empty { padding: var(--space-2); color: var(--ink-muted); }
|
|
4207
|
+
.alt-filter-search, .alt-filter-input {
|
|
4208
|
+
width: 100%; min-width: 0; height: 32px; margin-bottom: var(--space-1); padding: 0 var(--space-2); border: 1px solid var(--line-control); border-radius: var(--radius-sm);
|
|
4209
|
+
background: var(--surface-well); box-shadow: var(--well); color: var(--ink); font: 400 13.5px/18px var(--font-sans); outline: none; box-sizing: border-box;
|
|
4210
|
+
}
|
|
4211
|
+
.alt-filter-search:focus-visible, .alt-filter-input:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4212
|
+
.alt-filter-form { display: grid; gap: var(--space-2); padding: var(--space-2); }
|
|
4213
|
+
.alt-filter-form label { display: grid; gap: 2px; color: var(--ink-muted); font: 500 12px/16px var(--font-sans); }
|
|
4214
|
+
.alt-filter-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4215
|
+
@media (hover: hover) { [data-contrast="more"] .alt-filter-seg:hover { box-shadow: 0 2px 0 0 var(--ink); } }
|
|
4216
|
+
@media (forced-colors: active) {
|
|
4217
|
+
.alt-filter-token { border: 1px solid CanvasText; } .alt-filter-value[data-empty], .alt-filter-add { border: 1px dashed CanvasText; }
|
|
4218
|
+
.alt-filter-picker { border: 2px solid CanvasText; } .alt-filter-option[data-active] { forced-color-adjust: none; background: Highlight; color: HighlightText; }
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
/* ── Schedule: time you handle directly. The grid is ground, with line rules on the hour. Events are live work items
|
|
4222
|
+
raised one level like BoardCards (a tinted left edge), lifting on hover. Time opens only while something could land
|
|
4223
|
+
in it: while an event is carried or a span drawn, the days sink into wells; the slot under a carried event deepens
|
|
4224
|
+
to a trench with an accent rim, as long as the event (critical rim where it can't go). A carried event floats at
|
|
4225
|
+
level 3, tilted. Drawing makes a well the length of the drag, which rises into an event to be named. Pending writes
|
|
4226
|
+
hover after dur-grace; failures drop to a trench. The now line is accent-ink: it isn't status. ── */
|
|
4227
|
+
.alt-schedule { --cols: var(--days); position: relative; min-width: 0; color: var(--ink); font: 400 12.5px/16px var(--font-sans); }
|
|
4228
|
+
.alt-schedule-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }
|
|
4229
|
+
.alt-schedule-period { margin: 0; font: 600 15px/22px var(--font-sans); }
|
|
4230
|
+
.alt-schedule-nav { display: inline-flex; gap: 2px; }
|
|
4231
|
+
.alt-schedule-grid { display: grid; grid-template-columns: 52px repeat(var(--cols), minmax(0, 1fr)); min-width: 0; overflow-x: auto; animation: alt-schedule-fade var(--dur-travel-1) ease-out; }
|
|
4232
|
+
@keyframes alt-schedule-fade { from { opacity: 0; } }
|
|
4233
|
+
.alt-schedule-row, .alt-schedule-body { display: contents; }
|
|
4234
|
+
.alt-schedule-corner, .alt-schedule-day { position: sticky; top: 0; z-index: 3; padding: 6px 4px; background: var(--surface-ground); border-bottom: 1px solid var(--line); color: var(--ink-muted); font-weight: 500; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
4235
|
+
.alt-schedule-day[data-today] { color: var(--accent-ink); font-weight: 650; }
|
|
4236
|
+
.alt-schedule-row[aria-rowindex="2"] .alt-schedule-time { transform: none; }
|
|
4237
|
+
.alt-schedule-time { height: var(--slot); padding-right: 6px; color: var(--ink-muted); font-variant-numeric: tabular-nums; text-align: right; transform: translateY(-7px); }
|
|
4238
|
+
.alt-schedule-cell { position: relative; height: var(--slot); border-left: 1px solid var(--line); outline: none; transition: background-color var(--dur-travel-1) var(--ease-sink), box-shadow var(--dur-travel-1) var(--ease-sink); }
|
|
4239
|
+
.alt-schedule-row[data-hour] > .alt-schedule-cell { border-top: 1px solid var(--line); }
|
|
4240
|
+
.alt-schedule-cell:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-ink); z-index: 2; }
|
|
4241
|
+
.alt-schedule[data-carrying] .alt-schedule-cell { background: var(--surface-well); }
|
|
4242
|
+
.alt-schedule-event-box { position: absolute; z-index: 1; display: flex; }
|
|
4243
|
+
.alt-schedule-event {
|
|
4244
|
+
position: relative; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 1px; width: 100%; min-width: 0; margin: 0; padding: 3px 6px 3px 8px; overflow: hidden;
|
|
4245
|
+
border: 0; border-radius: var(--radius-sm); background: var(--surface-raised); box-shadow: var(--elev-1), inset 3px 0 0 0 var(--accent); color: var(--ink); font: inherit; text-align: left; cursor: grab;
|
|
4246
|
+
transition: box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise);
|
|
4247
|
+
}
|
|
4248
|
+
.alt-schedule-event > * { flex: none; }
|
|
4249
|
+
.alt-schedule-event[data-short] { flex-direction: row; align-items: center; gap: 6px; padding-block: 0; }
|
|
4250
|
+
.alt-schedule-event[data-short] small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
4251
|
+
.alt-schedule-event strong { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
4252
|
+
.alt-schedule-event small { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
|
|
4253
|
+
@media (hover: hover) { .alt-schedule-event:hover { box-shadow: var(--elev-2), inset 3px 0 0 0 var(--accent); } }
|
|
4254
|
+
.alt-schedule-event:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; z-index: 2; }
|
|
4255
|
+
.alt-schedule-event[data-carried] { z-index: 5; background: var(--surface-floating); box-shadow: var(--elev-3), inset 3px 0 0 0 var(--accent); rotate: calc(-1.2deg * var(--alt-travel)); cursor: grabbing; }
|
|
4256
|
+
.alt-schedule-event[data-status="pending"] { animation: alt-schedule-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
4257
|
+
@keyframes alt-schedule-pending { to { background: var(--surface-lifted); box-shadow: var(--elev-2), inset 3px 0 0 0 var(--accent); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
4258
|
+
.alt-schedule-event[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4259
|
+
.alt-schedule-event[data-held] { cursor: not-allowed; box-shadow: var(--elev-3), 0 0 0 2px var(--presence-1); }
|
|
4260
|
+
.alt-schedule-event[data-held="2"] { box-shadow: var(--elev-3), 0 0 0 2px var(--presence-2); }
|
|
4261
|
+
.alt-schedule-event[data-held="3"] { box-shadow: var(--elev-3), 0 0 0 2px var(--presence-3); }
|
|
4262
|
+
.alt-schedule-event[data-held="2"] .alt-schedule-holder { background: var(--presence-2); }
|
|
4263
|
+
.alt-schedule-event[data-held="3"] .alt-schedule-holder { background: var(--presence-3); }
|
|
4264
|
+
.alt-schedule-holder { position: absolute; right: 2px; bottom: 2px; padding: 0 4px; border-radius: var(--radius-sm); background: var(--presence-1); color: var(--on-presence); font: 600 10.5px/14px var(--font-sans); }
|
|
4265
|
+
.alt-schedule-handle { position: absolute; left: 25%; right: 25%; bottom: -3px; z-index: 2; height: 6px; border-radius: var(--radius-full); background: var(--surface-raised); box-shadow: var(--elev-1); cursor: ns-resize; opacity: 0; transition: opacity var(--dur-travel-1); }
|
|
4266
|
+
.alt-schedule-event-box:focus-within .alt-schedule-handle { opacity: 1; }
|
|
4267
|
+
@media (hover: hover) { .alt-schedule-event-box:hover .alt-schedule-handle { opacity: 1; } }
|
|
4268
|
+
.alt-schedule-slot { position: absolute; left: 2px; right: 2px; z-index: 0; border-radius: var(--radius-sm); background: var(--surface-trench); box-shadow: var(--trench), inset 0 0 0 2px var(--accent); }
|
|
4269
|
+
.alt-schedule-slot[data-blocked] { box-shadow: var(--trench), inset 0 0 0 2px var(--critical); }
|
|
4270
|
+
.alt-schedule-drawn { width: 100%; padding: 3px 6px; border-radius: var(--radius-sm); background: var(--surface-trench); box-shadow: var(--trench), inset 0 0 0 1px var(--accent); color: var(--ink-muted); }
|
|
4271
|
+
.alt-schedule-draft { display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 3px; border-radius: var(--radius-sm); background: var(--surface-raised); box-shadow: var(--elev-1), inset 3px 0 0 0 var(--accent); animation: alt-emerge var(--dur-travel-2) var(--ease-rise); }
|
|
4272
|
+
.alt-schedule-draft small { padding-left: 4px; color: var(--ink-muted); }
|
|
4273
|
+
.alt-schedule-field { display: flex; flex-direction: column; min-width: 0; }
|
|
4274
|
+
.alt-schedule-event-box > .alt-schedule-field { position: absolute; inset: 2px; z-index: 3; }
|
|
4275
|
+
.alt-schedule-title-input { width: 100%; min-width: 0; height: 24px; padding: 0 6px; border: 1px solid var(--line-control); border-radius: var(--radius-sm); outline: none; background: var(--surface-well); box-shadow: var(--well); color: var(--ink); font: 500 12.5px/16px var(--font-sans); box-sizing: border-box; }
|
|
4276
|
+
.alt-schedule-title-input:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4277
|
+
.alt-schedule-title-input[aria-invalid] { background: var(--critical-soft); border-color: var(--critical); }
|
|
4278
|
+
.alt-schedule-error { display: inline-flex; align-items: center; gap: 3px; color: var(--critical); font: 400 11px/14px var(--font-sans); }
|
|
4279
|
+
.alt-schedule-now { position: absolute; left: -4px; right: 0; z-index: 4; height: 2px; background: var(--accent-ink); pointer-events: none; }
|
|
4280
|
+
.alt-schedule-now::before { content: ""; position: absolute; left: 0; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent-ink); }
|
|
4281
|
+
.alt-schedule-failure { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-2); padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3); border-radius: var(--radius-md); background: var(--critical-soft); color: var(--ink); font: 400 13px/18px var(--font-sans); }
|
|
4282
|
+
.alt-schedule-failure > svg { color: var(--critical); }
|
|
4283
|
+
.alt-schedule-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4284
|
+
[data-contrast="more"] .alt-schedule-event { box-shadow: 0 2px 0 0 var(--ink), inset 3px 0 0 0 var(--accent); }
|
|
4285
|
+
[data-contrast="more"] .alt-schedule-event[data-carried] { box-shadow: 0 3px 0 0 var(--ink); }
|
|
4286
|
+
@media (forced-colors: active) {
|
|
4287
|
+
.alt-schedule-event { border: 1px solid CanvasText; } .alt-schedule-event[data-carried] { border-width: 2px; }
|
|
4288
|
+
.alt-schedule-slot, .alt-schedule-drawn { border: 2px dashed CanvasText; } .alt-schedule-now { background: Highlight; }
|
|
4289
|
+
.alt-schedule-event[data-status="pending"] { border-style: dotted; } .alt-schedule-event[data-status="failed"] { border: 2px dashed CanvasText; }
|
|
4290
|
+
}
|
|
4291
|
+
|
|
4292
|
+
/* ── DataGrid: a table edited cell by cell. Cells are the record, flush in Table's rows; the active cell is a focus
|
|
4293
|
+
ring, not a height, and a range is an accent-soft tint (choosing a target isn't live content). Editable columns
|
|
4294
|
+
carry a pencil in the header instead of a hover lift: a click selects rather than acts. The cell being edited opens
|
|
4295
|
+
into a well clipped to the cell, with the held rim; an invalid value deepens to a trench with its message beside it.
|
|
4296
|
+
A saving cell holds a small raised plate after dur-grace; a failed cell drops to a trench with a critical mark; a
|
|
4297
|
+
landed value settles, and a pasted range settles as one change. ── */
|
|
4298
|
+
.alt-data-grid { min-width: 0; }
|
|
4299
|
+
.alt-data-grid .alt-table { table-layout: fixed; }
|
|
4300
|
+
.alt-grid-head { display: inline-flex; align-items: center; gap: 4px; }
|
|
4301
|
+
.alt-grid-pencil { display: inline-flex; color: var(--ink-faint); }
|
|
4302
|
+
.alt-grid-cell { position: relative; outline: none; cursor: cell; overflow: visible; white-space: nowrap; }
|
|
4303
|
+
.alt-grid-value { display: block; overflow: hidden; text-overflow: ellipsis; }
|
|
4304
|
+
.alt-grid-cell[data-range] { background: var(--accent-soft); }
|
|
4305
|
+
.alt-grid-cell[data-active] { box-shadow: inset 0 0 0 2px var(--accent-ink); }
|
|
4306
|
+
.alt-grid-cell:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-ink); }
|
|
4307
|
+
.alt-grid-cell[data-editing] { padding: 0; background: var(--surface-well); box-shadow: var(--well), inset 0 0 0 2px var(--alt-rim-held, var(--accent)); animation: alt-grid-sink var(--dur-travel-1) var(--ease-sink); }
|
|
4308
|
+
@keyframes alt-grid-sink { from { background: transparent; box-shadow: none; } }
|
|
4309
|
+
.alt-grid-cell[data-editing]:has([aria-invalid="true"]) { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 2px var(--critical); }
|
|
4310
|
+
.alt-grid-cell[data-invalid]:not([data-editing]) { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4311
|
+
.alt-grid-cell[data-invalid][data-active]:not([data-editing]) { box-shadow: var(--trench), inset 0 0 0 2px var(--accent-ink); }
|
|
4312
|
+
@media (forced-colors: active) { .alt-grid-cell[data-invalid]:not([data-editing]) { outline: 2px dashed CanvasText; outline-offset: -3px; } }
|
|
4313
|
+
.alt-grid-input { display: block; width: 100%; height: 100%; min-height: 40px; padding: 10px 12px; border: 0; outline: none; background: none; color: var(--ink); font: inherit; box-sizing: border-box; }
|
|
4314
|
+
.alt-grid-cell[data-align="end"] .alt-grid-input { text-align: right; }
|
|
4315
|
+
.alt-grid-editor { display: block; padding: 3px; }
|
|
4316
|
+
.alt-grid-tip {
|
|
4317
|
+
position: absolute; left: 0; top: calc(100% + 4px); z-index: 6; max-width: 260px; padding: 4px 8px; border-radius: var(--radius-sm);
|
|
4318
|
+
background: var(--surface-floating); box-shadow: var(--elev-3); color: var(--critical); font: 400 12px/16px var(--font-sans); white-space: normal;
|
|
4319
|
+
}
|
|
4320
|
+
.alt-grid-unsaved { color: var(--ink-muted); }
|
|
4321
|
+
.alt-grid-cell[data-status="pending"] .alt-grid-plate { position: absolute; right: 6px; top: 50%; width: 8px; height: 8px; margin-top: -4px; border-radius: 2px; background: var(--surface-lifted); box-shadow: var(--elev-2); animation: alt-onset var(--dur-travel-1) ease-out var(--dur-grace) backwards; }
|
|
4322
|
+
.alt-grid-cell[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4323
|
+
.alt-grid-fail { position: absolute; right: 6px; top: 50%; translate: 0 -50%; display: inline-flex; color: var(--critical); }
|
|
4324
|
+
.alt-grid-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4325
|
+
[data-contrast="more"] .alt-grid-cell[data-editing] { box-shadow: inset 0 2px 0 0 var(--ink), inset 0 0 0 2px var(--accent); }
|
|
4326
|
+
@media (forced-colors: active) {
|
|
4327
|
+
.alt-grid-cell[data-active] { outline: 2px solid Highlight; outline-offset: -2px; } .alt-grid-cell[data-range] { forced-color-adjust: none; background: Highlight; color: HighlightText; }
|
|
4328
|
+
.alt-grid-cell[data-editing] { outline: 2px dashed CanvasText; } .alt-grid-cell[data-editing]:has([aria-invalid="true"]) { outline: 3px dashed CanvasText; }
|
|
4329
|
+
.alt-grid-cell[data-status="pending"] { outline: 1px dotted CanvasText; } .alt-grid-cell[data-status="failed"] { outline: 2px dashed CanvasText; }
|
|
4330
|
+
}
|
|
4331
|
+
|
|
4332
|
+
/* ── ItemGroup: a list changed in the list itself. Slats are the record, flush on the ground with line dividers, as
|
|
4333
|
+
Table rows. Editing lifts one slat at a time into its EditRegion (a static raised card) while its neighbours stay
|
|
4334
|
+
flush and readable. The add slat is an opening: a dashed well that deepens to a trench under the pointer. A slat
|
|
4335
|
+
picked up to reorder floats at level 3, tilted, over a slot that opens as a well. A recoverable removal leaves its
|
|
4336
|
+
RemovalReceipt in the slat's place, then glides the gap shut; a confirmed one asks in a stationary Dialog, then
|
|
4337
|
+
glides the slat shut. ── */
|
|
4338
|
+
.alt-item-group { position: relative; display: grid; gap: var(--space-2); min-width: 0; color: var(--ink); container-type: inline-size; }
|
|
4339
|
+
.alt-item-list { margin: 0; padding: 0; list-style: none; outline: none; }
|
|
4340
|
+
.alt-item-slat { position: relative; display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-3) 0; border-bottom: 1px solid var(--line); border-radius: 0; transition: transform var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1); }
|
|
4341
|
+
.alt-item-slat[data-editing] { border-bottom-color: transparent; }
|
|
4342
|
+
.alt-item-body { flex: 1; min-width: 0; }
|
|
4343
|
+
.alt-item-slat[data-carried] { z-index: 2; padding-inline: var(--space-2); border-radius: var(--radius-md); background: var(--surface-floating); box-shadow: var(--elev-3); rotate: calc(-0.5deg * var(--alt-travel)); border-bottom-color: transparent; }
|
|
4344
|
+
.alt-item-slat[data-status="pending"] { animation: alt-item-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
4345
|
+
@keyframes alt-item-pending { to { background: var(--surface-lifted); box-shadow: var(--elev-2); } }
|
|
4346
|
+
.alt-item-slot { position: absolute; left: 0; right: 0; top: -3px; height: 6px; border-radius: var(--radius-full); background: var(--surface-well); box-shadow: var(--well); }
|
|
4347
|
+
.alt-item-grip { display: inline-grid; place-items: center; flex: none; width: 28px; height: 28px; margin-top: 2px; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-muted); cursor: grab; }
|
|
4348
|
+
@media (hover: hover) { .alt-item-grip:hover { background: var(--surface-raised); box-shadow: var(--elev-1); } }
|
|
4349
|
+
.alt-item-grip[aria-pressed="true"] { cursor: grabbing; color: var(--ink); }
|
|
4350
|
+
.alt-item-grip:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4351
|
+
.alt-item { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
4352
|
+
.alt-item-media { display: inline-grid; place-items: center; flex: none; width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--surface-well); color: var(--ink-muted); }
|
|
4353
|
+
.alt-item-media[data-variant="avatar"] { background: none; }
|
|
4354
|
+
.alt-item-content { flex: 1; min-width: 0; }
|
|
4355
|
+
.alt-item-title { font: 500 14.5px/21px var(--font-sans); overflow-wrap: anywhere; }
|
|
4356
|
+
.alt-item-description { margin: 2px 0 0; color: var(--ink-muted); font: 400 13px/18px var(--font-sans); overflow-wrap: anywhere; }
|
|
4357
|
+
.alt-item-actions { display: flex; flex-wrap: wrap; gap: var(--space-1); flex: none; }
|
|
4358
|
+
.alt-item-description[data-truncate] { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--lines, 1); line-clamp: var(--lines, 1); overflow: hidden; }
|
|
4359
|
+
/* Narrow (#267): under 480px of the group's width, actions take their own row, lined up with the content (after the
|
|
4360
|
+
media), so a row's title keeps the width; narrow="inline" keeps them beside it. */
|
|
4361
|
+
@container (max-width: 480px) {
|
|
4362
|
+
.alt-item:has(> .alt-item-actions[data-narrow="below"]) { flex-wrap: wrap; }
|
|
4363
|
+
.alt-item-actions[data-narrow="below"] { flex-basis: 100%; }
|
|
4364
|
+
.alt-item:has(> .alt-item-media) > .alt-item-actions[data-narrow="below"] { padding-left: calc(36px + var(--space-3)); }
|
|
4365
|
+
}
|
|
4366
|
+
.alt-item-separator { margin: 0; border: 0; border-top: 1px solid var(--line); }
|
|
4367
|
+
.alt-item-add {
|
|
4368
|
+
display: inline-flex; align-items: center; gap: var(--space-2); width: 100%; min-height: 44px; padding: 0 var(--space-3); border: 1px dashed var(--line-control); border-radius: var(--radius-md);
|
|
4369
|
+
background: var(--surface-well); box-shadow: var(--well); color: var(--ink-muted); font: 500 14px/20px var(--font-sans); cursor: pointer;
|
|
4370
|
+
transition: background-color var(--dur-travel-1) var(--ease-sink), box-shadow var(--dur-travel-1) var(--ease-sink), color var(--dur-travel-1);
|
|
4371
|
+
}
|
|
4372
|
+
.alt-item-add:focus-visible { background: var(--surface-trench); box-shadow: var(--trench); color: var(--ink); }
|
|
4373
|
+
@media (hover: hover) { .alt-item-add:hover { background: var(--surface-trench); box-shadow: var(--trench); color: var(--ink); } }
|
|
4374
|
+
.alt-item-add:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
4375
|
+
.alt-item-plus { font: 500 17px/1 var(--font-sans); }
|
|
4376
|
+
.alt-item-new { animation: alt-emerge var(--dur-travel-2) var(--ease-rise); }
|
|
4377
|
+
.alt-item-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
4378
|
+
[data-contrast="more"] .alt-item-slat[data-carried] { box-shadow: var(--elev-3), 0 3px 0 0 var(--ink); }
|
|
4379
|
+
@media (forced-colors: active) {
|
|
4380
|
+
.alt-item-add, .alt-item-slot { border: 1px dashed CanvasText; } .alt-item-slat[data-carried] { border: 2px solid CanvasText; }
|
|
4381
|
+
.alt-item-slat[data-status="pending"] { outline: 1px dotted CanvasText; }
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4384
|
+
/* ── Board, edited in place. A column's add slot is an opening: a dashed well that deepens to a trench under the
|
|
4385
|
+
pointer. Activating it rises into a new card (static, level 1) holding a title field; the next slot is ready after
|
|
4386
|
+
each add. Renaming turns a card's title into a well; a card being edited can't be picked up. A pending card hovers
|
|
4387
|
+
after dur-grace; a failed one drops to a trench with its title kept and Retry. A fuller editor is an EditRegion in
|
|
4388
|
+
the card's place. ── */
|
|
4389
|
+
.alt-board-add-wrap { margin-top: var(--space-2); }
|
|
4390
|
+
.alt-board-add {
|
|
4391
|
+
display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: 40px; padding: 0 var(--space-3); border: 1px dashed var(--line-control); border-radius: var(--radius-md);
|
|
4392
|
+
background: var(--surface-well); box-shadow: var(--well); color: var(--ink-muted); font: 500 13.5px/18px var(--font-sans); cursor: pointer;
|
|
4393
|
+
transition: background-color var(--dur-travel-1) var(--ease-sink), box-shadow var(--dur-travel-1) var(--ease-sink), color var(--dur-travel-1);
|
|
4394
|
+
}
|
|
4395
|
+
.alt-board-add:focus-visible { background: var(--surface-trench); box-shadow: var(--trench); color: var(--ink); }
|
|
4396
|
+
@media (hover: hover) { .alt-board-add:hover { background: var(--surface-trench); box-shadow: var(--trench); color: var(--ink); } }
|
|
4397
|
+
.alt-board-add:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
4398
|
+
.alt-board-add > span { font: 500 16px/1 var(--font-sans); }
|
|
4399
|
+
.alt-board-new { display: grid; gap: var(--space-1); cursor: default; animation: alt-emerge var(--dur-travel-2) var(--ease-rise); }
|
|
4400
|
+
.alt-board-new[data-status="pending"], .alt-board-rename[data-status="pending"] { animation: alt-board-pending var(--dur-travel-1) var(--ease-rise) var(--dur-grace) both; }
|
|
4401
|
+
@keyframes alt-board-pending { to { box-shadow: var(--elev-2); transform: translateY(calc(-2px * var(--alt-travel))); } }
|
|
4402
|
+
.alt-board-new[data-status="failed"] { background: var(--critical-soft); box-shadow: var(--trench), inset 0 0 0 1px var(--critical); }
|
|
4403
|
+
.alt-board-add-hint { color: var(--ink-muted); font: 400 12px/16px var(--font-sans); }
|
|
4404
|
+
.alt-board-add-error { display: inline-flex; align-items: center; gap: 4px; color: var(--critical); font: 400 12px/16px var(--font-sans); }
|
|
4405
|
+
.alt-board-rename { display: grid; gap: var(--space-1); }
|
|
4406
|
+
.alt-board-card[data-editing] { cursor: default; }
|
|
4407
|
+
.alt-board-card-tools { position: absolute; top: 4px; right: 4px; display: inline-flex; gap: 2px; opacity: 0; transition: opacity var(--dur-travel-1); }
|
|
4408
|
+
.alt-board-card:focus-within .alt-board-card-tools, .alt-board-card:focus-visible .alt-board-card-tools { opacity: 1; }
|
|
4409
|
+
@media (hover: hover) { .alt-board-card:hover .alt-board-card-tools { opacity: 1; } }
|
|
4410
|
+
/* Without hover the tools are always there, or a finger could never find them (#254). */
|
|
4411
|
+
@media (hover: none) { .alt-board-card-tools { opacity: 1; } }
|
|
4412
|
+
.alt-board-card-editor { min-width: 0; }
|
|
4413
|
+
@media (forced-colors: active) { .alt-board-add { border: 1px dashed CanvasText; } .alt-board-new[data-status="pending"] { outline: 1px dotted CanvasText; } .alt-board-new[data-status="failed"] { outline: 2px dashed CanvasText; } }
|
|
4414
|
+
|
|
4415
|
+
/* ── DataTable, expanded and edited in its row. Read-only detail is a reveal of the record, so it stays on the ground:
|
|
4416
|
+
a full-width region under the row, inset by space-4, with a line rule, and the disclosure chevron turns. Editing is
|
|
4417
|
+
live, so it rises: the row and its region become one raised slab (elev-1, drawn by the first cell as for selected
|
|
4418
|
+
rows), holding a static EditRegion; the cells above keep the record identified. Sorting and reordering wait. ── */
|
|
4419
|
+
.alt-td-expand, .alt-th-expand { width: 40px; padding-inline: 6px; }
|
|
4420
|
+
/* Density: compact rows keep every target at least 24px tall (WCAG 2.5.8). Showing, hiding or reordering a column is a
|
|
4421
|
+
change of layout, not of the record, so the table doesn't animate it. */
|
|
4422
|
+
.alt-table[data-density="compact"] .alt-td { padding-block: 4px; }
|
|
4423
|
+
.alt-table[data-density="compact"] .alt-th { padding-block: 6px; }
|
|
4424
|
+
.alt-table-hidden { color: var(--ink-muted); font-weight: 400; }
|
|
4425
|
+
/* Paging: the footer holds Pagination (or "Load more") under a line. While the next page loads the current rows stay on
|
|
4426
|
+
the ground at half strength, as a refetching chart does, and the page thumb is held pressed. */
|
|
4427
|
+
.alt-table-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--line); margin-top: -1px; }
|
|
4428
|
+
.alt-table[data-refetching] tbody { opacity: 0.5; transition: opacity var(--dur-travel-2) var(--ease-sink); }
|
|
4429
|
+
/* Selecting past the page: once a page is selected, a flush accent-soft row offers every matching record; a selection of
|
|
4430
|
+
"all matching" is said in words. With actionsPlacement floating (or auto, once the toolbar scrolls away), the count and
|
|
4431
|
+
actions dock at the bottom edge as a bar at level 3: a selection is a set of records in your hand. */
|
|
4432
|
+
/* Saved views: folder Tabs whose active tab is one raised sheet with the filters and table beneath it. A view you've
|
|
4433
|
+
changed is a draft: a dot on its tab (and "modified" in its name), and Save view, Save as new view and Reset beside
|
|
4434
|
+
the tabs, as a SaveBar does for a page. */
|
|
4435
|
+
.alt-views { position: relative; }
|
|
4436
|
+
.alt-views-actions { position: absolute; top: 0; right: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); height: 38px; align-items: center; }
|
|
4437
|
+
.alt-views-dot { display: inline-block; width: 6px; height: 6px; margin-left: 6px; border-radius: var(--radius-full); background: var(--accent); vertical-align: 2px; }
|
|
4438
|
+
.alt-views-sheet { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
4439
|
+
.alt-views-new { display: flex; flex-direction: column; gap: var(--space-3); width: 240px; max-width: 100%; }
|
|
4440
|
+
.alt-views-new .alt-btn { align-self: flex-end; }
|
|
4441
|
+
@media (max-width: 640px) { .alt-views-actions { position: static; height: auto; margin-bottom: var(--space-2); justify-content: flex-start; } }
|
|
4442
|
+
@media (forced-colors: active) { .alt-views-dot { background: CanvasText; forced-color-adjust: none; } }
|
|
4443
|
+
.alt-table-allrow > .alt-td { padding: var(--space-2) var(--space-3); background: var(--accent-soft); text-align: center; font: 400 13px/18px var(--font-sans); color: var(--ink); }
|
|
4444
|
+
.alt-table-allrow .alt-link { padding: 0; border: 0; background: none; font: inherit; font-weight: 600; }
|
|
4445
|
+
.alt-table-floatbar {
|
|
4446
|
+
position: sticky; bottom: var(--space-3); z-index: 6; display: flex; justify-content: center; margin-top: var(--space-3); pointer-events: none;
|
|
4447
|
+
}
|
|
4448
|
+
.alt-table-floatbar > .alt-table-selection {
|
|
4449
|
+
pointer-events: auto; display: inline-flex; align-items: center; gap: var(--space-2); padding: 6px 8px 6px 10px; border-radius: var(--radius-lg);
|
|
4450
|
+
background: var(--surface-floating); box-shadow: var(--elev-3); font: 500 14px/20px var(--font-sans);
|
|
4451
|
+
animation: alt-emerge var(--dur-travel-3) var(--ease-rise); --alt-from-y: 12px;
|
|
4452
|
+
}
|
|
4453
|
+
@media (forced-colors: active) { .alt-table-floatbar > .alt-table-selection { border: 2px solid CanvasText; } }
|
|
4454
|
+
.alt-table-columns { display: flex; flex-direction: column; gap: var(--space-2); width: 240px; max-width: 100%; }
|
|
4455
|
+
.alt-table-columns-title { margin: 0; font: 600 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
4456
|
+
.alt-table-columns-title + * { margin-bottom: var(--space-2); }
|
|
4457
|
+
.alt-table-columns .alt-sort-item { padding: 6px 8px; }
|
|
4458
|
+
.alt-td-edit, .alt-th-edit { width: 1%; white-space: nowrap; }
|
|
4459
|
+
.alt-table-disclose { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-muted); cursor: pointer; transition: background-color var(--dur-travel-1), box-shadow var(--dur-travel-1); }
|
|
4460
|
+
@media (hover: hover) { .alt-table-disclose:hover { background: var(--surface-raised); box-shadow: var(--elev-1); color: var(--ink); } }
|
|
4461
|
+
.alt-table-disclose:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4462
|
+
.alt-table-disclose svg { rotate: -90deg; transition: rotate var(--dur-travel-2) var(--ease-settle); }
|
|
4463
|
+
.alt-table-disclose[aria-expanded="true"] svg { rotate: 0deg; }
|
|
4464
|
+
[dir="rtl"] .alt-table-disclose:not([aria-expanded="true"]) svg { rotate: 90deg; }
|
|
4465
|
+
.alt-table-detail > .alt-td { padding: var(--space-3) var(--space-4) var(--space-4); white-space: normal; animation: alt-emerge var(--dur-travel-2) var(--ease-glide); --alt-from-y: -4px; }
|
|
4466
|
+
.alt-tr[data-expanded]:not([data-editing]) > .alt-td { border-bottom-color: transparent; }
|
|
4467
|
+
.alt-tr[data-editing] { --row-bg: var(--surface-raised); --row-sh: var(--elev-1); }
|
|
4468
|
+
.alt-tr[data-editing]:not(.alt-table-editrow) > .alt-td { border-bottom-color: transparent; }
|
|
4469
|
+
.alt-tr[data-editing]:not(.alt-table-editrow) > .alt-td:first-child::before { bottom: -2px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; clip-path: inset(-12px -12px 0 -12px); }
|
|
4470
|
+
.alt-table-editrow > .alt-td:first-child::before { border-top-left-radius: 0; border-top-right-radius: 0; clip-path: inset(0 -12px -12px -12px); }
|
|
4471
|
+
.alt-table-editrow > .alt-td { padding: var(--space-2) var(--space-4) var(--space-4); white-space: normal; }
|
|
4472
|
+
.alt-table-editrow .alt-edit-region-surface { margin: 0; border: 0; padding: 0; background: none; box-shadow: none; }
|
|
4473
|
+
/* ItemGroup swaps a slat for its editor, so the editor keeps to the list's column rather than reaching past it. */
|
|
4474
|
+
.alt-item-group .alt-edit-region-surface { margin-inline: 0; }
|
|
4475
|
+
@media (forced-colors: active) { .alt-tr[data-editing] > .alt-td { border-block: 1px solid CanvasText; } }
|
|
4476
|
+
|
|
4477
|
+
/* ── PropertyList: one record's properties. They're the record, on the ground. An editable value is one plate (the
|
|
4478
|
+
InlineEdit trigger) that lifts one level when pointed at, because clicking it opens its editor; read-only values
|
|
4479
|
+
never lift, so editability shows without a pencil per row. An unset value is a small opening: a dashed well, the
|
|
4480
|
+
empty field's treatment at plate size, so a half-filled record reads at a glance. Edit all lifts the list into one
|
|
4481
|
+
raised EditRegion. ── */
|
|
4482
|
+
.alt-property-list { position: relative; min-width: 0; color: var(--ink); container-type: inline-size; }
|
|
4483
|
+
.alt-property-tools { display: flex; justify-content: flex-end; margin-bottom: var(--space-1); }
|
|
4484
|
+
.alt-property-dl { display: grid; gap: var(--space-3); margin: 0; }
|
|
4485
|
+
.alt-property { display: grid; gap: 2px; min-width: 0; }
|
|
4486
|
+
.alt-property-list[data-layout="inline"] .alt-property-dl { gap: 0; }
|
|
4487
|
+
.alt-property-list[data-layout="inline"] .alt-property { grid-template-columns: minmax(8rem, 30%) 1fr; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
|
|
4488
|
+
.alt-property-label { color: var(--ink-muted); font: 500 12.5px/17px var(--font-sans); }
|
|
4489
|
+
.alt-property-value { min-width: 0; margin: 0; font: 400 14px/20px var(--font-sans); overflow-wrap: anywhere; }
|
|
4490
|
+
.alt-property-empty { color: var(--ink-muted); }
|
|
4491
|
+
.alt-property .alt-inline-edit-read { display: block; }
|
|
4492
|
+
/* Proposed changes: a field with a proposal is status that asks for action, so it's raised one level (as Confirmation),
|
|
4493
|
+
showing the current value struck beside the proposed one (DiffView's property change), with Reject and Accept. Nothing
|
|
4494
|
+
touches the record until it's accepted; accepting hovers as pending, then settles with the afterglow. The summary
|
|
4495
|
+
offers Accept all and Reject all. */
|
|
4496
|
+
.alt-property-proposals { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); padding: 6px 6px 6px var(--space-3); border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--elev-1); }
|
|
4497
|
+
.alt-property-proposals-text { display: inline-flex; align-items: center; gap: 6px; margin-right: auto; font: 500 13px/18px var(--font-sans); color: var(--ink); }
|
|
4498
|
+
.alt-property[data-proposed] { margin: 4px calc(-1 * var(--space-2)); padding: var(--space-2); border-radius: var(--radius-md); background: var(--surface-raised); background-image: var(--alt-sheen); box-shadow: var(--elev-1); }
|
|
4499
|
+
.alt-property-list[data-layout="inline"] .alt-property[data-proposed] { align-items: start; padding: var(--space-2); border-bottom-color: transparent; }
|
|
4500
|
+
.alt-property-list[data-layout="inline"] .alt-property[data-proposed] .alt-property-label { padding-top: 6px; }
|
|
4501
|
+
.alt-property-proposal { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 2px var(--space-2); }
|
|
4502
|
+
.alt-property-change { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
4503
|
+
.alt-property-proposal-by { font: 400 12.5px/17px var(--font-sans); color: var(--ink-muted); }
|
|
4504
|
+
.alt-property-proposal-error { display: inline-flex; align-items: center; gap: 4px; color: var(--critical); }
|
|
4505
|
+
.alt-property-proposal-by { grid-column: 1; }
|
|
4506
|
+
.alt-property-proposal-actions { display: inline-flex; gap: 4px; grid-column: 2; grid-row: 1 / span 2; }
|
|
4507
|
+
/* Narrow (#258): under the narrow breakpoint, an inline list stacks each label over its value, and a proposal stacks its
|
|
4508
|
+
change, who proposed it and its actions, so the values are never squeezed into a column a few letters wide. */
|
|
4509
|
+
@container (max-width: 480px) {
|
|
4510
|
+
.alt-property-list[data-layout="inline"] .alt-property { grid-template-columns: minmax(0, 1fr); gap: 2px; }
|
|
4511
|
+
.alt-property-list[data-layout="inline"] .alt-property[data-proposed] .alt-property-label { padding-top: 0; }
|
|
4512
|
+
.alt-property-proposal { grid-template-columns: minmax(0, 1fr); }
|
|
4513
|
+
.alt-property-proposal-actions { grid-column: 1; grid-row: auto; margin-top: var(--space-1); }
|
|
4514
|
+
}
|
|
4515
|
+
@media (forced-colors: active) { .alt-property[data-proposed], .alt-property-proposals { border: 1px solid CanvasText; } }
|
|
4516
|
+
.alt-inline-edit-plate {
|
|
4517
|
+
display: inline-flex; align-items: center; min-height: 30px; max-width: 100%; padding: 2px 12px; border: 1px solid transparent; border-radius: var(--radius-sm);
|
|
4518
|
+
background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
|
|
4519
|
+
transition: background-color var(--dur-travel-1) var(--ease-rise), box-shadow var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-1) var(--ease-rise);
|
|
4520
|
+
}
|
|
4521
|
+
@media (hover: hover) { .alt-inline-edit-plate:hover:not(:disabled) { background: var(--surface-raised); box-shadow: var(--elev-1); transform: translateY(calc(-1px * var(--alt-travel))); } }
|
|
4522
|
+
.alt-inline-edit-plate:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
|
|
4523
|
+
.alt-inline-edit-plate:disabled { cursor: default; }
|
|
4524
|
+
.alt-inline-edit-plate[data-empty] { margin-left: 13px; padding-inline: 8px; border: 1px dashed var(--line-control); background: var(--surface-well); box-shadow: var(--well); color: var(--ink-muted); font-size: 13px; }
|
|
4525
|
+
@media (hover: hover) { .alt-inline-edit-plate[data-empty]:hover:not(:disabled) { background: var(--surface-trench); box-shadow: var(--trench); transform: none; color: var(--ink); } }
|
|
4526
|
+
@media (hover: hover) { [data-contrast="more"] .alt-inline-edit-plate:hover:not(:disabled) { box-shadow: 0 2px 0 0 var(--ink); } }
|
|
4527
|
+
@media (forced-colors: active) { .alt-inline-edit-plate[data-empty] { border: 1px dashed CanvasText; } .alt-inline-edit-plate:hover { outline: 1px solid CanvasText; } }
|
|
4528
|
+
|
|
4529
|
+
/* Workflow feedback uses the existing invalid trench and readable ground signals. */
|
|
4530
|
+
.alt-error-summary { border: 2px solid var(--critical); border-radius: var(--radius-lg); background: var(--critical-soft); color: var(--ink); padding: var(--space-4); overflow-wrap: anywhere; }
|
|
4531
|
+
.alt-error-summary h2 { margin: 0 0 var(--space-2); font: 600 var(--text-heading)/1.3 var(--font-sans); }
|
|
4532
|
+
.alt-error-summary ul { display: grid; gap: var(--space-1); margin: 0; padding-inline-start: var(--space-5); }
|
|
4533
|
+
/* Each link is a 24px target (WCAG 2.2 SC 2.5.8) whatever line-height the host page sets. */
|
|
4534
|
+
.alt-error-summary a { display: inline-flex; align-items: center; min-block-size: 24px; color: var(--ink); text-decoration: underline; }
|
|
4535
|
+
.alt-error-summary:focus { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
|
|
4536
|
+
@media (forced-colors: active) { .alt-error-summary { border-color: CanvasText; } .alt-error-summary a { color: LinkText; } }
|
|
4537
|
+
|
|
4538
|
+
/* ── Field text on touch (#250): under a coarse pointer, text you type is at least 16px, the size below which iOS
|
|
4539
|
+
Safari zooms the page when a field is focused (and leaves it zoomed). max(16px, 1em) keeps a field that inherits
|
|
4540
|
+
larger text (an in-place title) at its own size. It's a platform minimum, so it wins over every field's own font
|
|
4541
|
+
(the only !important in the stylesheet's sizing). Labels stay text-label; only the text you type grows. Never
|
|
4542
|
+
maximum-scale=1 instead: that stops pinch zoom and fails WCAG 1.4.4. ── */
|
|
4543
|
+
@media (pointer: coarse) {
|
|
4544
|
+
:is([class*="alt-"] :is(input, textarea, select), :is(input, textarea, select)[class*="alt-"]):not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"], [type="hidden"]) {
|
|
4545
|
+
font-size: max(16px, 1em) !important;
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
/* ── Hit areas (#261): a small face keeps its size and its tier, and a transparent ::after centred on it reaches
|
|
4550
|
+
size-target-min, 24px (WCAG 2.5.8) or 44px under a coarse pointer, so a finger finds it. It has no colour, border
|
|
4551
|
+
or shadow, so forced colours and every elevation are untouched. Date segments sit side by side, so theirs stops at
|
|
4552
|
+
24px rather than covering a neighbour. ── */
|
|
4553
|
+
:is(.alt-tag-remove, .alt-toast-close, .alt-cite, .alt-ailabel, .alt-filter-operator, .alt-date-seg, .alt-numfield-step, .alt-table-sort, .alt-link, .alt-reltime, .alt-chart-pin-remove) { position: relative; }
|
|
4554
|
+
:is(.alt-numfield-step, .alt-slider-thumb, .alt-brush-grip, .alt-tag-remove, .alt-toast-close, .alt-cite, .alt-ailabel, button.alt-chart-note, .alt-alert-more, .alt-filter-operator, .alt-table-sort, .alt-chart-pin-remove)::after {
|
|
4555
|
+
content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, var(--size-target-min)); height: max(100%, var(--size-target-min)); transform: translate(-50%, -50%);
|
|
4556
|
+
}
|
|
4557
|
+
.alt-numfield .alt-numfield-step::after { width: 100%; }
|
|
4558
|
+
/* Links and relative times sit in rows of text: they reach 24px tall, never wider, so neighbours stay apart. */
|
|
4559
|
+
:is(.alt-link, .alt-reltime):not(:is(p, li, dd, td) > *)::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: max(100%, 24px); transform: translateY(-50%); }
|
|
4560
|
+
.alt-date-seg::after { content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, 24px); height: max(100%, 24px); transform: translate(-50%, -50%); }
|
|
4561
|
+
|
|
4562
|
+
/* ── A menu as a sheet (#249): on a narrow touch screen DropdownMenu and Select open at the bottom edge, where the thumb
|
|
4563
|
+
is, rather than wherever the trigger happened to be. It's still the menu, at level 3 (surface-floating, elev-3):
|
|
4564
|
+
changing edges doesn't climb a tier. It clears the safe areas, holds at most 70% of the visible height, has a head
|
|
4565
|
+
with its title and Close, rows at size-row-md, and dims the page at half the scrim's strength. It rises from the
|
|
4566
|
+
edge over dur-travel-3 on ease-rise, as a Sheet does, and fades under reduced motion. ── */
|
|
4567
|
+
.alt-pop[data-presentation="sheet"] {
|
|
4568
|
+
position: fixed; top: auto; left: calc(var(--space-2) + env(safe-area-inset-left, 0px)); right: calc(var(--space-2) + env(safe-area-inset-right, 0px));
|
|
4569
|
+
bottom: calc(var(--space-2) + env(safe-area-inset-bottom, 0px)); translate: none; width: auto; min-width: 0; max-width: none;
|
|
4570
|
+
max-height: min(70vh, 560px); max-height: min(calc(var(--alt-vvh, 100dvh) * 0.7), 560px); overflow-y: auto; overscroll-behavior: contain;
|
|
4571
|
+
padding: 0 var(--space-1) var(--space-1); transform-origin: 50% 100%;
|
|
4572
|
+
box-shadow: var(--elev-3), 0 0 0 100vmax color-mix(in oklab, var(--scrim) 50%, transparent);
|
|
1703
4573
|
}
|
|
4574
|
+
.alt-pop[data-presentation="sheet"][data-state="closed"] { transform: translateY(calc(24px * var(--alt-travel))); }
|
|
4575
|
+
.alt-menu-sheet .alt-menu { position: relative; min-width: 0; padding: 0; }
|
|
4576
|
+
.alt-menu-sheet :is(.alt-menu-item, .alt-select-option) { min-height: var(--size-row-md); }
|
|
4577
|
+
.alt-menu-sheet-head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
|
|
4578
|
+
margin: 0 calc(-1 * var(--space-1)); padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); background: var(--surface-floating); }
|
|
4579
|
+
.alt-menu-sheet-title { min-width: 0; font: 600 15px/22px var(--font-sans); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4580
|
+
@media (forced-colors: active) { .alt-pop[data-presentation="sheet"] { border: 2px solid CanvasText; box-shadow: none; } }
|