@augurworks/augur 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CANVAS.md +858 -0
- package/INSTALL.md +603 -0
- package/LICENSE +27 -0
- package/README.md +150 -0
- package/TRADEMARKS.md +72 -0
- package/agents/README.md +65 -0
- package/agents/canon-extract.md +117 -0
- package/agents/canon.md +151 -0
- package/agents/canvas.md +88 -0
- package/agents/currency.md +74 -0
- package/agents/drafts.md +92 -0
- package/agents/identity.md +148 -0
- package/agents/prototype-contract.md +69 -0
- package/agents/publishing.md +324 -0
- package/agents/review-feedback.md +76 -0
- package/agents/space-json.md +132 -0
- package/agents/ui-skill.md +101 -0
- package/agents/working-marks.md +86 -0
- package/brand/augur-eye.svg +3 -0
- package/brand/augur-icon.svg +41 -0
- package/brand/augur-mark.svg +5 -0
- package/brand/lentianova-bulky.otf +0 -0
- package/brand/lentianova-median.otf +0 -0
- package/brand/lentianova.woff2 +0 -0
- package/build.js +8409 -0
- package/changelog.md +133 -0
- package/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/package.json +57 -0
- package/pitis/PETS.md +121 -0
- package/pitis/README.md +117 -0
- package/pitis/index.html +266 -0
- package/pitis/piti.build.js +81 -0
- package/pitis/piti.js +929 -0
- package/pitis/roast-agent.md +185 -0
- package/realtime/src/index.js +119 -0
- package/realtime/wrangler.example.toml +44 -0
- package/scripts/adopt.mjs +106 -0
- package/scripts/board-snapshot.mjs +197 -0
- package/scripts/build-seed-pack.mjs +33 -0
- package/scripts/bundle-rekey.mjs +112 -0
- package/scripts/bundle-tenancy-rehearsal.mjs +781 -0
- package/scripts/canon-extract.mjs +578 -0
- package/scripts/canon.mjs +599 -0
- package/scripts/canvas-screen.mjs +333 -0
- package/scripts/check-local.mjs +136 -0
- package/scripts/clawd-art.mjs +75 -0
- package/scripts/clawd-canvas.mjs +741 -0
- package/scripts/cli.mjs +100 -0
- package/scripts/clone.mjs +422 -0
- package/scripts/close.mjs +30 -0
- package/scripts/connect.mjs +103 -0
- package/scripts/deploy.mjs +157 -0
- package/scripts/dev.mjs +54 -0
- package/scripts/doc-lint.mjs +143 -0
- package/scripts/export.mjs +300 -0
- package/scripts/fork.mjs +64 -0
- package/scripts/freeze.mjs +74 -0
- package/scripts/frontdoor-parity.mjs +180 -0
- package/scripts/hook.mjs +67 -0
- package/scripts/identity-rekey.mjs +112 -0
- package/scripts/init.mjs +122 -0
- package/scripts/land.mjs +40 -0
- package/scripts/lib/adapters.mjs +156 -0
- package/scripts/lib/board-snapshot.mjs +511 -0
- package/scripts/lib/draft.mjs +385 -0
- package/scripts/lib/graduate.mjs +276 -0
- package/scripts/lib/instance.mjs +74 -0
- package/scripts/lib/marks.mjs +107 -0
- package/scripts/lib/materialize.mjs +138 -0
- package/scripts/lib/merge3.mjs +124 -0
- package/scripts/lib/offline-posture.mjs +120 -0
- package/scripts/lib/offline-respawn.mjs +18 -0
- package/scripts/lib/offline-wrangler.mjs +78 -0
- package/scripts/lib/publish-compose.mjs +12 -0
- package/scripts/lib/publish-conflict.mjs +78 -0
- package/scripts/lib/publish-evidence.mjs +144 -0
- package/scripts/lib/refine-compare.mjs +140 -0
- package/scripts/lib/refine-ledger.mjs +150 -0
- package/scripts/lib/refine-png.mjs +149 -0
- package/scripts/lib/seed-pack-build.mjs +141 -0
- package/scripts/lib/state-compare.mjs +68 -0
- package/scripts/lib/store.mjs +122 -0
- package/scripts/login.mjs +84 -0
- package/scripts/mark.mjs +112 -0
- package/scripts/migrate-board-keys.mjs +164 -0
- package/scripts/migrate.mjs +265 -0
- package/scripts/no-foreign-vocabulary.mjs +216 -0
- package/scripts/no-product-names.mjs +157 -0
- package/scripts/no-tenant-globals.mjs +1134 -0
- package/scripts/offline.mjs +247 -0
- package/scripts/og.mjs +170 -0
- package/scripts/one-tenant-resolver.mjs +146 -0
- package/scripts/open.mjs +63 -0
- package/scripts/publish.mjs +1041 -0
- package/scripts/read.mjs +31 -0
- package/scripts/refine.mjs +455 -0
- package/scripts/release-drift.mjs +138 -0
- package/scripts/restore.mjs +322 -0
- package/scripts/review.mjs +125 -0
- package/scripts/save.mjs +21 -0
- package/scripts/shell-lint.mjs +189 -0
- package/scripts/ship.mjs +460 -0
- package/scripts/shoot.mjs +264 -0
- package/scripts/smoke-invite.mjs +113 -0
- package/scripts/state-inventory.mjs +223 -0
- package/scripts/status.mjs +177 -0
- package/scripts/sync.mjs +27 -0
- package/scripts/tenant-do-rehearsal.mjs +1314 -0
- package/scripts/ui-copy-lint.mjs +202 -0
- package/scripts/watch.mjs +32 -0
- package/scripts/wrangler-preflight.mjs +246 -0
- package/seed/CANON.md +43 -0
- package/seed/README.md +119 -0
- package/seed/prototype-status.json +9 -0
- package/seed/registry.json +88 -0
- package/seed/skills/starter-ui/SKILL.md +52 -0
- package/seed/skills/starter-ui/skill.json +11 -0
- package/seed/skills/starter-ui/starter-tokens.css +73 -0
- package/seed/skills/starter-ui/starter-ui.css +318 -0
- package/seed/skills/starter-ui/starter-ui.js +50 -0
- package/seed/space.json +5 -0
- package/seed/start-here/prototypes/connect-your-terminal/index.html +428 -0
- package/seed/start-here/prototypes/connect-your-terminal/preview.webp +0 -0
- package/seed/start-here/prototypes/sample-with-comments/index.html +172 -0
- package/seed/start-here/prototypes/sample-with-comments/preview.webp +0 -0
- package/seed/start-here/prototypes/set-up-your-design-system/index.html +353 -0
- package/seed/start-here/prototypes/set-up-your-design-system/preview.webp +0 -0
- package/seed/threads.json +52 -0
- package/seed/worked-examples/prototypes/field-readings/index.html +273 -0
- package/seed/worked-examples/prototypes/field-readings/preview.webp +0 -0
- package/seed/worked-examples/prototypes/slide-deck/index.html +214 -0
- package/seed/worked-examples/prototypes/slide-deck/preview.webp +0 -0
- package/seed/worked-examples/prototypes/specimen-viewer/index.html +203 -0
- package/seed/worked-examples/prototypes/specimen-viewer/preview.webp +0 -0
- package/src/_worker.js +12668 -0
- package/src/board-key.mjs +57 -0
- package/src/board-room.mjs +881 -0
- package/src/bundle-keys.mjs +138 -0
- package/src/canon/collect-in-browser.js +215 -0
- package/src/canon/emit.mjs +232 -0
- package/src/canon/observe.mjs +260 -0
- package/src/canon/schema.mjs +451 -0
- package/src/canvas/DSEG-LICENSE.txt +95 -0
- package/src/canvas/DSEG7Classic-Bold.woff2 +0 -0
- package/src/canvas/canvas.css +885 -0
- package/src/canvas/canvas.js +5780 -0
- package/src/canvas/capture.js +485 -0
- package/src/chrome/appchrome.mjs +568 -0
- package/src/currency.mjs +203 -0
- package/src/delete-confirmation.mjs +254 -0
- package/src/drafts/drafts.js +326 -0
- package/src/entry.js +58 -0
- package/src/galleries.mjs +400 -0
- package/src/health-cron.mjs +173 -0
- package/src/identity.json +1 -0
- package/src/kv-codec.mjs +135 -0
- package/src/kv-identity.mjs +259 -0
- package/src/mail.mjs +599 -0
- package/src/provenance.mjs +74 -0
- package/src/publish-compose.mjs +363 -0
- package/src/publish-fork.mjs +221 -0
- package/src/publish-units.mjs +44 -0
- package/src/purge.mjs +81 -0
- package/src/review/COMMENTING-UX.md +306 -0
- package/src/review/cat.png +0 -0
- package/src/review/comment-cursor.svg +7 -0
- package/src/review/comments.js +1738 -0
- package/src/room-ticket.mjs +96 -0
- package/src/roster-chip.mjs +32 -0
- package/src/seed-pack.mjs +261 -0
- package/src/state-inventory.mjs +249 -0
- package/src/sw-logic.mjs +48 -0
- package/src/tenant-cache.mjs +141 -0
- package/src/tenant-context.mjs +386 -0
- package/src/tenant-do.js +2940 -0
- package/src/tenant-host.mjs +144 -0
- package/src/tenant-quotas.mjs +140 -0
- package/src/unit-core.mjs +103 -0
- package/src/unit-object.mjs +338 -0
- package/templates/README.md +67 -0
- package/templates/shell/deploy.yml +145 -0
- package/templates/shell/engine-bump.yml +142 -0
- package/templates/shell/health.yml +233 -0
- package/templates/shell/kv-backup.yml +201 -0
- package/templates/shell/roster-update.yml +110 -0
- package/templates/shell/space-preflight.yml +35 -0
- package/templates/shell/space-rebake.yml +79 -0
- package/templates/shell/store-backup.yml +152 -0
- package/templates/shell/wrangler.example.toml +266 -0
- package/templates/space/publish.yml +91 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
/* Augur Canvas — hand-rolled infinite-canvas engine. Served from /__canvas/canvas.css.
|
|
2
|
+
A "canvas" is a prototype that mounts Augur's shared infinite-canvas engine; nodes live in a
|
|
3
|
+
single CSS-transformed "world" layer, the UI sits in a fixed layer above it. See CANVAS.md.
|
|
4
|
+
The chrome is a full whiteboard suite: warm paper background + dot grid, the bottom pill
|
|
5
|
+
toolbar with illustrated marker/sticky tools, and the draw/shapes/stamps sub-toolbars. */
|
|
6
|
+
|
|
7
|
+
/* Chrome accents. Two tokens because they do two different jobs, and one hue cannot
|
|
8
|
+
do both well: --gvc-accent FILLS (white icon on top, so it must be dark) and
|
|
9
|
+
--gvc-accent-ring OUTLINES (drawn on white sub-bars and on the near-black font
|
|
10
|
+
menu alike, so it must be light enough to read on both). Both come from Augur's
|
|
11
|
+
own mark — the aubergine and the dawn gold in brand/augur-mark.svg.
|
|
12
|
+
Distinct from #2563eb on purpose: blue means "your selection out on the canvas",
|
|
13
|
+
these mean "the tool you are holding". Retune here, not at the call sites. */
|
|
14
|
+
:root {
|
|
15
|
+
--gvc-accent: #2c2150;
|
|
16
|
+
--gvc-accent-ring: #e2a14e;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
html, body { margin: 0; height: 100%; overflow: hidden; background: #f6f6f6; }
|
|
20
|
+
|
|
21
|
+
#gvc-root {
|
|
22
|
+
/* ⚠️ clip, NEVER hidden. `overflow: hidden` clips the same but leaves a SCROLL CONTAINER,
|
|
23
|
+
and the world's nodes give it a real scroll range (they extend far past the viewport in
|
|
24
|
+
both axes). Nothing user-facing can scroll an overflow:hidden box — but the BROWSER can,
|
|
25
|
+
and does: focus() on a contenteditable outside the viewport (enterEdit on a node at the
|
|
26
|
+
screen edge, a tile rename, a table cell) makes Chrome scroll it into view. #gvc-world is
|
|
27
|
+
absolutely positioned INSIDE this box so it goes with the scroll, while #gvc-ui is fixed
|
|
28
|
+
and does not — and every coordinate transform assumes the world's origin is the viewport's.
|
|
29
|
+
One stray focus therefore offset the whole board from the pointer for the rest of the
|
|
30
|
+
session: strokes landed hundreds of px to the left of the cursor, placed nodes missed the
|
|
31
|
+
click, and a selected node's toolbar sat far away from it. `overflow: clip` is not a scroll
|
|
32
|
+
container at all, so there is no scroll to leak. (`touch-action: none` never covered this —
|
|
33
|
+
the scroll came from the browser, not from a gesture.) */
|
|
34
|
+
position: fixed; inset: 0; overflow: clip; touch-action: none;
|
|
35
|
+
font: 14px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
36
|
+
color: #1e1e1e; user-select: none; -webkit-user-select: none;
|
|
37
|
+
/* Paper only — the dot grid moved to #gvc-grid (its own layer, transform-panned);
|
|
38
|
+
gridSpec()/applyTransform paint the dots there. */
|
|
39
|
+
background-color: #f6f6f6;
|
|
40
|
+
}
|
|
41
|
+
/* The dot grid lives on its OWN compositor layer, oversized by more than the largest
|
|
42
|
+
grid step (96px), and pans by transform only (see applyTransform) — the pattern
|
|
43
|
+
repeats, so a mod-step translate looks identical to the real camera translate and a
|
|
44
|
+
pan never repaints the viewport. Paint properties change only on zoom. */
|
|
45
|
+
#gvc-grid { position: absolute; inset: -128px; pointer-events: none; will-change: transform; }
|
|
46
|
+
#gvc-root.panning { cursor: grabbing; }
|
|
47
|
+
#gvc-root.hand { cursor: grab; }
|
|
48
|
+
#gvc-root.crosshair { cursor: crosshair; }
|
|
49
|
+
/* Option held = dragging a node duplicates it. Nodes set their own `cursor: grab`, so this has
|
|
50
|
+
to reach them specifically; the hand tool still wins (it's a pan, not a drag). */
|
|
51
|
+
#gvc-root.altcopy:not(.hand) .gvc-node, #gvc-root.altcopy:not(.hand) .gvc-node * { cursor: copy; }
|
|
52
|
+
#gvc-root.dropping::after {
|
|
53
|
+
content: "Drop image to place it"; position: fixed; inset: 0; display: flex;
|
|
54
|
+
align-items: center; justify-content: center; font-size: 22px; color: #2563eb; font-weight: 600;
|
|
55
|
+
background: rgba(37, 99, 235, .06); border: 3px dashed #2563eb; box-sizing: border-box; pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#gvc-world { position: absolute; top: 0; left: 0; width: 0; height: 0; transform-origin: 0 0; will-change: transform; }
|
|
59
|
+
|
|
60
|
+
/* ---- nodes ---------------------------------------------------------------- */
|
|
61
|
+
.gvc-node { position: absolute; box-sizing: border-box; }
|
|
62
|
+
/* rotation: only the nodes that HAVE an angle carry the transform (--gvc-rot, set in place()).
|
|
63
|
+
Applying it unconditionally would turn every node into a containing block + stacking
|
|
64
|
+
context — a real cost on a big board, for nothing. */
|
|
65
|
+
.gvc-node.rot { transform: rotate(var(--gvc-rot, 0deg)); }
|
|
66
|
+
/* The selection is a HAIRLINE flush to the node, not a ring standing off it — the node
|
|
67
|
+
stays the loudest thing on the board and the chrome gets out of its way. */
|
|
68
|
+
/* Screen-constant selection ring: --gvc-inv (set per zoom in applyTransform)
|
|
69
|
+
cancels the world scale, so the ring reads ~2.5px on screen at every zoom —
|
|
70
|
+
visible from afar without swallowing the node up close. */
|
|
71
|
+
.gvc-node.sel { outline: calc(2.5px * var(--gvc-inv, 1)) solid #2563eb; outline-offset: 0; }
|
|
72
|
+
.gvc-node > .gvc-name {
|
|
73
|
+
position: absolute; left: 0; top: -19px; font-size: 11px; color: #6b7280;
|
|
74
|
+
max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text;
|
|
75
|
+
}
|
|
76
|
+
.gvc-node > .gvc-name:empty { display: none; }
|
|
77
|
+
|
|
78
|
+
/* the host must NOT clip (it would cut the corner resize handles, which straddle the edge) —
|
|
79
|
+
the inner wrap carries the padding + overflow clip instead.
|
|
80
|
+
Proportions were measured off a reference screenshot and normalised to the note's
|
|
81
|
+
width (so zoom and DPR cancel): padding 8.7% a side, author label ~5% and sitting ~10% up
|
|
82
|
+
from the bottom edge, and a radius of exactly 0 — a sticky's corners are SHARP. */
|
|
83
|
+
.gvc-sticky {
|
|
84
|
+
min-width: 96px; min-height: 96px; border-radius: 0;
|
|
85
|
+
background: #fce495; box-shadow: 0 4px 10px rgba(0, 0, 0, .14); font-size: 16px; line-height: 1.35;
|
|
86
|
+
cursor: grab;
|
|
87
|
+
}
|
|
88
|
+
/* padding-bottom is the author's lane — it keeps the text off the name. Its vertical total
|
|
89
|
+
is STICKY_PAD_V in canvas.js; the two must move together or the fit measures the wrong box. */
|
|
90
|
+
.gvc-sticky .gvc-stickyin { position: absolute; inset: 0; padding: 19px 19px 40px; overflow: hidden; border-radius: 0; }
|
|
91
|
+
.gvc-sticky .gvc-txt { outline: none; white-space: pre-wrap; word-break: break-word; min-height: 1em; }
|
|
92
|
+
/* rich text: one <div> per line (the line model in canvas.js), lists as real ul/ol runs.
|
|
93
|
+
No margins anywhere — the line box IS the spacing, so a list sits flush with plain lines. */
|
|
94
|
+
/* A node's text is never draggable CONTENT. Chromium treats a press on selected text as a
|
|
95
|
+
request to drag that text — it lifts a ghost image of the glyphs and the node underneath
|
|
96
|
+
never moves, which is exactly what "I can only drag this node from the empty space next to
|
|
97
|
+
the text" feels like. exitEdit clears the selection on the way out; this makes the gesture
|
|
98
|
+
impossible even while the text IS selected (the root's dragstart guard is the third layer). */
|
|
99
|
+
.gvc-txt { -webkit-user-drag: none; user-drag: none; }
|
|
100
|
+
.gvc-txt ul, .gvc-txt ol { margin: 0; padding-left: 1.3em; }
|
|
101
|
+
.gvc-txt li { margin: 0; }
|
|
102
|
+
.gvc-txt li::marker { font-size: .95em; }
|
|
103
|
+
.gvc-sticky .gvc-txt:empty::before { content: "Type anything"; color: rgba(0, 0, 0, .32); }
|
|
104
|
+
.gvc-sticky .gvc-author { position: absolute; left: 19px; bottom: 18px; font-size: 11px; color: rgba(0, 0, 0, .5); pointer-events: none; max-width: calc(100% - 38px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
105
|
+
.gvc-sticky.editing, .gvc-text.editing, .gvc-shape.editing { cursor: text; }
|
|
106
|
+
.gvc-sticky.editing .gvc-txt, .gvc-text.editing .gvc-txt, .gvc-shape.editing .gvc-txt { cursor: text; }
|
|
107
|
+
|
|
108
|
+
.gvc-text { padding: 4px 6px; font-size: 16px; color: #1e1e1e; cursor: grab; }
|
|
109
|
+
.gvc-text .gvc-txt { outline: none; white-space: pre-wrap; word-break: break-word; min-width: 40px; min-height: 1em; }
|
|
110
|
+
.gvc-text .gvc-txt:empty::before { content: "Text"; color: rgba(0, 0, 0, .3); }
|
|
111
|
+
|
|
112
|
+
/* no overflow:hidden on the host — it would clip the .gvc-name label that floats above the
|
|
113
|
+
node; the inner wrap clips instead (which is also what makes a crop possible) */
|
|
114
|
+
.gvc-image { cursor: grab; box-shadow: 0 3px 10px rgba(0, 0, 0, .18); border-radius: 4px; background: #fff; }
|
|
115
|
+
/* A cut-out image has no edges to cast a shadow and no paper to sit on — the card would show
|
|
116
|
+
straight through its transparent pixels. Set by node.alpha, or by the load-time probe. */
|
|
117
|
+
.gvc-image.alpha { background: transparent; box-shadow: none; }
|
|
118
|
+
.gvc-image .gvc-imgwrap { position: absolute; inset: 0; overflow: hidden; border-radius: 4px; }
|
|
119
|
+
.gvc-image img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
|
|
120
|
+
/* cropped: percent size/offset set inline from node.crop show just the crop window */
|
|
121
|
+
.gvc-image img.cropped { position: absolute; max-width: none; object-fit: fill; }
|
|
122
|
+
|
|
123
|
+
.gvc-tile { border-radius: 8px; box-shadow: 0 4px 14px rgba(0, 0, 0, .16); }
|
|
124
|
+
/* the frame title: floats ABOVE the tile, counter-scaled by JS (scaleTileChrome)
|
|
125
|
+
so it reads at a constant 12px at any zoom; max-width follows the tile's VISUAL width */
|
|
126
|
+
.gvc-tilename { position: absolute; bottom: 100%; left: 0; margin-bottom: 6px; transform-origin: left bottom; font: 600 12px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #4b5563; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text; outline: none; padding: 1px 2px; }
|
|
127
|
+
.gvc-tilename:hover { color: #111827; }
|
|
128
|
+
.gvc-tile .gvc-tilebody { position: absolute; inset: 0; background: #f4f5f7; border-radius: 8px; overflow: hidden; }
|
|
129
|
+
.gvc-tile .gvc-tilebody img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; pointer-events: none; }
|
|
130
|
+
/* width/height/transform are set inline by fitFrame() — a fixed desktop viewport scaled to the tile */
|
|
131
|
+
.gvc-tile .gvc-tilebody iframe { display: block; border: 0; background: #fff; transform-origin: top left; }
|
|
132
|
+
/* tiles are ALWAYS live but inert: the hit overlay makes the tile select/drag like any node
|
|
133
|
+
(grab = the drag affordance); enterInteract() hides it while you drive the prototype */
|
|
134
|
+
.gvc-tile .gvc-hit { position: absolute; inset: 0; cursor: grab; }
|
|
135
|
+
.gvc-tile.interacting { box-shadow: 0 0 0 2.5px #2563eb, 0 4px 14px rgba(0, 0, 0, .16); }
|
|
136
|
+
.gvc-tile .gvc-tilebody .ph { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 12px; text-align: center; color: #9ca3af; font-size: 12px; }
|
|
137
|
+
|
|
138
|
+
.gvc-arrow { cursor: grab; }
|
|
139
|
+
.gvc-arrow svg { display: block; overflow: visible; }
|
|
140
|
+
.gvc-arrow svg path { fill: none; stroke: #374151; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
141
|
+
|
|
142
|
+
/* freehand strokes: the host box is the stroke bbox; the svg hangs -pad outside it so round
|
|
143
|
+
caps never clip. Highlighter multiplies like a real marker over content. */
|
|
144
|
+
.gvc-draw { cursor: grab; }
|
|
145
|
+
.gvc-draw svg { position: absolute; overflow: visible; }
|
|
146
|
+
.gvc-draw svg path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
|
147
|
+
.gvc-draw.hl svg { mix-blend-mode: multiply; }
|
|
148
|
+
.gvc-drawing { position: absolute; left: 0; top: 0; pointer-events: none; }
|
|
149
|
+
.gvc-drawing svg { position: absolute; overflow: visible; }
|
|
150
|
+
|
|
151
|
+
/* shapes: geometry svg stretched to the node box, editable text centered on top */
|
|
152
|
+
.gvc-shape { cursor: grab; }
|
|
153
|
+
.gvc-shape svg { position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
154
|
+
/* flex-direction: column — a shape's text is a stack of line blocks now (rich text), and a
|
|
155
|
+
row would lay them out side by side */
|
|
156
|
+
.gvc-shape .gvc-txt { position: absolute; inset: 12%; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; font-size: 14px; outline: none; overflow: hidden; white-space: pre-wrap; word-break: break-word; }
|
|
157
|
+
|
|
158
|
+
/* sections: translucent background containers with a colored label chip */
|
|
159
|
+
.gvc-section { background: rgba(255, 255, 255, .62); border: 1px solid rgba(0, 0, 0, .08); border-radius: 12px; cursor: grab; }
|
|
160
|
+
/* locked: the background is inert (click through to work on top / nothing); the label chip
|
|
161
|
+
stays clickable so it's how you re-select the section to unlock it */
|
|
162
|
+
.gvc-section.locked { pointer-events: none; }
|
|
163
|
+
.gvc-section.locked > .gvc-seclabel { pointer-events: auto; }
|
|
164
|
+
.gvc-seclabel { position: absolute; bottom: calc(100% + 7px); left: 0; transform-origin: left bottom; display: flex; align-items: center; gap: 6px;
|
|
165
|
+
padding: 4px 9px 4px 8px; border-radius: 8px; background: #e9ecef; color: #4a4f57;
|
|
166
|
+
font: 600 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
167
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, .12); max-width: calc(100% - 20px); cursor: grab; }
|
|
168
|
+
.gvc-seclabel.colored { color: #fff; }
|
|
169
|
+
.gvc-seclabel .ic { width: 15px; height: 15px; flex: 0 0 auto; }
|
|
170
|
+
.gvc-seclabel .lk { width: 13px; height: 13px; flex: 0 0 auto; opacity: .92; }
|
|
171
|
+
.gvc-seclabel .gvc-name { position: static; outline: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text; color: inherit; }
|
|
172
|
+
|
|
173
|
+
/* tables: plain white cells, thin inner gridlines; when selected, blue +
|
|
174
|
+
strips (right = add column, bottom = add row) */
|
|
175
|
+
.gvc-table { display: grid; background: #fff; border: 1px solid #cfd1d6; border-radius: 2px; box-shadow: 0 2px 8px rgba(0, 0, 0, .06); cursor: grab; }
|
|
176
|
+
.gvc-cell { border-right: 1px solid #e3e3e6; border-bottom: 1px solid #e3e3e6; padding: 8px 10px; font-size: 13px; outline: none; overflow: hidden; min-width: 0; }
|
|
177
|
+
.gvc-addrow, .gvc-addcol {
|
|
178
|
+
position: absolute; display: flex; align-items: center; justify-content: center;
|
|
179
|
+
background: #c9dff9; color: #2f80ed; font-size: 22px; font-weight: 500;
|
|
180
|
+
border-radius: 8px; cursor: pointer; z-index: 2; transition: background .12s;
|
|
181
|
+
}
|
|
182
|
+
.gvc-addrow:hover, .gvc-addcol:hover { background: #b4d3f7; }
|
|
183
|
+
.gvc-addrow { left: 0; right: 0; bottom: -46px; height: 38px; }
|
|
184
|
+
.gvc-addcol { top: 0; bottom: 0; right: -46px; width: 38px; }
|
|
185
|
+
|
|
186
|
+
/* sticker stamps: SVG stickers carry their own white die-cut outline (paint-order:stroke);
|
|
187
|
+
the stacked white drop-shadows give legacy emoji stamps the same look */
|
|
188
|
+
/* One soft shadow, and nothing else. The white die-cut edge is PAINTED in the art
|
|
189
|
+
(paint-order:stroke), so it stays crisp at any zoom; the white drop-shadow glows
|
|
190
|
+
that used to sit here blurred that edge into a halo. */
|
|
191
|
+
.gvc-stamp { display: flex; align-items: center; justify-content: center; cursor: grab; line-height: 1; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .24)); }
|
|
192
|
+
svg.stk { display: block; overflow: visible; }
|
|
193
|
+
.gvc-stamp svg.stk { width: 100%; height: 100%; }
|
|
194
|
+
|
|
195
|
+
.gvc-handle { position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%; background: #fff; border: 2px solid #2563eb; cursor: crosshair; z-index: 2; }
|
|
196
|
+
/* resize handles: all FOUR corners. Each is centred exactly on its corner
|
|
197
|
+
(left/top % + a half-size negative margin) so the JS counter-scale — transform:
|
|
198
|
+
scale(1/zoom), origin centre — keeps it pinned there at a constant screen size. */
|
|
199
|
+
.gvc-resize { position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: #fff; border: 1.5px solid #2563eb; box-shadow: 0 1px 2px rgba(0, 0, 0, .18); z-index: 3; }
|
|
200
|
+
.gvc-resize.nw { left: 0; top: 0; cursor: nwse-resize; }
|
|
201
|
+
.gvc-resize.ne { left: 100%; top: 0; cursor: nesw-resize; }
|
|
202
|
+
.gvc-resize.sw { left: 0; top: 100%; cursor: nesw-resize; }
|
|
203
|
+
.gvc-resize.se { left: 100%; top: 100%; cursor: nwse-resize; }
|
|
204
|
+
/* rotate pads: a square hanging just OUTSIDE each corner (the corner
|
|
205
|
+
dot resizes, a hair beyond it rotates). Invisible, cursor-only. transform-origin is the
|
|
206
|
+
corner it hangs off, so the JS counter-scale keeps it pinned there at every zoom. Below
|
|
207
|
+
the corner dots (z-index 1) so the resize grab always wins the overlap. */
|
|
208
|
+
.gvc-rot { position: absolute; width: 22px; height: 22px; z-index: 1; }
|
|
209
|
+
.gvc-rot.nw { left: 0; top: 0; margin: -22px 0 0 -22px; transform-origin: 100% 100%; }
|
|
210
|
+
.gvc-rot.ne { left: 100%; top: 0; margin: -22px 0 0 0; transform-origin: 0 100%; }
|
|
211
|
+
.gvc-rot.sw { left: 0; top: 100%; margin: 0 0 0 -22px; transform-origin: 100% 0; }
|
|
212
|
+
.gvc-rot.se { left: 100%; top: 100%; margin: 0; transform-origin: 0 0; }
|
|
213
|
+
/* Rotate cursor: one curved arrow, black on a white keyline (so it reads on any
|
|
214
|
+
node), TURNED PER CORNER — 45° off the vertical toward the corner it hangs on, so it
|
|
215
|
+
always points the way that corner will travel. Four literal data URIs because a cursor
|
|
216
|
+
image cannot be transformed in CSS; they differ only in the `rotate(N 16 16)` wrapper.
|
|
217
|
+
Same glyph each time: a 140° arc over the top of a 32×32 box with a solid head at the
|
|
218
|
+
right end, hotspot at the centre. */
|
|
219
|
+
.gvc-rot.nw { cursor: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg transform='rotate(-45 16 16)'%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%23fff' stroke-width='5.4' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%23fff' stroke='%23fff' stroke-width='3.4' stroke-linejoin='round'/%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%231e1e1e' stroke-width='2.1' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%231e1e1e'/%3E%3C/g%3E%3C/svg%3E") 16 16, crosshair; }
|
|
220
|
+
.gvc-rot.ne { cursor: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg transform='rotate(45 16 16)'%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%23fff' stroke-width='5.4' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%23fff' stroke='%23fff' stroke-width='3.4' stroke-linejoin='round'/%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%231e1e1e' stroke-width='2.1' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%231e1e1e'/%3E%3C/g%3E%3C/svg%3E") 16 16, crosshair; }
|
|
221
|
+
.gvc-rot.se { cursor: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg transform='rotate(135 16 16)'%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%23fff' stroke-width='5.4' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%23fff' stroke='%23fff' stroke-width='3.4' stroke-linejoin='round'/%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%231e1e1e' stroke-width='2.1' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%231e1e1e'/%3E%3C/g%3E%3C/svg%3E") 16 16, crosshair; }
|
|
222
|
+
.gvc-rot.sw { cursor: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg transform='rotate(225 16 16)'%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%23fff' stroke-width='5.4' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%23fff' stroke='%23fff' stroke-width='3.4' stroke-linejoin='round'/%3E%3Cpath d='M8.5 13.3A8 8 0 0 1 23.5 13.3' fill='none' stroke='%231e1e1e' stroke-width='2.1' stroke-linecap='round'/%3E%3Cpath d='M24.9 17.1 26.4 12.2 20.7 14.3Z' fill='%231e1e1e'/%3E%3C/g%3E%3C/svg%3E") 16 16, crosshair; }
|
|
223
|
+
/* the live angle readout while a rotate drag is in flight */
|
|
224
|
+
#gvc-rotbadge {
|
|
225
|
+
position: fixed; z-index: 60; pointer-events: none;
|
|
226
|
+
padding: 3px 7px; border-radius: 5px; background: #2563eb; color: #fff;
|
|
227
|
+
font: 600 11px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
228
|
+
font-variant-numeric: tabular-nums; box-shadow: 0 2px 6px rgba(0, 0, 0, .2);
|
|
229
|
+
}
|
|
230
|
+
#gvc-rotbadge.hidden { display: none; }
|
|
231
|
+
|
|
232
|
+
/* edge handles: invisible strips straddling each border along its FULL length, so a side can
|
|
233
|
+
be grabbed anywhere — not only at a corner. 8px thick, centred on the border by the negative
|
|
234
|
+
margin; the JS counter-scales the thickness axis only (scaleY/scaleX), so they stay 8px on
|
|
235
|
+
screen at any zoom while still spanning the side. Below the corners, which keep z-index 3. */
|
|
236
|
+
.gvc-resize.edge { border: 0; border-radius: 0; background: transparent; box-shadow: none; z-index: 2; }
|
|
237
|
+
.gvc-resize.n, .gvc-resize.s { left: 0; width: 100%; height: 8px; margin: -4px 0 0 0; cursor: ns-resize; }
|
|
238
|
+
.gvc-resize.e, .gvc-resize.w { top: 0; height: 100%; width: 8px; margin: 0 0 0 -4px; cursor: ew-resize; }
|
|
239
|
+
.gvc-resize.n { top: 0; }
|
|
240
|
+
.gvc-resize.s { top: 100%; }
|
|
241
|
+
.gvc-resize.w { left: 0; }
|
|
242
|
+
.gvc-resize.e { left: 100%; }
|
|
243
|
+
|
|
244
|
+
/* ---- image crop mode (double-click an image; non-destructive) -------------
|
|
245
|
+
.gvc-crop = the full image ghosted at its world rect; .gvc-cropwin = the visible window at
|
|
246
|
+
full opacity (inner .clip clips its copy of the image; the handles hang on the window edges
|
|
247
|
+
OUTSIDE the clip so they never get cut). Solid blue flush-inside handles, like the ref. */
|
|
248
|
+
.gvc-crop { position: absolute; z-index: 5; cursor: grab; }
|
|
249
|
+
.gvc-crop img { display: block; pointer-events: none; }
|
|
250
|
+
.gvc-crop .ghost { width: 100%; height: 100%; opacity: .35; border-radius: 4px; }
|
|
251
|
+
.gvc-cropwin { position: absolute; cursor: move; box-shadow: 0 0 0 1.5px #2563eb, 0 3px 10px rgba(0, 0, 0, .18); }
|
|
252
|
+
.gvc-cropwin .clip { position: absolute; inset: 0; overflow: hidden; background: #fff; }
|
|
253
|
+
.gvc-cropwin .clip img { position: absolute; max-width: none; }
|
|
254
|
+
.gvc-croph { position: absolute; background: #2563eb; z-index: 2; }
|
|
255
|
+
.gvc-croph.corner { width: 13px; height: 13px; }
|
|
256
|
+
.gvc-croph.nw { left: 0; top: 0; cursor: nwse-resize; }
|
|
257
|
+
.gvc-croph.ne { right: 0; top: 0; cursor: nesw-resize; }
|
|
258
|
+
.gvc-croph.sw { left: 0; bottom: 0; cursor: nesw-resize; }
|
|
259
|
+
.gvc-croph.se { right: 0; bottom: 0; cursor: nwse-resize; }
|
|
260
|
+
.gvc-croph.n { top: 0; left: 50%; margin-left: -12px; width: 24px; height: 7px; cursor: ns-resize; }
|
|
261
|
+
.gvc-croph.s { bottom: 0; left: 50%; margin-left: -12px; width: 24px; height: 7px; cursor: ns-resize; }
|
|
262
|
+
.gvc-croph.w { left: 0; top: 50%; margin-top: -12px; width: 7px; height: 24px; cursor: ew-resize; }
|
|
263
|
+
.gvc-croph.e { right: 0; top: 50%; margin-top: -12px; width: 7px; height: 24px; cursor: ew-resize; }
|
|
264
|
+
|
|
265
|
+
/* alignment guides: screen-space 1px lines drawn while a drag/resize is snapped to a
|
|
266
|
+
neighbour's edge or centre (signal red) */
|
|
267
|
+
#gvc-guides { position: fixed; inset: 0; pointer-events: none; z-index: 55; }
|
|
268
|
+
.gvc-guide { position: absolute; background: #f24822; }
|
|
269
|
+
.gvc-guide.x { width: 1px; }
|
|
270
|
+
.gvc-guide.y { height: 1px; }
|
|
271
|
+
|
|
272
|
+
/* ---- fixed UI layer ------------------------------------------------------- */
|
|
273
|
+
#gvc-ui { position: fixed; inset: 0; pointer-events: none; z-index: 60; transition: opacity .16s ease; }
|
|
274
|
+
#gvc-ui.hidden { opacity: 0; pointer-events: none !important; }
|
|
275
|
+
#gvc-ui > * { pointer-events: auto; }
|
|
276
|
+
|
|
277
|
+
#gvc-topbar { position: fixed; top: 14px; left: 16px; display: flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #e9e8e6; border-radius: 12px; box-shadow: 0 4px 16px rgba(0, 0, 0, .10); padding: 6px 8px; }
|
|
278
|
+
#gvc-topbar .back { border: 0; background: 0; padding: 6px 9px; border-radius: 8px; cursor: pointer; color: #374151; font: inherit; display: flex; align-items: center; gap: 5px; }
|
|
279
|
+
#gvc-topbar .back:hover { background: #f3f4f6; }
|
|
280
|
+
#gvc-topbar .nm { font-weight: 600; padding: 6px 8px; border-radius: 8px; cursor: text; outline: none; min-width: 60px; max-width: 300px; white-space: nowrap; overflow: hidden; }
|
|
281
|
+
#gvc-topbar .nm:hover { background: #f9fafb; }
|
|
282
|
+
|
|
283
|
+
#gvc-zoom { position: fixed; bottom: 24px; left: 16px; display: flex; align-items: center; gap: 2px; background: #fff; border-radius: 12px; box-shadow: 0 1px 3px rgba(0, 0, 0, .08), 0 4px 16px rgba(0, 0, 0, .10); padding: 5px; }
|
|
284
|
+
#gvc-zoom button { border: 0; background: 0; width: 30px; height: 30px; border-radius: 8px; cursor: pointer; font-size: 16px; color: #1e1e1e; }
|
|
285
|
+
#gvc-zoom button:hover { background: #f3f2f0; }
|
|
286
|
+
#gvc-zoom .pct { font-size: 13px; color: #1e1e1e; min-width: 48px; text-align: center; cursor: pointer; }
|
|
287
|
+
|
|
288
|
+
/* ---- bottom toolbar — the main pill --------------------------------------- */
|
|
289
|
+
/* metrics measured off the reference shots: pill 47px, small icons ~1/3 of pill height —
|
|
290
|
+
the airiness comes from small icons in a generous pill, not from a short pill */
|
|
291
|
+
#gvc-toolbar {
|
|
292
|
+
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
|
|
293
|
+
display: flex; align-items: center; gap: 6px; height: 47px;
|
|
294
|
+
/* A stadium, not a soft rect, and the insert button below is squared rather than
|
|
295
|
+
round — the inverse of the reference silhouette this was measured against, so
|
|
296
|
+
the two do not read as the same product at a glance. Radius tracks the height. */
|
|
297
|
+
background: #fff; border-radius: 23.5px; padding: 0 9px;
|
|
298
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, .10), 0 12px 32px rgba(0, 0, 0, .14);
|
|
299
|
+
}
|
|
300
|
+
#gvc-toolbar .tool {
|
|
301
|
+
display: flex; align-items: center; justify-content: center; width: 32px; height: 32px;
|
|
302
|
+
border-radius: 8px; cursor: pointer; color: #1e1e1e; user-select: none; position: relative;
|
|
303
|
+
transition: background .12s, color .12s;
|
|
304
|
+
}
|
|
305
|
+
#gvc-toolbar .tool:hover { background: #f2f1ef; }
|
|
306
|
+
#gvc-toolbar .tool.on { background: var(--gvc-accent); color: #fff; }
|
|
307
|
+
#gvc-toolbar .tool svg { width: 21px; height: 21px; }
|
|
308
|
+
#gvc-toolbar .sep { width: 1px; align-self: stretch; background: #e5e4e2; margin: 0 4px; }
|
|
309
|
+
/* phone (.gvc-mobile, ≤640px at boot): navigation-first. Toolbar collapses to
|
|
310
|
+
select + hand + a chevron that expands the full set; topbar is the bare arrow
|
|
311
|
+
(no "Back" label, no board name) to leave room for the top-right module. */
|
|
312
|
+
#gvc-toolbar .morebtn { display: none; }
|
|
313
|
+
.gvc-mobile #gvc-toolbar .morebtn { display: flex; flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; background: #f2f1ef; }
|
|
314
|
+
.gvc-mobile #gvc-toolbar .morebtn svg { stroke-width: 2.2; }
|
|
315
|
+
.gvc-mobile #gvc-toolbar:not(.open) > *:not([data-t="cursor"]):not([data-t="hand"]):not(.morebtn) { display: none; }
|
|
316
|
+
/* expanded = ONE lateral row, edge to edge, horizontally scrollable (no wrap); the
|
|
317
|
+
chevron rides sticky at the right so closing never needs a scroll-to-the-end */
|
|
318
|
+
.gvc-mobile #gvc-toolbar.open { left: 10px; right: 10px; transform: none; justify-content: flex-start; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
|
|
319
|
+
.gvc-mobile #gvc-toolbar.open::-webkit-scrollbar { display: none; }
|
|
320
|
+
.gvc-mobile #gvc-toolbar.open > * { flex: 0 0 auto; }
|
|
321
|
+
.gvc-mobile #gvc-toolbar.open .morebtn { position: sticky; right: 0; box-shadow: -6px 0 8px #fff, 6px 0 0 #fff; }
|
|
322
|
+
.gvc-mobile #gvc-toolbar.open .morebtn svg { transform: rotate(180deg); }
|
|
323
|
+
.gvc-mobile #gvc-topbar .back .bl, .gvc-mobile #gvc-topbar .nm { display: none; }
|
|
324
|
+
|
|
325
|
+
/* the illustrated marker + sticky: full-height slots whose art fills the pill and lifts on
|
|
326
|
+
hover; when armed the whole slot tints grey */
|
|
327
|
+
#gvc-toolbar .tool.big { width: 40px; height: 47px; border-radius: 8px; }
|
|
328
|
+
#gvc-toolbar .tool.big:hover { background: transparent; }
|
|
329
|
+
#gvc-toolbar .tool.big.armed { background: #ececea; }
|
|
330
|
+
/* the arts hang BELOW the pill's bottom edge and get cut by it; hovering lifts them
|
|
331
|
+
back up, revealing the hidden part. The pen apex sits ~10px below the pill top at rest and
|
|
332
|
+
never breaches it — the hover-lift is the only reveal. */
|
|
333
|
+
#gvc-toolbar .tool.big .artclip { position: absolute; inset: 0; pointer-events: none; clip-path: inset(-70px -30px 0 -30px); }
|
|
334
|
+
#gvc-toolbar .tool.big .art { position: absolute; bottom: -7px; left: 50%; transform: translateX(-50%); transition: transform .18s cubic-bezier(.3, 1.4, .6, 1); pointer-events: none; }
|
|
335
|
+
#gvc-toolbar .tool.big:hover .art { transform: translateX(-50%) translateY(-7px); }
|
|
336
|
+
#gvc-toolbar .art.pen { height: 44px; width: 26px; }
|
|
337
|
+
#gvc-toolbar .art.sticky { height: 42px; width: 53px; bottom: -5px; }
|
|
338
|
+
#gvc-toolbar .tool[data-t="sticky"] { width: 54px; }
|
|
339
|
+
|
|
340
|
+
/* the shape cluster (big square + long curved arrow + circle, drawn as one illustrated cluster) */
|
|
341
|
+
#gvc-toolbar .tool.cluster { width: 50px; height: 47px; border-radius: 8px; }
|
|
342
|
+
#gvc-toolbar .tool.cluster svg { width: 40px; height: 33px; }
|
|
343
|
+
#gvc-toolbar .tool.cluster.armed { background: #ececea; }
|
|
344
|
+
|
|
345
|
+
/* the round grey insert button at the end — its circle is exactly the size of the other
|
|
346
|
+
icons' tap targets */
|
|
347
|
+
#gvc-toolbar .tool.plus { width: 32px; height: 32px; border-radius: 9px; background: #f0efed; margin-left: 3px; }
|
|
348
|
+
#gvc-toolbar .tool.plus:hover { background: #e7e6e4; }
|
|
349
|
+
#gvc-toolbar .tool.plus svg { width: 17px; height: 17px; }
|
|
350
|
+
|
|
351
|
+
/* tooltips: dark with a caret notch, name + grey shortcut key, shown after a beat */
|
|
352
|
+
#gvc-toolbar .tool .tip, .gvc-subbar .sbtn .tip, #gvc-moreshapes .sbtn .tip {
|
|
353
|
+
position: absolute; bottom: calc(100% + 11px); left: 50%; transform: translateX(-50%);
|
|
354
|
+
background: #1e1e1e; color: #fff; font-size: 13px; font-weight: 400; white-space: nowrap;
|
|
355
|
+
padding: 6px 10px; border-radius: 7px; opacity: 0; pointer-events: none; transition: opacity .1s;
|
|
356
|
+
}
|
|
357
|
+
#gvc-toolbar .tool .tip::after, .gvc-subbar .sbtn .tip::after, #gvc-moreshapes .sbtn .tip::after {
|
|
358
|
+
content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
|
|
359
|
+
border: 5px solid transparent; border-top-color: #1e1e1e;
|
|
360
|
+
}
|
|
361
|
+
#gvc-toolbar .tool:hover .tip, .gvc-subbar .sbtn:hover .tip, #gvc-moreshapes .sbtn:hover .tip { opacity: 1; transition-delay: .35s; }
|
|
362
|
+
#gvc-toolbar.plusopen .tool.plus .tip { opacity: 0 !important; }
|
|
363
|
+
#gvc-toolbar .tool .tip .k, .gvc-subbar .sbtn .tip .k, #gvc-moreshapes .sbtn .tip .k { color: #999; margin-left: 8px; }
|
|
364
|
+
|
|
365
|
+
/* ---- sub-toolbars (draw / shapes / stamps) -------------------------------- */
|
|
366
|
+
.gvc-subbar {
|
|
367
|
+
position: fixed; bottom: 77px; left: 50%; transform: translateX(calc(-50% - 56px));
|
|
368
|
+
display: flex; align-items: center; gap: 2px;
|
|
369
|
+
background: #fff; border-radius: 12px; padding: 5px 6px;
|
|
370
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, .10), 0 10px 26px rgba(0, 0, 0, .14);
|
|
371
|
+
animation: gvc-rise .15s ease-out;
|
|
372
|
+
}
|
|
373
|
+
@keyframes gvc-rise { from { opacity: 0; margin-bottom: -6px; } }
|
|
374
|
+
|
|
375
|
+
/* the stamp wheel — a radial picker: grey segmented ring, white hub, stamps in the slices */
|
|
376
|
+
#gvc-stampwheel {
|
|
377
|
+
position: fixed; bottom: 77px; left: 50%; transform: translateX(calc(-50% + 60px));
|
|
378
|
+
width: 216px; height: 216px; border-radius: 50%; background: #f1f0ee;
|
|
379
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, .10), 0 16px 38px rgba(0, 0, 0, .18);
|
|
380
|
+
animation: gvc-rise .15s ease-out;
|
|
381
|
+
}
|
|
382
|
+
#gvc-stampwheel.hidden { display: none; }
|
|
383
|
+
#gvc-stampwheel::before {
|
|
384
|
+
content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
|
|
385
|
+
background: repeating-conic-gradient(from 20.5deg, #fff 0 4deg, transparent 4deg 45deg);
|
|
386
|
+
}
|
|
387
|
+
#gvc-stampwheel .hub {
|
|
388
|
+
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
|
389
|
+
width: 92px; height: 92px; border-radius: 50%; background: #fff;
|
|
390
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, .10);
|
|
391
|
+
display: flex; align-items: center; justify-content: center; text-align: center;
|
|
392
|
+
font-size: 17px; line-height: 1.25; white-space: pre-line; pointer-events: none;
|
|
393
|
+
}
|
|
394
|
+
#gvc-stampwheel .stampb {
|
|
395
|
+
position: absolute; width: 46px; height: 46px; margin: -23px 0 0 -23px;
|
|
396
|
+
display: flex; align-items: center; justify-content: center; font-size: 29px; line-height: 1;
|
|
397
|
+
cursor: pointer; border-radius: 50%; transition: transform .12s;
|
|
398
|
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .2));
|
|
399
|
+
}
|
|
400
|
+
#gvc-stampwheel .stampb:hover { transform: scale(1.18); }
|
|
401
|
+
#gvc-stampwheel .stampb.sel { background: rgba(123, 97, 255, .16); }
|
|
402
|
+
#gvc-stampwheel .stampb svg.stk { width: 33px; height: 33px; }
|
|
403
|
+
.gvc-subbar.hidden, #gvc-moreshapes.hidden, #gvc-plusmenu.hidden { display: none; }
|
|
404
|
+
.gvc-subbar .sbtn {
|
|
405
|
+
display: flex; align-items: center; justify-content: center; width: 34px; height: 34px;
|
|
406
|
+
border-radius: 8px; cursor: pointer; color: #1e1e1e; position: relative;
|
|
407
|
+
transition: background .12s;
|
|
408
|
+
}
|
|
409
|
+
.gvc-subbar .sbtn:hover { background: #f2f1ef; }
|
|
410
|
+
.gvc-subbar .sbtn.on { background: #e9e1ff; }
|
|
411
|
+
.gvc-subbar .sbtn.sel { box-shadow: inset 0 0 0 2px var(--gvc-accent-ring); }
|
|
412
|
+
.gvc-subbar .sbtn svg { width: 21px; height: 21px; }
|
|
413
|
+
.gvc-subbar .sbtn svg.shp { width: 17px; height: 17px; }
|
|
414
|
+
.gvc-subbar .dsep { width: 1px; align-self: stretch; background: #e5e4e2; margin: -5px 4px; }
|
|
415
|
+
|
|
416
|
+
/* current-shape chip + chevron at the left of the shapes bar */
|
|
417
|
+
.gvc-subbar .cur { display: flex; align-items: center; gap: 5px; padding: 0 9px; height: 34px; border-radius: 8px; cursor: pointer; }
|
|
418
|
+
.gvc-subbar .cur:hover { background: #f2f1ef; }
|
|
419
|
+
.gvc-subbar .cur svg.shp { width: 18px; height: 18px; }
|
|
420
|
+
.gvc-subbar .cur svg.chev { width: 10px; height: 6px; flex: 0 0 auto; }
|
|
421
|
+
|
|
422
|
+
/* marker color dots (+ the rainbow custom-color well) */
|
|
423
|
+
.gvc-subbar .dot { width: 20px; height: 20px; border-radius: 50%; margin: 0 3px; cursor: pointer; flex: 0 0 auto; position: relative; }
|
|
424
|
+
.gvc-subbar .dot.white { box-shadow: inset 0 0 0 1px #ddd; }
|
|
425
|
+
.gvc-subbar .dot.selc { box-shadow: 0 0 0 2.5px #fff, 0 0 0 4.5px var(--gvc-accent-ring); }
|
|
426
|
+
.gvc-subbar .dot.white.selc { box-shadow: inset 0 0 0 1px #ddd, 0 0 0 2.5px #fff, 0 0 0 4.5px var(--gvc-accent-ring); }
|
|
427
|
+
.gvc-subbar .dot.rainbow { background: conic-gradient(#ff3b30, #ffcc00, #34c759, #32ade6, #5856d6, #ff2d95, #ff3b30); }
|
|
428
|
+
.gvc-subbar .dot.rainbow input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
|
|
429
|
+
.gvc-subbar .more { border: 1px solid #e3e2e0; background: #fff; border-radius: 8px; padding: 7px 11px; font: inherit; font-size: 12.5px; font-weight: 500; color: #1e1e1e; cursor: pointer; margin-left: 4px; white-space: nowrap; }
|
|
430
|
+
.gvc-subbar .more:hover { background: #f7f6f5; }
|
|
431
|
+
|
|
432
|
+
/* the More-shapes tray floats above the shapes bar */
|
|
433
|
+
#gvc-moreshapes {
|
|
434
|
+
position: fixed; bottom: 130px; left: 50%; transform: translateX(calc(-50% + 120px));
|
|
435
|
+
display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px;
|
|
436
|
+
background: #fff; border-radius: 12px; padding: 6px;
|
|
437
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, .10), 0 10px 26px rgba(0, 0, 0, .16);
|
|
438
|
+
}
|
|
439
|
+
#gvc-moreshapes .sbtn { width: 34px; height: 34px; border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #1e1e1e; }
|
|
440
|
+
#gvc-moreshapes .sbtn:hover { background: #f2f1ef; }
|
|
441
|
+
#gvc-moreshapes .sbtn svg.shp { width: 17px; height: 17px; }
|
|
442
|
+
|
|
443
|
+
/* + insert menu */
|
|
444
|
+
#gvc-plusmenu {
|
|
445
|
+
position: fixed; bottom: 77px; left: 50%; transform: translateX(calc(-50% + 210px));
|
|
446
|
+
background: #fff; border-radius: 12px; padding: 5px;
|
|
447
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, .10), 0 10px 26px rgba(0, 0, 0, .16); min-width: 150px;
|
|
448
|
+
}
|
|
449
|
+
#gvc-plusmenu .row { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: 13px; color: #1e1e1e; }
|
|
450
|
+
#gvc-plusmenu .row:hover { background: #f2f1ef; }
|
|
451
|
+
#gvc-plusmenu .row svg { width: 18px; height: 18px; }
|
|
452
|
+
|
|
453
|
+
/* sticky selection toolbar + color palette (dark) */
|
|
454
|
+
#gvc-selbar, #gvc-palette { position: fixed; z-index: 65; background: #1f2430; border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, .34); display: flex; align-items: center; padding: 5px; gap: 2px; }
|
|
455
|
+
#gvc-selbar.hidden, #gvc-palette.hidden { display: none; }
|
|
456
|
+
#gvc-selbar .sw { display: flex; align-items: center; gap: 5px; padding: 5px 8px; border-radius: 8px; cursor: pointer; }
|
|
457
|
+
#gvc-selbar .sw:hover, #gvc-selbar .btn:hover { background: rgba(255, 255, 255, .12); }
|
|
458
|
+
#gvc-selbar .dot { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid rgba(255, 255, 255, .5); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06); }
|
|
459
|
+
#gvc-selbar .chev { color: #aeb4c0; font-size: 10px; }
|
|
460
|
+
#gvc-selbar .div { width: 1px; align-self: stretch; background: rgba(255, 255, 255, .14); margin: 4px 3px; }
|
|
461
|
+
#gvc-selbar .btn { min-width: 32px; height: 30px; padding: 0 9px; border-radius: 8px; color: #e8eaee; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 14px; border: 0; background: 0; }
|
|
462
|
+
#gvc-selbar .btn.on { background: rgba(255, 255, 255, .18); }
|
|
463
|
+
#gvc-selbar .btn.wide { font-size: 12px; white-space: nowrap; }
|
|
464
|
+
#gvc-palette .pc { width: 26px; height: 26px; border-radius: 50%; margin: 2px; cursor: pointer; border: 2px solid transparent; box-sizing: border-box; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .08); }
|
|
465
|
+
#gvc-palette .pc.on { border-color: var(--gvc-accent-ring); }
|
|
466
|
+
#gvc-selbar .btn.lock { gap: 3px; }
|
|
467
|
+
#gvc-selbar .btn.lock .chev { color: #aeb4c0; font-size: 10px; }
|
|
468
|
+
/* section lock dropdown (dark menu) */
|
|
469
|
+
#gvc-lockmenu { position: fixed; z-index: 66; background: #1f2430; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, .34); padding: 6px; min-width: 186px; }
|
|
470
|
+
#gvc-lockmenu.hidden { display: none; }
|
|
471
|
+
#gvc-lockmenu .item { padding: 8px 12px; border-radius: 7px; color: #e8eaee; font-size: 13px; cursor: pointer; white-space: nowrap; }
|
|
472
|
+
#gvc-lockmenu .item:hover { background: rgba(255, 255, 255, .12); }
|
|
473
|
+
#gvc-fontmenu { position: fixed; z-index: 66; background: #1f2430; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, .34); padding: 6px; min-width: 168px; }
|
|
474
|
+
#gvc-fontmenu.hidden { display: none; }
|
|
475
|
+
#gvc-fontmenu .item { display: flex; align-items: center; gap: 8px; padding: 4px 10px; border-radius: 7px; color: #e8eaee; cursor: pointer; white-space: nowrap; line-height: 1.35; }
|
|
476
|
+
#gvc-fontmenu .item:hover { background: rgba(255, 255, 255, .12); }
|
|
477
|
+
#gvc-fontmenu .item .ck { flex: none; width: 12px; color: #cfd3da; font-size: 12px; }
|
|
478
|
+
#gvc-fontmenu .item .lb { font-weight: 500; }
|
|
479
|
+
#gvc-fontmenu .num { margin-top: 5px; width: 100%; box-sizing: border-box; background: #12151c; border: 1px solid var(--gvc-accent-ring); border-radius: 6px; color: #e8eaee; font: inherit; font-size: 13px; padding: 6px 9px; outline: none; }
|
|
480
|
+
#gvc-fontmenu .num::-webkit-inner-spin-button { opacity: .5; }
|
|
481
|
+
#gvc-selbar .btn.fontbtn { gap: 4px; }
|
|
482
|
+
#gvc-selbar .btn.fontbtn .fslabel { font-size: 12px; }
|
|
483
|
+
#gvc-selbar .btn svg { width: 16px; height: 16px; }
|
|
484
|
+
|
|
485
|
+
/* deep links: the copy confirmation, and the pulse on the node you land on */
|
|
486
|
+
/* sits ABOVE the bottom toolbar pill, not on it */
|
|
487
|
+
#gvc-toast { position: fixed; z-index: 90; left: 50%; bottom: 104px; transform: translate(-50%, 8px); background: #1f2430; color: #e8eaee; font-size: 13px; padding: 9px 15px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, .34); white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .16s, transform .16s; }
|
|
488
|
+
#gvc-toast.show { opacity: 1; transform: translate(-50%, 0); }
|
|
489
|
+
/* the save watchdog's chip — floating bottom-right, persistent while edits aren't landing,
|
|
490
|
+
out of the way of the work (unlike the toast above); green flash when the dirt lands */
|
|
491
|
+
#gvc-savewarn { position: fixed; z-index: 90; right: 16px; bottom: 24px; transform: translateY(8px); background: #3a2024; color: #ffd9dd; border: 1px solid #b4525a; font-size: 12px; font-weight: 600; padding: 7px 13px; border-radius: 999px; box-shadow: 0 6px 20px rgba(0, 0, 0, .28); white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .16s, transform .16s, background .16s, border-color .16s; }
|
|
492
|
+
#gvc-savewarn.show { opacity: 1; transform: translateY(0); }
|
|
493
|
+
#gvc-savewarn.ok { background: #1d3327; color: #c9f0d6; border-color: #3f8f5f; }
|
|
494
|
+
@keyframes gvc-linkpulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(123, 97, 255, 0); } 22% { box-shadow: 0 0 0 7px rgba(123, 97, 255, .42); } 60% { box-shadow: 0 0 0 4px rgba(123, 97, 255, .22); } }
|
|
495
|
+
.gvc-node.gvc-linked { animation: gvc-linkpulse 1.7s ease-out; }
|
|
496
|
+
|
|
497
|
+
.gvc-marquee { position: fixed; z-index: 55; border: 1px solid #2563eb; background: rgba(37, 99, 235, .09); pointer-events: none; }
|
|
498
|
+
.gvc-ghost { position: fixed; pointer-events: none; z-index: 100; opacity: .9; transform: translate(-50%, -50%); }
|
|
499
|
+
.gvc-ghost .tip { display: none; }
|
|
500
|
+
/* smooth grab-and-drop: the sticky ghost springs up under the cursor, the dropped node pops in */
|
|
501
|
+
.gvc-ghost-sticky { width: 66px; height: 66px; border-radius: 0; background: #a9cbf5; box-shadow: 0 12px 26px rgba(0, 0, 0, .24); opacity: 0; transform: translate(-50%, -50%) rotate(-5deg) scale(.5); transition: transform .17s cubic-bezier(.34, 1.56, .64, 1), opacity .12s; }
|
|
502
|
+
.gvc-ghost-sticky.in { opacity: .96; transform: translate(-50%, -50%) rotate(-4deg) scale(1); }
|
|
503
|
+
/* the ghost is the sticker you are about to press: same size, same crisp edge */
|
|
504
|
+
.gvc-ghost-stamp { font-size: 42px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .25)); }
|
|
505
|
+
.gvc-ghost-stamp svg.stk { width: 46px; height: 46px; }
|
|
506
|
+
/* the pop composes with the node's own angle — a stamp lands tilted AND springs in */
|
|
507
|
+
@keyframes gvc-pop { from { transform: rotate(var(--gvc-rot, 0deg)) scale(.7); } to { transform: rotate(var(--gvc-rot, 0deg)) scale(1); } }
|
|
508
|
+
.gvc-node.gvc-pop { animation: gvc-pop .2s cubic-bezier(.34, 1.56, .64, 1); }
|
|
509
|
+
|
|
510
|
+
/* insert picker — search prototypes / pages / components */
|
|
511
|
+
#gvc-picker { position: fixed; top: 74px; left: 50%; transform: translateX(-50%); width: min(920px, calc(100vw - 32px)); max-height: 80vh; background: #fff; border: 1px solid #e9e8e6; border-radius: 16px; box-shadow: 0 20px 54px rgba(0, 0, 0, .24); z-index: 80; display: flex; flex-direction: column; overflow: hidden; }
|
|
512
|
+
#gvc-picker.hidden { display: none; }
|
|
513
|
+
#gvc-picker .head { display: flex; align-items: center; gap: 8px; padding: 12px 14px 10px; }
|
|
514
|
+
#gvc-picker .head input { flex: 1; border: 1.5px solid #d9dce1; border-radius: 10px; padding: 10px 12px; font: inherit; outline: none; }
|
|
515
|
+
#gvc-picker .head input:focus { border-color: var(--gvc-accent-ring); }
|
|
516
|
+
#gvc-picker .head .x { border: 0; background: 0; font-size: 20px; line-height: 1; color: #6b7280; cursor: pointer; width: 32px; height: 32px; border-radius: 8px; }
|
|
517
|
+
#gvc-picker .head .x:hover { background: #f3f4f6; }
|
|
518
|
+
#gvc-picker .tabs { display: flex; gap: 4px; padding: 0 14px 8px; border-bottom: 1px solid #f0f1f3; }
|
|
519
|
+
#gvc-picker .tab { font-size: 13px; padding: 6px 12px; border-radius: 8px; cursor: pointer; color: #6b7280; }
|
|
520
|
+
#gvc-picker .tab.on { background: #eef0f3; color: #111827; font-weight: 600; }
|
|
521
|
+
/* flex:1 + min-height:0 make this a proper scroll region inside the flex-column picker;
|
|
522
|
+
grid-auto-rows:max-content stops the auto rows collapsing to ~8px when flex shrinks it. */
|
|
523
|
+
#gvc-picker .grid { padding: 14px; flex: 1; min-height: 0; overflow: auto; display: grid; grid-template-columns: repeat(2, 1fr); grid-auto-rows: max-content; gap: 14px; align-content: start; }
|
|
524
|
+
#gvc-picker .card { border: 1px solid #eceef1; border-radius: 10px; overflow: hidden; cursor: pointer; background: #fff; transition: box-shadow .12s, transform .12s; }
|
|
525
|
+
#gvc-picker .card:hover { box-shadow: 0 8px 20px rgba(0, 0, 0, .13); transform: translateY(-2px); }
|
|
526
|
+
/* A tall fixed height (NOT aspect-ratio — that collapsed the card to ~8px because its computed
|
|
527
|
+
height didn't contribute to the card's block layout, and overflow:hidden clipped the poster to
|
|
528
|
+
a sliver). cover + top center shows the page's identifying top half, big and legible. */
|
|
529
|
+
#gvc-picker .card .thumb { height: 240px; background-color: #f4f5f7; background-size: cover; background-position: top center; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center; color: #b6bcc6; font-size: 11px; text-transform: capitalize; }
|
|
530
|
+
/* components are small pieces (and many have no poster) — show the whole thing on a shorter tile */
|
|
531
|
+
#gvc-picker .card.is-component .thumb { height: 120px; background-size: contain; background-position: center; background-color: #fafbfc; }
|
|
532
|
+
#gvc-picker .card .cap { padding: 8px 9px; }
|
|
533
|
+
#gvc-picker .card .cap .t { font-size: 12px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #1f2430; }
|
|
534
|
+
#gvc-picker .card .cap .ty { font-size: 10px; color: #9ca3af; text-transform: capitalize; margin-top: 1px; }
|
|
535
|
+
#gvc-picker .empty { grid-column: 1 / -1; padding: 34px; text-align: center; color: #9ca3af; font-size: 13px; }
|
|
536
|
+
|
|
537
|
+
/* ---- mobile (phones / touch) ---------------------------------------------
|
|
538
|
+
One finger pans, two fingers pinch-zoom (handled in JS); here the chrome compacts:
|
|
539
|
+
the toolbar fits the screen and scrolls sideways if it must, sub-bars follow, the
|
|
540
|
+
zoom pill hides (pinch replaces it), and hover tooltips are dropped on touch. */
|
|
541
|
+
@media (pointer: coarse) {
|
|
542
|
+
#gvc-toolbar .tool .tip, .gvc-subbar .sbtn .tip, #gvc-moreshapes .sbtn .tip { display: none; }
|
|
543
|
+
}
|
|
544
|
+
@media (max-width: 760px) {
|
|
545
|
+
#gvc-toolbar {
|
|
546
|
+
bottom: 12px;
|
|
547
|
+
max-width: calc(100vw - 12px); overflow-x: auto; overflow-y: hidden;
|
|
548
|
+
scrollbar-width: none; -webkit-overflow-scrolling: touch;
|
|
549
|
+
}
|
|
550
|
+
#gvc-toolbar::-webkit-scrollbar { display: none; }
|
|
551
|
+
#gvc-toolbar > * { flex: 0 0 auto; }
|
|
552
|
+
.gvc-subbar {
|
|
553
|
+
bottom: 60px; max-width: calc(100vw - 12px); overflow-x: auto; overflow-y: hidden;
|
|
554
|
+
scrollbar-width: none;
|
|
555
|
+
left: 50%; transform: translateX(-50%);
|
|
556
|
+
}
|
|
557
|
+
.gvc-subbar::-webkit-scrollbar { display: none; }
|
|
558
|
+
.gvc-subbar > * { flex: 0 0 auto; }
|
|
559
|
+
#gvc-stampwheel { transform: translateX(-50%); bottom: 60px; }
|
|
560
|
+
#gvc-moreshapes { bottom: 112px; transform: translateX(-50%); }
|
|
561
|
+
#gvc-plusmenu { bottom: 60px; left: auto; right: 8px; transform: none; }
|
|
562
|
+
#gvc-zoom { display: none; }
|
|
563
|
+
#gvc-topbar { top: 8px; left: 8px; padding: 4px 6px; }
|
|
564
|
+
#gvc-topbar .nm { max-width: 40vw; }
|
|
565
|
+
}
|
|
566
|
+
/* on narrow phones the bar can overflow — a right-edge fade hints at the hidden tools
|
|
567
|
+
(position:sticky scroll-shadow trick; sits at content end, pinned to the visible edge) */
|
|
568
|
+
@media (max-width: 480px) {
|
|
569
|
+
#gvc-toolbar::after {
|
|
570
|
+
content: ""; position: sticky; right: 0; flex: 0 0 24px; margin-left: -24px;
|
|
571
|
+
align-self: stretch; pointer-events: none; border-radius: 0 12px 12px 0;
|
|
572
|
+
background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 82%);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* ---- multiplayer: peer cursors · presence · remote editing focus ---------- */
|
|
577
|
+
/* cursors live OUTSIDE #gvc-ui so ⌘. (hide UI) keeps collaborators visible */
|
|
578
|
+
#gvc-cursors { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 58; }
|
|
579
|
+
/* no transform transition: the spline interpolator (mpCursorStep) animates at rAF —
|
|
580
|
+
a CSS tween on top would double-smooth into visible lag */
|
|
581
|
+
.gvc-cursor { position: absolute; left: 0; top: 0; will-change: transform; transition: opacity .4s; }
|
|
582
|
+
/* the piti arrow glyph (fill+outline live in the SVG); margin puts the TIP (hotspot
|
|
583
|
+
5,3 of 21px) exactly on the peer's position, like the local cursor hotspot */
|
|
584
|
+
.gvc-cursor svg { display: block; width: 21px; height: 21px; margin: -3px 0 0 -5px; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .3)); }
|
|
585
|
+
/* the pill's SHARP corner = top-left, pointing back at the cursor tip it hangs from */
|
|
586
|
+
.gvc-cursor .lbl { position: absolute; left: 13px; top: 17px; padding: 2px 8px 3px; border-radius: 3px 10px 10px 10px; color: #fff; font: 600 11px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; white-space: nowrap; box-shadow: 0 1px 4px rgba(0, 0, 0, .22); }
|
|
587
|
+
.gvc-cursor.idle { opacity: 0; }
|
|
588
|
+
/* agent peers wear Clawd (icons8): hotspot at the mascot's TOP-LEFT (points at the target like a
|
|
589
|
+
normal cursor tip); the icon's body starts partway down its 48x48 box, so the margin lifts it
|
|
590
|
+
so the body's top-left sits on the point. Name pill trails to the bottom-right. */
|
|
591
|
+
/* --clawd-k (set per-frame from zoom) scales ONLY the mascot; origin = the hotspot point
|
|
592
|
+
(5,15 inside the svg box after margins) so the tip stays planted while Clawd shrinks.
|
|
593
|
+
pointer-events back ON (the layer is none): Clawd is pettable — see boing/hop below */
|
|
594
|
+
.gvc-cursor.agent svg { width: 40px; height: 40px; margin: -15px 0 0 -5px; transform: scale(var(--clawd-k, 1)); transform-origin: 5px 15px; pointer-events: auto; cursor: pointer; }
|
|
595
|
+
/* the pill keeps constant SIZE but its anchor tracks the scaled mascot's bottom-right */
|
|
596
|
+
.gvc-cursor.agent .lbl { left: calc(30px * var(--clawd-k, 1)); top: calc(24px * var(--clawd-k, 1)); }
|
|
597
|
+
/* a parked/sleeping agent stays FULLY visible (never fades like an idle human cursor) */
|
|
598
|
+
.gvc-cursor.agent.idle { opacity: 1; }
|
|
599
|
+
/* walking: a little vertical bob while the agent's cursor is moving */
|
|
600
|
+
@keyframes gvc-clawd-bob { 0%, 100% { transform: scale(var(--clawd-k, 1)) translateY(0); } 50% { transform: scale(var(--clawd-k, 1)) translateY(-2.5px); } }
|
|
601
|
+
.gvc-cursor.agent.walking svg { animation: gvc-clawd-bob .32s ease-in-out infinite; }
|
|
602
|
+
/* Clawd is pettable (local-only delight): hover = a little boing, click = a wound-up hop.
|
|
603
|
+
Every keyframe carries the zoom scale so the tip stays planted. Order matters: these
|
|
604
|
+
rules sit AFTER .walking so a pet wins over the walk bob while it plays. */
|
|
605
|
+
@keyframes gvc-clawd-boing {
|
|
606
|
+
0%, 100% { transform: scale(var(--clawd-k, 1)); }
|
|
607
|
+
35% { transform: scale(calc(var(--clawd-k, 1) * 1.14)) translateY(-4px); }
|
|
608
|
+
70% { transform: scale(calc(var(--clawd-k, 1) * .94)); }
|
|
609
|
+
}
|
|
610
|
+
.gvc-cursor.agent svg:hover { animation: gvc-clawd-boing .5s ease-in-out; }
|
|
611
|
+
@keyframes gvc-clawd-hop {
|
|
612
|
+
0% { transform: scale(var(--clawd-k, 1)); }
|
|
613
|
+
25% { transform: scale(var(--clawd-k, 1)) scaleY(.72); }
|
|
614
|
+
55% { transform: scale(var(--clawd-k, 1)) translateY(-16px) rotate(-8deg); }
|
|
615
|
+
78% { transform: scale(var(--clawd-k, 1)) translateY(0) rotate(5deg); }
|
|
616
|
+
100% { transform: scale(var(--clawd-k, 1)) rotate(0deg); }
|
|
617
|
+
}
|
|
618
|
+
.gvc-cursor.agent.poked svg { animation: gvc-clawd-hop .7s cubic-bezier(.34, 1.56, .64, 1); }
|
|
619
|
+
/* the presence row lives INSIDE the top-right card — no chrome of its own. The left
|
|
620
|
+
padding absorbs the -7px chip-overlap margin of whichever chip renders first (flex
|
|
621
|
+
`order` reorders them, so :first-child can't know which one that is). */
|
|
622
|
+
#gvc-presence { display: flex; align-items: center; padding-left: 14px; }
|
|
623
|
+
#gvc-presence.hidden { display: none; }
|
|
624
|
+
.gvc-peerchip { position: relative; width: 27px; height: 27px; border-radius: 50%; margin-left: -7px; display: flex; align-items: center; justify-content: center; color: #fff; font: 700 10.5px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .16); transition: transform .12s; }
|
|
625
|
+
.gvc-peerchip.me { order: 1; }
|
|
626
|
+
/* click a peer's face to fly to them; hover names them (our own label, not the OS tooltip —
|
|
627
|
+
that took a second to appear and looked foreign on the board) */
|
|
628
|
+
.gvc-peerchip.jump { cursor: pointer; }
|
|
629
|
+
.gvc-peerchip.jump:hover { transform: translateY(-1px) scale(1.08); z-index: 2; }
|
|
630
|
+
/* A highlighted chip — the one you're following, the Clawd asking for you — keeps EXACTLY
|
|
631
|
+
the footprint of every other avatar: the ring is drawn INSIDE the circle (an inset ring
|
|
632
|
+
over the rim of the face), never as an outer halo. An outer ring grew the chip past the
|
|
633
|
+
white card it lives in and dwarfed its neighbours.
|
|
634
|
+
The chip's own white rim takes the colour and a white band is inset behind it, so the ring
|
|
635
|
+
reads the same on a photo, a letter chip, or a Clawd — the anatomy of the old outer halo,
|
|
636
|
+
folded inwards. */
|
|
637
|
+
.gvc-peerchip.following { z-index: 2; border-color: var(--halo, #2563eb); }
|
|
638
|
+
.gvc-peerchip.following::after, .gvc-peerchip.attn::after {
|
|
639
|
+
content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; z-index: 1;
|
|
640
|
+
box-shadow: inset 0 0 0 2px #fff;
|
|
641
|
+
}
|
|
642
|
+
/* …and the whole viewport wears their color: border + Following pill.
|
|
643
|
+
The pill is PART of the border — same background color, hanging flush from the top
|
|
644
|
+
edge — with Stop as a darker inset segment. Both colors are set inline (the peer's). */
|
|
645
|
+
#gvc-follow-frame { position: fixed; inset: 0; border: 5px solid #2563eb; pointer-events: none; z-index: 70; }
|
|
646
|
+
#gvc-follow-pill { position: fixed; top: 5px; left: 50%; transform: translateX(-50%); z-index: 71; display: flex; align-items: center; gap: 10px; background: #2563eb; color: #fff; padding: 5px 6px 7px 16px; border-radius: 0 0 10px 10px; font: 600 12.5px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; white-space: nowrap; }
|
|
647
|
+
#gvc-follow-pill .stop { border: 0; background: rgba(0, 0, 0, .28); color: #fff; font: 700 12px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; padding: 4px 11px; border-radius: 7px; cursor: pointer; }
|
|
648
|
+
#gvc-follow-pill .stop:hover { background: rgba(0, 0, 0, .42); }
|
|
649
|
+
.gvc-peerchip .lbl {
|
|
650
|
+
position: absolute; top: calc(100% + 7px); left: 50%; transform: translateX(-50%);
|
|
651
|
+
padding: 3px 8px 4px; border-radius: 7px; background: #1e1e1e; color: #fff;
|
|
652
|
+
font: 600 11px/1.35 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; white-space: nowrap;
|
|
653
|
+
opacity: 0; pointer-events: none; transition: opacity .12s; box-shadow: 0 2px 8px rgba(0, 0, 0, .22);
|
|
654
|
+
}
|
|
655
|
+
.gvc-peerchip:hover .lbl { opacity: 1; }
|
|
656
|
+
/* the peer you just jumped to pulses once, so you spot them on arrival. Animate the SVG, never
|
|
657
|
+
the .gvc-cursor itself — its transform carries the cursor's position. */
|
|
658
|
+
.gvc-cursor.pinged svg { animation: gvc-ping .9s ease-out; }
|
|
659
|
+
@keyframes gvc-ping {
|
|
660
|
+
0% { transform: scale(var(--clawd-k, 1)); }
|
|
661
|
+
22% { transform: scale(calc(var(--clawd-k, 1) * 1.6)); }
|
|
662
|
+
55% { transform: scale(calc(var(--clawd-k, 1) * .92)); }
|
|
663
|
+
100% { transform: scale(var(--clawd-k, 1)); }
|
|
664
|
+
}
|
|
665
|
+
/* agent (Clawd) chip: a cream CIRCLE with a mini Clawd instead of a letter initial */
|
|
666
|
+
.gvc-peerchip.agent { overflow: visible; }
|
|
667
|
+
.gvc-peerchip.agent svg { display: block; }
|
|
668
|
+
/* a peer is typing in this node — colored ring + name tag (border-color/background set inline) */
|
|
669
|
+
.gvc-remote-focus { position: absolute; inset: -4px; border: 2px solid; border-radius: 7px; pointer-events: none; z-index: 4; }
|
|
670
|
+
.gvc-remote-focus .who { position: absolute; top: -20px; left: -2px; padding: 1px 7px 2px; border-radius: 8px 8px 8px 2px; color: #fff; font: 600 10.5px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; white-space: nowrap; }
|
|
671
|
+
/* ---- cursor chat + the agents strip --------------------------------------- */
|
|
672
|
+
/* chat bubble at a cursor: a MOMENT (6s, then gone) — states live in the agents strip */
|
|
673
|
+
.gvc-cursor .chat {
|
|
674
|
+
position: absolute; left: 13px; top: 40px; max-width: 260px; padding: 5px 10px 6px;
|
|
675
|
+
border-radius: 3px 12px 12px 12px; color: #fff;
|
|
676
|
+
font: 500 12.5px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
677
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, .25); white-space: pre-wrap; word-break: break-word;
|
|
678
|
+
}
|
|
679
|
+
.gvc-cursor .chat.hidden { display: none; }
|
|
680
|
+
.gvc-cursor.self { pointer-events: none; z-index: 71; }
|
|
681
|
+
#gvc-cursorchat { position: fixed; z-index: 72; }
|
|
682
|
+
#gvc-cursorchat input {
|
|
683
|
+
width: 220px; padding: 7px 11px; border: 0; border-radius: 3px 12px 12px 12px;
|
|
684
|
+
background: #1e1e1e; color: #fff; font: 500 12.5px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
685
|
+
outline: none; box-shadow: 0 4px 14px rgba(0, 0, 0, .3);
|
|
686
|
+
}
|
|
687
|
+
#gvc-cursorchat input::placeholder { color: rgba(255, 255, 255, .45); }
|
|
688
|
+
/* the Clawd that needs you pulses amber at the cursor (locality) — the strip is the home */
|
|
689
|
+
.gvc-cursor.attention svg { animation: gvc-attn 1.1s ease-in-out infinite; }
|
|
690
|
+
@keyframes gvc-attn {
|
|
691
|
+
0%, 100% { filter: drop-shadow(0 0 0 rgba(245, 158, 11, 0)); }
|
|
692
|
+
50% { filter: drop-shadow(0 0 7px rgba(245, 158, 11, .95)); }
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/* agents strip: the Clawds' home, bottom-right — glance here instead of the terminal.
|
|
696
|
+
working = breathing face + live dot · idle = dimmed, still · attention = amber pulse ·
|
|
697
|
+
done = one green flash. Click a row to follow that Clawd. */
|
|
698
|
+
/* Clawds sit IN the presence row as avatar chips, wearing their status as the dot on the
|
|
699
|
+
face — like people, just with a working/idle/attention/done heartbeat. Attention makes
|
|
700
|
+
the whole chip jump; the status text + actions live in the click-open card below. */
|
|
701
|
+
.gvc-peerchip .st { position: absolute; right: -3px; bottom: -3px; width: 9px; height: 9px; border-radius: 50%; border: 2px solid #fff; z-index: 2; }
|
|
702
|
+
.gvc-peerchip .st.working { background: #22c55e; animation: gvc-live 1.6s ease-in-out infinite; }
|
|
703
|
+
.gvc-peerchip .st.idle { background: #c4c9d4; }
|
|
704
|
+
.gvc-peerchip .st.attention { background: #f59e0b; }
|
|
705
|
+
.gvc-peerchip .st.done { background: #22c55e; }
|
|
706
|
+
.gvc-peerchip.resting { opacity: .62; }
|
|
707
|
+
.gvc-peerchip.attn { animation: gvc-jump 2.2s ease-in-out infinite; border-color: #f59e0b; box-shadow: 0 2px 6px rgba(0, 0, 0, .22); z-index: 2; }
|
|
708
|
+
@keyframes gvc-live { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
|
|
709
|
+
/* two quick hops, then rest — repeated, so it beckons without vibrating */
|
|
710
|
+
@keyframes gvc-jump {
|
|
711
|
+
0%, 26%, 100% { transform: translateY(0); }
|
|
712
|
+
8% { transform: translateY(-6px); }
|
|
713
|
+
16% { transform: translateY(0); }
|
|
714
|
+
21% { transform: translateY(-2.5px); }
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/* the agent card: status text + actions under the clicked chip */
|
|
718
|
+
#gvc-agentpop { position: fixed; z-index: 80; width: 218px; background: #fff; border: 1px solid #e9e8e6; border-radius: 12px; box-shadow: 0 8px 28px rgba(0, 0, 0, .15); padding: 11px; font: 500 12.5px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #1e1e1e; }
|
|
719
|
+
#gvc-agentpop .hd { display: flex; align-items: center; gap: 8px; }
|
|
720
|
+
#gvc-agentpop .hd .face { width: 23px; height: 23px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex: 0 0 auto; }
|
|
721
|
+
#gvc-agentpop .hd .face svg { display: block; }
|
|
722
|
+
#gvc-agentpop .hd .who { font-weight: 700; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
723
|
+
#gvc-agentpop .hd .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: #c4c9d4; }
|
|
724
|
+
#gvc-agentpop .hd .dot.working { background: #22c55e; animation: gvc-live 1.6s ease-in-out infinite; }
|
|
725
|
+
#gvc-agentpop .hd .dot.attention { background: #f59e0b; }
|
|
726
|
+
#gvc-agentpop .hd .dot.done { background: #22c55e; }
|
|
727
|
+
#gvc-agentpop .txt { margin: 7px 1px 10px; color: #6b7280; }
|
|
728
|
+
#gvc-agentpop .txt.attention { color: #b45309; font-weight: 600; }
|
|
729
|
+
#gvc-agentpop .acts { display: flex; gap: 6px; }
|
|
730
|
+
#gvc-agentpop .acts button { flex: 1; border: 1px solid #e5e7eb; background: #fff; border-radius: 8px; padding: 6px 8px; font: inherit; font-weight: 600; cursor: pointer; color: #1e1e1e; }
|
|
731
|
+
#gvc-agentpop .acts button:hover { background: #f9fafb; border-color: #d1d5db; }
|
|
732
|
+
#gvc-agentpop .acts .kick { color: #b91c1c; border-color: #fecaca; }
|
|
733
|
+
#gvc-agentpop .acts .kick:hover { background: #fef2f2; border-color: #fca5a5; }
|
|
734
|
+
|
|
735
|
+
/* peers' drags arrive at tick rate (~8Hz) — a short linear tween makes them glide */
|
|
736
|
+
/* 90ms: just over the 50ms drag fast-path cadence, so remote motion is always mid-tween
|
|
737
|
+
(continuous glide) instead of hop-pause-hop at the old 120ms ops tick */
|
|
738
|
+
.gvc-node.gvc-remote-move { transition: left .09s linear, top .09s linear, width .09s linear, height .09s linear, transform .09s linear; }
|
|
739
|
+
/* what a peer has SELECTED — thin ring in their color (typing gets the thicker named ring) */
|
|
740
|
+
.gvc-peer-sel { position: absolute; inset: -3px; border: calc(1.5px * var(--gvc-inv, 1)) solid; border-radius: 6px; pointer-events: none; z-index: 4; opacity: .85; }
|
|
741
|
+
/* interact-mode drag grip: pinned to the tile's top-right corner, counter-scaled like the
|
|
742
|
+
name chip; its pointerdown bubbles to the root drag path — no special handling */
|
|
743
|
+
.gvc-draghandle { position: absolute; top: -13px; right: -13px; width: 30px; height: 30px; display: none; align-items: center; justify-content: center; background: #2563eb; color: #fff; border-radius: 50%; box-shadow: 0 2px 8px rgba(0, 0, 0, .28); cursor: grab; transform-origin: top right; z-index: 6; }
|
|
744
|
+
.gvc-draghandle svg { width: 15px; height: 15px; }
|
|
745
|
+
.gvc-tile.interacting .gvc-draghandle { display: flex; }
|
|
746
|
+
|
|
747
|
+
/* ---- session: the shared timer + music ------------------------------------
|
|
748
|
+
Lives in the top bar because it belongs to the MEETING, not to the selection: it stays
|
|
749
|
+
put and stays readable while you work, which is the whole point of a countdown. */
|
|
750
|
+
/* the top-right row: [timer pill] [presence chips] — one flex line so the pill keeps its place
|
|
751
|
+
as peers come and go, and so it survives presence hiding itself when you're on the board alone */
|
|
752
|
+
/* ONE white card for the whole room: [presence avatars][session inset]. The colored
|
|
753
|
+
session state (lavender live, red over) is a segment INSIDE this card, never its own
|
|
754
|
+
floating pill — the reference's big-white-chip anatomy. */
|
|
755
|
+
#gvc-topright { position: fixed; top: 14px; right: 16px; display: flex; align-items: center; gap: 9px; background: #fff; border: 1px solid #e9e8e6; border-radius: 14px; box-shadow: 0 4px 16px rgba(0, 0, 0, .10); padding: 5px; }
|
|
756
|
+
/* the seven-segment display face (shipped under /__canvas/, license alongside) */
|
|
757
|
+
@font-face { font-family: "DSEG7"; src: url("/__canvas/DSEG7Classic-Bold.woff2") format("woff2"); font-weight: 700; font-display: block; }
|
|
758
|
+
|
|
759
|
+
/* the session inset: [mini record] [time or track name] [speaker while music plays] —
|
|
760
|
+
a light-bordered segment when idle, colored fill when the room is live */
|
|
761
|
+
.gvc-sesspill { border: 1px solid #e5e7eb; background: #fff; padding: 5px 10px; border-radius: 10px; cursor: pointer; color: #374151; font: inherit; display: flex; align-items: center; gap: 7px; }
|
|
762
|
+
.gvc-sesspill:hover, .gvc-sesspill.on { background: #f9fafb; }
|
|
763
|
+
/* one quiet timer glyph — the pill is a COUNTDOWN first; music is what the panel behind it
|
|
764
|
+
holds, not something the pill has to advertise (the track name + speaker already say it) */
|
|
765
|
+
.gvc-sesspill .ico { display: flex; color: #b3b6bd; }
|
|
766
|
+
.gvc-sesspill .ico svg { width: 15px; height: 15px; display: block; }
|
|
767
|
+
/* time (or name). Ghost = the pending duration in unlit gray; digits modes render in the
|
|
768
|
+
seven-segment face, the name mode in the UI face */
|
|
769
|
+
.gvc-sesspill .t { font-family: DSEG7, ui-monospace, monospace; font-weight: 700; font-size: 12.5px; letter-spacing: .04em; color: #cbccd0; }
|
|
770
|
+
.gvc-sesspill .t[data-mode="name"] { font-family: inherit; font-weight: 500; font-size: 12px; letter-spacing: 0; color: #4338ca; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
771
|
+
.gvc-sesspill .spk { display: none; color: #6366f1; padding-left: 8px; margin-left: 0; border-left: 1px solid #e5e7eb; align-self: stretch; align-items: center; }
|
|
772
|
+
.gvc-sesspill .spk.shown { display: flex; }
|
|
773
|
+
.gvc-sesspill .spk:hover { color: #4338ca; }
|
|
774
|
+
.gvc-sesspill .spk svg { width: 17px; height: 17px; }
|
|
775
|
+
/* a running timer colors the inset so it reads from across the board, not just up close */
|
|
776
|
+
.gvc-sesspill.live, .gvc-sesspill.music { background: #eef2ff; border-color: transparent; }
|
|
777
|
+
.gvc-sesspill.live .t { color: #4338ca; }
|
|
778
|
+
.gvc-sesspill.live .ico, .gvc-sesspill.music .ico { color: #6366f1; }
|
|
779
|
+
.gvc-sesspill.live:hover, .gvc-sesspill.music:hover { background: #e0e7ff; }
|
|
780
|
+
|
|
781
|
+
#gvc-sesspanel { position: fixed; top: 58px; right: 16px; width: 264px; background: #fff; border: 1px solid #e9e8e6; border-radius: 14px; box-shadow: 0 8px 28px rgba(0, 0, 0, .13); font-size: 12.5px; color: #111827; }
|
|
782
|
+
#gvc-sesspanel.hidden { display: none; }
|
|
783
|
+
#gvc-sesspanel .head { display: flex; align-items: center; justify-content: space-between; padding: 10px 9px 10px 13px; border-bottom: 1px solid #ebebed; }
|
|
784
|
+
#gvc-sesspanel .ttl { font-weight: 650; font-size: 13.5px; }
|
|
785
|
+
#gvc-sesspanel .x { border: 0; background: 0; padding: 4px; border-radius: 6px; cursor: pointer; color: #6b7280; display: flex; }
|
|
786
|
+
#gvc-sesspanel .x:hover { background: #f3f4f6; color: #111827; }
|
|
787
|
+
#gvc-sesspanel .x svg { width: 16px; height: 16px; }
|
|
788
|
+
#gvc-sesspanel .row { padding: 11px 13px; border-bottom: 1px solid #ebebed; }
|
|
789
|
+
/* no tracks installed → the music surface hides wholesale (the panel row; the pill shows
|
|
790
|
+
the timer either way) */
|
|
791
|
+
#gvc-sesspanel .row.hidden { display: none; }
|
|
792
|
+
#gvc-sesspanel .row:last-child { border-bottom: 0; }
|
|
793
|
+
|
|
794
|
+
#gvc-sesspanel .row.vol { display: flex; align-items: center; gap: 10px; }
|
|
795
|
+
#gvc-sesspanel .vol .vol { border: 0; background: 0; padding: 0; cursor: pointer; color: #4b5563; display: flex; flex: 0 0 auto; }
|
|
796
|
+
#gvc-sesspanel .vol .vol:hover { color: #111827; }
|
|
797
|
+
#gvc-sesspanel .vol svg { width: 16px; height: 16px; }
|
|
798
|
+
/* filled-track slider: the split gradient is repainted from JS at the thumb's position */
|
|
799
|
+
#gvc-sesspanel input[type=range] { flex: 1; -webkit-appearance: none; appearance: none; height: 6px; border-radius: 999px; background: linear-gradient(90deg, #4f46e5 60%, #e5e7eb 60%); outline: none; }
|
|
800
|
+
#gvc-sesspanel input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #fff; border: 1px solid #d4d5d9; box-shadow: 0 1px 4px rgba(0, 0, 0, .22); cursor: pointer; }
|
|
801
|
+
#gvc-sesspanel input[type=range]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: #fff; border: 1px solid #d4d5d9; box-shadow: 0 1px 4px rgba(0, 0, 0, .22); cursor: pointer; }
|
|
802
|
+
|
|
803
|
+
/* the clock: unlit 88:88 ghost under the live digits; the ghost copies the input's exact
|
|
804
|
+
box metrics (font, size, padding, centering) so segments overlay segment-for-segment */
|
|
805
|
+
#gvc-sesspanel .clock { position: relative; background: #f1f1f4; border-radius: 12px; }
|
|
806
|
+
#gvc-sesspanel .clock .ghost { position: absolute; inset: 0; width: 100%; box-sizing: border-box; border: 1px solid transparent; background: transparent; border-radius: 12px; padding: 14px 0; text-align: center; font-family: DSEG7, ui-monospace, monospace; font-size: 36px; font-weight: 700; letter-spacing: .06em; color: #e4e4e7; pointer-events: none; user-select: none; outline: none; }
|
|
807
|
+
/* mid-edit values ("5:0", "15") center-float and can't sit on the fixed 88:88 cells —
|
|
808
|
+
hide the ghost while typing; blur normalizes to mm:ss, which aligns again */
|
|
809
|
+
#gvc-sesspanel .clock:focus-within .ghost { visibility: hidden; }
|
|
810
|
+
#gvc-sesspanel .digits { position: relative; width: 100%; box-sizing: border-box; border: 1px solid transparent; background: transparent; border-radius: 12px; padding: 14px 0; text-align: center; font-family: DSEG7, ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 36px; font-weight: 700; letter-spacing: .06em; color: #1f2023; outline: none; }
|
|
811
|
+
#gvc-sesspanel .digits:not(.live):hover { border-color: #d9d8d6; }
|
|
812
|
+
#gvc-sesspanel .digits:not(.live):focus { border-color: #4f46e5; }
|
|
813
|
+
|
|
814
|
+
#gvc-sesspanel .idle, #gvc-sesspanel .run { display: flex; align-items: center; gap: 7px; margin-top: 10px; }
|
|
815
|
+
#gvc-sesspanel .idle.hidden, #gvc-sesspanel .run.hidden { display: none; }
|
|
816
|
+
#gvc-sesspanel .idle .sp, #gvc-sesspanel .run .sp { flex: 1; }
|
|
817
|
+
|
|
818
|
+
/* circular controls; .go is the filled one (start timer / play music) */
|
|
819
|
+
#gvc-sesspanel .act { border: 1px solid #e5e7eb; background: #fff; border-radius: 999px; width: 34px; height: 34px; flex: none; padding: 0; display: flex; align-items: center; justify-content: center; gap: 6px; font: inherit; color: #374151; cursor: pointer; }
|
|
820
|
+
#gvc-sesspanel .act:hover { background: #f9fafb; border-color: #d1d5db; }
|
|
821
|
+
#gvc-sesspanel .act.hidden { display: none; }
|
|
822
|
+
#gvc-sesspanel .act.wide { width: auto; padding: 0 12px; border-radius: 999px; font-size: 12.5px; }
|
|
823
|
+
#gvc-sesspanel .act svg { width: 16px; height: 16px; }
|
|
824
|
+
#gvc-sesspanel .go { border: 0; background: #4f46e5; color: #fff; border-radius: 999px; width: 34px; height: 34px; flex: none; display: flex; align-items: center; justify-content: center; font: inherit; cursor: pointer; }
|
|
825
|
+
#gvc-sesspanel .go:hover { background: #4338ca; }
|
|
826
|
+
#gvc-sesspanel .go svg { width: 17px; height: 17px; margin-left: 2px; }
|
|
827
|
+
#gvc-sesspanel .go.playing { background: #fff; border: 1px solid #e5e7eb; color: #374151; }
|
|
828
|
+
#gvc-sesspanel .go.playing:hover { background: #f9fafb; border-color: #d1d5db; }
|
|
829
|
+
#gvc-sesspanel .go.playing svg { margin-left: 0; }
|
|
830
|
+
/* sound is waiting on this tab's first click (autoplay policy) — say so quietly */
|
|
831
|
+
#gvc-sesspanel .go.blocked, #gvc-sesspanel .act.blocked { border: 1px solid #fcd34d; background: #fffbeb; color: #b45309; }
|
|
832
|
+
|
|
833
|
+
/* dark hover tooltips (data-tip), deliberately quiet: no caret, slight delay */
|
|
834
|
+
#gvc-sesspanel [data-tip] { position: relative; }
|
|
835
|
+
#gvc-sesspanel [data-tip]:hover::after { content: attr(data-tip); position: absolute; top: calc(100% + 8px); left: 50%; transform: translateX(-50%); background: #1e1e1e; color: #fff; font-size: 12px; font-weight: 500; padding: 6px 10px; border-radius: 7px; white-space: nowrap; z-index: 7; pointer-events: none; opacity: 0; animation: gvc-tipin .12s ease .4s forwards; }
|
|
836
|
+
@keyframes gvc-tipin { to { opacity: 1; } }
|
|
837
|
+
|
|
838
|
+
/* the turntable, measured off the reference: disc inside the box over its shadow, the
|
|
839
|
+
arm above it with its own soft shadow, the grill at the right */
|
|
840
|
+
#gvc-sesspanel .row.music { position: static; }
|
|
841
|
+
#gvc-sesspanel .deck { position: relative; background: #f1f1f4; border-radius: 12px; height: 86px; margin-bottom: 10px; }
|
|
842
|
+
#gvc-sesspanel .deck .decksvg { position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
843
|
+
#gvc-sesspanel .deck .vinyl { transform-box: fill-box; transform-origin: center; }
|
|
844
|
+
#gvc-sesspanel .deck.playing .vinyl { animation: gvc-spin 2s linear infinite; }
|
|
845
|
+
/* idle label = white with the dark motif (the reference's resting look); playing takes the
|
|
846
|
+
track color. CSS fill wins over the SVG presentation attribute, so no rebuild needed. */
|
|
847
|
+
#gvc-sesspanel .deck:not(.playing) .label circle { fill: #fff; }
|
|
848
|
+
/* the arm rotates only 14°, so a fixed-direction shadow stays believable */
|
|
849
|
+
#gvc-sesspanel .deck .arm { transform-box: view-box; transform-origin: 160px 14px; transition: transform .5s ease; filter: drop-shadow(0 3px 2.5px rgba(0, 0, 0, .18)); }
|
|
850
|
+
#gvc-sesspanel .deck.playing .arm { transform: rotate(12deg); }
|
|
851
|
+
#gvc-sesspanel .deck.off { opacity: .75; }
|
|
852
|
+
#gvc-sesspanel .deck.off .decksvg { filter: grayscale(1); }
|
|
853
|
+
@keyframes gvc-spin { to { transform: rotate(360deg); } }
|
|
854
|
+
|
|
855
|
+
#gvc-sesspanel .musicrow { display: flex; align-items: center; gap: 10px; }
|
|
856
|
+
#gvc-sesspanel .trackbtn { flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px; border: 1px solid #e5e7eb; background: #fff; border-radius: 10px; height: 40px; padding: 0 10px; font: inherit; font-size: 12.5px; font-weight: 400; color: #1f2023; cursor: pointer; }
|
|
857
|
+
#gvc-sesspanel .trackbtn:hover, #gvc-sesspanel .trackbtn.on { background: #f9fafb; border-color: #d1d5db; }
|
|
858
|
+
#gvc-sesspanel .trackbtn .nm { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
859
|
+
#gvc-sesspanel .trackbtn .chev { display: flex; color: #6b7280; }
|
|
860
|
+
#gvc-sesspanel .trackbtn .chev svg { width: 15px; height: 15px; }
|
|
861
|
+
#gvc-sesspanel .chip { width: 24px; height: 24px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; flex: none; }
|
|
862
|
+
#gvc-sesspanel .chip svg { width: 19px; height: 19px; }
|
|
863
|
+
|
|
864
|
+
/* the track menu: dark, over the deck; selected row lights up and gains its check */
|
|
865
|
+
#gvc-sesspanel .trackmenu { position: absolute; left: 16px; right: 64px; bottom: 14px; background: #1e1e1e; border-radius: 12px; padding: 6px; box-shadow: 0 12px 32px rgba(0, 0, 0, .32); z-index: 6; }
|
|
866
|
+
#gvc-sesspanel .trackmenu.hidden { display: none; }
|
|
867
|
+
#gvc-sesspanel .trackmenu .item { display: flex; align-items: center; gap: 10px; width: 100%; border: 0; background: transparent; color: #fff; font: inherit; font-size: 13.5px; padding: 8px 10px; border-radius: 8px; cursor: pointer; text-align: left; outline: none; }
|
|
868
|
+
#gvc-sesspanel .trackmenu .item:hover, #gvc-sesspanel .trackmenu .item:focus-visible { background: #333336; }
|
|
869
|
+
#gvc-sesspanel .trackmenu .item.sel, #gvc-sesspanel .trackmenu .item.sel:hover { background: #4f46e5; }
|
|
870
|
+
#gvc-sesspanel .trackmenu .item .tick { display: none; width: 16px; color: #fff; }
|
|
871
|
+
#gvc-sesspanel .trackmenu .item .tick svg { width: 15px; height: 15px; }
|
|
872
|
+
#gvc-sesspanel .trackmenu .item.sel .tick { display: flex; }
|
|
873
|
+
#gvc-sesspanel .trackmenu .item .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
874
|
+
|
|
875
|
+
#gvc-sesspanel .empty { color: #9ca3af; font-size: 12px; padding: 2px 0; }
|
|
876
|
+
|
|
877
|
+
/* 00:00 is AUDIBLE, not visual: the last five seconds tick, the chime closes them, and the
|
|
878
|
+
timer reverts to the duration it ran. Nothing turns red and nothing shakes — an alarm
|
|
879
|
+
state parked in the corner of everyone's board outlives the moment it's announcing. */
|
|
880
|
+
|
|
881
|
+
@media (max-width: 640px) {
|
|
882
|
+
#gvc-topright { top: 8px; right: 8px; }
|
|
883
|
+
#gvc-sesspanel { left: 8px; right: 8px; width: auto; top: 58px; }
|
|
884
|
+
.gvc-sesspill .t { font-size: 12px; }
|
|
885
|
+
}
|