@felloinnovations/brand-kit 0.5.1 → 0.5.3
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/AGENTS.md +29 -9
- package/CHANGELOG.md +86 -0
- package/README.md +10 -4
- package/design-system/01-how-claude-decides.md +5 -3
- package/design-system/03-colour.md +19 -1
- package/design-system/05-space-and-layout.md +2 -1
- package/design-system/08-choosing-components.md +20 -4
- package/design-system/09-charts.md +6 -3
- package/design-system/11-page-recipes.md +40 -1
- package/design-system/13-motion.md +1 -1
- package/design-system/15-ai-experiences.md +11 -0
- package/design-system/17-decisions.md +41 -0
- package/design-system/components/AppShell/README.md +89 -21
- package/design-system/components/AppShell/preview.html +148 -3
- package/design-system/components/AppShell/style.css +47 -0
- package/design-system/components/Avatar/README.md +6 -3
- package/design-system/components/Avatar/preview.html +2 -1
- package/design-system/components/Avatar/style.css +3 -0
- package/design-system/components/Banner/README.md +9 -5
- package/design-system/components/Banner/preview.html +9 -0
- package/design-system/components/Banner/style.css +3 -0
- package/design-system/components/Button/README.md +29 -2
- package/design-system/components/Button/preview.html +33 -1
- package/design-system/components/Button/style.css +21 -0
- package/design-system/components/Calendar/README.md +148 -0
- package/design-system/components/Calendar/preview.html +449 -0
- package/design-system/components/Calendar/style.css +61 -0
- package/design-system/components/Card/README.md +13 -0
- package/design-system/components/Card/preview.html +23 -0
- package/design-system/components/Card/style.css +19 -0
- package/design-system/components/ChatCard/README.md +74 -3
- package/design-system/components/ChatCard/preview.html +126 -25
- package/design-system/components/ChatCard/style.css +6 -0
- package/design-system/components/Chip/README.md +16 -7
- package/design-system/components/Chip/preview.html +25 -1
- package/design-system/components/Chip/style.css +5 -0
- package/design-system/components/Combobox/README.md +10 -4
- package/design-system/components/Combobox/preview.html +19 -4
- package/design-system/components/Combobox/style.css +7 -1
- package/design-system/components/CommentThread/README.md +138 -0
- package/design-system/components/CommentThread/preview.html +134 -0
- package/design-system/components/CommentThread/style.css +41 -0
- package/design-system/components/DateInput/README.md +38 -4
- package/design-system/components/DateInput/preview.html +50 -2
- package/design-system/components/DateInput/style.css +6 -0
- package/design-system/components/Dialog/README.md +26 -3
- package/design-system/components/Dialog/preview.html +80 -0
- package/design-system/components/Dialog/style.css +28 -3
- package/design-system/components/EmptyState/README.md +1 -1
- package/design-system/components/Fold/README.md +21 -0
- package/design-system/components/Fold/preview.html +44 -1
- package/design-system/components/Fold/style.css +30 -0
- package/design-system/components/InlineEdit/README.md +116 -0
- package/design-system/components/InlineEdit/preview.html +135 -0
- package/design-system/components/InlineEdit/style.css +34 -0
- package/design-system/components/Input/README.md +20 -1
- package/design-system/components/Input/preview.html +24 -0
- package/design-system/components/Input/style.css +13 -1
- package/design-system/components/KanbanBoard/README.md +68 -4
- package/design-system/components/KanbanBoard/preview.html +129 -0
- package/design-system/components/KanbanBoard/style.css +31 -0
- package/design-system/components/PlanTimeline/README.md +144 -0
- package/design-system/components/PlanTimeline/preview.html +109 -0
- package/design-system/components/PlanTimeline/style.css +64 -0
- package/design-system/components/README.md +11 -6
- package/design-system/components/Skeleton/README.md +66 -2
- package/design-system/components/Skeleton/preview.html +83 -1
- package/design-system/components/Starter/README.md +1 -1
- package/design-system/components/Starter/preview.html +1 -1
- package/design-system/components/Stepper/README.md +81 -5
- package/design-system/components/Stepper/preview.html +64 -1
- package/design-system/components/Stepper/style.css +29 -0
- package/design-system/components/StreakCalendar/README.md +1 -0
- package/design-system/components/Table/README.md +7 -3
- package/design-system/components/Table/preview.html +15 -0
- package/design-system/components/Table/style.css +2 -0
- package/design-system/components/TextDiff/README.md +96 -0
- package/design-system/components/TextDiff/preview.html +42 -0
- package/design-system/components/TextDiff/style.css +21 -0
- package/design-system/components/Timeline/README.md +3 -1
- package/design-system/components/TrendLine/README.md +38 -7
- package/design-system/components/TrendLine/preview.html +140 -3
- package/design-system/components/TrendLine/style.css +12 -2
- package/design-system/components/bundle.css +483 -7
- package/design-system/tokens.json +15 -1
- package/dist/fello-core.css +2 -2
- package/dist/fello.css +2 -2
- package/dist/fello.scoped.css +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/scoped.js +2 -2
- package/dist/tokens.d.ts +8 -0
- package/dist/tokens.js +8 -0
- package/package.json +7 -2
- package/scripts/check-drift.mjs +325 -0
- package/scripts/token-model.mjs +2 -1
- package/tokens/_dark.css +2 -0
- package/tokens/_product.css +2 -0
- package/tokens/palettes.ts +1 -1
- package/tokens/shadcn-compat.css +1 -1
- package/tokens/shadcn-compat.hsl.css +1 -1
- package/tokens/tokens.css +14 -1
- package/tokens/tokens.md +53 -46
- package/tokens/tokens.root.css +14 -1
- package/tokens/tokens.ts +9 -1
|
@@ -402,6 +402,27 @@ body { margin: 0; background: var(--color-bg-page); color: var(--color-text-body
|
|
|
402
402
|
.ws-btn-split { display: flex; flex-wrap: wrap; gap: var(--space-xs); width: 100%; }
|
|
403
403
|
.ws-btn-split > .ws-btn { flex: 1 1 var(--space-8xl); min-width: fit-content; }
|
|
404
404
|
|
|
405
|
+
/* An act with a menu of its other forms (#22): the act and a caret joined into one shape, the caret opening a Menu of
|
|
406
|
+
the same act done another way ("Approve and post now", "Approve and schedule"). Not ws-btn-split (two different acts
|
|
407
|
+
side by side). The inner corners go square; a hairline in the words' colour at 35 % parts the halves, inset 6 from the
|
|
408
|
+
top and bottom (on navy, the white words' line; two outlined halves share one edge instead); the caret is a square of
|
|
409
|
+
its height with a 16 px chevron (28 at small and medium, 40 large). The pair gives to a press as one: neither half
|
|
410
|
+
moves alone, so the join never opens. Each half takes its own focus ring. */
|
|
411
|
+
.ws-btn-join { display: inline-flex; flex-shrink: 0; max-width: 100%; }
|
|
412
|
+
.ws-btn-join > .ws-btn { --ws-press: 1; }
|
|
413
|
+
.ws-btn-join > .ws-btn:first-child { min-width: 0; border-start-end-radius: 0; border-end-end-radius: 0; }
|
|
414
|
+
.ws-btn-join > .ws-btn:last-child { --ws-btn-pad: var(--space-xxs-plus); border-start-start-radius: 0; border-end-start-radius: 0; }
|
|
415
|
+
.ws-btn-join > .ws-btn--lg:last-child { --ws-btn-pad: var(--space-sm); }
|
|
416
|
+
@media (pointer: coarse) { .ws-btn-join > .ws-btn:last-child { --ws-btn-pad: var(--space-sm); } }
|
|
417
|
+
.ws-btn-join > .ws-btn:last-child > svg { width: var(--ws-icon); height: var(--ws-icon); }
|
|
418
|
+
.ws-btn-join > .ws-btn + .ws-btn::after { content: ""; position: absolute; inset-block: var(--space-xxs-plus); inset-inline-start: 0; width: var(--ws-hairline); background: currentColor; opacity: 0.35; pointer-events: none; }
|
|
419
|
+
.ws-btn-join > .ws-btn--secondary + .ws-btn--secondary { margin-inline-start: calc(var(--ws-hairline) * -1); }
|
|
420
|
+
.ws-btn-join > .ws-btn--secondary + .ws-btn--secondary::after { content: none; }
|
|
421
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
422
|
+
.ws-btn-join { transition: scale var(--ws-spring-press-duration) var(--ws-spring-press); }
|
|
423
|
+
.ws-btn-join:active { scale: var(--ws-press-scale); }
|
|
424
|
+
}
|
|
425
|
+
|
|
405
426
|
/* LinkPending: a link card or a ButtonLink whose page is on its way. Its 1 px navy edge arrives after a beat (a quick
|
|
406
427
|
answer shows nothing) and breathes until the page answers; it leaves at once. The link holds it (position: relative). */
|
|
407
428
|
.ws-link-pending { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0; }
|
|
@@ -466,10 +487,15 @@ body { margin: 0; background: var(--color-bg-page); color: var(--color-text-body
|
|
|
466
487
|
.ws-chip--live { background: var(--ws-tint-live); color: var(--ws-word-positive); box-shadow: none; }
|
|
467
488
|
.ws-chip--navy { background: var(--ws-surface-sunk); color: var(--color-text-heading); font-weight: 600; box-shadow: none; }
|
|
468
489
|
.ws-chip--needs { background: var(--ws-tint-needs); color: var(--ws-word-needs); box-shadow: none; }
|
|
490
|
+
.ws-chip--alert { background: var(--ws-tint-alert); color: var(--ws-word-alert); box-shadow: none; } /* an error status (#15) */
|
|
469
491
|
/* Rating tones (D10): strong is --live; the middle band ("Getting there") is mid blue; needs work is Seth's pink as a
|
|
470
492
|
20% tint, its words the AA step (#B42D68, 301:2018). */
|
|
471
493
|
.ws-chip--middle { background: var(--ws-card-tint-blue); color: var(--ws-rating-middle); box-shadow: none; }
|
|
472
494
|
.ws-chip--needs-work { background: var(--ws-tint-needs-work); color: var(--ws-word-needs-work); box-shadow: none; }
|
|
495
|
+
/* Small (#30): the dense step for several tags in one place (a board card, a table cell, a list row): 20 tall, 12 / 16
|
|
496
|
+
medium words, 6 px sides, the 12 px icon 4 before them. A fact only, never a control: a chip that acts keeps 24. Any
|
|
497
|
+
tone. */
|
|
498
|
+
.ws-chip--sm { height: calc(var(--space-md) + var(--space-xxs)); padding: 0 var(--space-xxs-plus); font-size: var(--ws-text-dense); line-height: var(--space-md); }
|
|
473
499
|
/* Example (issue #8, Seth's v7 first open): made-up numbers are chipped. A 1 px dashed edge, 11 px semibold, a round tag. */
|
|
474
500
|
.ws-chip--example { border-radius: var(--ws-radius-bubble); background: transparent; color: var(--color-text-muted); font-size: var(--ws-text-micro); line-height: var(--ws-leading-micro); font-weight: 600; box-shadow: none; outline: var(--ws-line) dashed var(--color-text-muted); outline-offset: calc(var(--ws-line) * -1); }
|
|
475
501
|
/* ---- Badge: a count or a dot on a face, a row or a button (and the kit's marketing pill). North's count (0.5.0, D1;
|
|
@@ -515,9 +541,21 @@ body { margin: 0; background: var(--color-bg-page); color: var(--color-text-body
|
|
|
515
541
|
.ws-search-wrap { position: relative; display: flex; min-width: 0; }
|
|
516
542
|
.ws-search-wrap svg { position: absolute; left: var(--space-xs); top: 50%; width: var(--ws-icon); height: var(--ws-icon); transform: translateY(-50%); color: var(--color-text-muted); pointer-events: none; }
|
|
517
543
|
.ws-search { height: var(--ws-control-h-md); padding: 0 var(--space-sm) 0 var(--space-xl); }
|
|
544
|
+
/* The colour field (#29): a swatch that opens the browser's colour picker (a native <input type="color">) and the hex
|
|
545
|
+
field beside it, 8 apart. The swatch is a square the field's height, its 4 px corners and 0.5 px edge drawn inside, so
|
|
546
|
+
a white or cream colour still shows its edge; its fill is the input's own value, the one colour the kit paints that
|
|
547
|
+
no token sets. An invalid hex marks the field and leaves the swatch on the last good colour. */
|
|
548
|
+
.ws-colour-field { display: flex; align-items: center; gap: var(--space-xs); min-width: 0; }
|
|
549
|
+
.ws-colour-field__swatch { flex-shrink: 0; width: var(--ws-control-h); height: var(--ws-control-h); padding: 0; border: 0; border-radius: var(--ws-radius-control); background: none; cursor: pointer; }
|
|
550
|
+
.ws-colour-field__swatch::-webkit-color-swatch-wrapper { padding: 0; }
|
|
551
|
+
/* The edge sits on the swatch itself: the browser paints the colour over anything drawn on the input's own box. */
|
|
552
|
+
.ws-colour-field__swatch::-webkit-color-swatch { border: var(--ws-hairline) solid var(--ws-edge-card); border-radius: inherit; }
|
|
553
|
+
.ws-colour-field__swatch::-moz-color-swatch { border: var(--ws-hairline) solid var(--ws-edge-card); border-radius: inherit; }
|
|
554
|
+
.ws-colour-field__swatch:disabled { cursor: not-allowed; opacity: 0.5; }
|
|
555
|
+
.ws-colour-field .ws-input { font-variant-numeric: tabular-nums; }
|
|
518
556
|
/* Touch: 40 tall (the house's touch height, restated after the field's own so it still wins), the words 16 so a phone
|
|
519
557
|
does not zoom the page into the field when it takes focus (iOS zooms any field under 16 px). */
|
|
520
|
-
@media (pointer: coarse) { .ws-input, .ws-search { height: var(--ws-touch); font-size: var(--ws-text-value); } }
|
|
558
|
+
@media (pointer: coarse) { .ws-input, .ws-search { height: var(--ws-touch); font-size: var(--ws-text-value); } .ws-colour-field__swatch { width: var(--ws-touch); height: var(--ws-touch); } }
|
|
521
559
|
/* ---- Select: a native select dressed as the house field (0.5.0, restated so the house's Fields copy can go): Input's
|
|
522
560
|
ground, edge, hover, focus ring and error, a 16 px navy chevron at the right (Seth's chevrons are navy), the chosen
|
|
523
561
|
option cut with an ellipsis when it is longer than the field. --sm: Seth's chip select (319:3460), 28 tall on the
|
|
@@ -792,6 +830,25 @@ a.ws-card[aria-busy="true"]::before { content: ""; position: absolute; inset: ca
|
|
|
792
830
|
/* Selected, in a row of cards (North): the rim darkens and a 2 px navy tab sits 8 under the card, 16 in from each side. */
|
|
793
831
|
.ws-card--selected { position: relative; border-color: var(--ws-well-hover); box-shadow: none; }
|
|
794
832
|
.ws-card--selected::after { content: ""; position: absolute; left: var(--space-md); right: var(--space-md); top: calc(100% + var(--ws-rim) + var(--space-xs)); height: var(--ws-ring); border-radius: var(--ws-radius-chip); background: var(--color-text-heading); }
|
|
833
|
+
/* A picture in a card (ws-card__media, its first part: an image tile, a template or asset choice): edge to edge, its top
|
|
834
|
+
corners rounded to the cream's own (7), so the card is never clipped (K9) and a menu, a tip or the selected tab still
|
|
835
|
+
shows. The sunk cream behind it while it loads; a logo on a checker is the app's own ground. */
|
|
836
|
+
:where(button).ws-card { padding: 0; } /* the browser's button padding is not the card's: a picture runs edge to edge; the padding steps below still win */
|
|
837
|
+
.ws-card__media ~ .ws-card__body { display: block; padding: var(--space-xs) var(--space-sm) var(--space-sm); } /* a caption under a picture: 8 above, 12 round */
|
|
838
|
+
.ws-card__media { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--ws-radius-card-inner) var(--ws-radius-card-inner) 0 0; background: var(--ws-surface-sunk); }
|
|
839
|
+
/* Selected in a grid, or on a card that clips its content (--ring): the tab under the card would fall in the gap above
|
|
840
|
+
the next row, so the choice is drawn on the card itself. The rim turns navy with a hairline more outside it, a 2 px
|
|
841
|
+
ring, flush (the focus ring stands 2 off, so the two never look alike), and a tick (ws-card__tick, in the markup at
|
|
842
|
+
rest and shown only when selected) sits on the top right corner, on a cream ring so it reads over a photo. */
|
|
843
|
+
.ws-card--ring { position: relative; }
|
|
844
|
+
.ws-card--ring.ws-card--selected, .ws-card--ring.ws-card--selected:hover { border-color: var(--color-text-heading); box-shadow: 0 0 0 calc(var(--ws-ring) - var(--ws-rim)) var(--color-text-heading); }
|
|
845
|
+
.ws-card--ring.ws-card--selected::after { content: none; }
|
|
846
|
+
.ws-card__tick { position: absolute; top: var(--space-xs); right: var(--space-xs); z-index: 1; display: grid; place-items: center; width: var(--space-lg); height: var(--space-lg); border-radius: var(--radius-full); background: var(--color-text-heading); color: var(--ws-on-act); box-shadow: 0 0 0 var(--ws-ring) var(--color-bg-page); opacity: 0; transform: scale(var(--ws-pop-scale)); pointer-events: none; }
|
|
847
|
+
.ws-card__tick svg { width: var(--ws-icon-sm); height: var(--ws-icon-sm); }
|
|
848
|
+
.ws-card--selected > .ws-card__tick { opacity: 1; transform: none; }
|
|
849
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
850
|
+
.ws-card__tick { transition: opacity var(--ws-duration-instant) var(--ws-ease-soft), transform var(--ws-duration-settle) var(--ws-ease-pop); }
|
|
851
|
+
}
|
|
795
852
|
/* The card's own padding steps. */
|
|
796
853
|
.ws-card--pad-sm { padding: var(--ws-card-padding); }
|
|
797
854
|
.ws-card--pad-md { padding: var(--ws-card-padding-lg); }
|
|
@@ -940,6 +997,9 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
940
997
|
house fade of the whole disc left them at 3.7:1); a photo in it fades to 60 %. */
|
|
941
998
|
.ws-avatar--dim { opacity: 1; color: var(--color-text-muted); }
|
|
942
999
|
.ws-avatar--dim > img { opacity: 0.6; }
|
|
1000
|
+
/* Extra small (#16): 20, a line of words' height, for a face inside a chip, a dense picker's row or a task row's
|
|
1001
|
+
assignee: initials 11 px semibold. A stack of them overlaps 4, as every stack. */
|
|
1002
|
+
.ws-avatar--xs { width: calc(var(--space-md) + var(--space-xxs)); height: calc(var(--space-md) + var(--space-xxs)); font-size: var(--ws-text-micro); }
|
|
943
1003
|
/* "+N" in a stack: the same disc, tabular figures, so +9 and +12 sit alike. */
|
|
944
1004
|
.ws-avatar-stack__more { font-variant-numeric: tabular-nums; }
|
|
945
1005
|
/* ---- ActionList: a card's ranked list of next acts: who or what, the reason in words, the one act at the right. --brief
|
|
@@ -1080,6 +1140,35 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1080
1140
|
/* A row's count: Seth's navy pill, 18 tall, its figure on the act fill's word colour. */
|
|
1081
1141
|
.ws-app-shell__count { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; min-width: calc(var(--space-md) + var(--space-3xs)); height: calc(var(--space-md) + var(--space-3xs)); padding: 0 var(--space-xxs); border-radius: var(--radius-full); background: var(--color-text-heading); color: var(--ws-on-act); font-size: var(--ws-text-micro); line-height: 1; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
1082
1142
|
|
|
1143
|
+
/* A section: a row that folds its pages open in place (North "Sidebar sections open / close", the house's fold on every
|
|
1144
|
+
kit <details>). The row is the <summary>, wearing __row, its chevron at the end turning down as it opens; its pages sit
|
|
1145
|
+
one level in, the icon box and its gap (24), so their words start on the section's words. A closed section that holds
|
|
1146
|
+
the current page wears the current pill itself, so the page is never lost behind a fold. */
|
|
1147
|
+
.ws-app-shell__section > summary { list-style: none; }
|
|
1148
|
+
.ws-app-shell__section > summary::-webkit-details-marker { display: none; }
|
|
1149
|
+
.ws-app-shell__section > .ws-app-shell__rows { margin-inline-start: calc(var(--ws-icon-lg) + var(--space-xxs)); padding-block-start: var(--space-3xs); }
|
|
1150
|
+
.ws-app-shell__row > .ws-app-shell__chevron { color: var(--ws-mark-muted); }
|
|
1151
|
+
.ws-app-shell__section[open] > summary > .ws-app-shell__chevron { transform: rotate(90deg); }
|
|
1152
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--color-text-brand-strong); }
|
|
1153
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary > svg:first-child { color: var(--ws-chart-under-half); }
|
|
1154
|
+
/* Folded, a section cannot open in 64 px: its row (__fly, a button) opens its pages as a menu panel beside the rail
|
|
1155
|
+
(__panel, a native popover: the top layer, light dismiss, Escape back to the button). The <details> leaves the rail.
|
|
1156
|
+
The panel names the section (its eyebrow, which the rail hides) over its pages as menu rows. */
|
|
1157
|
+
.ws-app-shell__fly { display: none; }
|
|
1158
|
+
.ws-app-shell--folded .ws-app-shell__section { display: none; }
|
|
1159
|
+
.ws-app-shell--folded .ws-app-shell__fly { display: flex; }
|
|
1160
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--color-text-brand-strong); }
|
|
1161
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) > svg { color: var(--ws-chart-under-half); }
|
|
1162
|
+
.ws-app-shell--folded .ws-app-shell__panel .ws-app-shell__eyebrow { display: block; }
|
|
1163
|
+
.ws-app-shell__panel > .ws-app-shell__eyebrow { margin: 0; }
|
|
1164
|
+
.ws-app-shell__panel > ul { display: flex; flex-direction: column; gap: var(--space-3xs); }
|
|
1165
|
+
/* Beside the rail, its top on the button's, 8 past the rail's edge (the button sits 12 inside it); it flips up when the
|
|
1166
|
+
room below runs out. It grows from that corner. */
|
|
1167
|
+
.ws-app-shell__panel { --ws-menu-from: 0px; transform-origin: top left; }
|
|
1168
|
+
@supports (position-area: block-end) {
|
|
1169
|
+
.ws-app-shell__panel.ws-menu[popover] { margin: 0 var(--space-xs) 0 calc(var(--space-sm) + var(--space-xs)); position-area: inline-end span-block-end; justify-self: auto; position-try-fallbacks: flip-block; }
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1083
1172
|
/* The person, pinned at the foot under a full-width rule. */
|
|
1084
1173
|
.ws-app-shell__foot { flex-shrink: 0; display: flex; align-items: center; gap: var(--space-xs-plus); margin-top: auto; padding: var(--space-sm) var(--space-xs) var(--space-xxs-plus) var(--space-sm); border-top: var(--ws-line) solid var(--ws-edge-card); }
|
|
1085
1174
|
.ws-app-shell__foot > :first-child { margin-inline-start: var(--space-xs-plus); }
|
|
@@ -1091,6 +1180,14 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1091
1180
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > :is(.ws-app-shell__wordmark, .ws-app-shell__foot) { position: sticky; z-index: 1; background: var(--ws-frame); background-attachment: fixed; }
|
|
1092
1181
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__wordmark { top: 0; min-height: calc(var(--ws-header-h) + var(--ws-line)); margin-inline: 0; padding: var(--ws-line) var(--space-xs) 0 calc(var(--space-sm) + var(--space-xs-plus)); }
|
|
1093
1182
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__foot { bottom: 0; padding-bottom: var(--space-sm-plus); }
|
|
1183
|
+
/* The person as a button (__me) that opens the account menu (Menu, above it once it flips): the foot keeps its height and
|
|
1184
|
+
edges, the button takes a row's 10 sides and 6 px corners, so the avatar stays 22 in and the wash reads as a row's. */
|
|
1185
|
+
.ws-app-shell__me { display: flex; flex: 1; align-items: center; gap: var(--space-xs-plus); min-width: 0; margin-block: calc(var(--space-3xs) * -1); padding: var(--space-3xs) var(--space-xs-plus); border: 0; border-radius: var(--ws-radius-row); background: none; font: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
1186
|
+
.ws-app-shell__foot > .ws-app-shell__me { margin-inline: 0; }
|
|
1187
|
+
.ws-app-shell__me:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
1188
|
+
@media (hover: hover) { .ws-app-shell__me:hover { background: var(--ws-hover); } }
|
|
1189
|
+
.ws-app-shell__me:active { background: var(--ws-hover-strong); }
|
|
1190
|
+
.ws-app-shell--folded .ws-app-shell__me { flex: 0 0 auto; gap: 0; }
|
|
1094
1191
|
.ws-app-shell__foot-words { display: flex; flex-direction: column; min-width: 0; }
|
|
1095
1192
|
.ws-app-shell__foot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--ws-text-control); line-height: var(--space-md); font-weight: 500; color: var(--color-text-heading); }
|
|
1096
1193
|
|
|
@@ -1180,6 +1277,9 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1180
1277
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))) { position: relative; anchor-scope: --ws-app-shell-current; }
|
|
1181
1278
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups)))::before { content: ""; position: absolute; position-anchor: --ws-app-shell-current; inset: anchor(top) anchor(right) anchor(bottom) anchor(left); border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); opacity: 0; pointer-events: none; }
|
|
1182
1279
|
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { anchor-name: --ws-app-shell-current; }
|
|
1280
|
+
/* A closed section, or a folded section's button, stands in for the current page it holds (the page itself has no box). */
|
|
1281
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary,
|
|
1282
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) { anchor-name: --ws-app-shell-current; }
|
|
1183
1283
|
.ws-app-shell__eyebrow { position: relative; } /* a group's name stays over the pill as it passes, as the rows do */
|
|
1184
1284
|
}
|
|
1185
1285
|
|
|
@@ -1188,10 +1288,17 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1188
1288
|
.ws-app-shell > .ws-app-shell__nav { transition: width var(--ws-duration-shift) var(--ws-ease-shift); }
|
|
1189
1289
|
.ws-app-shell :is(.ws-app-shell__wordmark, .ws-app-shell__name, .ws-app-shell__foot-words) { transition: opacity var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1190
1290
|
.ws-app-shell__row { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft), color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1291
|
+
.ws-app-shell__me { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1292
|
+
.ws-app-shell__chevron { transition: transform var(--ws-spring-fold-duration) var(--ws-spring-fold); }
|
|
1293
|
+
@supports (position-area: block-end) {
|
|
1294
|
+
.ws-app-shell__panel.ws-menu[popover]:not(:popover-open) { margin-block-start: 0; }
|
|
1295
|
+
@starting-style { .ws-app-shell__panel.ws-menu[popover]:popover-open { margin-block-start: 0; } }
|
|
1296
|
+
}
|
|
1191
1297
|
.ws-app-shell__bar { transition: box-shadow var(--ws-duration-settle) var(--ws-ease-soft); }
|
|
1192
1298
|
@supports (anchor-name: --a) {
|
|
1193
1299
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within)::before { opacity: 1; transition: inset var(--ws-spring-nav-duration) var(--ws-spring-nav); }
|
|
1194
1300
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within) .ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { background: none; box-shadow: none; }
|
|
1301
|
+
.ws-app-shell__groups:is(:hover, :focus-within) :is(.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary, .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current))) { background: none; box-shadow: none; }
|
|
1195
1302
|
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { transition: color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1196
1303
|
}
|
|
1197
1304
|
}
|
|
@@ -1213,6 +1320,9 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1213
1320
|
.ws-banner--positive .ws-banner__mark, .ws-banner--positive .ws-banner__lead { color: var(--ws-word-positive); }
|
|
1214
1321
|
.ws-banner--working { background: var(--ws-card-tint-blue); }
|
|
1215
1322
|
.ws-banner--working .ws-banner__mark, .ws-banner--working .ws-banner__lead { color: var(--color-text-interactive); }
|
|
1323
|
+
/* An error status (#15): something wrong now, the alert red's tint and words. */
|
|
1324
|
+
.ws-banner--alert { background: var(--ws-tint-alert); }
|
|
1325
|
+
.ws-banner--alert .ws-banner__mark, .ws-banner--alert .ws-banner__lead { color: var(--ws-word-alert); }
|
|
1216
1326
|
@media (pointer: coarse) { .ws-banner { --ws-banner-band: var(--ws-touch); } }
|
|
1217
1327
|
/* ---- Breadcrumb: where a page sits, Seth's North crumbs (D1): 14 px words in the body grey, a muted "/" between, the
|
|
1218
1328
|
current page navy medium; one line. The root crumb never shrinks, the current page gives way first, every link's
|
|
@@ -1231,6 +1341,67 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1231
1341
|
.ws-breadcrumb__ellipsis { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; min-width: var(--space-lg); min-height: var(--space-lg); margin-inline: calc((var(--space-xxs-plus) + var(--ws-line)) * -1); padding: 0 var(--space-xxs); border: 0; border-radius: var(--ws-radius-control); background: none; font-size: inherit; color: inherit; cursor: pointer; }
|
|
1232
1342
|
@media (hover: hover) { .ws-breadcrumb__ellipsis:hover { background: var(--ws-hover-strong); color: var(--color-text-heading); text-decoration: none; } }
|
|
1233
1343
|
@media (prefers-reduced-motion: no-preference) { .ws-breadcrumb :is(a, .ws-breadcrumb__ellipsis) { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft), color var(--ws-duration-hover) var(--ws-ease-soft); } }
|
|
1344
|
+
/* ---- Calendar: things on dates. A month (or a week) of days, Monday first, each day holding its items as rows that
|
|
1345
|
+
open; the steps and the view switch over it. The days abut on 0.5 px lines; today's number is ringed in the needs
|
|
1346
|
+
step, as DateInput's is. Under 40rem of its own width the month reads as an agenda: a row a day, the days with
|
|
1347
|
+
nothing hidden, today kept. ---- */
|
|
1348
|
+
.ws-calendar { container: ws-calendar / inline-size; display: flex; flex-direction: column; gap: var(--space-sm); min-width: 0; }
|
|
1349
|
+
.ws-calendar__bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-xs) var(--space-sm); }
|
|
1350
|
+
.ws-calendar__title { margin-right: auto; }
|
|
1351
|
+
.ws-calendar__steps { display: flex; align-items: center; gap: var(--space-xxs); }
|
|
1352
|
+
/* The key: what each item's tone means, in the meta words, each beside its mark (__item-mark with the item's tone class). */
|
|
1353
|
+
/* Fixed 160 columns, so a key that wraps keeps its marks on shared edges. */
|
|
1354
|
+
.ws-calendar__key { display: grid; grid-template-columns: repeat(auto-fill, var(--space-8xl)); gap: var(--space-xxs) var(--space-sm); font-size: var(--ws-text-dense); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
1355
|
+
.ws-calendar__key > span { display: inline-flex; align-items: center; gap: var(--space-xxs-plus); }
|
|
1356
|
+
/* The lit period's swatch in the key: the tint on a 0.5 px edge. */
|
|
1357
|
+
.ws-calendar__key-lit { flex: none; width: var(--space-sm); height: var(--space-sm); border-radius: var(--ws-radius-chip); background: var(--ws-tint-accent); box-shadow: inset 0 0 0 var(--ws-hairline) var(--ws-edge-card); }
|
|
1358
|
+
/* Weekday names once over the grid, 11 px medium muted, centred over their days as the numbers are; read aloud with
|
|
1359
|
+
each day instead. */
|
|
1360
|
+
.ws-calendar__weekdays { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); font-size: var(--ws-text-micro); line-height: var(--ws-leading-micro); font-weight: 500; color: var(--color-text-muted); text-align: center; }
|
|
1361
|
+
/* The days: the 0.5 px lines are the grid's own ground showing between them. */
|
|
1362
|
+
.ws-calendar__days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--ws-hairline); overflow: hidden; border: var(--ws-hairline) solid var(--ws-edge-card); border-radius: var(--ws-radius-control); background: var(--ws-edge-card); }
|
|
1363
|
+
.ws-calendar__day { display: flex; flex-direction: column; gap: var(--space-3xs); min-width: 0; min-height: var(--space-7xl); padding: var(--space-xxs); background: var(--color-bg-page); }
|
|
1364
|
+
/* Days in a period the view is about (the launch week, the phase in hand): one quiet tint, named in the key. */
|
|
1365
|
+
.ws-calendar__day--lit { background: var(--ws-tint-accent); }
|
|
1366
|
+
.ws-calendar__day-head { display: flex; align-items: center; justify-content: center; min-width: 0; min-height: var(--space-lg); }
|
|
1367
|
+
.ws-calendar__weekday { display: none; }
|
|
1368
|
+
.ws-calendar__date { display: inline-flex; flex: none; align-items: center; justify-content: center; min-width: var(--space-lg); height: var(--space-lg); padding: 0 var(--space-xxs); border-radius: var(--radius-full); font-size: var(--ws-text-dense); line-height: var(--space-md); font-weight: 500; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
|
|
1369
|
+
.ws-calendar__day--outside .ws-calendar__date { color: var(--color-text-muted); font-weight: 400; }
|
|
1370
|
+
/* Today: a 1 px ring and the number in the salmon's AA step (the salmon itself is 2.6:1 on the cream). */
|
|
1371
|
+
.ws-calendar__date[aria-current="date"] { box-shadow: inset 0 0 0 var(--ws-line) var(--ws-word-needs); color: var(--ws-word-needs); font-weight: 600; }
|
|
1372
|
+
.ws-calendar__items { display: flex; flex-direction: column; gap: var(--space-3xs); min-width: 0; }
|
|
1373
|
+
/* An item: a 24 px row on the sunk cream, its tone a 6 px dot (a milestone a diamond), its words one line, whole in
|
|
1374
|
+
its title. A link or a button when it opens the thing; hover is the wash, never a lift. */
|
|
1375
|
+
.ws-calendar__item { --ws-calendar-tone: var(--tone, var(--color-text-heading)); display: flex; align-items: center; gap: var(--space-xxs-plus); min-width: 0; min-height: var(--space-lg); padding: 0 var(--space-xxs-plus); border-radius: var(--ws-radius-control); background-color: var(--ws-surface-sunk); font-size: var(--ws-text-dense); line-height: var(--space-md); color: var(--color-text-heading); text-align: left; text-decoration: none; }
|
|
1376
|
+
.ws-calendar__item::before, .ws-calendar__item-mark { content: ""; flex: none; width: var(--space-xxs-plus); height: var(--space-xxs-plus); border-radius: var(--radius-full); background: var(--ws-calendar-tone, var(--color-text-heading)); }
|
|
1377
|
+
.ws-calendar__item > span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
1378
|
+
.ws-calendar__item--2 { --ws-calendar-tone: var(--tone, var(--ws-chart-finished)); }
|
|
1379
|
+
.ws-calendar__item--3 { --ws-calendar-tone: var(--tone, var(--ws-chart-past-half)); }
|
|
1380
|
+
.ws-calendar__item--needs { --ws-calendar-tone: var(--tone, var(--ws-chart-under-half)); }
|
|
1381
|
+
.ws-calendar__item--milestone::before, .ws-calendar__item-mark.ws-calendar__item--milestone { border-radius: var(--ws-radius-bar); transform: rotate(45deg); }
|
|
1382
|
+
.ws-calendar__item--milestone { font-weight: 500; }
|
|
1383
|
+
:is(a, button).ws-calendar__item { cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1384
|
+
@media (hover: hover) { :is(a, button).ws-calendar__item:hover { background-image: linear-gradient(var(--ws-hover), var(--ws-hover)); } }
|
|
1385
|
+
:is(a, button).ws-calendar__item:active { background-image: linear-gradient(var(--ws-hover-strong), var(--ws-hover-strong)); }
|
|
1386
|
+
/* Touch: an item is a boxed control, so 40 tall (K10). */
|
|
1387
|
+
@media (pointer: coarse) { .ws-calendar__item { min-height: var(--ws-touch); } }
|
|
1388
|
+
.ws-calendar__item:focus-visible { outline: var(--ws-focus-width) solid var(--ws-focus-color); outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
1389
|
+
.ws-calendar__more { align-self: flex-start; justify-self: start; margin-left: 0; font-size: var(--ws-text-dense); }
|
|
1390
|
+
.ws-calendar__empty { padding: var(--space-xxs) var(--space-xxs-plus); font-size: var(--ws-text-dense); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
1391
|
+
/* Week: one row of seven taller days. */
|
|
1392
|
+
.ws-calendar--week .ws-calendar__day { min-height: var(--space-9xl); }
|
|
1393
|
+
/* Agenda: a narrow column (a phone) reads the same markup as a list of the days that hold something. */
|
|
1394
|
+
@container ws-calendar (width < 40rem) {
|
|
1395
|
+
.ws-calendar__weekdays { display: none; }
|
|
1396
|
+
.ws-calendar__days { grid-template-columns: minmax(0, 1fr); gap: 0; border: 0; border-radius: 0; background: none; }
|
|
1397
|
+
.ws-calendar__days > .ws-calendar__day { display: grid; grid-template-columns: var(--space-2xl) minmax(0, 1fr); align-items: start; gap: var(--space-3xs) var(--space-sm); min-height: 0; padding: var(--space-xs) 0; }
|
|
1398
|
+
.ws-calendar__items, .ws-calendar__more, .ws-calendar__empty { grid-column: 2; }
|
|
1399
|
+
.ws-calendar__day + .ws-calendar__day { border-top: var(--ws-hairline) solid var(--ws-edge-card); }
|
|
1400
|
+
.ws-calendar__day--outside, .ws-calendar__day:not(:has(.ws-calendar__item, [aria-current="date"])) { display: none; }
|
|
1401
|
+
.ws-calendar__day--lit { background: none; }
|
|
1402
|
+
.ws-calendar__day-head { flex-direction: column; gap: 0; }
|
|
1403
|
+
.ws-calendar__weekday { display: block; font-size: var(--ws-text-micro); line-height: var(--ws-leading-micro); font-weight: 500; color: var(--color-text-muted); }
|
|
1404
|
+
}
|
|
1234
1405
|
/* ---- ChatCard: a card the agent brings into the chat (North's chat card: at most 556, 20 inside, the 80 px tray): a door on the left of its band, its acts on the right ---- */
|
|
1235
1406
|
/* North's chat card keeps the 0.5 px card edge on the chat's glass (the page's tray rim is for page cards). It
|
|
1236
1407
|
measures itself, so a narrow chat gets the narrow band. */
|
|
@@ -1248,6 +1419,12 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1248
1419
|
.ws-chat-card:has(> .ws-chat-card__close) > .ws-chat-card__head { padding-right: var(--space-xl); }
|
|
1249
1420
|
.ws-chat-card__facts { display: flex; flex-wrap: wrap; gap: var(--space-xxs) var(--space-md); font-size: var(--ws-text-dense); color: var(--color-text-body); }
|
|
1250
1421
|
.ws-chat-card__facts b { font-weight: 600; color: var(--color-text-heading); }
|
|
1422
|
+
/* An email, a call or a text: when it happened sits at the head's end, one line, in the meta words. */
|
|
1423
|
+
.ws-chat-card__when { flex-shrink: 0; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1424
|
+
/* Its own words (an email's opening lines, a text) quoted on a sunk plate, never adopted: at most three lines, the door
|
|
1425
|
+
opens the rest. The clamp sits on the words, not the plate, so a fourth line never shows in the plate's padding. */
|
|
1426
|
+
.ws-chat-card__quote { margin: 0; }
|
|
1427
|
+
.ws-chat-card__quote > p { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; }
|
|
1251
1428
|
/* A chart answer's drawing: the dotted field runs to the card's edges and sits only behind the drawing, 24 under it. */
|
|
1252
1429
|
.ws-chat-card__graph { margin: var(--space-xs) calc(var(--ws-card-inset) * -1) 0; padding: var(--space-md) var(--ws-card-inset) var(--space-lg); }
|
|
1253
1430
|
/* A breakdown under the answer: rows edge to edge, 44 tall, 0.5 px rules, the label left and the figure right. */
|
|
@@ -1522,7 +1699,13 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1522
1699
|
(never cut, never scrolled sideways); one line is 32 tall. */
|
|
1523
1700
|
.ws-combobox__option { display: block; padding: calc((var(--ws-combobox-row, var(--ws-control-h)) - var(--space-md) - var(--space-xxs)) / 2) var(--space-sm); border-radius: var(--ws-radius-row); font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); cursor: pointer; }
|
|
1524
1701
|
@media (hover: hover) { .ws-combobox__option:hover { background: var(--ws-hover); } }
|
|
1525
|
-
|
|
1702
|
+
/* Two marks that never look alike (#19): the keyboard's row is the sunk cream; the chosen row carries Menu's tick, 16 px
|
|
1703
|
+
navy at its end, centred on its first line, so a list that opens on the current value and is then walked with the
|
|
1704
|
+
arrows says which is which. A list of several choices (aria-multiselectable) ticks each one. The tick is a mask, so
|
|
1705
|
+
the app's markup does not change. */
|
|
1706
|
+
.ws-combobox__option--active { background: var(--ws-surface-sunk); }
|
|
1707
|
+
.ws-combobox__option[aria-selected="true"] { --ws-combobox-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E"); position: relative; padding-inline-end: calc(var(--space-sm) + var(--ws-icon) + var(--space-xs)); }
|
|
1708
|
+
.ws-combobox__option[aria-selected="true"]::after { content: ""; position: absolute; top: calc((var(--ws-combobox-row, var(--ws-control-h)) - var(--ws-icon)) / 2); right: var(--space-sm); width: var(--ws-icon); height: var(--ws-icon); background: var(--color-text-heading); -webkit-mask: var(--ws-combobox-tick) center / contain no-repeat; mask: var(--ws-combobox-tick) center / contain no-repeat; }
|
|
1526
1709
|
.ws-combobox__match { font-weight: 500; color: var(--color-text-heading); }
|
|
1527
1710
|
/* pre-wrap keeps the rest's leading space (" Street") and still wraps a long one. */
|
|
1528
1711
|
.ws-combobox__rest { color: var(--color-text-body); white-space: pre-wrap; }
|
|
@@ -1558,6 +1741,47 @@ a.ws-card__list-row:focus-visible, button.ws-card__list-row:focus-visible { outl
|
|
|
1558
1741
|
.ws-combobox__chip button { min-height: 0; }
|
|
1559
1742
|
.ws-combobox__chip button::after { inset: calc((var(--space-md) - var(--ws-touch)) / 2); }
|
|
1560
1743
|
}
|
|
1744
|
+
/* ---- CommentThread: people commenting on a thing (a brief's section, a line of copy, a task). Threads in a list,
|
|
1745
|
+
divided by the full-width hairline: the quote of what each is on, its first remark, its replies one level in under the
|
|
1746
|
+
words' column, the acts (Reply, Resolve) in the meta size. A remark is a 28 px avatar beside its name, its time and
|
|
1747
|
+
its words. A mention of someone is their name in the link's mid blue; a mention of you is on the needs tint, since it
|
|
1748
|
+
waits on you. On the content, a commented stretch wears the blue card tint, a step stronger while its thread is the
|
|
1749
|
+
one open. ---- */
|
|
1750
|
+
.ws-comments { display: flex; flex-direction: column; gap: var(--space-sm); min-width: 0; }
|
|
1751
|
+
.ws-comments__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); }
|
|
1752
|
+
.ws-comments__list { display: flex; flex-direction: column; }
|
|
1753
|
+
.ws-comment { display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-sm) 0; border-top: var(--ws-divider); }
|
|
1754
|
+
.ws-comment:last-child { border-bottom: var(--ws-divider); }
|
|
1755
|
+
/* The thread open beside its content: the blue card tint, the rows' 12 px sides so its words do not touch the edge. */
|
|
1756
|
+
.ws-comment[aria-current="true"] { margin: 0 calc(var(--space-sm) * -1); padding: var(--space-sm); border-radius: var(--ws-radius-control); background: var(--ws-card-tint-blue); }
|
|
1757
|
+
/* What the thread is on: its words quoted on the sunk plate (as ChatCard quotes a message), two lines at most, the
|
|
1758
|
+
whole quote a button that brings the content into view. */
|
|
1759
|
+
.ws-comment__anchor { display: block; width: 100%; padding: var(--space-xxs) var(--space-xs); border: 0; border-radius: var(--ws-radius-control); background: var(--ws-surface-sunk); color: var(--color-text-body); font: inherit; font-size: var(--ws-text-meta); line-height: var(--space-md); text-align: start; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
1760
|
+
.ws-comment__anchor > span { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
|
|
1761
|
+
@media (pointer: coarse) { .ws-comment__anchor { min-height: var(--ws-touch); } }
|
|
1762
|
+
@media (hover: hover) { .ws-comment__anchor:hover { background-image: linear-gradient(var(--ws-hover), var(--ws-hover)); } }
|
|
1763
|
+
/* A remark: the avatar, then everything else in the words' column. */
|
|
1764
|
+
.ws-comment__remark { display: grid; grid-template-columns: var(--ws-control-h-sm) minmax(0, 1fr); column-gap: var(--space-xs); row-gap: var(--space-xxs); }
|
|
1765
|
+
.ws-comment__remark > :not(.ws-avatar) { grid-column: 2; }
|
|
1766
|
+
.ws-comment__head { display: flex; align-items: center; gap: var(--space-xs); min-height: var(--ws-control-h-sm); min-width: 0; }
|
|
1767
|
+
.ws-comment__name { overflow: hidden; color: var(--color-text-heading); font-size: var(--ws-text-control); font-weight: 500; line-height: var(--ws-leading-control); text-overflow: ellipsis; white-space: nowrap; }
|
|
1768
|
+
.ws-comment__head > .ws-icon-btn { margin-inline-start: auto; }
|
|
1769
|
+
.ws-comment__body { overflow-wrap: anywhere; white-space: pre-line; }
|
|
1770
|
+
.ws-comment__acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); font-size: var(--ws-text-meta); line-height: var(--space-md); }
|
|
1771
|
+
.ws-comment__replies { display: flex; flex-direction: column; gap: var(--space-sm); padding-top: var(--space-xs); }
|
|
1772
|
+
/* Resolved: the words step back to muted; a meta line says who resolved it and when, Reopen in the acts. */
|
|
1773
|
+
.ws-comment--resolved .ws-comment__body { color: var(--color-text-muted); }
|
|
1774
|
+
/* Mentions inside a remark's words or the composer's preview. */
|
|
1775
|
+
.ws-mention { color: var(--color-text-interactive); font-weight: 500; }
|
|
1776
|
+
.ws-mention--you { padding: 0 var(--space-3xs); border-radius: calc(var(--ws-radius-control) / 2); background: var(--ws-tint-needs); color: var(--ws-word-needs); }
|
|
1777
|
+
/* The composer: the house textarea, then a foot with the hint and its acts. */
|
|
1778
|
+
.ws-comments__composer { display: flex; flex-direction: column; gap: var(--space-xs); }
|
|
1779
|
+
.ws-comments__composer-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-xs); }
|
|
1780
|
+
/* The acts keep to the end even when the hint wraps above them. */
|
|
1781
|
+
.ws-comments__composer-foot > :last-child { margin-inline-start: auto; }
|
|
1782
|
+
/* On the content: the stretch a thread is on. */
|
|
1783
|
+
.ws-comment-mark { border-radius: calc(var(--ws-radius-control) / 2); background: var(--ws-card-tint-blue); color: inherit; }
|
|
1784
|
+
.ws-comment-mark[aria-current="true"] { background: color-mix(in srgb, var(--ws-rating-middle) 20%, var(--color-surface)); }
|
|
1561
1785
|
/* ---- Composer: the one chat box (North's): a square-cornered field 52 tall that grows with the words, its tools on the bottom line, a context tab riding on top ---- */
|
|
1562
1786
|
/* The field: white, the 0.5 px card edge, 4 px corners, 52 at least, 8 inside (16 on the left of the words). It grows
|
|
1563
1787
|
with the words and its tools stay on the bottom line. Focus draws the kit's 2 px navy ring on the box's own edge
|
|
@@ -1677,6 +1901,12 @@ input.ws-composer__words { text-overflow: ellipsis; }
|
|
|
1677
1901
|
.ws-menu.ws-date-input__picker { transform-origin: top left; }
|
|
1678
1902
|
.ws-date-input__head { display: flex; align-items: center; justify-content: space-between; height: var(--ws-control-h); padding: 0 var(--space-xxs); }
|
|
1679
1903
|
.ws-date-input__month { font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 500; color: var(--color-text-heading); }
|
|
1904
|
+
/* A far month or year (#20): the month words become two small selects between the arrows (Seth's chip select), so a
|
|
1905
|
+
date years away is one choice, and the native list types ahead ("2045"). */
|
|
1906
|
+
.ws-date-input__jump { display: flex; align-items: center; gap: var(--space-xxs); min-width: 0; }
|
|
1907
|
+
.ws-date-input__jump > .ws-select-wrap { align-self: center; }
|
|
1908
|
+
/* The foot, under Menu's line: Today at the start, Clear at the end for a date that may be left empty. */
|
|
1909
|
+
.ws-date-input__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); padding: 0 var(--space-xxs) var(--space-3xs); }
|
|
1680
1910
|
.ws-date-input__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-3xs) 0; padding: var(--space-xxs); }
|
|
1681
1911
|
.ws-date-input__letter { text-align: center; font-size: var(--ws-text-micro); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
1682
1912
|
.ws-date-input__cell { position: relative; display: flex; align-items: center; justify-content: center; height: var(--ws-control-h); padding: 0; border: 0; border-radius: var(--ws-radius-control); background: none; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-heading); font-variant-numeric: tabular-nums; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
@@ -1711,6 +1941,12 @@ input.ws-composer__words { text-overflow: ellipsis; }
|
|
|
1711
1941
|
.ws-dialog { --ws-dialog-inset: var(--ws-band-inset); display: flex; flex-direction: column; width: calc(100% - var(--space-xl)); max-width: 42rem; /* raw: Ignite's Dialog cap (max-w-2xl); no width token */ max-height: calc(100dvh - var(--space-xl)); padding: 0; overflow: visible; border: var(--ws-rim) solid var(--ws-tray); border-radius: var(--ws-radius-drawer); background: var(--ws-raised); color: var(--color-text-body); box-shadow: var(--ws-shadow-float); }
|
|
1712
1942
|
dialog.ws-dialog:not([open]) { display: none; }
|
|
1713
1943
|
.ws-dialog::backdrop { background: var(--ws-scrim); }
|
|
1944
|
+
/* Mounted by the app (--mounted: a library such as Radix renders the pane as a div in a portal, for its focus trap or a
|
|
1945
|
+
test runner with no showModal()). The scrim is its own element before the pane (__scrim, Radix's Overlay), the pane
|
|
1946
|
+
fixed over the window and centred by its auto margins (a drawer keeps its own), both on z-modal. No translate of the
|
|
1947
|
+
app's own: the motion moves the pane. Before the drawer's rules, so its height and margins win. */
|
|
1948
|
+
.ws-dialog__scrim { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--ws-scrim); }
|
|
1949
|
+
.ws-dialog--mounted { position: fixed; inset: 0; z-index: var(--z-modal); height: fit-content; margin: auto; }
|
|
1714
1950
|
/* The compact head (K6): one title, at most one line under it, the close and at most two icon acts. Facts, chips and
|
|
1715
1951
|
buttons go in the content. It never wraps (a card's head drops its aside under a short title; here the close stays
|
|
1716
1952
|
top right and the title wraps beside it). The close is Seth's 32 px tray square, centred on the title's first line
|
|
@@ -1754,9 +1990,20 @@ dialog.ws-dialog:not([open]) { display: none; }
|
|
|
1754
1990
|
content and the foot rise 6 px out of a 3 px blur over .32 s soft, .08 s after it and 22 ms apart, as North's opened
|
|
1755
1991
|
tile's details). It leaves faster and shorter: 8 px down to .98 over .12 s. A drawer slides in from its edge on the
|
|
1756
1992
|
grow curve over .55 s ("Card grows open") and goes back over .4 s ("Card closes back into place"). Native <dialog>
|
|
1757
|
-
only: @starting-style for the entrance, display and overlay allow-discrete so the exit plays.
|
|
1758
|
-
simply appears and goes. */
|
|
1759
|
-
dialog.ws-dialog { --ws-dialog-after: 80ms; /* raw: North's details wait .08 s (open.content delay) */ }
|
|
1993
|
+
only: @starting-style for the entrance, display and overlay allow-discrete so the exit plays. A mounted pane
|
|
1994
|
+
(--mounted) plays the same as keyframes, below. With reduced motion it simply appears and goes. */
|
|
1995
|
+
dialog.ws-dialog, .ws-dialog--mounted { --ws-dialog-after: 80ms; /* raw: North's details wait .08 s (open.content delay) */ }
|
|
1996
|
+
/* A mounted pane has no [open] to transition: it plays the same moves as keyframes on mount, and on its way out once the
|
|
1997
|
+
app marks it leaving (Radix sets data-state="closed" on the pane and the scrim and waits for their animations to end
|
|
1998
|
+
before it unmounts them; anything else sets data-leaving and removes them when theirs end, as Menu and Toast do). */
|
|
1999
|
+
.ws-dialog--mounted.ws-dialog--drawer { --ws-dialog-edge: calc(100% + var(--ws-stage-inset)); }
|
|
2000
|
+
.ws-dialog--mounted.ws-dialog--drawer-start { --ws-dialog-edge: calc(-100% - var(--ws-stage-inset)); }
|
|
2001
|
+
@keyframes ws-dialog-in { from { opacity: 0; transform: translateY(var(--space-sm-plus)) scale(var(--ws-open-scale-pane)); } }
|
|
2002
|
+
@keyframes ws-dialog-out { to { opacity: 0; transform: translateY(var(--space-xs)) scale(var(--ws-open-scale-pane)); } }
|
|
2003
|
+
@keyframes ws-dialog-edge-in { from { transform: translateX(var(--ws-dialog-edge)); } }
|
|
2004
|
+
@keyframes ws-dialog-edge-out { to { transform: translateX(var(--ws-dialog-edge)); } }
|
|
2005
|
+
@keyframes ws-dialog-fade-in { from { opacity: 0; } }
|
|
2006
|
+
@keyframes ws-dialog-fade-out { to { opacity: 0; } }
|
|
1760
2007
|
@media (prefers-reduced-motion: no-preference) {
|
|
1761
2008
|
dialog.ws-dialog, dialog.ws-dialog::backdrop { transition: opacity var(--ws-duration-exit) var(--ws-ease-exit), transform var(--ws-duration-exit) var(--ws-ease-exit), overlay var(--ws-duration-exit) var(--ws-ease-exit) allow-discrete, display var(--ws-duration-exit) var(--ws-ease-exit) allow-discrete; }
|
|
1762
2009
|
dialog.ws-dialog:not([open]) { opacity: 0; transform: translateY(var(--space-xs)) scale(var(--ws-open-scale-pane)); }
|
|
@@ -1766,6 +2013,14 @@ dialog.ws-dialog { --ws-dialog-after: 80ms; /* raw: North's details wait .08 s (
|
|
|
1766
2013
|
dialog.ws-dialog--drawer[open] { transition: opacity var(--ws-duration-grow) var(--ws-ease-grow), transform var(--ws-duration-grow) var(--ws-ease-grow), overlay 0s allow-discrete, display 0s allow-discrete; }
|
|
1767
2014
|
dialog.ws-dialog--drawer:not([open]) { transform: translateX(calc(100% + var(--ws-stage-inset))); transition-duration: var(--ws-duration-shrink); transition-timing-function: var(--ws-ease-grow); }
|
|
1768
2015
|
dialog.ws-dialog--drawer-start:not([open]) { transform: translateX(calc(-100% - var(--ws-stage-inset))); }
|
|
2016
|
+
.ws-dialog--mounted { animation: ws-dialog-in var(--ws-spring-settle-duration) var(--ws-spring-settle) backwards; }
|
|
2017
|
+
.ws-dialog--mounted.ws-dialog--drawer { animation: ws-dialog-edge-in var(--ws-duration-grow) var(--ws-ease-grow) backwards; }
|
|
2018
|
+
.ws-dialog--mounted:is([data-leaving], [data-state="closed"]) { animation: ws-dialog-out var(--ws-duration-exit) var(--ws-ease-exit) forwards; pointer-events: none; }
|
|
2019
|
+
.ws-dialog--mounted.ws-dialog--drawer:is([data-leaving], [data-state="closed"]) { animation: ws-dialog-edge-out var(--ws-duration-shrink) var(--ws-ease-grow) forwards; }
|
|
2020
|
+
.ws-dialog__scrim { animation: ws-dialog-fade-in var(--ws-duration-hover) var(--ws-ease-soft) backwards; }
|
|
2021
|
+
.ws-dialog__scrim:is([data-leaving], [data-state="closed"]) { animation: ws-dialog-fade-out var(--ws-duration-exit) var(--ws-ease-exit) forwards; }
|
|
2022
|
+
.ws-dialog--mounted:not([data-leaving], [data-state="closed"]) > * { animation: ws-settle var(--ws-duration-settle) var(--ws-ease-soft) var(--ws-dialog-after) backwards; }
|
|
2023
|
+
@supports (animation-delay: calc(sibling-index() * 1ms)) { .ws-dialog--mounted:not([data-leaving], [data-state="closed"]) > * { animation-delay: calc(var(--ws-dialog-after) + (sibling-index() - 1) * var(--ws-stagger-detail)); } }
|
|
1769
2024
|
dialog.ws-dialog[open] > * { animation: ws-settle var(--ws-duration-settle) var(--ws-ease-soft) var(--ws-dialog-after) backwards; }
|
|
1770
2025
|
@supports (animation-delay: calc(sibling-index() * 1ms)) { dialog.ws-dialog[open] > * { animation-delay: calc(var(--ws-dialog-after) + (sibling-index() - 1) * var(--ws-stagger-detail)); } }
|
|
1771
2026
|
@starting-style {
|
|
@@ -1874,6 +2129,8 @@ dialog.ws-dialog { --ws-dialog-after: 80ms; /* raw: North's details wait .08 s (
|
|
|
1874
2129
|
/* Card look: the summary is 44 tall, 4 / 16 (16 all round made it 48 and more); the body 4 / 16 / 16 under it. */
|
|
1875
2130
|
.ws-fold__summary--card { min-height: calc(var(--space-2xl) + var(--space-xxs)); flex-wrap: wrap; gap: var(--space-xs); border-radius: var(--ws-radius-card-inner); padding: var(--space-xxs) var(--ws-card-padding-lg); }
|
|
1876
2131
|
.ws-fold__summary--card > .ws-fold__title { flex: 1 1 0; min-width: min(100%, var(--space-8xl)); }
|
|
2132
|
+
/* The note or end status keeps to the far end, on the title's line or, with no room, on its own line under it. */
|
|
2133
|
+
.ws-fold__summary--card > :not(.ws-fold__chevron, .ws-fold__title, .ws-fold__peek) { margin-left: auto; }
|
|
1877
2134
|
details[open] > .ws-fold__summary--card { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
|
|
1878
2135
|
.ws-fold__summary--card:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
1879
2136
|
.ws-fold__body--card { padding: var(--space-xxs) var(--ws-card-padding-lg) var(--ws-card-padding-lg); }
|
|
@@ -1916,6 +2173,34 @@ details[open] > summary > .ws-fold__chevron--down { transform: rotate(180deg); }
|
|
|
1916
2173
|
@media (prefers-reduced-motion: no-preference) {
|
|
1917
2174
|
.ws-fold__chevron { transition: transform var(--ws-spring-fold-duration) var(--ws-spring-fold); }
|
|
1918
2175
|
}
|
|
2176
|
+
/* A card fold with more in its head (Madan, Marketing OS): acts that must not toggle it, words that show only while it
|
|
2177
|
+
is folded, and a tone for the whole block.
|
|
2178
|
+
Acts (ws-fold, the card around the details, and ws-fold__act after it): a button may not sit inside a summary (one
|
|
2179
|
+
control inside another), so the acts sit beside it in the card's own grid. The details is a subgrid of the card's two
|
|
2180
|
+
columns and rows: the summary takes the first column, the acts the second on the summary's row (filling it, so
|
|
2181
|
+
its tint is whole; the acts centred on the summary's first 44, the title's band, not on a peek under it), the body
|
|
2182
|
+
both columns under them. A long title wraps beside the acts instead of running under them. The acts' room tints with the summary under the pointer. */
|
|
2183
|
+
.ws-fold { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
|
|
2184
|
+
.ws-fold > details { display: grid; grid-template-columns: subgrid; grid-template-rows: subgrid; grid-column: 1 / -1; grid-row: 1 / span 2; border-radius: inherit; }
|
|
2185
|
+
.ws-fold > details > summary { grid-column: 1; border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
2186
|
+
.ws-fold > details > :not(summary), .ws-fold > details::details-content { grid-column: 1 / -1; }
|
|
2187
|
+
.ws-fold__act { display: grid; grid-auto-flow: column; grid-template-rows: calc(var(--space-2xl) + var(--space-xxs)); grid-column: 2; grid-row: 1; align-content: start; align-items: center; gap: var(--space-xs); padding-right: var(--ws-card-padding-lg); border-top-right-radius: var(--ws-radius-card-inner); transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
2188
|
+
.ws-fold:has(> details:not([open])) > .ws-fold__act { border-bottom-right-radius: var(--ws-radius-card-inner); }
|
|
2189
|
+
@media (hover: hover) { .ws-fold:has(> details > summary:hover) > .ws-fold__act { background-color: var(--ws-fold-hover, var(--ws-surface-sunk)); } }
|
|
2190
|
+
/* Folded words (ws-fold__peek, the summary's last part): a line under the title, past the chevron, that shows only while
|
|
2191
|
+
the fold is shut (a strip of variant chips). It is inside the summary, so it holds words and marks, never an act. */
|
|
2192
|
+
.ws-fold__peek { display: flex; flex-wrap: wrap; flex-basis: 100%; gap: var(--space-xxs); padding: 0 0 var(--space-sm) calc(var(--ws-icon) + var(--space-xs)); }
|
|
2193
|
+
details[open] > summary > .ws-fold__peek { display: none; }
|
|
2194
|
+
/* Tone (ws-fold--needs, --positive, --working on the card): Banner's three tints and words, for a fold that is a notice
|
|
2195
|
+
(three checks failed, the import finished). The ground takes the tint, the title and the chevron the tone's words; a
|
|
2196
|
+
hover is the navy wash over the tint, not the sunk cream. */
|
|
2197
|
+
.ws-fold--needs { --ws-fold-tint: var(--ws-tint-needs); --ws-fold-word: var(--ws-word-needs); }
|
|
2198
|
+
.ws-fold--positive { --ws-fold-tint: var(--ws-tint-live); --ws-fold-word: var(--ws-word-positive); }
|
|
2199
|
+
.ws-fold--working { --ws-fold-tint: var(--ws-card-tint-blue); --ws-fold-word: var(--color-text-interactive); }
|
|
2200
|
+
:is(.ws-fold--needs, .ws-fold--positive, .ws-fold--working) { --ws-fold-hover: var(--ws-hover); background-color: var(--ws-fold-tint); }
|
|
2201
|
+
:is(.ws-fold--needs, .ws-fold--positive, .ws-fold--working) .ws-fold__summary :is(.ws-fold__title, .ws-fold__chevron) { color: var(--ws-fold-word); }
|
|
2202
|
+
@media (hover: hover) { :is(.ws-fold--needs, .ws-fold--positive, .ws-fold--working) .ws-fold__summary--card:hover { background-color: var(--ws-hover); } }
|
|
2203
|
+
:is(.ws-fold--needs, .ws-fold--positive, .ws-fold--working) .ws-fold__summary--card:active { background-color: var(--ws-hover-strong); }
|
|
1919
2204
|
/* ---- Footer: the public site's navy footer, the wordmark, link columns and the legal line ---- */
|
|
1920
2205
|
/* The band is navy in every theme, so its focus ring is white: the house navy ring would vanish on it. */
|
|
1921
2206
|
.ws-footer { --ws-focus-color: var(--ws-on-inverse); width: 100%; background: var(--ws-inverse); padding: var(--space-4xl) var(--layout-margin); }
|
|
@@ -2267,6 +2552,40 @@ a.ws-goal-bars__label { border-radius: var(--ws-radius-chip); }
|
|
|
2267
2552
|
.ws-hover-card[data-leaving] { animation: ws-hover-card-out var(--ws-duration-exit) var(--ws-ease-exit) forwards; pointer-events: none; }
|
|
2268
2553
|
.ws-hover-card--follow { transition: left var(--ws-spring-follow-duration) var(--ws-spring-follow), top var(--ws-spring-follow-duration) var(--ws-spring-follow); }
|
|
2269
2554
|
}
|
|
2555
|
+
/* ---- InlineEdit: words that become a field in place. The rest state is a button holding the words, the editing state
|
|
2556
|
+
an input or a textarea; both are __value, so they share one box (4 / 8 inside, pulled back 8 at the sides so the words
|
|
2557
|
+
stay on the content edge) and the words' own type (the wrapper wears ws-section, ws-title or ws-body), and nothing jumps when
|
|
2558
|
+
one turns into the other. At rest: no ground, a navy wash under the pointer, a pencil mark that shows on hover and
|
|
2559
|
+
focus (always on touch, where there is no hover). Editing: the field's white ground, its fine edge and the focus
|
|
2560
|
+
ring, the words keeping their own colour and weight. ---- */
|
|
2561
|
+
.ws-inline-edit { display: grid; gap: var(--space-3xs); min-width: 0; }
|
|
2562
|
+
.ws-inline-edit__value { display: block; box-sizing: border-box; width: calc(100% + var(--space-xs) * 2); min-width: 0; margin: 0 calc(var(--space-xs) * -1); padding: var(--space-xxs) var(--space-xs); border: 0; border-radius: var(--ws-radius-control); background: transparent; color: inherit; font: inherit; letter-spacing: inherit; text-align: start; text-wrap: inherit; overflow-wrap: anywhere; }
|
|
2563
|
+
button.ws-inline-edit__value { cursor: text; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
2564
|
+
@media (hover: hover) { button.ws-inline-edit__value:hover { background: var(--ws-hover); } }
|
|
2565
|
+
button.ws-inline-edit__value:active { background: var(--ws-hover-strong); }
|
|
2566
|
+
/* No value yet: the placeholder's words, still the target. */
|
|
2567
|
+
.ws-inline-edit__value[data-empty] { color: var(--color-text-placeholder); }
|
|
2568
|
+
/* The pencil after the words (a ws-icon at the words' size, 16 at most, so it sits on their line): a hint, never the only
|
|
2569
|
+
target (the whole box is). */
|
|
2570
|
+
.ws-inline-edit__mark { width: min(1em, var(--ws-icon)); height: min(1em, var(--ws-icon)); margin-inline-start: var(--space-xxs); color: var(--color-text-muted); opacity: 0; transition: opacity var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
2571
|
+
@media (hover: hover) { button.ws-inline-edit__value:hover .ws-inline-edit__mark { opacity: 1; } }
|
|
2572
|
+
button.ws-inline-edit__value:focus-visible .ws-inline-edit__mark { opacity: 1; }
|
|
2573
|
+
@media (pointer: coarse) { .ws-inline-edit__mark { opacity: 1; } }
|
|
2574
|
+
/* Editing: the same box as a field. A textarea grows with its words where the browser can (field-sizing). */
|
|
2575
|
+
.ws-inline-edit__value:is(input, textarea) { background: var(--ws-field); box-shadow: var(--ws-shadow-hairline-in); }
|
|
2576
|
+
/* The ring is drawn inside the box, so it never crosses a label or a line 4 above or below. */
|
|
2577
|
+
.ws-inline-edit__value:focus-visible, .ws-inline-edit__value--typing { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
2578
|
+
textarea.ws-inline-edit__value { resize: none; field-sizing: content; }
|
|
2579
|
+
.ws-inline-edit__value:is(input, textarea)::placeholder { color: var(--color-text-placeholder); }
|
|
2580
|
+
.ws-inline-edit__value:is(input, textarea)[aria-invalid="true"] { box-shadow: inset 0 0 0 var(--ws-line) var(--ws-word-needs); }
|
|
2581
|
+
/* Touch: the box is 40 tall at least, at rest and editing alike (so it still does not jump), and the editing words are
|
|
2582
|
+
16 at least: a field under 16 px makes iOS zoom the page. */
|
|
2583
|
+
@media (pointer: coarse) {
|
|
2584
|
+
.ws-inline-edit__value { min-height: var(--ws-touch); }
|
|
2585
|
+
.ws-inline-edit__value:is(input, textarea) { font-size: max(1em, var(--ws-text-value)); }
|
|
2586
|
+
}
|
|
2587
|
+
/* The editing state drawn still, for a preview or a mockup: the focus ring a real focus draws. */
|
|
2588
|
+
.ws-inline-edit__value--typing { outline: var(--ws-focus-width) solid var(--ws-focus-color); }
|
|
2270
2589
|
/* ---- KanbanBoard: stages as equal 280 px columns on the sunk plate; the board scrolls sideways inside itself, a stage
|
|
2271
2590
|
at a time on touch, the next stage peeking in; --fill makes it the page's remaining height with each column scrolling,
|
|
2272
2591
|
so the sideways bar sits at the foot of the window (K16) ---- */
|
|
@@ -2298,9 +2617,40 @@ a.ws-goal-bars__label { border-radius: var(--ws-radius-chip); }
|
|
|
2298
2617
|
.ws-kanban-board__card--dragging { box-shadow: var(--ws-shadow-pop); transform: rotate(-1deg); }
|
|
2299
2618
|
.ws-kanban-board__who { display: flex; align-items: center; gap: var(--space-xs); min-width: 0; }
|
|
2300
2619
|
.ws-kanban-board__meta { font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-muted); }
|
|
2620
|
+
/* The standard card: one anatomy for every board (a ticket, an onboarding, a deal), its slots in a fixed order: the head
|
|
2621
|
+
(the kind and its figure, the time in stage), the title, the meta, the chips, the foot (people, a tally, one act), an
|
|
2622
|
+
optional act under it. A card with acts is an article: its title is the link and covers the whole card (__open), the
|
|
2623
|
+
acts sit above that cover. */
|
|
2624
|
+
.ws-kanban-board__card { position: relative; }
|
|
2625
|
+
.ws-kanban-board__card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); min-width: 0; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); }
|
|
2626
|
+
.ws-kanban-board__kind { min-width: 0; overflow: hidden; font-weight: 500; color: var(--color-text-muted); white-space: nowrap; text-overflow: ellipsis; }
|
|
2627
|
+
.ws-kanban-board__kind > strong { font-weight: 600; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
|
|
2628
|
+
/* Time in stage, muted; a stall takes the needs words and the pause mark, never a coloured rail. */
|
|
2629
|
+
.ws-kanban-board__age { display: inline-flex; flex: none; align-items: center; gap: var(--space-xxs); margin-left: auto; color: var(--color-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
2630
|
+
.ws-kanban-board__age > svg { width: var(--ws-icon-sm); height: var(--ws-icon-sm); }
|
|
2631
|
+
.ws-kanban-board__age--stalled { font-weight: 500; color: var(--ws-word-needs); }
|
|
2632
|
+
/* The title: 14 / 20 medium navy, two lines at most (whole in its title), never underlined. Its cover is the card's
|
|
2633
|
+
target and carries the focus ring, 2 px out as an a.ws-card's does. */
|
|
2634
|
+
.ws-kanban-board__open { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; font-size: var(--ws-text-body); line-height: var(--ws-leading-row); font-weight: 500; color: var(--color-text-heading); text-decoration: none; overflow-wrap: anywhere; }
|
|
2635
|
+
.ws-kanban-board__open::after { content: ""; position: absolute; inset: calc(var(--ws-rim) * -1); border-radius: var(--ws-radius-card); }
|
|
2636
|
+
.ws-kanban-board__open:focus-visible { outline: 0; }
|
|
2637
|
+
.ws-kanban-board__open:focus-visible::after { outline: var(--ws-focus-width) solid var(--ws-focus-color); outline-offset: var(--ws-focus-offset); }
|
|
2638
|
+
.ws-kanban-board__chips { display: flex; flex-wrap: wrap; gap: var(--space-xxs); min-width: 0; margin-top: var(--space-xxs); }
|
|
2639
|
+
.ws-kanban-board__chips > .ws-chip { max-width: 100%; }
|
|
2640
|
+
.ws-kanban-board__card-foot { display: flex; align-items: center; gap: var(--space-xs); min-width: 0; min-height: var(--ws-control-h-sm); margin-top: var(--space-xxs); }
|
|
2641
|
+
.ws-kanban-board__card-foot > .ws-kanban-board__who { flex: 1; }
|
|
2642
|
+
.ws-kanban-board__name { min-width: 0; overflow: hidden; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-body); white-space: nowrap; text-overflow: ellipsis; }
|
|
2643
|
+
.ws-kanban-board__card-end { display: flex; flex: none; align-items: center; gap: var(--space-xs); margin-left: auto; }
|
|
2644
|
+
.ws-kanban-board__tally { display: inline-flex; align-items: center; gap: var(--space-xxs); font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
2645
|
+
.ws-kanban-board__tally > svg { width: var(--ws-icon-sm); height: var(--ws-icon-sm); }
|
|
2646
|
+
/* Acts sit above the title's cover, so a press on one is the act, not the card. */
|
|
2647
|
+
.ws-kanban-board__card :is(.ws-icon-btn, .ws-btn) { position: relative; z-index: 1; }
|
|
2648
|
+
.ws-kanban-board__card-act { width: 100%; margin-top: var(--space-xxs); }
|
|
2301
2649
|
.ws-kanban-board__empty { padding: var(--space-md) var(--space-xs); text-align: center; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-muted); }
|
|
2302
2650
|
/* Loading: a ghost the height of a card under the shimmer (add ws-shimmer). */
|
|
2303
2651
|
.ws-kanban-board__ghost { display: block; height: calc(var(--ws-card-padding) * 2 + var(--ws-control-h-sm) + var(--space-xxs) + var(--ws-leading-control)); border-radius: var(--ws-radius-card); background: var(--ws-track); }
|
|
2652
|
+
/* The standard card's ghost: the height of a head, two title lines, a meta line and the foot. */
|
|
2653
|
+
.ws-kanban-board__ghost--card { height: calc(var(--ws-card-padding) * 2 + var(--ws-leading-control) * 2 + var(--ws-leading-row) * 2 + var(--ws-control-h-sm) + var(--space-xxs) * 4); }
|
|
2304
2654
|
.ws-kanban-board__ghost.ws-shimmer { background: var(--ws-shimmer); background-size: 300% 100%; }
|
|
2305
2655
|
/* A board page (K16): the board takes the rest of a height-bound column (the shell's body, a fill page), every column
|
|
2306
2656
|
runs its full height and scrolls on its own with its head stuck, so the sideways bar is always on screen. */
|
|
@@ -3151,6 +3501,70 @@ a.ws-leaderboard__row:focus-visible, button.ws-leaderboard__row:focus-visible {
|
|
|
3151
3501
|
.ws-password-input__strength--fair .ws-password-input__segment:nth-child(-n + 2),
|
|
3152
3502
|
.ws-password-input__strength--good .ws-password-input__segment:nth-child(-n + 3),
|
|
3153
3503
|
.ws-password-input__strength--strong .ws-password-input__segment { background: var(--ws-password-tone); }
|
|
3504
|
+
/* ---- PlanTimeline: a plan's phases and tasks as bars across its dates. A row a phase (a task one step in), its name and
|
|
3505
|
+
dates stuck at the left; each bar from its start to its end on one scale (--days in the window, --start and --span in
|
|
3506
|
+
days from its first day); the phase in hand navy, done teal, late salmon, to come on the track; milestones diamonds;
|
|
3507
|
+
today one fine line in the needs step through every row. Past its box (a phone) it scrolls sideways inside itself, the
|
|
3508
|
+
names staying put. ---- */
|
|
3509
|
+
.ws-plan-timeline { --ws-plan-timeline-name: var(--space-8xl); --ws-plan-timeline-day: var(--space-xs); container: ws-plan-timeline / inline-size; min-width: 0; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin; padding: var(--space-xxs) var(--space-xxs) var(--space-xxs) 0; margin: calc(var(--space-xxs) * -1) calc(var(--space-xxs) * -1) calc(var(--space-xxs) * -1) 0; }
|
|
3510
|
+
/* 4 px of room above, below and at the far end (pulled back by the margin) so a bar's focus ring is never cut; none at
|
|
3511
|
+
the start, where the names stick. */
|
|
3512
|
+
.ws-plan-timeline:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
3513
|
+
/* A bar reached by Tab scrolls clear of the stuck names (the wider name column, so it holds on a phone too). */
|
|
3514
|
+
.ws-plan-timeline { scroll-padding-inline-start: calc(var(--space-8xl) + var(--space-xxs)); }
|
|
3515
|
+
/* The plot: the names' column and the window, never narrower than a day at 8 px, so a long plan scrolls rather than
|
|
3516
|
+
squeezing its bars to slivers. */
|
|
3517
|
+
.ws-plan-timeline__plot { position: relative; min-width: calc(var(--ws-plan-timeline-name) + var(--days) * var(--ws-plan-timeline-day)); }
|
|
3518
|
+
@container ws-plan-timeline (width < 40rem) { .ws-plan-timeline__plot { --ws-plan-timeline-name: var(--space-7xl); } }
|
|
3519
|
+
/* The scale: a tick per month (or week) at its first day, 11 px medium muted, over the window only. Rows and the scale
|
|
3520
|
+
are flex rows, not grids, so the stuck names can travel the whole row (a grid item sticks only inside its cell). */
|
|
3521
|
+
.ws-plan-timeline__scale { position: relative; display: flex; height: var(--space-lg); }
|
|
3522
|
+
.ws-plan-timeline__tick { position: absolute; top: 0; bottom: 0; left: calc(var(--ws-plan-timeline-name) + (100% - var(--ws-plan-timeline-name)) * var(--at) / var(--days)); display: flex; align-items: center; padding-left: var(--space-xxs); border-left: var(--ws-hairline) solid var(--ws-edge-card); font-size: var(--ws-text-micro); line-height: var(--ws-leading-micro); font-weight: 500; color: var(--color-text-muted); white-space: nowrap; }
|
|
3523
|
+
.ws-plan-timeline__rows { display: flex; flex-direction: column; }
|
|
3524
|
+
.ws-plan-timeline__row { display: flex; border-top: var(--ws-hairline) solid var(--ws-edge-card); }
|
|
3525
|
+
/* The name and its dates, stuck at the left on the card's cream. */
|
|
3526
|
+
.ws-plan-timeline__name { position: sticky; left: 0; z-index: 2; display: flex; flex: 0 0 var(--ws-plan-timeline-name); flex-direction: column; justify-content: center; min-width: 0; padding: var(--space-xs) var(--space-sm) var(--space-xs) 0; background: var(--ws-plan-timeline-ground, var(--color-bg-page)); }
|
|
3527
|
+
/* Names and dates wrap rather than cut: the column is narrow on a phone and a plan's names are its whole point. */
|
|
3528
|
+
.ws-plan-timeline__title { font-size: var(--ws-text-control); line-height: var(--ws-leading-control); font-weight: 500; color: var(--color-text-heading); overflow-wrap: anywhere; }
|
|
3529
|
+
.ws-plan-timeline__when { font-size: var(--ws-text-dense); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
3530
|
+
.ws-plan-timeline__row:has([aria-current]) .ws-plan-timeline__title { font-weight: 600; }
|
|
3531
|
+
/* A late phase or task says so under its name, in needs-you words. */
|
|
3532
|
+
.ws-plan-timeline__when--late { font-weight: 500; color: var(--ws-word-needs); }
|
|
3533
|
+
/* A task: one step in under its phase, its name in the body words, its bar thinner. */
|
|
3534
|
+
.ws-plan-timeline__row--task .ws-plan-timeline__name { padding-left: var(--space-md); }
|
|
3535
|
+
.ws-plan-timeline__row--task .ws-plan-timeline__title { font-weight: 400; color: var(--color-text-body); }
|
|
3536
|
+
.ws-plan-timeline__track { position: relative; flex: 1; min-width: 0; min-height: var(--space-2xl); }
|
|
3537
|
+
/* A bar: 4 px corners, at least 24 wide so a one-day task is still a target; a link when it opens the phase. */
|
|
3538
|
+
.ws-plan-timeline__bar { --ws-plan-timeline-tone: var(--tone, var(--ws-track)); position: absolute; top: 50%; left: calc(var(--start) / var(--days) * 100%); width: max(calc(var(--span) / var(--days) * 100%), var(--space-lg)); height: var(--space-lg); border-radius: var(--ws-radius-control); background-color: var(--ws-plan-timeline-tone); transform: translateY(-50%); }
|
|
3539
|
+
.ws-plan-timeline__bar--wait { box-shadow: inset 0 0 0 var(--ws-hairline) var(--ws-edge-card); }
|
|
3540
|
+
.ws-plan-timeline__bar--done { --ws-plan-timeline-tone: var(--tone, var(--ws-chart-finished)); }
|
|
3541
|
+
.ws-plan-timeline__bar[aria-current] { --ws-plan-timeline-tone: var(--tone, var(--color-text-heading)); }
|
|
3542
|
+
.ws-plan-timeline__bar--late { --ws-plan-timeline-tone: var(--tone, var(--ws-chart-under-half)); }
|
|
3543
|
+
.ws-plan-timeline__row--task .ws-plan-timeline__bar { height: var(--space-md); }
|
|
3544
|
+
/* Every bar that opens keeps a 24 px target (a task's 16 px bar too), 40 on touch (K10). */
|
|
3545
|
+
a.ws-plan-timeline__bar::after { content: ""; position: absolute; inset: calc((100% - var(--ws-plan-timeline-hit, var(--space-lg))) / 2) 0; }
|
|
3546
|
+
@media (pointer: coarse) { .ws-plan-timeline { --ws-plan-timeline-hit: var(--ws-touch); } }
|
|
3547
|
+
a.ws-plan-timeline__bar { color: var(--color-text-heading); cursor: pointer; }
|
|
3548
|
+
@media (hover: hover) { a.ws-plan-timeline__bar:hover { background-image: linear-gradient(var(--ws-hover-strong), var(--ws-hover-strong)); } }
|
|
3549
|
+
a.ws-plan-timeline__bar:focus-visible { outline: var(--ws-focus-width) solid var(--ws-focus-color); outline-offset: var(--ws-focus-offset); }
|
|
3550
|
+
/* A milestone: a 10 px diamond on the day it falls, the cream inside a navy edge until it is reached, teal after. It
|
|
3551
|
+
opens nothing (its phase's bar does); its name and date are in its label and title. */
|
|
3552
|
+
.ws-plan-timeline__milestone { position: absolute; z-index: 1; top: 50%; left: calc(var(--at) / var(--days) * 100%); width: var(--space-xs-plus); height: var(--space-xs-plus); border-radius: var(--ws-radius-chip); background: var(--color-bg-page); box-shadow: inset 0 0 0 calc(var(--ws-hairline) * 3) var(--color-text-heading); transform: translate(-50%, -50%) rotate(45deg); }
|
|
3553
|
+
.ws-plan-timeline__milestone--done { background: var(--ws-chart-finished); box-shadow: inset 0 0 0 calc(var(--ws-hairline) * 3) var(--color-bg-page); }
|
|
3554
|
+
/* Today: its word over the window and one 1 px line down through every row, in the salmon's AA step; under the stuck
|
|
3555
|
+
names when the plan scrolls. Leave it out when today is outside the window. */
|
|
3556
|
+
.ws-plan-timeline__now { position: absolute; z-index: 1; top: 0; bottom: 0; left: calc(var(--ws-plan-timeline-name) + (100% - var(--ws-plan-timeline-name)) * var(--at) / var(--days)); display: flex; flex-direction: column; align-items: center; font-size: var(--ws-text-micro); line-height: var(--space-lg); font-weight: 600; color: var(--ws-word-needs); transform: translateX(-50%); pointer-events: none; }
|
|
3557
|
+
/* On a track that scrolls (a phone) the plan opens on today: the browser scrolls to it once, on load. */
|
|
3558
|
+
.ws-plan-timeline__now { scroll-snap-align: center; scroll-initial-target: nearest; }
|
|
3559
|
+
.ws-plan-timeline__now > span { padding: 0 var(--space-xxs); background: var(--ws-plan-timeline-ground, var(--color-bg-page)); }
|
|
3560
|
+
.ws-plan-timeline__now::after { content: ""; flex: 1; width: var(--ws-line); background: var(--ws-word-needs); }
|
|
3561
|
+
/* The names' column over the scale, so a tick scrolled under it hides. */
|
|
3562
|
+
.ws-plan-timeline__scale::before { content: ""; position: sticky; z-index: 2; left: 0; flex: 0 0 var(--ws-plan-timeline-name); background: var(--ws-plan-timeline-ground, var(--color-bg-page)); }
|
|
3563
|
+
/* The far edge fades while days are hidden past it, lifting at the end; a focused plan drops it (the ring says it scrolls). */
|
|
3564
|
+
@supports (animation-timeline: scroll()) {
|
|
3565
|
+
.ws-plan-timeline { mask-image: linear-gradient(90deg, currentColor calc(100% - var(--ws-fade-end)), transparent); animation: ws-scroll-edges linear; animation-timeline: scroll(self inline); }
|
|
3566
|
+
.ws-plan-timeline:focus-visible { mask-image: none; }
|
|
3567
|
+
}
|
|
3154
3568
|
/* ---- Plate: a card's facts set apart on the sunk cream (Seth's facts plate): label and value rows, stats, a track, a foot ---- */
|
|
3155
3569
|
/* Seth's plate is 14 / 16 inside (the house had 12 all round). */
|
|
3156
3570
|
.ws-plate { padding: var(--space-sm-plus) var(--space-md); }
|
|
@@ -3761,6 +4175,35 @@ a.ws-stat-tiles__foot:focus-visible { outline-offset: calc(var(--ws-focus-width)
|
|
|
3761
4175
|
.ws-stepper__body { display: flex; flex: 1; min-width: 0; flex-direction: column; padding-top: var(--space-3xs); }
|
|
3762
4176
|
.ws-stepper__title { font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 500; color: var(--color-text-heading); }
|
|
3763
4177
|
|
|
4178
|
+
/* A stall in words: the in-hand step's note when it has sat too long ("40 days in this stage"), and a milestone past its target. */
|
|
4179
|
+
.ws-stepper__late { color: var(--ws-word-needs); }
|
|
4180
|
+
/* Across: stages left to right, each mark over its words, the line running mark to mark. Equal columns (so the line
|
|
4181
|
+
can reach the next mark's centre from its own item), each at least 120; past that the track scrolls inside and fades
|
|
4182
|
+
at the edge that hides stages, like Segmented's stages. */
|
|
4183
|
+
.ws-stepper--across { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(var(--space-7xl), 1fr); overflow-x: auto; scrollbar-width: none; }
|
|
4184
|
+
.ws-stepper--across::-webkit-scrollbar { display: none; }
|
|
4185
|
+
.ws-stepper--across > .ws-stepper__item { flex-direction: column; align-items: center; gap: var(--space-xs); padding: 0 var(--space-xxs); text-align: center; }
|
|
4186
|
+
.ws-stepper--across .ws-stepper__line { top: calc(var(--space-lg) / 2 - var(--ws-hairline)); bottom: auto; left: calc(50% + var(--space-lg) / 2); right: calc(var(--space-lg) / 2 - 50%); width: auto; height: calc(var(--ws-hairline) * 2); }
|
|
4187
|
+
.ws-stepper--across .ws-stepper__line-fill { transform-origin: left; }
|
|
4188
|
+
.ws-stepper--across .ws-stepper__body { align-items: center; padding-top: 0; }
|
|
4189
|
+
.ws-stepper--across .ws-stepper__title { text-wrap: balance; }
|
|
4190
|
+
/* The in-hand stage opens in view on a track that scrolls (a phone): the browser scrolls to it once, on load. */
|
|
4191
|
+
.ws-stepper--across > [aria-current="step"] { scroll-snap-align: center; scroll-initial-target: nearest; }
|
|
4192
|
+
/* Milestones: dated points on one row, an 8 px dot and its words, the line between them taking the room left. The
|
|
4193
|
+
measure sits on the milestone (a date, or days since the start), so the line carries no words. 4 px of room on every
|
|
4194
|
+
side, pulled back by a margin, so an act's focus ring and a text button's bleed are never cut by the scroll box. */
|
|
4195
|
+
.ws-stepper--milestones { flex-direction: row; gap: var(--space-xs); overflow-x: auto; scrollbar-width: none; padding: var(--space-xxs); margin: calc(var(--space-xxs) * -1); }
|
|
4196
|
+
.ws-stepper--milestones::-webkit-scrollbar { display: none; }
|
|
4197
|
+
.ws-stepper--milestones > .ws-stepper__item { flex: 1 0 auto; align-items: center; gap: var(--space-xs); padding: 0; }
|
|
4198
|
+
.ws-stepper--milestones > .ws-stepper__item:last-child { flex-grow: 0; }
|
|
4199
|
+
.ws-stepper--milestones .ws-stepper__line { position: relative; inset: auto; order: 1; flex: 1 0 var(--space-lg); width: auto; height: calc(var(--ws-hairline) * 2); }
|
|
4200
|
+
.ws-stepper--milestones .ws-stepper__line-fill { transform-origin: left; }
|
|
4201
|
+
.ws-stepper--milestones .ws-stepper__mark { width: var(--space-xs); height: var(--space-xs); }
|
|
4202
|
+
.ws-stepper--milestones .ws-stepper__mark--wait { box-shadow: inset 0 0 0 calc(var(--ws-hairline) * 2) var(--ws-edge-card); }
|
|
4203
|
+
.ws-stepper--milestones .ws-stepper__body { flex: 0 0 auto; flex-direction: row; align-items: baseline; gap: var(--space-xxs-plus); padding-top: 0; white-space: nowrap; }
|
|
4204
|
+
@supports (animation-timeline: scroll()) {
|
|
4205
|
+
.ws-stepper--across, .ws-stepper--milestones { mask-image: linear-gradient(90deg, transparent, currentColor var(--ws-fade-start), currentColor calc(100% - var(--ws-fade-end)), transparent); animation: ws-scroll-edges linear; animation-timeline: scroll(self inline); }
|
|
4206
|
+
}
|
|
3764
4207
|
/* ---- StreakCalendar: one person's weeks as day cells, a navy dot a day done, today ringed in salmon ---- */
|
|
3765
4208
|
.ws-streak-calendar__body { padding: 0 var(--ws-card-inset) var(--space-md); }
|
|
3766
4209
|
.ws-streak-calendar__grid { display: grid; width: 100%; max-width: calc(var(--space-9xl) * 2 + var(--space-6xl) - var(--space-lg) - var(--space-sm)); grid-template-columns: calc(var(--space-4xl) - var(--space-xxs)) repeat(7, minmax(0, 1fr)); align-items: center; column-gap: var(--space-xxs); row-gap: var(--space-xxs); padding-top: var(--space-sm-plus); }
|
|
@@ -3814,6 +4257,8 @@ a.ws-stat-tiles__foot:focus-visible { outline-offset: calc(var(--ws-focus-width)
|
|
|
3814
4257
|
.ws-table tr > :first-child { padding-left: var(--ws-card-inset); }
|
|
3815
4258
|
.ws-table tr > :last-child { padding-right: var(--ws-card-inset); }
|
|
3816
4259
|
.ws-table .ws-table__wrap { white-space: normal; min-width: var(--space-9xl); }
|
|
4260
|
+
/* A short prose cell (a note, a reason) that wraps at whatever width its column gets, with no floor (#21). */
|
|
4261
|
+
.ws-table .ws-table__prose { white-space: normal; text-wrap: pretty; }
|
|
3817
4262
|
/* Head: 13 / 18 medium muted labels, stuck to the top of the scroller. */
|
|
3818
4263
|
.ws-table thead th { position: sticky; top: 0; z-index: 2; height: var(--ws-row-h-dense); font-size: var(--ws-text-control); line-height: var(--ws-leading-control); font-weight: 500; color: var(--color-text-muted); border-bottom: var(--ws-divider); }
|
|
3819
4264
|
.ws-table thead th[aria-sort="ascending"], .ws-table thead th[aria-sort="descending"] { color: var(--color-text-heading); }
|
|
@@ -3953,6 +4398,27 @@ a.ws-stat-tiles__foot:focus-visible { outline-offset: calc(var(--ws-focus-width)
|
|
|
3953
4398
|
.ws-testimonial__person { min-width: 0; }
|
|
3954
4399
|
.ws-testimonial__name { font-size: var(--font-size-sm); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 500; color: var(--color-text-heading); }
|
|
3955
4400
|
.ws-testimonial__role { font-size: var(--font-size-2xs); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
4401
|
+
/* ---- TextDiff: two versions of a text, what was removed and what was added, told apart by more than colour. By line
|
|
4402
|
+
(`ws-diff`): a framed block, each changed line on its tint with a sign in a 16 px gutter (minus removed, plus added),
|
|
4403
|
+
unchanged runs folded behind a quiet act. Inline (`ws-diff__del`, `ws-diff__ins` in running words): removed words on
|
|
4404
|
+
the needs tint and struck, added words on the live tint and underlined. Every change also says itself to a screen
|
|
4405
|
+
reader (a ws-sr word inside the del or the ins). ---- */
|
|
4406
|
+
.ws-diff { display: grid; min-width: 0; overflow: clip; border-radius: var(--ws-radius-control); background: var(--ws-field); box-shadow: var(--ws-shadow-hairline-in); color: var(--color-text-body); font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); }
|
|
4407
|
+
.ws-diff__line { display: grid; grid-template-columns: var(--space-md) minmax(0, 1fr); column-gap: var(--space-xs); padding: var(--space-3xs) var(--space-sm); white-space: pre-wrap; overflow-wrap: anywhere; text-decoration: none; }
|
|
4408
|
+
.ws-diff__sign { font-weight: 600; text-align: center; user-select: none; }
|
|
4409
|
+
.ws-diff__line--removed { background: var(--ws-tint-needs); color: var(--ws-word-needs); }
|
|
4410
|
+
.ws-diff__line--added { background: var(--ws-tint-live); color: var(--ws-word-positive); }
|
|
4411
|
+
/* An unchanged run folded: the row itself is the act (it opens the run), on the sunk ground, its words in the text
|
|
4412
|
+
column saying how many lines it holds, the wash under the pointer. */
|
|
4413
|
+
.ws-diff__skip { display: grid; grid-template-columns: var(--space-md) minmax(0, 1fr); column-gap: var(--space-xs); width: 100%; min-height: var(--space-lg); padding: var(--space-3xs) var(--space-sm); border: 0; background: var(--ws-surface-sunk); color: var(--color-text-heading); font: inherit; font-size: var(--ws-text-meta); font-weight: 500; text-align: start; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
4414
|
+
.ws-diff__skip > * { grid-column: 2; }
|
|
4415
|
+
@media (pointer: coarse) { .ws-diff__skip { min-height: var(--ws-touch); } }
|
|
4416
|
+
@media (hover: hover) { .ws-diff__skip:hover { background-image: linear-gradient(var(--ws-hover), var(--ws-hover)); } }
|
|
4417
|
+
.ws-diff__skip:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
4418
|
+
/* Inline: changed words inside a sentence. */
|
|
4419
|
+
.ws-diff__del, .ws-diff__ins { border-radius: calc(var(--ws-radius-control) / 2); text-decoration-thickness: var(--ws-line); }
|
|
4420
|
+
.ws-diff__del { background: var(--ws-tint-needs); color: var(--ws-word-needs); text-decoration-line: line-through; }
|
|
4421
|
+
.ws-diff__ins { background: var(--ws-tint-live); color: var(--ws-word-positive); text-decoration-line: underline; text-underline-offset: var(--space-3xs); }
|
|
3956
4422
|
/* ---- ThemeSwitch: System, Light or Dark (D12), from gtm-ai-impact's switch (ds-dark.css:181-188): the word "Theme" in
|
|
3957
4423
|
the muted words and a small Segmented of three icon radios (System, Light, Dark), the chosen one the navy act. It
|
|
3958
4424
|
sits at the foot of the app's nav. The head script in its README sets <html data-theme> before the page paints. ---- */
|
|
@@ -4328,6 +4794,16 @@ a.ws-timeline__title { display: block; font-weight: 500; text-decoration: none;
|
|
|
4328
4794
|
.ws-trend-line__lines path { fill: none; stroke: var(--ws-trend-line-tone); stroke-width: var(--ws-chart-line); stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
|
|
4329
4795
|
/* What is still to come (a projection): a dotted 1 px line. */
|
|
4330
4796
|
.ws-trend-line__lines .ws-trend-line__ahead { stroke-width: var(--ws-line); stroke-dasharray: 0.1 calc(var(--ws-hairline) * 5); }
|
|
4797
|
+
/* Several series (up to 3, one unit, one axis): the first is the figure's own __draw and points; each other is a
|
|
4798
|
+
__series box (display: contents, so its points still sit on the plot) holding its own __draw (the path only) and its
|
|
4799
|
+
points. The tones run navy, teal, purple (salmon is the reference's), each series' own --tone over it; the key's
|
|
4800
|
+
__key--2 and __key--3 match. With several series: no hatch (one area claims a whole, two areas cover each other), and
|
|
4801
|
+
the period in progress keeps its series' tone (its name and the dotted __ahead say it). */
|
|
4802
|
+
.ws-trend-line__series { display: contents; }
|
|
4803
|
+
.ws-trend-line__series, .ws-trend-line__legend > .ws-trend-line__key--2 { --ws-trend-line-tone: var(--tone, var(--ws-chart-finished)); }
|
|
4804
|
+
.ws-trend-line__series ~ .ws-trend-line__series, .ws-trend-line__legend > .ws-trend-line__key--3 { --ws-trend-line-tone: var(--tone, var(--ws-chart-past-half)); }
|
|
4805
|
+
.ws-trend-line__area:has(> .ws-trend-line__series) .ws-trend-line__hatch { display: none; }
|
|
4806
|
+
.ws-trend-line__area:has(> .ws-trend-line__series) .ws-trend-line__point--now { --ws-trend-line-mark: var(--ws-trend-line-tone); }
|
|
4331
4807
|
/* A point: its period's slot, from 12 above its value (the pointer's room) to the baseline. The column (::before) runs
|
|
4332
4808
|
from the value to the baseline, its corners 1 px, cut by the grid's lines (set --ticks on the figure to the number of
|
|
4333
4809
|
even steps the grid has; 1 cuts only at the top line); the dot (::after) is 8 on the value, ringed in the ground so the
|
|
@@ -4358,14 +4834,14 @@ a.ws-timeline__title { display: block; font-weight: 500; text-decoration: none;
|
|
|
4358
4834
|
.ws-trend-line__point:is(:focus-visible, .ws-trend-line__point--on) { anchor-name: --ws-trend-line-at; }
|
|
4359
4835
|
.ws-trend-line__band { position-anchor: --ws-trend-line-at; left: anchor(left); right: anchor(right); width: auto; top: calc(anchor(top) + var(--ws-trend-line-hit)); background: repeating-linear-gradient(to bottom, var(--ws-trend-line-tone) 0 var(--ws-ring), transparent 0 calc(var(--ws-ring) * 2)) center / var(--ws-line) 100% no-repeat; }
|
|
4360
4836
|
.ws-trend-line--bars .ws-trend-line__band { top: 0; background: var(--ws-hover); }
|
|
4361
|
-
.ws-trend-line__area:has(
|
|
4837
|
+
.ws-trend-line__area:has(.ws-trend-line__point:is(:focus-visible, .ws-trend-line__point--on)) > .ws-trend-line__band { opacity: 1; }
|
|
4362
4838
|
}
|
|
4363
4839
|
.ws-trend-line__point:is(:focus-visible, .ws-trend-line__point--on)::after { transform: scale(1.5); box-shadow: 0 0 0 var(--ws-trend-line-ring) var(--color-bg-page), 0 0 0 var(--space-xxs-plus) color-mix(in srgb, var(--ws-trend-line-mark) 12%, transparent); }
|
|
4364
4840
|
.ws-trend-line--bars .ws-trend-line__area:has(> .ws-trend-line__point:is(:focus-visible, .ws-trend-line__point--on)) > .ws-trend-line__point:not(:focus-visible, .ws-trend-line__point--on)::before { opacity: var(--ws-dim); }
|
|
4365
4841
|
@media (hover: hover) {
|
|
4366
4842
|
@supports (anchor-name: --a) {
|
|
4367
4843
|
.ws-trend-line__point:hover { anchor-name: --ws-trend-line-at; }
|
|
4368
|
-
.ws-trend-line__area:has(
|
|
4844
|
+
.ws-trend-line__area:has(.ws-trend-line__point:hover) > .ws-trend-line__band { opacity: 1; }
|
|
4369
4845
|
}
|
|
4370
4846
|
.ws-trend-line__point:hover::after { transform: scale(1.5); box-shadow: 0 0 0 var(--ws-trend-line-ring) var(--color-bg-page), 0 0 0 var(--space-xxs-plus) color-mix(in srgb, var(--ws-trend-line-mark) 12%, transparent); }
|
|
4371
4847
|
.ws-trend-line--bars .ws-trend-line__area:has(> .ws-trend-line__point:hover) > .ws-trend-line__point:not(:hover)::before { opacity: var(--ws-dim); }
|