@usableapp/cardds 0.7.14 → 0.7.16

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +6 -8
  3. package/css/actions.css +24 -1
  4. package/css/base.css +2 -2
  5. package/css/card.css +9 -13
  6. package/css/choice.css +2 -2
  7. package/css/journey.css +3 -3
  8. package/css/layover.css +5 -17
  9. package/css/media.css +6 -5
  10. package/css/numbers.css +4 -3
  11. package/css/palette.css +81 -40
  12. package/css/people.css +3 -2
  13. package/css/sheet.css +4 -6
  14. package/css/stack.css +6 -10
  15. package/css/step.css +3 -5
  16. package/css/theme.template.css +2 -1
  17. package/css/tokens.css +12 -1
  18. package/dist/actions/Dropdown.js +4 -5
  19. package/dist/cardds.css +164 -110
  20. package/dist/cards/Card.d.ts +3 -3
  21. package/dist/choice/Calendar.js +3 -5
  22. package/dist/choice/Mood.d.ts +10 -5
  23. package/dist/choice/Mood.js +4 -2
  24. package/dist/forms/Pin.js +7 -8
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/lists/Row.d.ts +2 -1
  28. package/dist/lists/Row.js +2 -2
  29. package/dist/media/Postcard.js +3 -5
  30. package/dist/media/Tile.d.ts +1 -1
  31. package/dist/numbers/Band.d.ts +1 -1
  32. package/dist/numbers/Picker.js +4 -5
  33. package/dist/numbers/Track.d.ts +4 -2
  34. package/dist/numbers/Track.js +3 -3
  35. package/dist/people/AvatarPick.d.ts +3 -1
  36. package/dist/people/AvatarPick.js +2 -2
  37. package/dist/scaffold/Centre.d.ts +1 -1
  38. package/dist/scaffold/Centre.js +1 -1
  39. package/dist/scaffold/Screen.d.ts +1 -1
  40. package/dist/scaffold/Screen.js +3 -4
  41. package/dist/scaffold/TopBar.js +3 -5
  42. package/dist/sheets/Sheet.js +5 -7
  43. package/dist/sheets/SheetStack.js +4 -5
  44. package/dist/sheets/SheetSteps.js +5 -6
  45. package/dist/sheets/StackSheet.d.ts +1 -1
  46. package/dist/sheets/StackSheet.js +4 -5
  47. package/dist/state.d.ts +7 -0
  48. package/dist/state.js +17 -0
  49. package/dist/templates/CallSheet.js +3 -4
  50. package/dist/templates/Home.d.ts +1 -1
  51. package/dist/templates/Home.js +3 -4
  52. package/dist/templates/Splash.js +2 -1
  53. package/dist/templates/Walkthrough.js +4 -5
  54. package/dist/type/Icon.js +3 -5
  55. package/dist/type/textScale.d.ts +6 -0
  56. package/dist/type/textScale.js +12 -0
  57. package/package.json +1 -1
  58. package/dist/layover/Deck.d.ts +0 -9
  59. package/dist/layover/Deck.js +0 -10
package/dist/cardds.css CHANGED
@@ -89,7 +89,7 @@
89
89
  --btn-h: calc(var(--tap-base) * 13 / 12); /* 52 button height */
90
90
  --btn-xl-h: calc(var(--tap-base) * 11 / 6); /* 88 the one giant CTA */
91
91
  --chip-h: calc(var(--tap-base) * 19 / 24); /* 38 chip height */
92
- --bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell, deck actions */
92
+ --bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell */
93
93
  --header-h: calc(var(--tap-base) * 5 / 4); /* 60 top bar — .topbar and .appbar alike */
94
94
  --card-pad-rest: var(--sp-5); /* 20 a card's padding before any variant — .card publishes it as --card-pad; the sheet's peek strip is built from it where no card is in scope */
95
95
  --card-gap-rest: var(--sp-4); /* 16 …and its gap */
@@ -136,6 +136,9 @@
136
136
  --motion-sheet: 0.35s;
137
137
  --motion-slide: 0.22s; /* a centred card sliding out and the next in (card.css) — quick, a page turn (Lh, 2026-09-12) */
138
138
  --motion-flip: 0.45s; /* a postcard turning over (media.css) — slow enough to read as a card in the hand */
139
+ --dim: 0.7; /* secondary words dimmed on a ground whose muted ink does not fit (the dark card --3, a drawer, a tile's sub line): ONE share for all */
140
+ --motion-press: 0.12s; /* a control giving under the finger and springing back (actions.css PRESS) */
141
+ --press-scale: 0.96; /* how far it gives — a share of its size */
139
142
 
140
143
  /* ---- typography: one scale, rem so it follows the root ---- */
141
144
  --font-ui: "PK Nonthaburi", -apple-system, system-ui, sans-serif; /* fonts.css ships it; the rest is the fallback while it loads */
@@ -208,6 +211,9 @@
208
211
  --card-1-bg: var(--color-paper, #ffffff); --card-1-ink: var(--color-ink, #141414);
209
212
  --card-2-bg: var(--_tone-2, #e6e6e3); --card-2-ink: var(--color-ink, #141414);
210
213
  --card-3-bg: var(--color-brand, #141414); --card-3-ink: var(--color-on-brand, #ffffff);
214
+ /* tones 4 · 5 (Lh 2026-09-25): more card colours for a palette that has them — the wireframe keeps them grey */
215
+ --card-4-bg: #d4d4d0; --card-4-ink: var(--color-ink, #141414); --card-4-muted: #555555;
216
+ --card-5-bg: #3d3d3d; --card-5-ink: #ffffff; --card-5-muted: #c4c4c4;
211
217
 
212
218
  --card-border: #141414; /* wireframe: visible outline; palettes may set transparent */
213
219
  --card-muted: var(--_muted-on-paper, #6b6b6b); /* secondary text inside cards (the default, tone-1 card) */
@@ -228,6 +234,11 @@
228
234
  --neg: #3d3d3d;
229
235
  --dot-a: var(--color-brand, #141414);
230
236
  --dot-b: #8a8a8a;
237
+ --signal-warn: #8a6d00; /* the full signal set (Lh 2026-09-25): error · warn · ok · info — each with an icon and words */
238
+ --signal-ok: #2e7d32;
239
+ --signal-info: #2f5f9e;
240
+ /* highlights (Lh 2026-09-25): four atmosphere colours a palette may set for decoration — a count, a mark, a chart dot; the wireframe keeps them grey */
241
+ --highlight-1: #3d3d3d; --highlight-2: #8a8a8a; --highlight-3: #5f5f5f; --highlight-4: #b0b0b0;
231
242
  --signal-error: #c62828; /* the ONE colour in the wireframe (Lh 2026-09-24): something went wrong — a field's edge, the words that say what. Always with an icon and words, never the colour alone */
232
243
 
233
244
  /* derived tints — read the element's own ink, so they work on any slot */
@@ -288,46 +299,6 @@
288
299
  --dot-a: #e05c3a; --dot-b: #6f6be0;
289
300
  }
290
301
 
291
- /* ---- SHINE — shiny: periwinkle surface, coral / black / gold cards ---- */
292
- [data-palette="shine"] {
293
- --surface: #6d6ae4;
294
- --on-surface: #ffffff;
295
- --on-surface-muted: #d3d2f7;
296
-
297
- --card-1-bg: #eec85a; --card-1-ink: #16161a;
298
- --card-2-bg: #e8624a; --card-2-ink: #16161a;
299
- --card-3-bg: #1b1b1f; --card-3-ink: #ffffff;
300
-
301
- --card-border: transparent;
302
- --card-muted: #4c4a44;
303
-
304
- --accent: #1b1b1f; --on-accent: #ffffff;
305
- --action-bg: #ffffff; --action-ink: #16161a; --action-border: transparent;
306
-
307
- --pos: #2e9e5b; --neg: #d6402a;
308
- --dot-a: #e8624a; --dot-b: #6d6ae4;
309
- }
310
-
311
- /* ---- FIELD — warm peach editorial: cream surface, orange accent ---- */
312
- [data-palette="field"] {
313
- --surface: #f9e8dc;
314
- --on-surface: #241f1b;
315
- --on-surface-muted: #8a7d72;
316
-
317
- --card-1-bg: #ffffff; --card-1-ink: #241f1b;
318
- --card-2-bg: #f6d9c5; --card-2-ink: #241f1b;
319
- --card-3-bg: #ef4e0b; --card-3-ink: #fff4ec;
320
-
321
- --card-border: transparent;
322
- --card-muted: #8a7d72;
323
-
324
- --accent: #ef4e0b; --on-accent: #fff4ec;
325
- --action-bg: #ffffff; --action-ink: #241f1b; --action-border: transparent;
326
-
327
- --pos: #2e8f52; --neg: #d13b1a;
328
- --dot-a: #ef4e0b; --dot-b: #241f1b;
329
- }
330
-
331
302
  /* ---- ROAST — coffee: warm cream surface, caramel accent ---- */
332
303
  [data-palette="roast"] {
333
304
  --surface: #f6f1e7;
@@ -368,6 +339,87 @@
368
339
  --dot-a: #3d63e0; --dot-b: #eec85a;
369
340
  }
370
341
 
342
+ /* ---- ORCHARD — modern earth: calm, real, touchable (Lh 2026-09-25). Linen base, bone paper, moss brand; every
343
+ tone's ink + muted is a deep shade of ITS OWN ground, so lines (currentColor mixes: --hairline, --outline, field
344
+ edges) come out as a darker tint of the ground under them — never a grey on a warm card. Four highlights for the
345
+ atmosphere, off the earth wheel on purpose: maple red · bright yellow · sky · fuchsia. ---- */
346
+ [data-palette="orchard"] {
347
+ --surface: #f4efe6; /* linen */
348
+ --on-surface: #2a241c; /* umber */
349
+ --on-surface-muted: #6e6353;
350
+
351
+ --card-1-bg: #fbf8f2; --card-1-ink: #2a241c; /* bone paper — sheets */
352
+ --card-2-bg: #e6d8bf; --card-2-ink: #33291c; /* sand */
353
+ --card-3-bg: #5c6b47; --card-3-ink: #f6f1e6; --card-3-muted: #d3d8c4; /* moss — the brand */
354
+ --card-4-bg: #d3d9c4; --card-4-ink: #2b3322; --card-4-muted: #515c42; /* sage mist */
355
+ --card-5-bg: #dcc2a6; --card-5-ink: #3a2a1c; --card-5-muted: #6b503a; /* clay */
356
+
357
+ --card-border: transparent;
358
+ --card-muted: #736754;
359
+ --card-2-muted: #66573f;
360
+
361
+ --accent: #5c6b47; --on-accent: #f6f1e6;
362
+ --action-bg: #fbf8f2; --action-ink: #2a241c; --action-border: transparent;
363
+
364
+ /* highlights — decoration and atmosphere, a small dose: counts, marks, chart dots, a badge on a tile */
365
+ --highlight-1: #d93a1f; /* maple red */
366
+ --highlight-2: #f5c518; /* bright yellow */
367
+ --highlight-3: #3a9fe0; /* sky */
368
+ --highlight-4: #e0457b; /* fuchsia */
369
+ --dot-a: var(--highlight-1); --dot-b: var(--highlight-3);
370
+
371
+ /* signals from Sweet Salmon #dd9787 — words and edges, always with an icon */
372
+ --pos: #6f8f5a; --neg: #c4694f;
373
+ --signal-error: #b5503b; /* salmon, deepened */
374
+ --signal-warn: #a0721f; /* ochre */
375
+ --signal-ok: #4f7f4a; /* leaf */
376
+ --signal-info: #4d6f96; /* slate */
377
+ }
378
+ /* on the dark moss card the signals turn to their light shades — the same hues, readable on the ground */
379
+ /* where the highlights land — written as the component's own rule, its knob still first, so a project's knob wins (a palette never DECLARES a knob) */
380
+ [data-palette="orchard"] .badge:not(.badge--soft) { background: var(--badge-bg, var(--highlight-1)); color: var(--badge-ink, #fff8f0); }
381
+ [data-palette="orchard"] .tile-badge:not(.tile-badge--plain) { background: var(--tile-badge-bg, var(--highlight-2)); color: var(--tile-badge-ink, #2a241c); }
382
+ [data-palette="orchard"] .calendar__day--mark { background: var(--calendar-day-mark-bg, color-mix(in srgb, var(--highlight-2) 40%, transparent)); }
383
+ /* fields are WHITE boxes on every ground (Lh 2026-09-25) — the words typed in them read dark, even on the moss card */
384
+ [data-palette="orchard"] .field { background: var(--field-bg, #ffffff); color: #2a241c; }
385
+ [data-palette="orchard"] .pin__cell { background: var(--pin-cell-bg, #ffffff); color: #2a241c; }
386
+ /* the vivid ones, tried on the live parts (Lh 2026-09-25): primary = yellow · a user avatar = fuchsia · ring = sky · a switch on = sky · maple only on small dots (the count, dot a); the chosen stack tab stays moss */
387
+ [data-palette="orchard"] .btn--primary { background: var(--btn-primary-bg, var(--highlight-2)); color: var(--btn-primary-ink, #2a241c); }
388
+ [data-palette="orchard"] .avatar:not(.avatar--outline, .avatar--add, .avatar--on) { background: var(--avatar-bg, var(--highlight-4)); color: var(--avatar-ink, #ffffff); }
389
+ [data-palette="orchard"] .ring::before { background: conic-gradient(var(--highlight-3) calc(var(--value) * 1%), var(--ink-tint) 0); }
390
+ [data-palette="orchard"] .toggle:checked { background: var(--highlight-3); }
391
+ [data-palette="orchard"] .toggle:checked::after { background: #ffffff; }
392
+ [data-palette="orchard"] .card--3 { --signal-error: #f4b7a6; --signal-warn: #f1d18a; --signal-ok: #cfe6b8; --signal-info: #bcd0ec; }
393
+
394
+ /* ---- VERS — Lh's own-palette pick (2026-09-25): white surface, champagne paper, sage brand; earth tones for tones 2·4·5 ---- */
395
+ [data-palette="vers"] {
396
+ --surface: #ffffff;
397
+ --on-surface: #1f1b16;
398
+ --on-surface-muted: #6b6358;
399
+
400
+ --card-1-bg: #f6e7cb; --card-1-ink: #1f1b16; /* paper — Champagne */
401
+ --card-2-bg: #e3cfa6; --card-2-ink: #1f1b16; /* sand */
402
+ --card-3-bg: #678d58; --card-3-ink: #ffffff; /* brand — Sage */
403
+ --card-4-bg: #c9d6b0; --card-4-ink: #1f1b16; --card-4-muted: #4a5438; /* lichen */
404
+ --card-5-bg: #d4bf9c; --card-5-ink: #1f1b16; --card-5-muted: #5e4a36; /* wheat — bark was too dark */
405
+
406
+ --card-border: transparent;
407
+ --card-muted: #6b5f4c;
408
+ --card-2-muted: #5c4f3a;
409
+ --card-3-muted: #dfe8d6;
410
+
411
+ --accent: #678d58; --on-accent: #ffffff;
412
+ --action-bg: #f6e7cb; --action-ink: #1f1b16; --action-border: transparent;
413
+
414
+ /* signals from Sweet Salmon #dd9787, deepened to read on white/champagne */
415
+ --pos: #7fa86f; --neg: #dd9787;
416
+ --signal-error: #b5503b; /* salmon */
417
+ --signal-warn: #a5782a; /* ochre */
418
+ --signal-ok: #4f7f4a; /* leaf */
419
+ --signal-info: #4d6f96; /* slate */
420
+ --dot-a: #dd9787; --dot-b: #b08a5a;
421
+ }
422
+
371
423
  /* Colored palettes trade wireframe outlines for soft elevation. */
372
424
  [data-palette] :is(.card, .band-stack) { box-shadow: var(--shadow-card); }
373
425
  /* COLOURED MODE is the attribute, whatever its value: a project with colours of its own (the five
@@ -390,7 +442,7 @@
390
442
  the member wants to copy), and inputs are untouched. Native buttons and
391
443
  links are matched by element; the rest by the class that makes them one */
392
444
  button, [role="button"], [role="tab"], [role="option"], [role="menuitem"], a.btn, a.icon-btn, a.chip, label.chip, .chip--pick,
393
- .segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .dropdown__trigger, .track__step, .pager__at {
445
+ .segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .track__step, .pager__at {
394
446
  user-select: none;
395
447
  -webkit-user-select: none;
396
448
  -webkit-touch-callout: none;
@@ -483,7 +535,7 @@ button { font: inherit; cursor: pointer; }
483
535
 
484
536
  .t-muted { color: var(--on-surface-muted); }
485
537
  .card .t-muted { color: var(--card-muted); } /* muted follows the card's ink, not the surface's */
486
- .card--3 .t-muted { color: inherit; opacity: 0.7; } /* card-muted is tuned for light cards; dim instead on the dark slot */
538
+ .card--3 .t-muted { color: inherit; opacity: var(--dim); } /* card-muted is tuned for light cards; dim instead on the dark slot */
487
539
  .t-caps { text-transform: uppercase; }
488
540
  .grow { flex: 1; min-width: 0; } /* the child that takes the remaining row space */
489
541
  /* read, not shown (Lh 2026-09-23): words a screen reader needs — a page's h1 the design leaves out — kept in the page, taking no room.
@@ -564,6 +616,8 @@ button { font: inherit; cursor: pointer; }
564
616
 
565
617
  .card--2 { --card-bg: var(--card-2-bg); --card-ink: var(--card-2-ink); --card-muted: var(--card-2-muted); }
566
618
  .card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); }
619
+ .card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
620
+ .card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
567
621
 
568
622
  /* dark/accent slot: accent-toned controls would vanish — the slot re-points
569
623
  the accent tokens, so every accent consumer (btn--primary, chip--on,
@@ -697,8 +751,7 @@ button { font: inherit; cursor: pointer; }
697
751
  one is there, and the screen's gap), or twice the room below it (the
698
752
  bar's reserve) — whichever is less. --_lead is the title bar's height
699
753
  only on a screen that HAS one: without a .topbar/.appbar before it the
700
- block keeps that room (a deck and its two buttons, a card and its pager
701
- under it, need it on a short phone; Lh, 2026-09-11). */
754
+ block keeps that room (a card and its pager under it needs it on a short phone; Lh, 2026-09-11). */
702
755
  --_lead: 0rem;
703
756
  max-height: calc(2 * min(var(--screen-centre) - var(--_lead) - var(--screen-gap), 100% - var(--screen-centre) - var(--bar-reserve)));
704
757
  overflow-y: auto;
@@ -709,9 +762,8 @@ button { font: inherit; cursor: pointer; }
709
762
 
710
763
  /* ---- centred block: the same place, for one thing to read that is not a
711
764
  lone card — an empty state, a card with its quiet second button and the
712
- pager under it, a deck with its two round buttons. The block runs edge to
713
- edge and keeps the screen's inset as padding, so a deck's fanned cards can
714
- overhang and clip at the screen's edges (correct — the fold visual), and
765
+ pager under it. The block runs edge to
766
+ edge and keeps the screen's inset as padding, and
715
767
  its children stack at the screen's own rhythm (Lh, 2026-09-11):
716
768
  <div class="screen screen--fill"><header class="topbar">…</header>
717
769
  <div class="screen__centre"><article class="card">…</article><button class="btn btn--quiet btn--block">…</button></div>
@@ -784,14 +836,10 @@ button { font: inherit; cursor: pointer; }
784
836
  animation-timing-function: ease-out;
785
837
  mix-blend-mode: normal;
786
838
  }
787
- :root[data-slide="next"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-left; }
788
- :root[data-slide="next"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-right; }
789
- :root[data-slide="back"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-right; }
790
- :root[data-slide="back"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-left; }
791
- :root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
792
- :root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
793
- :root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
794
- :root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
839
+ :root[data-slide="next"]::view-transition-old(centre-card-out), :root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
840
+ :root[data-slide="next"]::view-transition-new(centre-card-in), :root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
841
+ :root[data-slide="back"]::view-transition-old(centre-card-out), :root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
842
+ :root[data-slide="back"]::view-transition-new(centre-card-in), :root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
795
843
  @keyframes centre-card-out-left { to { translate: -100% 0; opacity: 0; } }
796
844
  @keyframes centre-card-out-right { to { translate: 100% 0; opacity: 0; } }
797
845
  @keyframes centre-card-in-right { from { translate: 100% 0; opacity: 0; } }
@@ -1098,9 +1146,6 @@ button { font: inherit; cursor: pointer; }
1098
1146
  padding-left: 0;
1099
1147
  transition: padding-left 0.35s ease;
1100
1148
  }
1101
- .sheet-stack--tap > .card.is-open > .card__head {
1102
- padding-left: calc(var(--tap-sm) + var(--sp-3));
1103
- }
1104
1149
  .sheet-stack--tap .sheet-stack__back {
1105
1150
  position: absolute;
1106
1151
  left: 0;
@@ -1109,6 +1154,12 @@ button { font: inherit; cursor: pointer; }
1109
1154
  opacity: 0;
1110
1155
  visibility: hidden;
1111
1156
  transition: opacity 0.35s ease, visibility 0.35s;
1157
+ /* SMALL (see "the back button, SMALL" below) */
1158
+ width: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
1159
+ height: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
1160
+ min-width: 0;
1161
+ min-height: 0;
1162
+ padding: 0;
1112
1163
  }
1113
1164
  .sheet-stack--tap > .card.is-open .sheet-stack__back {
1114
1165
  opacity: 1;
@@ -1168,13 +1219,6 @@ button { font: inherit; cursor: pointer; }
1168
1219
  tappable"): a SMALL DISC — its ring kept, 32px instead of 44, the chevron in it at 20 — so the head gives it 32px + a gap
1169
1220
  (was 56px). Its tap target stays 44px: an invisible ::after around the disc. ---- */
1170
1221
  .sheet-stack--tap > .card.is-open > .card__head { padding-left: var(--stack-back-room, calc(var(--stack-back-size, calc(var(--tap-base) * 2 / 3)) + var(--sp-2))); }
1171
- .sheet-stack--tap .sheet-stack__back {
1172
- width: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
1173
- height: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
1174
- min-width: 0;
1175
- min-height: 0;
1176
- padding: 0;
1177
- }
1178
1222
  .sheet-stack--tap .sheet-stack__back > .icon { width: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); height: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); flex: none; } /* 26: the chevron fills the small disc (Lh 2026-09-23) */
1179
1223
  .sheet-stack--tap .sheet-stack__back::after { content: ""; position: absolute; inset: calc((var(--stack-back-size, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2); }
1180
1224
 
@@ -1568,9 +1612,8 @@ button { font: inherit; cursor: pointer; }
1568
1612
  bottom of the body, each its title line. Every step has a thin rule on top. A step opens and folds as a 1fr ↔ 0fr grid row, so the pile rises and
1569
1613
  falls — no height is written. Nothing fades: done and next read at full ink. */
1570
1614
  .sheet__body:has(> .sheet-steps) { display: flex; flex-direction: column; }
1571
- .sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; }
1615
+ .sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; position: relative; }
1572
1616
  .sheet-steps__holder { position: absolute; top: 0; left: 0; width: 1rem; height: 1rem; padding: 0; border: 0; opacity: 0; pointer-events: none; font-size: 1rem; } /* holds the cursor (and the phone's keyboard) while a step folds; font 1rem = the browser's own size, so iOS does not zoom to it */
1573
- .sheet-steps { position: relative; }
1574
1617
  .sheet-steps__track { list-style: none; margin: 0; padding: 0; flex: 1; display: flex; flex-direction: column; }
1575
1618
 
1576
1619
  .sheet-step {
@@ -1588,23 +1631,22 @@ button { font: inherit; cursor: pointer; }
1588
1631
  display: flex; align-items: center; gap: var(--sp-3); min-height: var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)); /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
1589
1632
  padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
1590
1633
  background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
1591
- border-top: var(--sheet-step-border-w, 0px) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
1634
+ border-top: var(--sheet-step-border-w, 0rem) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
1592
1635
  }
1593
- .sheet-step__summary { color: inherit; opacity: 0.8; }
1594
1636
  .sheet-step__title { font: var(--sheet-step-title-font, var(--type-label)); margin: 0; flex: 0 1 auto; min-width: 0; outline: none; /* the label size, not a body title: a pile of rows must stay small (Lh 2026-09-24) */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1595
1637
  .sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — a white disc on the bar, the number in the ink */
1596
1638
  flex: none; display: grid; place-items: center; inline-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2)); block-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2));
1597
1639
  border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
1598
1640
  background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
1599
1641
  }
1600
- .sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1642
+ .sheet-step__summary { color: inherit; opacity: 0.8; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1601
1643
  .sheet-step__check { margin-inline-start: auto; }
1602
1644
  .sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
1603
1645
  .sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); }
1604
1646
  .sheet-step__inner > * { min-width: 0; }
1605
1647
  .sheet-step__inner > .link { justify-self: center; } /* a link of its own row in a step (skip, "change number", "set PIN again"): centred under the step, as the skip (Lh 2026-09-25) */
1606
1648
  .sheet-step:not(.sheet-step--now) > .sheet-step__body { visibility: hidden; transition: visibility 0s linear var(--motion-sheet); }
1607
- .sheet-step__go { position: absolute; inset: min(0px, (var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
1649
+ .sheet-step__go { position: absolute; inset: min(0rem, (var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
1608
1650
  /* the last step's finish, when the app gives one: under the steps */
1609
1651
  .sheet-steps__finish { display: grid; padding-top: var(--card-gap); }
1610
1652
  @media (prefers-reduced-motion: reduce) { .sheet-step { transition: none; } }
@@ -1704,8 +1746,6 @@ button { font: inherit; cursor: pointer; }
1704
1746
  .step:has(> .sheet--peek) > :is(.page, .pages) { padding-bottom: var(--sheet-peek); }
1705
1747
  .step > :is(.page, .pages) {
1706
1748
  flex: 0 1 auto;
1707
- }
1708
- .step > :is(.page, .pages) {
1709
1749
  min-height: 0;
1710
1750
  margin: var(--_lead, 0) var(--screen-pad) 0; /* the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
1711
1751
  overflow-y: auto;
@@ -1717,12 +1757,12 @@ button { font: inherit; cursor: pointer; }
1717
1757
  padding above AND below — so the words begin near the middle and, when a big text size runs them long, scroll up clear.
1718
1758
  The walk shows no scrollbar. */
1719
1759
  .step > .pages { margin: 0; scrollbar-width: none; touch-action: pan-y; } /* pan-y: the walk scrolls itself up and down; a sideways swipe is the Walkthrough's (it turns the page) */ /* the scrolling box is the whole content: from the step's top edge, edge to edge — no margin (Lh 2026-09-23) */
1720
- .step > .pages > .page { padding-inline: var(--screen-pad); } /* the words keep the inset INSIDE it */
1721
- .step > .pages > .page { margin-top: var(--_lead, 0rem); } /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
1722
- .step > .pages::-webkit-scrollbar { display: none; }
1723
1760
  .step > .pages > .page {
1761
+ padding-inline: var(--screen-pad); /* the words keep the inset INSIDE it */
1762
+ margin-top: var(--_lead, 0rem); /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
1724
1763
  padding-bottom: var(--bottom-h, 0rem); /* exactly the bottom layer (measured by Screen): a page scrolls ONLY when the layer would cover its words, and then just far enough to clear them — at 100% nothing scrolls (Lh 2026-09-23) */
1725
1764
  }
1765
+ .step > .pages::-webkit-scrollbar { display: none; }
1726
1766
 
1727
1767
  /* ============================================================
1728
1768
  TOP + CONTENT in one flex column; BOTTOM is a LAYER over them (Lh 2026-09-23, second call — replaces "the bar is a row"):
@@ -1893,7 +1933,7 @@ button { font: inherit; cursor: pointer; }
1893
1933
  .route__point--end .route__code::after { content: none; }
1894
1934
  .route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
1895
1935
  .route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
1896
- .card--3 .route__place { color: inherit; opacity: 0.7; }
1936
+ .card--3 .route__place { color: inherit; opacity: var(--dim); }
1897
1937
 
1898
1938
  .route__via { grid-column: 2; grid-row: 2; align-self: center; display: flex; align-items: center; opacity: 0.65; font-size: var(--route-via-font-size, var(--fs-body)); }
1899
1939
 
@@ -1907,7 +1947,7 @@ button { font: inherit; cursor: pointer; }
1907
1947
  background: var(--tile-badge-bg, var(--card-3-bg));
1908
1948
  color: var(--tile-badge-ink, var(--card-3-ink));
1909
1949
  }
1910
- .tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: 0.7; }
1950
+ .tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: var(--dim); }
1911
1951
  .tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
1912
1952
 
1913
1953
  /* plain: no fill — a date box beside a title (Jun / 18) */
@@ -1925,7 +1965,7 @@ button { font: inherit; cursor: pointer; }
1925
1965
  font: var(--note-row-font, var(--type-caption));
1926
1966
  color: var(--note-row-ink, var(--card-muted));
1927
1967
  }
1928
- .card--3 .note-row { color: inherit; opacity: 0.7; }
1968
+ .card--3 .note-row { color: inherit; opacity: var(--dim); }
1929
1969
 
1930
1970
  /* ---- css/actions.css — topbar, chips, buttons, CTA pack ---- */
1931
1971
  /* ============================================================
@@ -2002,7 +2042,7 @@ button { font: inherit; cursor: pointer; }
2002
2042
  border-radius: var(--appbar-who-radius, var(--r-chip));
2003
2043
  -webkit-tap-highlight-color: transparent;
2004
2044
  }
2005
- .appbar__who:focus-visible { outline: var(--focus); outline-offset: 2px; }
2045
+ .appbar__who:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
2006
2046
  .appbar__title, .appbar__sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.35; } /* clipped lines: room for Thai marks */
2007
2047
  .appbar__title { font: var(--type-label); }
2008
2048
  .appbar__sub { font: var(--type-caption); color: var(--on-surface-muted); }
@@ -2264,6 +2304,29 @@ button { font: inherit; cursor: pointer; }
2264
2304
  .action-note__words { display: block; } /* ONE run of words: an icon inside sits in the line, the spaces around it kept */
2265
2305
  .action-note__words > .icon { display: inline-block; vertical-align: -0.2em; width: 1.25em; height: 1.25em; margin-inline: 0.15em; color: var(--action-note-icon-ink, inherit); } /* a project may colour the icon apart from the words — e.g. the system blue of the control it points at (Lh 2026-09-24) */
2266
2306
 
2307
+ /* PRESS (Lh 2026-09-25): every CONTROL answers a tap — it gives under the finger and springs back, its choice fill fades in;
2308
+ INFORMATION never does (no pointer, no press). One list, zero specificity: a component's own rule always wins. */
2309
+ :where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
2310
+ .calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item) {
2311
+ cursor: pointer;
2312
+ touch-action: manipulation;
2313
+ transition: scale var(--motion-press) ease-out;
2314
+ }
2315
+ /* a CHOICE's fill fades in as it is picked (a button's colour never changes on a tap, so it does not fade) */
2316
+ :where(.chip--pick, .tile--pick, .day, .segment__item, .calendar__day, .float-bar__item, .menu__item) {
2317
+ transition: scale var(--motion-press) ease-out, background-color var(--motion-press) ease-out, color var(--motion-press) ease-out, border-color var(--motion-press) ease-out;
2318
+ }
2319
+ :where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
2320
+ .calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item):active:not(:disabled, [aria-disabled="true"]) {
2321
+ scale: var(--press-scale);
2322
+ }
2323
+ :where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, a.tile, button.tile, .day, .segment__item, .calendar__day, .float-bar__item,
2324
+ .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn):focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
2325
+ @media (prefers-reduced-motion: reduce) {
2326
+ :where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
2327
+ .calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item):active { scale: none; }
2328
+ }
2329
+
2267
2330
  /* ---- css/numbers.css — ring, track, band, dot grid, picker, badge, bars ---- */
2268
2331
  /* ============================================================
2269
2332
  cardds/numbers.css — big numbers inside cards:
@@ -2300,7 +2363,7 @@ button { font: inherit; cursor: pointer; }
2300
2363
  }
2301
2364
  .ring__value { font: var(--ring-value-font, var(--type-stat)); letter-spacing: var(--track-display); }
2302
2365
  .ring__label { font: var(--ring-label-font, var(--type-caption)); color: var(--ring-label-ink, var(--card-muted)); }
2303
- .card--3 .ring__label { color: inherit; opacity: 0.7; }
2366
+ .card--3 .ring__label { color: inherit; opacity: var(--dim); }
2304
2367
  .ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
2305
2368
  .ring--sm .ring__value { font: 800 var(--fs-2xl)/1 var(--font-display); }
2306
2369
  .ring--xs { --_size: var(--tap-sm); --_w: var(--dot-xs); } /* inline counter (step 3 of 5) */
@@ -2353,6 +2416,7 @@ button { font: inherit; cursor: pointer; }
2353
2416
  background: color-mix(in srgb, var(--_ink) 30%, transparent);
2354
2417
  color: var(--_ink);
2355
2418
  }
2419
+ .track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-title)); font-variant-numeric: tabular-nums; } /* a number in the round (TrackStep num) */
2356
2420
  .track__step.is-done > .icon,
2357
2421
  .track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
2358
2422
 
@@ -2374,7 +2438,7 @@ button { font: inherit; cursor: pointer; }
2374
2438
  }
2375
2439
  .band__num small { font-size: 0.5em; font-weight: 700; margin-right: 0.1em; } /* unit or % */
2376
2440
  .band__foot { grid-area: foot; font: var(--band-foot-font, var(--type-caption)); color: var(--band-foot-ink, var(--card-muted)); }
2377
- .card--3 .band__foot { color: inherit; opacity: 0.7; }
2441
+ .card--3 .band__foot { color: inherit; opacity: var(--dim); }
2378
2442
  /* band stack: ONE card boundary; bands are frameless inside it, a hairline
2379
2443
  between each. Bands may still carry slot tints (card--2 / card--3). */
2380
2444
  .band-stack {
@@ -2473,7 +2537,7 @@ button { font: inherit; cursor: pointer; }
2473
2537
  }
2474
2538
  .picker::after { left: auto; right: calc(100% / 3); }
2475
2539
  .picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
2476
- .card--3 .picker__unit { color: inherit; opacity: 0.7; }
2540
+ .card--3 .picker__unit { color: inherit; opacity: var(--dim); }
2477
2541
  /* the scrolled-to item lights up by position — no JS */
2478
2542
  @supports (animation-timeline: view(x)) {
2479
2543
  .picker__item { opacity: 0.3; animation: picker-focus linear both; animation-timeline: view(x); }
@@ -2574,7 +2638,7 @@ button { font: inherit; cursor: pointer; }
2574
2638
  .avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
2575
2639
  /* outline avatar: no fill, an edge — icons in a ring, "add" slots */
2576
2640
  .avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
2577
- .avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); cursor: pointer; }
2641
+ .avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); }
2578
2642
  /* accent avatar: the "+7" tail, the selected person */
2579
2643
  .avatar--on { background: var(--avatar-on-bg, var(--accent)); color: var(--avatar-on-ink, var(--on-accent)); }
2580
2644
  /* halo: faint concentric rings behind a hero avatar (decorative) */
@@ -2619,7 +2683,8 @@ button { font: inherit; cursor: pointer; }
2619
2683
  }
2620
2684
  .avatar-pick__name { font: var(--avatar-pick-name-font, var(--type-label)); font-size: var(--avatar-pick-name-font-size, var(--fs-sm)); }
2621
2685
  .avatar-pick__role { font: var(--avatar-pick-role-font, var(--type-caption)); color: var(--avatar-pick-role-ink, var(--card-muted)); margin-top: calc(-1 * var(--sp-1)); }
2622
- .card--3 .avatar-pick__role { color: inherit; opacity: 0.7; }
2686
+ .avatar-pick__item[aria-pressed="true"] > .avatar { background: var(--avatar-pick-on-bg, var(--accent)); color: var(--avatar-pick-on-ink, var(--on-accent)); border-color: transparent; } /* the picked person */
2687
+ .card--3 .avatar-pick__role { color: inherit; opacity: var(--dim); }
2623
2688
 
2624
2689
  /* ---- css/choice.css — check, toggle, toggle chip, chip grid, days, calendar, mood, pin, composer ---- */
2625
2690
  /* ============================================================
@@ -2763,7 +2828,7 @@ button { font: inherit; cursor: pointer; }
2763
2828
  .calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
2764
2829
  .calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
2765
2830
  .calendar__wd { font: var(--calendar-wd-font, var(--type-caption)); color: var(--calendar-wd-ink, var(--card-muted)); padding-block: var(--calendar-wd-pad-y, var(--sp-2)); }
2766
- .card--3 .calendar__wd { color: inherit; opacity: 0.7; }
2831
+ .card--3 .calendar__wd { color: inherit; opacity: var(--dim); }
2767
2832
  .calendar__day {
2768
2833
  width: 100%;
2769
2834
  max-width: var(--calendar-day-max-w, var(--tap));
@@ -2866,7 +2931,7 @@ button { font: inherit; cursor: pointer; }
2866
2931
  .composer__input::placeholder { color: inherit; opacity: 0.4; }
2867
2932
  .composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
2868
2933
  .composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
2869
- .card--3 .composer__hint { color: inherit; opacity: 0.7; }
2934
+ .card--3 .composer__hint { color: inherit; opacity: var(--dim); }
2870
2935
 
2871
2936
  /* ---- slider: one value along a track, a thumb-size knob ----
2872
2937
  <label class="slider"><span class="slider__label">ขนาดตัวหนังสือ</span><input class="slider__range" type="range" min="100" max="160" step="15" aria-label="ขนาดตัวหนังสือ"></label>
@@ -3209,7 +3274,7 @@ button { font: inherit; cursor: pointer; }
3209
3274
  .quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
3210
3275
  .quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
3211
3276
  .quote__by { font: var(--quote-by-font, var(--type-caption)); color: var(--quote-by-ink, var(--card-muted)); }
3212
- .card--3 .quote__by { color: inherit; opacity: 0.7; }
3277
+ .card--3 .quote__by { color: inherit; opacity: var(--dim); }
3213
3278
 
3214
3279
  /* ---- tile grid: big square tiles — icon top, label bottom ----
3215
3280
  <div class="tile-grid"><button class="tile"><svg class="icon"/><span class="tile__label">Light</span></button>
@@ -3235,11 +3300,10 @@ button { font: inherit; cursor: pointer; }
3235
3300
  font: inherit;
3236
3301
  text-align: left;
3237
3302
  position: relative;
3238
- cursor: pointer;
3239
3303
  }
3240
3304
  .tile__label { font: var(--tile-label-font, var(--type-title)); }
3241
3305
  .tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
3242
- .tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: 0.7; }
3306
+ .tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: var(--dim); }
3243
3307
  .tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
3244
3308
  .tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
3245
3309
  .tile--pick > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
@@ -3247,7 +3311,7 @@ button { font: inherit; cursor: pointer; }
3247
3311
  .tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
3248
3312
  .tile--wide { grid-column: 1 / -1; }
3249
3313
  /* stat: READ, not pressed — no frame of its own inside a card (a frame says "tap me"): icon · value · words on the card's ground */
3250
- .tile--stat { border-color: transparent; background: none; padding-inline: 0; cursor: default; }
3314
+ .tile--stat { border-color: transparent; background: none; padding-inline: 0; }
3251
3315
  .tile--stat { min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
3252
3316
  /* upload: dashed, centred plus + label */
3253
3317
  .tile--upload {
@@ -3261,6 +3325,8 @@ button { font: inherit; cursor: pointer; }
3261
3325
  }
3262
3326
  /* tinted tiles: slot colors inside a card (schedule, notes) */
3263
3327
  .tile--2 { background: var(--tile-2-bg, var(--card-2-bg)); color: var(--tile-2-ink, var(--card-2-ink)); border-color: transparent; }
3328
+ .tile--4 { background: var(--tile-4-bg, var(--card-4-bg)); color: var(--tile-4-ink, var(--card-4-ink)); border-color: transparent; }
3329
+ .tile--5 { background: var(--tile-5-bg, var(--card-5-bg)); color: var(--tile-5-ink, var(--card-5-ink)); border-color: transparent; }
3264
3330
  .tile--3 { background: var(--tile-3-bg, var(--card-3-bg)); color: var(--tile-3-ink, var(--card-3-ink)); border-color: transparent; }
3265
3331
 
3266
3332
  /* ---- fold: a sticky-note corner on any card (decorative) ----
@@ -3269,7 +3335,7 @@ button { font: inherit; cursor: pointer; }
3269
3335
  the corner read as a notch with a line through it rather than a lifted flap.
3270
3336
  The COLOUR goes transparent rather than the border going to 0, so the box
3271
3337
  keeps its geometry and a fold card still measures the same as the cards
3272
- beside it in a deck. */
3338
+ beside it. */
3273
3339
  .card--fold {
3274
3340
  border-color: transparent;
3275
3341
  border-bottom-right-radius: 0;
@@ -3446,7 +3512,7 @@ button { font: inherit; cursor: pointer; }
3446
3512
  /* ============================================================
3447
3513
  cardds/layover.css — cards that lay OVER other content:
3448
3514
  Modal (ask) · popover menu · expanding banner · float bar ·
3449
- callout · swipe deck. The sheet (peek / half / full / page)
3515
+ callout. The sheet (peek / half / full / page)
3450
3516
  lives in sheet.css; these compose with .sheet-stage.
3451
3517
  ============================================================ */
3452
3518
 
@@ -3517,7 +3583,7 @@ button { font: inherit; cursor: pointer; }
3517
3583
  --accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
3518
3584
  --on-accent: var(--card-3-bg);
3519
3585
  }
3520
- .modal__drawer .t-caption { opacity: 0.7; }
3586
+ .modal__drawer .t-caption { opacity: var(--dim); }
3521
3587
  .modal__drawer .icon-btn { background: transparent; color: inherit; border-color: var(--ink-rail); }
3522
3588
  .modal__drawer .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; width: var(--tap-lg); height: var(--tap-lg); }
3523
3589
  @media (prefers-reduced-motion: no-preference) {
@@ -3589,7 +3655,7 @@ button { font: inherit; cursor: pointer; }
3589
3655
  cursor: pointer;
3590
3656
  }
3591
3657
  .menu__item:hover, .menu__item:focus-visible { background: var(--ink-tint); outline: none; }
3592
- .menu__item[aria-current], .menu__item.is-on { background: var(--accent); color: var(--on-accent); }
3658
+ .menu__item[aria-current] { background: var(--accent); color: var(--on-accent); }
3593
3659
  @supports (anchor-name: --a) {
3594
3660
  .menu[popover] {
3595
3661
  position-area: block-end span-inline-end;
@@ -3700,8 +3766,7 @@ button { font: inherit; cursor: pointer; }
3700
3766
  }
3701
3767
  /* aria-current="false" is the correct way to mark the OTHER tabs, so the
3702
3768
  bare attribute selector lights every one of them up */
3703
- .float-bar__item[aria-current]:not([aria-current="false"]),
3704
- .float-bar__item.is-on { background: var(--accent); color: var(--on-accent); opacity: 1; }
3769
+ .float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); opacity: 1; }
3705
3770
  .float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
3706
3771
 
3707
3772
  /* ---- callout: a small card with a tail — over a map, a media, a mascot ----
@@ -3735,18 +3800,7 @@ button { font: inherit; cursor: pointer; }
3735
3800
  border-left: 0;
3736
3801
  }
3737
3802
  .callout__title { font: var(--callout-title-font, var(--type-title)); }
3738
- .callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: 0.7; }
3739
- /* ---- deck: swipe cards; the last child is on top ----
3740
- <div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
3741
- <div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
3742
- .deck { display: grid; place-items: center; padding: var(--deck-pad-top, var(--sp-4)) var(--deck-pad-x, var(--sp-4)) var(--deck-pad-bottom, var(--sp-6)); }
3743
- .deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
3744
- .deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
3745
- .deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
3746
- .deck > .card:nth-last-child(n + 4) { display: none; }
3747
- .deck__actions { display: flex; justify-content: center; gap: var(--deck-actions-gap, var(--sp-8)); }
3748
- .deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
3749
-
3803
+ .callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: var(--dim); }
3750
3804
  /* ---- splash: the app's mark at the human centre while it boots (Lh 2026-09-24) ----
3751
3805
  <section class="screen screen--fill splash"><div class="splash__group"><div class="splash__mark">{the app's lottie/img}</div>
3752
3806
  <p class="splash__text">…</p></div></section>
@@ -1,14 +1,14 @@
1
1
  import { type ComponentProps } from 'react';
2
2
  export interface CardProps extends ComponentProps<'article'> {
3
- /** the tone slot: 1 (default, light) · 2 (tinted) · 3 (dark / accent — controls invert automatically) */
4
- tone?: 1 | 2 | 3;
3
+ /** the tone slot: 1 (default, light) · 2 (tinted) · 3 (dark / accent — controls invert automatically) · 4 · 5 (more colours, from the palette) */
4
+ tone?: 1 | 2 | 3 | 4 | 5;
5
5
  /** compact: a small card inside a card or a sheet body (a note, a promo) */
6
6
  sm?: boolean;
7
7
  /** a list row that is its own card: thumb · body · end, in a CardList */
8
8
  row?: boolean;
9
9
  /** the image IS the card: a Placeholder or img first, text over a scrim */
10
10
  cover?: boolean;
11
- /** a sticky-note corner, no border (swipe decks, quotes) */
11
+ /** a sticky-note corner, no border (quotes) */
12
12
  fold?: boolean;
13
13
  /** one card at the human centre of a `Screen fill` (the introduction screens) */
14
14
  centre?: boolean;
@@ -1,13 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useControlled } from '../state.js';
3
3
  import { cx } from '../cx.js';
4
4
  import { IconBtn } from '../actions/IconBtn.js';
5
5
  /** Calendar — a month grid of 44px round cells: one accent day, optional marks; prev/next in the head. It shows ONE month and knows no dates:
6
6
  * the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). */
7
7
  export function Calendar({ title, days = 30, start = 1, selected, defaultSelected, onSelect, onPrev, onNext, prevLabel = 'Previous', nextLabel = 'Next', marks = [], marks2 = [], disabled = [], weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], nav, className, ...rest }) {
8
- const [inner, setInner] = useState(defaultSelected);
9
- const day = selected !== undefined ? selected : inner;
10
- const pick = (d) => { if (selected === undefined)
11
- setInner(d); onSelect?.(d); };
8
+ const [day, setOwn] = useControlled(selected, defaultSelected);
9
+ const pick = (d) => { setOwn(d); onSelect?.(d); };
12
10
  return (_jsxs("div", { className: cx('calendar', className), ...rest, children: [_jsxs("div", { className: "calendar__head", children: [_jsx("span", { className: "t-title", children: title }), nav ?? (_jsxs("span", { className: "icon-row", children: [_jsx(IconBtn, { sm: true, icon: "chevron-left", label: prevLabel, onClick: onPrev }), _jsx(IconBtn, { sm: true, icon: "chevron-right", label: nextLabel, onClick: onNext })] }))] }), _jsxs("div", { className: "calendar__grid", children: [weekdays.map((d) => _jsx("span", { className: "calendar__wd", children: d }, d)), Array.from({ length: days }, (_, i) => i + 1).map((d) => (_jsx("button", { type: "button", className: cx('calendar__day', marks.includes(d) && 'calendar__day--mark', marks2.includes(d) && 'calendar__day--mark2'), "aria-selected": d === day ? 'true' : undefined, onClick: () => pick(d), disabled: disabled.includes(d), style: d === 1 ? { '--start': start } : undefined, children: d }, d)))] })] }));
13
11
  }