@usableapp/cardds 0.7.95 → 0.7.96

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/dist/cardds.css CHANGED
@@ -144,9 +144,6 @@
144
144
  --motion-quick: 0.2s; /* a small state flip that isn't a press: a chevron turning, a popover/banner opening */
145
145
  --motion-slide: 0.22s; /* a centred card sliding out and the next in (card.css) — quick, a page turn (Lh, 2026-09-12) */
146
146
  --motion-flip: 0.45s; /* a postcard turning over (media.css) — slow enough to read as a card in the hand */
147
- --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 */
148
- --rest: 0.6; /* an unpicked choice at rest: .day, .segment__item, .float-bar__item */
149
- --faint: 0.3; /* further still: rails, far dots, unselected picker items */
150
147
  --motion-press: 0.12s; /* a control giving under the finger and springing back (actions.css PRESS) */
151
148
  --press-scale: 0.96; /* how far a finger-sized control gives — a share of its size */
152
149
  --press-dim: 0.6; /* how far a card-wide one (a row, a summary line, a card) fades under the finger */
@@ -215,40 +212,45 @@
215
212
  value. That is what the fallbacks are for — a private mix (--_*) that
216
213
  reads an unset base is invalid, so var(--_x, #grey) lands on the grey.
217
214
  Set, the roles derive: tone 2 = the brand washed into the surface, the
218
- muted inks = the ink washed into their ground. A role set directly
215
+ muted inks = the ink washed into its ground. A role set directly
219
216
  (a palette, or a project's theme.css) always wins over its derivation.
220
217
  ============================================================ */
221
- --_muted-on-surface: color-mix(in srgb, var(--color-ink) 66%, var(--color-surface, #f2f2f0));
222
- --_muted-on-paper: color-mix(in srgb, var(--color-ink) 66%, var(--color-paper, #ffffff));
223
218
  --_tone-2: color-mix(in srgb, var(--color-brand) 14%, var(--color-surface, #f2f2f0));
224
- --_muted-on-tone-2: color-mix(in srgb, var(--color-ink, #141414) 72%, var(--_tone-2));
225
- --_muted-on-brand: color-mix(in srgb, var(--color-on-brand) 70%, var(--color-brand)); /* the tone-3 card's secondary words: on-brand faded into brand — was opacity 0.7, which no theme could set (timebank #132, 2026-09-22) */
219
+ --_onlight-muted: color-mix(in srgb, var(--color-ink) 70%, var(--_tone-2));
220
+ --_ondark-muted: color-mix(in srgb, var(--color-on-brand) 70%, var(--color-brand));
221
+
222
+ /* INK: TWO SETS, ONLIGHT AND ONDARK (Lh 2026-10-18: "there are text colours ONLIGHT and ONDARK. When the ground is light, use
223
+ onlight; when the ground is dark, use ondark"). Words and glyphs alike (icons, marks, dots, a check, a knob). Each set holds its
224
+ ink and its muted shade to 4.5:1 on every ground of its side. README "Ink follows its ground". */
225
+ --ink-onlight: var(--color-ink, #141414);
226
+ --ink-onlight-muted: var(--_onlight-muted, #575757); /* 4.6:1 on the water note and 4.9 on tone 4's grey, the darkest light grounds */
227
+ --ink-ondark: var(--color-on-brand, #ffffff);
228
+ --ink-ondark-muted: var(--_ondark-muted, #a8a8a8); /* 4.6:1 on tone 5's grey, the lightest dark ground */
229
+
230
+ /* SIDES: every ground says whether it is dark (1) or light (0); nothing else. Its words and glyphs follow from the side
231
+ (the surface list below). Dark mode is these switches turned to 1 with darker fills, and no rule per piece. */
232
+ --surface-dark: 0;
233
+ --card-1-dark: 0; --card-2-dark: 0; --card-3-dark: 1; --card-4-dark: 0; --card-5-dark: 1;
234
+ --accent-dark: 1; /* the brand fill: a primary button, a picked chip, a picked day */
235
+ --action-dark: 0; /* the paper fill of neutral controls: a button, a chip, a field, a plain tile */
236
+ --neg-dark: 1; /* a falling Delta's fill */
226
237
 
227
238
  /* app background ("earth-toned surface or shiny color, up to the app") */
228
239
  --surface: var(--color-surface, #f2f2f0);
229
- --on-surface: var(--color-ink, #141414);
230
- --on-surface-muted: var(--_muted-on-surface, #6b6b6b);
231
240
 
232
- /* card boundaries — 3 slots so stacked/adjacent cards can differ */
233
- --card-1-bg: var(--color-paper, #ffffff); --card-1-ink: var(--color-ink, #141414);
234
- --card-2-bg: var(--_tone-2, #e6e6e3); --card-2-ink: var(--color-ink, #141414);
235
- --card-3-bg: var(--color-brand, #141414); --card-3-ink: var(--color-on-brand, #ffffff);
241
+ /* card boundaries — five tones; each one's words follow its side */
242
+ --card-1-bg: var(--color-paper, #ffffff);
243
+ --card-2-bg: var(--_tone-2, #e6e6e3);
244
+ --card-3-bg: var(--color-brand, #141414);
236
245
  /* tones 4 · 5 (Lh 2026-09-25): more card colours for a palette that has them — the wireframe keeps them grey */
237
- --card-4-bg: #d4d4d0; --card-4-ink: var(--color-ink, #141414); --card-4-muted: #555555;
238
- --card-5-bg: #3d3d3d; --card-5-ink: #ffffff; --card-5-muted: #c4c4c4;
246
+ --card-4-bg: #d4d4d0;
247
+ --card-5-bg: #3d3d3d;
239
248
 
240
- --card-border: #141414; /* wireframe: visible outline; palettes may set transparent */
241
- --card-muted: var(--_muted-on-paper, #6b6b6b); /* secondary text inside cards (the default, tone-1 card) */
242
- --card-3-muted: var(--_muted-on-brand, #b9b9b9); /* the same text on the tone-3 card — a real colour, like the two above: a theme whose brand is not near-black sets it to reach its contrast (the wireframe's #b9b9b9 = white at 0.7 on #141414, what the opacity gave) */
243
- --card-2-muted: var(--_muted-on-tone-2, #5f5f5f); /* the same text on the tone-2 card — 5.1:1 on its grey (#6b6b6b is 4.3:1, under AA);
244
- a palette whose tone-2 ground is strong
245
- sets this to its tone-2 ink — a muted shade cannot reach AA there (clay) */
249
+ --card-border: #141414; /* wireframe: visible outline; coloured mode: --on-surface-muted (palette.css). Its edge, or the fill, holds 3:1 on the ground: cardGrounds() */
246
250
 
247
251
  /* actions */
248
252
  --accent: var(--color-brand, #141414); /* primary CTA fill */
249
- --on-accent: var(--color-on-brand, #ffffff);
250
253
  --action-bg: var(--color-paper, #ffffff); /* neutral buttons/chips/inputs */
251
- --action-ink: var(--color-ink, #141414);
252
254
  --action-border: #141414;
253
255
 
254
256
  /* signals (chip dots, deltas) */
@@ -259,12 +261,15 @@
259
261
  /* THE SIGNAL SET (Lh 2026-09-25; the standard, Lh 2026-10-02): ok (green) · warn (yellow) · error (red) · info (blue) — the same four
260
262
  meanings in every project, wireframe included; a theme may retune the shade, never the meaning. Each is dark enough for words and
261
263
  edges and comes with an icon and words, never the colour alone. `--signal-*-soft` (below :root) = its light ground. */
262
- --signal-warn: #8a6d00;
263
- --signal-ok: #2e7d32;
264
- --signal-info: #2f5f9e;
264
+ --signal-warn-onlight: #705903; /* each onlight shade 4.5:1 on every light ground, tone 4's grey the darkest (#7f6400 was 3.8 there) */
265
+ --signal-ok-onlight: #27692a;
266
+ --signal-info-onlight: #2e5c98;
265
267
  /* 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 */
266
268
  --highlight-1: #3d3d3d; --highlight-2: #8a8a8a; --highlight-3: #5f5f5f; --highlight-4: #b0b0b0;
267
- --signal-error: #c62828; /* something went wrong — a field's edge, the words that say what */
269
+ --signal-error-onlight: #af2525; /* something went wrong — a field's edge, the words that say what */
270
+ /* on a dark ground no shade of one hue holds 4.5:1 on every palette's dark fill and stays itself: a signal there is the ondark ink,
271
+ and the icon and the words carry the meaning. A palette may give its own (orchard) */
272
+ --signal-ok-ondark: var(--ink-ondark); --signal-warn-ondark: var(--ink-ondark); --signal-error-ondark: var(--ink-ondark); --signal-info-ondark: var(--ink-ondark);
268
273
 
269
274
  /* derived tints — read the element's own ink, so they work on any slot */
270
275
  --outline: var(--action-border); /* control edges (palette.css softens) */
@@ -293,14 +298,43 @@
293
298
  bars take over the page when the app runs edge to edge (viewport-fit=cover: Android Chrome, an iPhone home-screen app).
294
299
  PUBLISHED: backgrounds still run to the edge; what the member reads or taps stands clear of them. 0 without cover.
295
300
  --_safe-b-open = the bottom inset the keyboard does NOT already cover (a keyboard up hides the gesture bar). */
296
- /* a signal's SOFT ground: the signal washed 14% into the surface (tone 2's formula) — what a Snackbar stands on. Re-derived where a
297
- palette is set, so a palette's own signal shade gives its own soft */
298
- :root, [data-palette] {
299
- --signal-ok-soft: color-mix(in srgb, var(--signal-ok) 14%, var(--surface));
300
- --signal-warn-soft: color-mix(in srgb, var(--signal-warn) 14%, var(--surface));
301
- --signal-error-soft: color-mix(in srgb, var(--signal-error) 14%, var(--surface));
302
- --signal-info-soft: color-mix(in srgb, var(--signal-info) 14%, var(--surface));
303
- }
301
+ /* THE ROLES OF THE SCREEN AND OF THE PAPER CONTROLS, from their sides. Re-derived wherever a palette or a mode is set, since one may
302
+ sit on <body>: a custom property is resolved where it is declared. A signal's SOFT ground (what a Snackbar stands on) is its
303
+ onlight shade washed 14% into the surface (tone 2's formula) */
304
+ :root, [data-palette], [data-mode] {
305
+ --on-surface: color-mix(in srgb, var(--ink-ondark) calc(var(--surface-dark) * 100%), var(--ink-onlight));
306
+ --on-surface-muted: color-mix(in srgb, var(--ink-ondark-muted) calc(var(--surface-dark) * 100%), var(--ink-onlight-muted));
307
+ --action-ink: color-mix(in srgb, var(--ink-ondark) calc(var(--action-dark) * 100%), var(--ink-onlight));
308
+ --action-muted: color-mix(in srgb, var(--ink-ondark-muted) calc(var(--action-dark) * 100%), var(--ink-onlight-muted));
309
+ /* the brand as a glyph on the paper (a typed PIN digit, a map's place mark): the brand on a light paper, ondark on a dark one. It stays
310
+ the paper's on a brand card, where --accent turns to the card's ink */
311
+ --action-accent: color-mix(in srgb, var(--ink-ondark) calc(var(--action-dark) * 100%), var(--accent));
312
+ --signal-ok-soft: color-mix(in srgb, var(--signal-ok-onlight) 14%, var(--surface));
313
+ --signal-warn-soft: color-mix(in srgb, var(--signal-warn-onlight) 14%, var(--surface));
314
+ --signal-error-soft: color-mix(in srgb, var(--signal-error-onlight) 14%, var(--surface));
315
+ --signal-info-soft: color-mix(in srgb, var(--signal-info-onlight) 14%, var(--surface));
316
+ }
317
+ /* INK FOLLOWS ITS GROUND (Lh 2026-10-18: "When the ground is light, use onlight; when the ground is dark, use ondark"). A surface
318
+ says only which side it is, `--dark: 0` (light) or `1` (dark), from its fill's side token above; it does so in its own file
319
+ (card.css, media.css…) and is named in this list, which gives it the inks every piece on it reads: --card-ink (words, icons,
320
+ marks, dots: currentColor), --card-muted (secondary words), the four signals, --on-accent (the ink on the accent fill, whose
321
+ side is --accent-dark) and --accent-ink (the brand as a glyph). A piece with no fill of its own reads only these, so a dark
322
+ surface turns everything on it to ondark with no rule per piece. A piece with no fill that stands in for a surface (a stat
323
+ tile) takes its ground's side: `--dark: inherit`. inkGrounds() measures it; README "Ink follows its ground" */
324
+ :where(body, [data-palette], [data-mode], .card, .tile, .modal__drawer, .postcard__front, .postcard__back, .sticky, .post-paper,
325
+ .float-bar__pill, .snackbar__body, .delta, .track, .tile-badge, .avatar, .bubble__body, .map__hint, .sheet--paper > .sheet__body,
326
+ .postcard__band, .track__step, .toggle, .chip) {
327
+ --card-ink: color-mix(in srgb, var(--ink-ondark) calc(var(--dark) * 100%), var(--ink-onlight));
328
+ --card-muted: color-mix(in srgb, var(--ink-ondark-muted) calc(var(--dark) * 100%), var(--ink-onlight-muted));
329
+ --on-accent: color-mix(in srgb, var(--ink-ondark) calc(var(--accent-dark) * 100%), var(--ink-onlight));
330
+ --accent-ink: color-mix(in srgb, var(--ink-ondark) calc(var(--dark) * 100%), var(--accent));
331
+ --signal-ok: color-mix(in srgb, var(--signal-ok-ondark) calc(var(--dark) * 100%), var(--signal-ok-onlight));
332
+ --signal-warn: color-mix(in srgb, var(--signal-warn-ondark) calc(var(--dark) * 100%), var(--signal-warn-onlight));
333
+ --signal-error: color-mix(in srgb, var(--signal-error-ondark) calc(var(--dark) * 100%), var(--signal-error-onlight));
334
+ --signal-info: color-mix(in srgb, var(--signal-info-ondark) calc(var(--dark) * 100%), var(--signal-info-onlight));
335
+ }
336
+ :where(body, [data-palette], [data-mode]) { --dark: var(--surface-dark); }
337
+
304
338
  :root { --safe-top: calc(env(safe-area-inset-top, 0px) + var(--vv-top, 0px)); /* + the phone's pan while a keyboard is up (cardds.js) */ --safe-bottom: env(safe-area-inset-bottom, 0px); }
305
339
  * { --_safe-b-open: max(0px, var(--safe-bottom) - var(--kb, 0px)); }
306
340
 
@@ -322,157 +356,139 @@
322
356
  Apply with <html data-palette="clay"> (or <body>).
323
357
  No attribute = wireframe grayscale from tokens.css.
324
358
  Each palette overrides role tokens only — components never change.
359
+ A palette sets its grounds, its two ink sets (onlight, ondark; README "Ink follows its ground") and the side of any ground
360
+ that is not the default's (tokens.css: tones 1, 2, 4 and the paper light; tone 3, tone 5 and the brand dark). Each ink set
361
+ holds 4.5:1 on every ground of its side; a tone with words on its 12% tint (a banner, a step bar, today) holds them there too.
325
362
  ============================================================ */
326
363
 
327
364
  /* ---- CLAY — earth-toned: cream surface, terracotta / mustard / ink cards ---- */
328
365
  [data-palette="clay"] {
329
366
  --surface: #ece5d8;
330
- --on-surface: #191713;
331
- --on-surface-muted: #6f675a;
332
-
333
- --card-1-bg: #e8b647; --card-1-ink: #191713; /* mustard hero */
334
- --card-2-bg: #e05c3a; --card-2-ink: #191713; /* terracotta */
335
- --card-3-bg: #1c1a17; --card-3-ink: #f4efe4; /* ink */
367
+ --ink-onlight: #191713; --ink-onlight-muted: #2a2723; /* the terracotta is a middle ground: no muted shade a step lighter holds 4.5:1 on it (4.6) */
368
+ --ink-ondark: #f4efe4; --ink-ondark-muted: #aeaaa1; /* 4.6:1 on tone 5's grey */
336
369
 
337
- --card-border: transparent;
338
- --card-muted: #45412f; /* 5.5:1 on the mustard card — AA for captions and metas (#55503f was 4.3:1) */
339
- --card-2-muted: var(--card-2-ink); /* terracotta holds 5:1 for its ink alone; nothing lighter passes AA, so tone 2 has no muted */
370
+ --card-1-bg: #e8b647; /* mustard hero */
371
+ --card-2-bg: #eb6644; /* terracotta, a step lighter: the ink 5.5:1, 4.5 on its own tint (#e05c3a was 4.9 and 4.1) */
372
+ --card-3-bg: #1c1a17; /* ink */
340
373
 
341
- --accent: #1c1a17; --on-accent: #f4efe4;
342
- --action-bg: #f7f3e9; --action-ink: #191713; --action-border: transparent;
374
+ --accent: #1c1a17;
375
+ --action-bg: #f7f3e9; --action-border: transparent;
343
376
 
344
377
  --pos: #3e7d4f; --neg: #c23c1d;
345
- --dot-a: #e05c3a; --dot-b: #6f6be0;
378
+ --dot-a: #eb6644; --dot-b: #6f6be0;
379
+ /* the signals, deep enough for words on the terracotta (4.5:1) and so on every lighter ground; nearly the ink, the hue stays */
380
+ --signal-ok-onlight: #1b2c18; --signal-warn-onlight: #2f280d; --signal-error-onlight: #471a17; --signal-info-onlight: #1d2938;
346
381
  }
347
382
 
348
383
  /* ---- ROAST — coffee: warm cream surface, caramel accent ---- */
349
384
  [data-palette="roast"] {
350
385
  --surface: #f6f1e7;
351
- --on-surface: #2b211a;
352
- --on-surface-muted: #8c7f70;
386
+ --ink-onlight: #2b211a; --ink-onlight-muted: #5e554d; /* 4.6:1 on the water note, the darkest light ground */
387
+ --ink-ondark: #fdf9f1; --ink-ondark-muted: #ecdfcb; /* 4.6:1 on the caramel */
353
388
 
354
- --card-1-bg: #ffffff; --card-1-ink: #2b211a;
355
- --card-2-bg: #e9dcc6; --card-2-ink: #2b211a;
356
- --card-3-bg: #b97e3c; --card-3-ink: #fdf9f1;
389
+ --card-1-bg: #ffffff;
390
+ --card-2-bg: #e9dcc6;
391
+ --card-3-bg: #8c5402; /* caramel, on its own hue as far as the light ink holds 4.5:1 on it and on its tint (#b97e3c held it at 3.3) */
357
392
 
358
- --card-border: transparent;
359
- --card-muted: #8c7f70;
360
-
361
- --accent: #b97e3c; --on-accent: #fdf9f1;
362
- --action-bg: #ffffff; --action-ink: #2b211a; --action-border: transparent;
393
+ --accent: #8c5402;
394
+ --action-bg: #ffffff; --action-border: transparent;
363
395
 
364
396
  --pos: #3e7d4f; --neg: #bf4028;
365
- --dot-a: #b97e3c; --dot-b: #2b211a;
397
+ --dot-a: #8c5402; --dot-b: #2b211a;
366
398
  }
367
399
 
368
400
  /* ---- HYDRO — cool shiny blue: airy surface, saturated blue CTA ---- */
369
401
  [data-palette="hydro"] {
370
402
  --surface: #e9f0fb;
371
- --on-surface: #17203a;
372
- --on-surface-muted: #64729a;
373
-
374
- --card-1-bg: #ffffff; --card-1-ink: #17203a;
375
- --card-2-bg: #cddcf7; --card-2-ink: #17203a;
376
- --card-3-bg: #3d63e0; --card-3-ink: #f2f6ff;
403
+ --ink-onlight: #17203a; --ink-onlight-muted: #4e566c; /* 4.6:1 on the water note, the darkest light ground */
404
+ --ink-ondark: #ffffff; --ink-ondark-muted: #e0e6f9; /* 4.6:1 on the blue */
377
405
 
378
- --card-border: transparent;
379
- --card-muted: #64729a;
406
+ --card-1-bg: #ffffff;
407
+ --card-2-bg: #cddcf7;
408
+ --card-3-bg: #365bd7; /* white 5.7:1, 4.5 on its tint (#3d63e0 was 5.2 and 4.1) */
380
409
 
381
- --accent: #3d63e0; --on-accent: #f2f6ff;
382
- --action-bg: #ffffff; --action-ink: #17203a; --action-border: transparent;
410
+ --accent: #365bd7;
411
+ --action-bg: #ffffff; --action-border: transparent;
383
412
 
384
413
  --pos: #2e8f52; --neg: #d13b1a;
385
- --dot-a: #3d63e0; --dot-b: #eec85a;
414
+ --dot-a: #365bd7; --dot-b: #eec85a;
386
415
  }
387
416
 
388
- /* ---- ORCHARD — modern earth: calm, real, touchable (Lh 2026-09-25). Linen base, bone paper, moss brand; every
389
- tone's ink + muted is a deep shade of ITS OWN ground, so lines (currentColor mixes: --hairline, --outline, field
390
- edges) come out as a darker tint of the ground under them — never a grey on a warm card. Four highlights for the
391
- atmosphere, off the earth wheel on purpose: maple red · bright yellow · sky · fuchsia. ---- */
417
+ /* ---- ORCHARD — modern earth: calm, real, touchable (Lh 2026-09-25). Linen base, bone paper, moss brand. Four highlights for
418
+ the atmosphere, off the earth wheel on purpose: maple red · bright yellow · sky · fuchsia. ---- */
392
419
  [data-palette="orchard"] {
393
420
  --surface: #f4efe6; /* linen */
394
- --on-surface: #2a241c; /* umber */
395
- --on-surface-muted: #6e6353;
421
+ --ink-onlight: #2a241c; --ink-onlight-muted: #565148; /* umber; muted 4.6:1 on the clay tone */
422
+ --ink-ondark: #f6f1e6; --ink-ondark-muted: #d7d6c6; /* linen; muted 4.6:1 on the moss */
396
423
 
397
- --card-1-bg: #fbf8f2; --card-1-ink: #2a241c; /* bone paper — sheets */
398
- --card-2-bg: #e6d8bf; --card-2-ink: #33291c; /* sand */
399
- --card-3-bg: #5c6b47; --card-3-ink: #f6f1e6; --card-3-muted: #d3d8c4; /* moss — the brand */
400
- --card-4-bg: #d3d9c4; --card-4-ink: #2b3322; --card-4-muted: #515c42; /* sage mist */
401
- --card-5-bg: #dcc2a6; --card-5-ink: #3a2a1c; --card-5-muted: #6b503a; /* clay */
424
+ --card-1-bg: #fbf8f2; /* bone paper — sheets */
425
+ --card-2-bg: #e6d8bf; /* sand */
426
+ --card-3-bg: #52613d; /* moss — the brand: its ink 5.9:1, 4.5 on its tint (#5c6b47 was 5.1 and 4.0) */
427
+ --card-4-bg: #d3d9c4; /* sage mist */
428
+ --card-5-bg: #dcc2a6; --card-5-dark: 0; /* clay: a light tone here */
402
429
 
403
- --card-border: transparent;
404
- --card-muted: #736754;
405
- --card-2-muted: #66573f;
406
-
407
- --accent: #5c6b47; --on-accent: #f6f1e6;
408
- --action-bg: #fbf8f2; --action-ink: #2a241c; --action-border: transparent;
430
+ --accent: #52613d;
431
+ --action-bg: #fbf8f2; --action-border: transparent;
409
432
 
410
433
  /* highlights — decoration and atmosphere, a small dose: counts, marks, chart dots, a badge on a tile */
411
- --highlight-1: #d93a1f; /* maple red */
434
+ --highlight-1: #d03114; /* maple red — the linen ink 4.5:1 on it (#d93a1f was 4.1) */
412
435
  --highlight-2: #f5c518; /* bright yellow */
413
436
  --highlight-3: #3a9fe0; /* sky */
414
- --highlight-4: #e0457b; /* fuchsia */
437
+ --highlight-4: #ca2e69; /* fuchsia — the linen ink 4.5:1 on it (#e0457b was 3.5) */
415
438
  --dot-a: var(--highlight-1); --dot-b: var(--highlight-3);
416
439
 
417
440
  /* signals from Sweet Salmon #dd9787 — words and edges, always with an icon */
418
- --pos: #6f8f5a; --neg: #c4694f;
419
- --signal-error: #b5503b; /* salmon, deepened */
420
- --signal-warn: #a0721f; /* ochre */
421
- --signal-ok: #4f7f4a; /* leaf */
422
- --signal-info: #4d6f96; /* slate */
423
- }
424
- /* on the dark moss card the signals turn to their light shades — the same hues, readable on the ground */
425
- /* 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) */
426
- [data-palette="orchard"] .badge:not(.badge--soft) { background: var(--badge-bg, var(--highlight-1)); color: var(--badge-ink, #fff8f0); }
427
- [data-palette="orchard"] .tile-badge:not(.tile-badge--plain) { background: var(--tile-badge-bg, var(--highlight-2)); color: var(--tile-badge-ink, #2a241c); }
428
- [data-palette="orchard"] .calendar__day--mark { background: var(--calendar-day-mark-bg, color-mix(in srgb, var(--highlight-2) 40%, transparent)); }
429
- /* fields are WHITE boxes on every ground (Lh 2026-09-25) — the words typed in them read dark, even on the moss card */
430
- [data-palette="orchard"] .field { background: var(--field-bg, #ffffff); color: #2a241c; }
431
- [data-palette="orchard"] .pin__cell { background: var(--pin-cell-bg, #ffffff); color: #2a241c; }
441
+ --pos: #6f8f5a; --neg: #ad553b; /* a falling delta's linen words 4.5:1 (#c4694f was 3.4) */
442
+ --signal-error-onlight: #82402f; --signal-warn-onlight: #6b4e1b; --signal-ok-onlight: #3e5a37; --signal-info-onlight: #40556c; /* 4.5:1 on the clay tone */
443
+ --signal-error-ondark: #f9ccc0; --signal-warn-ondark: #f2d28b; --signal-ok-ondark: #cfe6b8; --signal-info-ondark: #c5d7ef; /* the same hues, 4.5:1 on the moss */
444
+ }
445
+ /* 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
446
+ knob). Each fill says its side; the words on it follow */
447
+ [data-palette="orchard"] .badge:not(.badge--soft) { background: var(--badge-bg, var(--highlight-1)); color: var(--badge-ink, var(--ink-ondark)); }
448
+ [data-palette="orchard"] .tile-badge:not(.tile-badge--plain) { background: var(--tile-badge-bg, var(--highlight-2)); --dark: 0; }
449
+ [data-palette="orchard"] .calendar__day--mark { background: var(--calendar-day-mark-bg, color-mix(in srgb, var(--highlight-2) 15%, transparent)); }
450
+ /* fields are WHITE boxes on every ground (Lh 2026-09-25): a light ground, so the words typed in them are onlight, even on the moss card */
451
+ [data-palette="orchard"] .field { background: var(--field-bg, #ffffff); color: var(--ink-onlight); }
452
+ [data-palette="orchard"] .pin__cell { background: var(--pin-cell-bg, #ffffff); color: var(--ink-onlight); }
432
453
  /* 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 */
433
- [data-palette="orchard"] .btn--primary { background: var(--btn-primary-bg, var(--highlight-2)); color: var(--btn-primary-ink, #2a241c); }
434
- [data-palette="orchard"] .avatar:not(.avatar--outline, .avatar--add, .avatar--on) { background: var(--avatar-bg, var(--highlight-4)); color: var(--avatar-ink, #ffffff); }
454
+ [data-palette="orchard"] .btn--primary { background: var(--btn-primary-bg, var(--highlight-2)); color: var(--btn-primary-ink, var(--ink-onlight)); }
455
+ [data-palette="orchard"] .avatar:not(.avatar--outline, .avatar--add, .avatar--on) { background: var(--avatar-bg, var(--highlight-4)); --dark: 1; }
435
456
  [data-palette="orchard"] .ring::before { background: conic-gradient(var(--highlight-3) calc(var(--value) * 1%), var(--ink-tint) 0); }
436
- [data-palette="orchard"] .toggle:checked { background: var(--highlight-3); }
437
- [data-palette="orchard"] .toggle:checked::after { background: #ffffff; }
438
- [data-palette="orchard"] .card--3 { --signal-error: #f4b7a6; --signal-warn: #f1d18a; --signal-ok: #cfe6b8; --signal-info: #bcd0ec; }
457
+ [data-palette="orchard"] .toggle:checked { background: var(--highlight-3); --accent-dark: 0; } /* sky is a light ground: its knob is onlight */
439
458
 
440
459
  /* ---- VERS — Lh's own-palette pick (2026-09-25): white surface, champagne paper, sage brand; earth tones for tones 2·4·5 ---- */
441
460
  [data-palette="vers"] {
442
461
  --surface: #ffffff;
443
- --on-surface: #1f1b16;
444
- --on-surface-muted: #6b6358;
445
-
446
- --card-1-bg: #f6e7cb; --card-1-ink: #1f1b16; /* paper — Champagne */
447
- --card-2-bg: #e3cfa6; --card-2-ink: #1f1b16; /* sand */
448
- --card-3-bg: #678d58; --card-3-ink: #ffffff; /* brand — Sage */
449
- --card-4-bg: #c9d6b0; --card-4-ink: #1f1b16; --card-4-muted: #4a5438; /* lichen */
450
- --card-5-bg: #d4bf9c; --card-5-ink: #1f1b16; --card-5-muted: #5e4a36; /* wheat — bark was too dark */
451
-
452
- --card-border: transparent;
453
- --card-muted: #6b5f4c;
454
- --card-2-muted: #5c4f3a;
455
- --card-3-muted: #dfe8d6;
456
-
457
- --accent: #678d58; --on-accent: #ffffff;
458
- --action-bg: #f6e7cb; --action-ink: #1f1b16; --action-border: transparent;
459
-
460
- /* signals from Sweet Salmon #dd9787, deepened to read on white/champagne */
461
- --pos: #7fa86f; --neg: #dd9787;
462
- --signal-error: #b5503b; /* salmon */
463
- --signal-warn: #a5782a; /* ochre */
464
- --signal-ok: #4f7f4a; /* leaf */
465
- --signal-info: #4d6f96; /* slate */
462
+ --ink-onlight: #1f1b16; --ink-onlight-muted: #534f4c; /* 4.5:1 on the wheat */
463
+ --ink-ondark: #ffffff; --ink-ondark-muted: #dee5dc; /* 4.6:1 on the sage */
464
+
465
+ --card-1-bg: #f6e7cb; /* paper — Champagne */
466
+ --card-2-bg: #e3cfa6; /* sand */
467
+ --card-3-bg: #496d3a; /* brand — Sage, on its own hue as far as white holds 4.5:1 on it and on its tint (#678d58 held it at 3.8) */
468
+ --card-4-bg: #c9d6b0; /* lichen */
469
+ --card-5-bg: #d4bf9c; --card-5-dark: 0; /* wheat — a light tone here */
470
+
471
+ --accent: #496d3a;
472
+ --action-bg: #f6e7cb; --action-border: transparent;
473
+
474
+ /* signals from Sweet Salmon #dd9787, deepened to read on every light ground (4.5:1 on the wheat) */
475
+ --pos: #7fa86f; --neg: #a76557; /* the salmon deepened as far as a falling delta's white words hold 4.5:1 (#dd9787 was 2.4) */
476
+ --signal-error-onlight: #803c2d; --signal-warn-onlight: #654b1e; --signal-ok-onlight: #3b5734; --signal-info-onlight: #3c5169;
466
477
  --dot-a: #dd9787; --dot-b: #b08a5a;
467
478
  }
468
479
 
469
- /* Colored palettes trade wireframe outlines for soft elevation. */
480
+ /* Colored palettes add a soft elevation under the card's edge. */
470
481
  [data-palette] :is(.card, .band-stack) { box-shadow: var(--shadow-card); }
471
482
  /* COLOURED MODE is the attribute, whatever its value: a project with colours of its own (the five
472
483
  --color-* bases, or roles, in its theme.css) writes <html data-palette="own"> — no block here matches
473
484
  "own", so only the three generic rules apply: the card's shadow, the soft outline, the field's soft
474
- edge (forms.css); and the two wireframe borders step back, as in every stock palette. */
475
- [data-palette] { --card-border: transparent; --action-border: transparent; }
485
+ edge (forms.css); and the controls' wireframe border steps back, as in every stock palette.
486
+ THE CARD KEEPS AN EDGE (Lh 2026-10-18, timebank's help sheet: white cards on the white sheet, "no different from
487
+ the background at all"). A card is one boundary, and WCAG 2.2 SC 1.4.11 asks 3:1 for it against what it stands
488
+ on. Light cards on a light ground cannot reach 3:1 by fill (a tone-1 card in a tone-1 sheet is 1:1), so the
489
+ edge carries it: the muted ink of the ground, which every palette already holds to AA on its surface.
490
+ `cardGrounds()` measures each tone on each ground (tests/card-grounds.spec.js). */
491
+ [data-palette] { --card-border: var(--on-surface-muted); --action-border: transparent; }
476
492
  /* …but controls keep a faint edge so they still read as controls */
477
493
  [data-palette] { --outline: color-mix(in srgb, currentColor 25%, transparent); }
478
494
 
@@ -506,7 +522,7 @@ html { font-size: 100%; }
506
522
  body {
507
523
  margin: 0;
508
524
  background: var(--surface);
509
- color: var(--on-surface);
525
+ color: var(--card-ink);
510
526
  font: var(--type-body);
511
527
  -webkit-font-smoothing: antialiased;
512
528
  }
@@ -582,7 +598,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
582
598
  .screen > * { min-width: 0; } /* content never widens the column past the viewport */
583
599
  /* a screen that carries its own palette (data-palette on the element, not on <html>)
584
600
  paints its own surface, so a design tool can show one screen per palette */
585
- .screen[data-palette] { background: var(--surface); color: var(--on-surface); }
601
+ .screen[data-palette] { background: var(--surface); color: var(--card-ink); }
586
602
 
587
603
  /* app screen: exactly one phone height — a height, not a minimum, so that a
588
604
  .step (step.css) has a bound to fill and its card and sheet scroll INSIDE
@@ -619,9 +635,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
619
635
  .t-stat--mono { font-family: var(--font-mono); letter-spacing: 0; } /* clocks, ETAs, codes */
620
636
  .t-stat--xl { font-size: var(--fs-hero); } /* the one number on a sheet */
621
637
 
622
- .t-muted { color: var(--on-surface-muted); }
623
- .card .t-muted { color: var(--card-muted); } /* muted follows the card's ink, not the surface's */
624
- /* .card--3 .t-muted (dim instead of --card-muted on the dark slot) is declared once in card.css, with the rest of that family */
638
+ .t-muted { color: var(--card-muted); } /* the muted words of whatever it stands on: the screen, a card of any tone, a drawer */
625
639
  .t-caps { text-transform: uppercase; }
626
640
  /* ---- mark: a highlighter stroke over a word INSIDE running text (a sender's name in a greeting) ----
627
641
  <p class="t-body"><mark class="mark">ธนาคารเวลา</mark> ส่งแสตมป์ดวงแรกให้คุณ</p>
@@ -690,7 +704,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
690
704
  .empty-state__action { margin-top: var(--sp-2); }
691
705
  /* inside a card (a card's own "nothing yet"): the card's ink */
692
706
  .card .empty-state { color: var(--card-ink); padding-block: var(--sp-6); }
693
- .card--3 .empty-state { color: inherit; }
694
707
 
695
708
  /* ---- css/card.css — card boundary + list variants ---- */
696
709
  /* ============================================================
@@ -701,7 +714,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
701
714
 
702
715
  .card {
703
716
  --card-bg: var(--card-1-bg); /* the slot, readable by children (avatar rings, fold) */
704
- --card-ink: var(--card-1-ink);
717
+ --dark: var(--card-1-dark); /* its side: the inks follow (tokens.css) */
705
718
  --card-pad: var(--card-pad-rest); /* published so .bleed can cancel it exactly */
706
719
  --card-gap: var(--card-gap-rest); /* published so a sheet's peek strip (sheet.css) is exact */
707
720
  background: var(--card-bg);
@@ -729,19 +742,13 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
729
742
  .card--away { translate: 0 var(--_y); }
730
743
  @media (prefers-reduced-motion: reduce) { :is(.card--leaver, .post-desk__card) { transition: none; } }
731
744
 
732
- .card--2 { --card-bg: var(--card-2-bg); --card-ink: var(--card-2-ink); --card-muted: var(--card-2-muted); }
733
- /* dark/accent slot: accent-toned controls would vanish — the slot re-points
734
- the accent tokens, so every accent consumer (btn--primary, chip--on,
735
- check, toggle, ring, day…) inverts without per-component rules */
736
- .card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
737
- .card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
738
- .card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
739
- /* the dark slot's captions/hints/footnotes: --card-muted is tuned for a light card, so these dim the full ink
740
- instead (one rule, matching specificity to what it replaces — .card--3 <space> <one class>) — merged from
741
- base.css/choice.css/media.css/numbers.css/journey.css/people.css (2026-09-27 review). lists.css's .legend
742
- differs (opacity 0.8, not --dim) and stays there. */
743
- .card--3 :is(.t-muted, .calendar__wd, .composer__hint, .quote__by, .ring__label, .band__foot, .picker__unit,
744
- .route__place, .note-row, .avatar-pick__role) { color: inherit; opacity: var(--dim); }
745
+ /* each tone is a fill and a side; its words and glyphs follow from the side (tokens.css) */
746
+ .card--2 { --card-bg: var(--card-2-bg); --dark: var(--card-2-dark); }
747
+ /* the brand slot: accent-toned controls would vanish on it — the slot re-points the accent to its own ink, and that fill's side is
748
+ the other one, so every accent consumer (btn--primary, chip--on, check, toggle, ring, day…) inverts without per-component rules */
749
+ .card--3 { --card-bg: var(--card-3-bg); --dark: var(--card-3-dark); --accent: var(--card-ink); --accent-dark: calc(1 - var(--dark)); }
750
+ .card--4 { --card-bg: var(--card-4-bg); --dark: var(--card-4-dark); }
751
+ .card--5 { --card-bg: var(--card-5-bg); --dark: var(--card-5-dark); }
745
752
 
746
753
  /* header row: title left, floating action right — centered, since the
747
754
  trailing control (chip/badge/icon-btn/link) is almost always taller than
@@ -1012,7 +1019,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
1012
1019
  display: grid;
1013
1020
  place-items: center;
1014
1021
  font: var(--card-thumb-font, var(--type-label));
1015
- opacity: 0.85;
1016
1022
  }
1017
1023
 
1018
1024
  /* ---- card feed: a queue of cards, all in sight — one big, the rest small (Lh 2026-10-09, the whole queue 2026-10-15) ----
@@ -1059,9 +1065,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
1059
1065
  padding: var(--delta-pad-y, var(--sp-1)) var(--delta-pad-x, var(--sp-2));
1060
1066
  border-radius: var(--delta-radius, var(--r-chip));
1061
1067
  background: var(--delta-bg, var(--card-3-bg));
1062
- color: var(--delta-ink, var(--card-3-ink));
1068
+ --dark: var(--card-3-dark);
1069
+ color: var(--delta-ink, var(--card-ink));
1063
1070
  }
1064
- .delta--neg { background: var(--delta-neg-bg, var(--neg)); color: var(--delta-neg-ink, var(--card-3-ink)); } /* light ink on the signal color in every slot */
1071
+ .delta--neg { background: var(--delta-neg-bg, var(--neg)); --dark: var(--neg-dark); color: var(--delta-neg-ink, var(--card-ink)); }
1065
1072
 
1066
1073
  /* ---- panes: a few panes side by side inside a card or a sheet, one in sight (Lh 2026-10-02) ----
1067
1074
  <div class="panes" role="group"><div class="panes__rail"><div class="panes__pane">…</div>…</div><span class="pager__at">…</span></div>
@@ -1820,12 +1827,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
1820
1827
  the default spacing), the body's own top padding, and only when the sheet is paper.
1821
1828
  A plain sheet keeps padding-top 0. A sheet in a stack wears the same class and the same
1822
1829
  inset: the stack's own gap under the head is the card's colour, so the row still needs this white.
1823
- The ink falls back to the wireframe word colour: an unset --color-ink would otherwise
1824
- inherit a dark card's light ink, and the words would vanish on the white. */
1830
+ The paper is a surface of the plain card's side: on a dark sheet its words still turn to onlight. */
1825
1831
  .sheet--paper > .sheet__body {
1826
1832
  --card-bg: var(--color-paper, #fff);
1827
- --card-ink: var(--color-ink, #141414);
1828
- --card-muted: var(--_muted-on-paper, #555555);
1833
+ --dark: var(--card-1-dark);
1829
1834
  background: var(--card-bg);
1830
1835
  color: var(--card-ink);
1831
1836
  padding-top: var(--sp-6);
@@ -2006,7 +2011,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2006
2011
  bottom: var(--sp-2);
2007
2012
  padding: var(--sp-0) var(--sp-2);
2008
2013
  border-radius: var(--map-credit-radius, var(--r-chip));
2009
- background: var(--map-credit-bg, color-mix(in srgb, var(--surface) 80%, transparent));
2014
+ background: var(--map-credit-bg, var(--surface)); /* solid: at 80% over a map the muted words fell under 4.5:1 (clay, 3.9) */
2010
2015
  color: var(--map-credit-ink, var(--on-surface-muted));
2011
2016
  font: var(--map-credit-font, var(--type-caption));
2012
2017
  pointer-events: none;
@@ -2022,7 +2027,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2022
2027
  padding: var(--sp-2) var(--sp-4);
2023
2028
  border-radius: var(--map-hint-radius, var(--r-chip));
2024
2029
  background: var(--map-hint-bg, var(--card-3-bg));
2025
- color: var(--map-hint-ink, var(--card-3-ink));
2030
+ --dark: var(--card-3-dark);
2031
+ color: var(--map-hint-ink, var(--card-ink));
2026
2032
  font: var(--map-hint-font, var(--type-label));
2027
2033
  text-align: center;
2028
2034
  box-shadow: var(--map-hint-shadow, var(--shadow-float));
@@ -2071,9 +2077,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2071
2077
  width: var(--map-pick-size, var(--sp-6));
2072
2078
  height: var(--map-pick-size, var(--sp-6));
2073
2079
  border-radius: 50%;
2074
- border: var(--border-w) solid var(--map-pick-border, var(--accent));
2080
+ border: var(--border-w) solid var(--map-pick-border, var(--action-accent));
2075
2081
  background: var(--map-pick-bg, var(--surface));
2076
- color: var(--map-pick-mark-ink, var(--accent));
2082
+ color: var(--map-pick-mark-ink, var(--action-accent));
2077
2083
  box-shadow: var(--map-pick-shadow, var(--shadow-float));
2078
2084
  pointer-events: none;
2079
2085
  }
@@ -2110,7 +2116,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2110
2116
  display: grid;
2111
2117
  place-items: center;
2112
2118
  font: var(--sheet-stage-foot-font, var(--type-caption));
2113
- color: var(--sheet-stage-foot-ink, var(--card-3-ink));
2119
+ /* words on the scrim: white on a strip of the scrim over it. The scrim alone is half black, so over a white page it is #808080, where white
2120
+ is 3.9:1; doubled, #404040, 10:1. It read --card-3-ink, a brand's light ink, on a black scrim */
2121
+ background: var(--sheet-stage-foot-bg, var(--scrim));
2122
+ color: var(--sheet-stage-foot-ink, #ffffff);
2114
2123
  z-index: 2;
2115
2124
  }
2116
2125
 
@@ -2153,6 +2162,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2153
2162
  display: flex; align-items: center; gap: var(--sp-3); min-height: var(--_row-h); min-width: 0; /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
2154
2163
  padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
2155
2164
  background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
2165
+ --card-muted: var(--card-ink); /* a tint of the ink: its secondary words are the full ink (no lighter shade holds 4.5:1 there) */
2156
2166
  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 */
2157
2167
  }
2158
2168
  .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; }
@@ -2163,7 +2173,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2163
2173
  border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); line-height: 1; font-variant-numeric: tabular-nums;
2164
2174
  background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
2165
2175
  }
2166
- .sheet-step__summary { color: inherit; opacity: 0.8; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2176
+ .sheet-step__summary { color: var(--sheet-step-summary-ink, var(--card-muted)); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2167
2177
  .sheet-step__check { margin-inline-start: auto; }
2168
2178
  .sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
2169
2179
  .sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); padding-inline: var(--sheet-step-inner-pad-x, var(--sheet-step-bar-pad-x, var(--sp-3))); } /* the open step's content in the frame of its title's words (the bar's own inset), so it reads as that step's (Lh 2026-09-28, #153) */
@@ -2488,7 +2498,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2488
2498
  .route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
2489
2499
  .route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
2490
2500
 
2491
- .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)); }
2501
+ .route__via { grid-column: 2; grid-row: 2; align-self: center; display: flex; align-items: center; color: var(--route-via-ink, var(--card-muted)); font-size: var(--route-via-font-size, var(--fs-body)); }
2492
2502
 
2493
2503
  /* ---- tile badge: tiny label over a big value (gate, seat, zone) ---- */
2494
2504
  .tile-badge {
@@ -2498,13 +2508,16 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2498
2508
  padding: var(--tile-badge-pad-y, var(--sp-1)) var(--tile-badge-pad-x, var(--sp-2));
2499
2509
  border-radius: var(--tile-badge-radius, var(--r-badge));
2500
2510
  background: var(--tile-badge-bg, var(--card-3-bg));
2501
- color: var(--tile-badge-ink, var(--card-3-ink));
2511
+ --dark: var(--card-3-dark);
2512
+ color: var(--tile-badge-ink, var(--card-ink));
2502
2513
  }
2503
- .tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: var(--dim); }
2514
+ /* the label is the badge's own ink, smaller: a badge is a small fill of any colour (a palette paints it yellow), and only its own ink is
2515
+ sure to hold 4.5:1 on it; --dim faded it to 2.4:1 */
2516
+ .tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); color: var(--tile-badge-label-ink, inherit); }
2504
2517
  .tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
2505
2518
 
2506
2519
  /* plain: no fill — a date box beside a title (Jun / 18) */
2507
- .tile-badge--plain { background: transparent; color: inherit; padding: 0; min-width: var(--tile-badge-plain-min-w, var(--tap-sm)); }
2520
+ .tile-badge--plain { background: transparent; --dark: inherit; color: inherit; padding: 0; min-width: var(--tile-badge-plain-min-w, var(--tap-sm)); }
2508
2521
  .tile-badge--plain .tile-badge__value { font: 800 var(--fs-h1)/1 var(--font-display); }
2509
2522
 
2510
2523
  /* corner badge: pin a tile badge on media (rating, price) — .card__media is relative */
@@ -2700,7 +2713,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2700
2713
  toggles, tiles and the dropdown already use. */
2701
2714
  border: var(--border-w) solid var(--chip-border, var(--outline));
2702
2715
  background: var(--chip-bg, var(--action-bg));
2703
- color: var(--chip-ink, var(--action-ink));
2716
+ --dark: var(--action-dark); /* a chip is a surface of the paper's side: a signal dot on it reads onlight, on any card */
2717
+ color: var(--chip-ink, var(--card-ink));
2704
2718
  padding: var(--chip-pad-y, var(--sp-2)) var(--chip-pad-x, var(--sp-3));
2705
2719
  font: var(--type-label);
2706
2720
  font-size: var(--chip-font-size, var(--fs-sm));
@@ -2719,10 +2733,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2719
2733
  .dot--info { background: var(--signal-info); }
2720
2734
  /* --sm: a small LABEL (a name on a note, a tag in a tight head) — low and tight; never a tap target */
2721
2735
  .chip--sm { min-height: 0; gap: var(--chip-sm-gap, var(--sp-1)); padding: var(--chip-sm-pad-y, var(--sp-0)) var(--chip-sm-pad-x, var(--sp-2)); }
2722
- .chip--on { background: var(--chip-on-bg, var(--accent)); color: var(--chip-on-ink, var(--on-accent)); border-color: transparent; }
2736
+ .chip--on { background: var(--chip-on-bg, var(--accent)); --dark: var(--accent-dark); color: var(--chip-on-ink, var(--card-ink)); border-color: transparent; }
2723
2737
  /* muted: an untouched default — the words wash out, the edge stays; emphasis is the difference from the neighbours (README 13) */
2724
- .chip--muted { color: var(--chip-muted-ink, var(--card-muted)); }
2725
- /* dark-slot inversion lives in card.css (.card--3 :is(...)) */
2738
+ .chip--muted { color: var(--chip-muted-ink, var(--card-muted)); } /* the chip's own fill's muted words: a muted chip on the dark card is still a light chip */
2726
2739
 
2727
2740
  .chip-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
2728
2741
  .chip-row.bleed { flex-wrap: nowrap; } /* actions.css loads after card.css — the strip stays one line */
@@ -2752,7 +2765,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2752
2765
  text-align: left;
2753
2766
  }
2754
2767
  /* muted: the value is still the default (README 13) */
2755
- .dropdown--muted .dropdown__btn { color: var(--dropdown-muted-ink, var(--card-muted)); }
2768
+ .dropdown--muted .dropdown__btn { color: var(--dropdown-muted-ink, var(--action-muted)); }
2756
2769
  .dropdown__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2757
2770
  .dropdown__btn > .icon { transition: rotate var(--motion-quick) ease; }
2758
2771
  .dropdown:has(.menu:popover-open) .dropdown__btn > .icon { rotate: 180deg; }
@@ -2764,7 +2777,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2764
2777
  /* ---- segment (day picker / tab strip) — items share width, shrink on small screens ---- */
2765
2778
  .segment { display: flex; gap: var(--sp-1); }
2766
2779
  .segment__item {
2767
- opacity: var(--rest);
2780
+ color: var(--segment-rest-ink, var(--card-muted)); /* unpicked: the ground's muted words */
2768
2781
  border-radius: var(--r-chip);
2769
2782
  padding: var(--sp-2) var(--sp-1);
2770
2783
  font: var(--type-label);
@@ -2776,7 +2789,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2776
2789
  .segment__item[aria-selected="true"] {
2777
2790
  background: var(--accent);
2778
2791
  color: var(--on-accent);
2779
- opacity: 1;
2780
2792
  }
2781
2793
 
2782
2794
  /* ---- bottom CTA pack: the screen's bottom (Screen `bottom`) ----
@@ -2847,10 +2859,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2847
2859
  width: var(--dot);
2848
2860
  height: var(--dot);
2849
2861
  border-radius: 50%;
2850
- background: currentColor;
2851
- opacity: var(--faint);
2862
+ background: var(--card-muted); /* a glyph: the dots at rest are the ground's muted ink (an opacity of 0.3 was 1.5:1), the one where you are its full ink */
2852
2863
  }
2853
- .pager__at > .is-at { background: var(--accent); opacity: 1; }
2864
+ .pager__at > .is-at { background: var(--card-ink); }
2854
2865
  /* ---- tiers: a control row above the buttons ----
2855
2866
  A screen whose one move comes with a control — the introduction's text-size
2856
2867
  slider above its pager. Each tier is a row; the bar stacks them, and any
@@ -2900,11 +2911,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
2900
2911
  gap: var(--action-note-gap, var(--sp-1));
2901
2912
  padding-inline: var(--sp-2);
2902
2913
  font: var(--action-note-font, var(--type-body));
2903
- color: var(--action-note-ink, var(--on-surface-muted));
2914
+ color: var(--action-note-ink, var(--card-muted));
2904
2915
  text-align: center;
2905
2916
  cursor: default;
2906
2917
  }
2907
- .card .action-note { color: var(--action-note-ink, var(--card-muted)); }
2908
2918
  .action-note__words { display: block; } /* ONE run of words: an icon inside sits in the line, the spaces around it kept */
2909
2919
  .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) */
2910
2920
 
@@ -3017,25 +3027,21 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3017
3027
  .track {
3018
3028
  --_step: calc(var(--tap-base) * 2 / 3); /* 32 the round of one step */
3019
3029
  --_pad: calc((var(--header-h) - var(--_step)) / 2); /* 14 round + its room = the top bar's height; the end rounds sit concentric in the pill */
3020
- --_bg: var(--card-3-bg);
3021
- --_ink: var(--card-3-ink);
3022
- /* the track lays down its own dark ground, so like the dark card slot it
3023
- re-points the accent to its own ink (README rule 10). Without this, any
3024
- palette where --accent equals --card-3-bg — field, hydro, and the
3025
- wireframe default — paints done steps invisibly on the track. */
3026
- --accent: var(--_ink);
3027
- --on-accent: var(--_bg);
3030
+ /* the track lays down the accent fill of where it stands (the brand on a light card, the card's ink on the brand card), so it is a
3031
+ surface of that fill's side; its steps re-point the accent to its ink, as the brand card slot does (README rule 10) */
3032
+ --dark: var(--accent-dark);
3028
3033
  list-style: none;
3029
3034
  margin: 0;
3030
3035
  padding: var(--track-pad, var(--_pad));
3031
3036
  display: flex;
3032
3037
  align-items: center;
3033
3038
  border-radius: var(--track-radius, var(--r-chip));
3034
- background: var(--_bg);
3035
- color: var(--_ink);
3039
+ background: var(--accent);
3040
+ color: var(--card-ink);
3036
3041
  }
3037
- .card--3 .track { --_bg: var(--card-3-ink); --_ink: var(--card-3-bg); }
3038
3042
  .track__step {
3043
+ --accent: var(--card-ink);
3044
+ --accent-dark: calc(1 - var(--dark));
3039
3045
  flex: 1;
3040
3046
  display: flex;
3041
3047
  align-items: center;
@@ -3047,7 +3053,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3047
3053
  content: "";
3048
3054
  flex: 1;
3049
3055
  height: var(--sp-2);
3050
- background: var(--ink-rail); /* the track's own ink at 30% — identical to color-mix(in srgb, var(--_ink) 30%, transparent): --ink-rail already reads currentColor, and .track sets color to --_ink */
3056
+ background: var(--ink-rail); /* the track's own ink at 30%: --ink-rail reads currentColor */
3051
3057
  }
3052
3058
  .track__step:first-child::before { display: none; }
3053
3059
  .track__step.is-done::before { background: var(--accent); }
@@ -3059,8 +3065,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3059
3065
  padding: calc(var(--_step) * 7 / 32); /* the glyph = 18 in the 32 round */
3060
3066
  border-radius: 50%;
3061
3067
  background: var(--ink-rail);
3062
- color: var(--_ink);
3068
+ color: var(--card-ink);
3063
3069
  }
3070
+ .track__step > .track__dot { background: var(--ink-tint); } /* a number is words: on the 30% rail it fell under 4.5:1 on a mid-dark track; the 12% tint is what every tone holds its ink on */
3064
3071
  .track__step > .track__dot { /* a number in the round (TrackStep num) */
3065
3072
  display: grid; place-items: center; box-sizing: border-box;
3066
3073
  font: var(--track-dot-font, var(--type-caption)); line-height: 1; font-variant-numeric: tabular-nums;
@@ -3157,10 +3164,11 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3157
3164
  font: var(--picker-item-font, var(--type-stat));
3158
3165
  letter-spacing: var(--track-display);
3159
3166
  font-variant-numeric: tabular-nums;
3160
- opacity: var(--faint);
3167
+ --_rest: var(--picker-rest-ink, var(--card-muted)); /* the values either side: the ground's muted words (they faded to 0.3, 1.8:1) */
3168
+ color: var(--_rest);
3161
3169
  text-align: center;
3162
3170
  }
3163
- .picker__item[aria-selected="true"] { opacity: 1; }
3171
+ .picker__item[aria-selected="true"] { color: var(--card-ink); }
3164
3172
  /* centre rules either side of the selected value */
3165
3173
  .picker::before,
3166
3174
  .picker::after {
@@ -3176,10 +3184,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3176
3184
  .picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
3177
3185
  /* the scrolled-to item lights up by position — no JS */
3178
3186
  @supports (animation-timeline: view(x)) {
3179
- .picker__item { animation: picker-focus linear both; animation-timeline: view(x); } /* opacity: var(--faint) already set above — the animation alone is new here */
3187
+ .picker__item { animation: picker-focus linear both; animation-timeline: view(x); }
3180
3188
  @keyframes picker-focus {
3181
- 0%, 100% { opacity: var(--faint); }
3182
- 45%, 55% { opacity: 1; }
3189
+ 0%, 100% { color: var(--_rest); }
3190
+ 45%, 55% { color: var(--card-ink); }
3183
3191
  }
3184
3192
  }
3185
3193
 
@@ -3266,7 +3274,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3266
3274
  display: grid;
3267
3275
  place-items: center;
3268
3276
  background: var(--avatar-bg, var(--card-2-bg));
3269
- color: var(--avatar-ink, var(--card-2-ink));
3277
+ --dark: var(--card-2-dark);
3278
+ color: var(--avatar-ink, var(--card-ink));
3270
3279
  font: var(--avatar-font, var(--type-label));
3271
3280
  font-size: calc(var(--_size) * 0.3);
3272
3281
  text-transform: uppercase;
@@ -3280,10 +3289,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3280
3289
  .avatar--lg { --_size: var(--avatar-lg); }
3281
3290
  .avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
3282
3291
  /* outline avatar: no fill, an edge — icons in a ring, "add" slots */
3283
- .avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
3284
- .avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); }
3292
+ .avatar--outline { background: transparent; --dark: inherit; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
3293
+ .avatar--add { background: transparent; --dark: inherit; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); }
3285
3294
  /* accent avatar: the "+7" tail, the selected person */
3286
- .avatar--on { background: var(--avatar-on-bg, var(--accent)); color: var(--avatar-on-ink, var(--on-accent)); }
3295
+ .avatar--on { background: var(--avatar-on-bg, var(--accent)); --dark: var(--accent-dark); color: var(--avatar-on-ink, var(--card-ink)); }
3287
3296
  /* halo: faint concentric rings behind a hero avatar (decorative) */
3288
3297
  .avatar--halo::before {
3289
3298
  content: "";
@@ -3381,6 +3390,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3381
3390
  border-radius: var(--toggle-radius, var(--r-chip));
3382
3391
  border: var(--border-w) solid var(--toggle-border, var(--outline));
3383
3392
  background: var(--toggle-bg, var(--action-bg));
3393
+ --dark: var(--action-dark); /* the paper's side: its knob at rest is the paper's muted ink, on the accent the accent's */
3394
+ color: var(--card-ink);
3384
3395
  position: relative;
3385
3396
  transition: background-color var(--motion-press) ease-out, border-color var(--motion-press) ease-out;
3386
3397
  }
@@ -3392,12 +3403,11 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3392
3403
  width: calc(var(--_h) - 2 * var(--_pad) - 2 * var(--border-w));
3393
3404
  height: calc(var(--_h) - 2 * var(--_pad) - 2 * var(--border-w));
3394
3405
  border-radius: 50%;
3395
- background: currentColor;
3396
- opacity: 0.45;
3397
- transition: translate var(--motion-quick) ease, opacity var(--motion-quick) ease;
3406
+ background: var(--card-muted); /* a glyph: a shade of its side, never an opacity (0.45 of the ink was 2.4:1 on the paper) */
3407
+ transition: translate var(--motion-quick) ease;
3398
3408
  }
3399
3409
  .toggle:checked { background: var(--accent); border-color: transparent; }
3400
- .toggle:checked::after { translate: calc(var(--_w) - var(--_h)) 0; background: var(--on-accent); opacity: 1; }
3410
+ .toggle:checked::after { translate: calc(var(--_w) - var(--_h)) 0; background: var(--on-accent); }
3401
3411
 
3402
3412
  /* ---- chip pick: a chip that is a choice — a native radio/checkbox inside the label, the picked one filled with accent.
3403
3413
  Full-width in a .chip-grid (one slot / many options), or hugging its word on a .chip-row with --sign (an interest
@@ -3419,7 +3429,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3419
3429
  a chip, a mood dot, a tile (media.css), a sticky's tone swatch (sticky.css), a place on a map (sheet.css), a card that is a label round a radio
3420
3430
  (a pickable row card: the app gives the picked one its `tone`) — the native input lies over its whole box, unseen */
3421
3431
  :is(.chip--pick, .mood__opt, .tile--pick, .sticky-pick__swatch, .map-pick) > input, label.card > input[type="radio"] { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: inherit; }
3422
- :is(.chip--pick, .tile--pick):has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
3432
+ :is(.chip--pick, .tile--pick):has(:checked) { background: var(--accent); --dark: var(--accent-dark); color: var(--card-ink); border-color: transparent; }
3423
3433
  /* --sign: the trailing + that turns 45° into an × when picked — one mask, one rotate, no swap (Lh 2026-09-16) */
3424
3434
  .chip--sign { padding-right: var(--sp-2); }
3425
3435
  .chip--sign::after {
@@ -3453,13 +3463,13 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3453
3463
  is the same shape on every day */
3454
3464
  padding-inline: 0;
3455
3465
  border-radius: var(--day-radius, var(--r-chip));
3456
- opacity: var(--rest);
3466
+ color: var(--day-rest-ink, var(--card-muted)); /* an unpicked day at rest: the muted words of the ground it stands on (an opacity faded it under AA) */
3457
3467
  }
3458
3468
  .day__name { font: var(--day-name-font, var(--type-caption)); }
3459
3469
  .day__num { font: 700 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
3460
3470
  /* today: a light tone, no louder than a rest; the selected pill below wins when it is both (it comes after and weighs more) */
3461
- .day--today { background: var(--day-today-bg, var(--ink-tint)); color: var(--day-today-ink, currentColor); }
3462
- .day[aria-selected="true"] { background: var(--day-selected-bg, var(--accent)); color: var(--day-selected-ink, var(--on-accent)); opacity: 1; }
3471
+ .day--today { background: var(--day-today-bg, var(--ink-tint)); color: var(--day-today-ink, var(--card-ink)); }
3472
+ .day[aria-selected="true"] { background: var(--day-selected-bg, var(--accent)); color: var(--day-selected-ink, var(--on-accent)); }
3463
3473
  .day__dot { grid-row: 3; align-self: center; width: var(--day-dot-size, var(--dot-xs)); height: var(--day-dot-size, var(--dot-xs)); border-radius: 50%; background: currentColor; }
3464
3474
 
3465
3475
  /* ---- calendar: month grid, 44px cells, one accent day, optional marks ----
@@ -3479,8 +3489,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3479
3489
  font-variant-numeric: tabular-nums;
3480
3490
  grid-column-start: var(--start, auto); /* --start on the 1st: weekday column 1–7 */
3481
3491
  }
3482
- .calendar__day--mark { background: var(--calendar-day-mark-bg, var(--accent-soft)); }
3483
- .calendar__day--mark2 { background: var(--calendar-day-mark2-bg, var(--accent-mid)); color: var(--calendar-day-mark2-ink, var(--on-accent)); }
3492
+ /* a marked day keeps the ground's ink, so its fill is a light tint (12%, as --ink-tint: every tone holds its ink 4.5:1 on that); the
3493
+ strong mark adds a ring of the accent. A stronger fill sat at the middle, where neither the ink nor --on-accent holds 4.5:1 */
3494
+ .calendar__day--mark { background: var(--calendar-day-mark-bg, color-mix(in srgb, var(--accent) 12%, transparent)); }
3495
+ .calendar__day--mark2 { background: var(--calendar-day-mark2-bg, color-mix(in srgb, var(--accent) 12%, transparent)); color: var(--calendar-day-mark2-ink, inherit); box-shadow: var(--calendar-day-mark2-shadow, inset 0 0 0 var(--border-w) var(--accent)); }
3484
3496
  .calendar__day[aria-selected="true"] { background: var(--calendar-day-selected-bg, var(--accent)); color: var(--calendar-day-selected-ink, var(--on-accent)); font-weight: 700; }
3485
3497
  .calendar__day:disabled { opacity: 0.3; }
3486
3498
 
@@ -3536,7 +3548,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3536
3548
  edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
3537
3549
  is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
3538
3550
  }
3539
- .pin__cell:not(:placeholder-shown) { color: var(--pin-cell-filled-ink, var(--accent)); } /* a typed digit: coloured words on the cell's own ground — no fill (Lh 2026-09-24; was an accent-filled pill) */
3551
+ .pin__cell:not(:placeholder-shown) { color: var(--pin-cell-filled-ink, var(--action-accent)); } /* a typed digit: coloured words on the cell's own ground — no fill (Lh 2026-09-24; was an accent-filled pill) */
3540
3552
  /* a PIN (secret): the cell DRAWS the dot, the character itself is not painted — except for a moment, when it has just been typed
3541
3553
  (Lh 2026-10-01: "each digit must flash, then go at once"). It was `-webkit-text-security: disc`: a phone's browser then shows
3542
3554
  the last typed character of a masked field for a second or more ("password echo", Android) — per FIELD, and a Pin is six
@@ -3706,7 +3718,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3706
3718
  .row__body { flex: 1; min-width: 0; display: grid; gap: var(--row-body-gap, var(--sp-0)); }
3707
3719
  .row__title { font: var(--row-title-font, var(--type-title)); overflow-wrap: anywhere; } /* wraps freely — no clip, so no padding trick for Thai marks (Lh 2026-09-16, same call as the card heading) */
3708
3720
  .row__meta { font: var(--row-meta-font, var(--type-caption)); color: var(--row-meta-ink, var(--card-muted)); display: flex; align-items: center; gap: var(--row-meta-gap, var(--sp-1)); }
3709
- .card--3 .row__meta { color: var(--card-3-muted); }
3710
3721
  .row__end { margin-left: auto; text-align: right; display: grid; gap: var(--row-end-gap, var(--sp-0)); justify-items: end; flex: none; }
3711
3722
  .row--lg { min-height: var(--row-lg-min-h, var(--fab)); } /* menu rows: a taller target, round arrow at the end; .rows gives them more air */
3712
3723
  .row--lg .row__title { font: 700 var(--fs-lg)/1.2 var(--font-display); }
@@ -3725,7 +3736,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3725
3736
  at --tap-sm, but not an input: nothing to press. */
3726
3737
  .row--done { pointer-events: none; cursor: default; }
3727
3738
  .row--done .row__title { color: var(--card-muted); }
3728
- .card--3 .row--done .row__title { color: var(--card-3-muted); }
3729
3739
  .row__done {
3730
3740
  width: var(--row-done-size, var(--tap-sm));
3731
3741
  height: var(--row-done-size, var(--tap-sm));
@@ -3770,7 +3780,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3770
3780
  .kv-cell > .icon { margin-bottom: var(--sp-1); }
3771
3781
  .kv-cell__val { font: var(--kv-cell-val-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
3772
3782
  .kv-cell__key { font: var(--kv-cell-key-font, var(--type-caption)); color: var(--kv-cell-key-ink, var(--card-muted)); }
3773
- .card--3 .kv-cell__key { color: var(--card-3-muted); }
3774
3783
 
3775
3784
  /* ---- bubble: a chat message — theirs on the left, mine on the right (Lh 2026-09-18, replaces the chip's tail) ----
3776
3785
  No tail: a rounded block whose corner nearest the sender is square-ish
@@ -3799,7 +3808,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3799
3808
  border-radius: var(--bubble-body-radius, var(--r-card));
3800
3809
  border-bottom-left-radius: var(--r-control);
3801
3810
  background: var(--bubble-body-bg, var(--card-2-bg));
3802
- color: var(--bubble-body-ink, var(--card-2-ink));
3811
+ --dark: var(--card-2-dark);
3812
+ color: var(--bubble-body-ink, var(--card-ink));
3803
3813
  font: var(--bubble-body-font, var(--type-body));
3804
3814
  overflow-wrap: anywhere;
3805
3815
  }
@@ -3810,10 +3820,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3810
3820
  color: var(--on-accent);
3811
3821
  text-align: right;
3812
3822
  }
3813
- .bubble__meta { display: flex; align-items: center; gap: var(--bubble-meta-gap, var(--sp-1)); font: var(--bubble-meta-font, var(--type-caption)); color: var(--bubble-meta-ink, var(--on-surface-muted)); }
3823
+ .bubble__meta { display: flex; align-items: center; gap: var(--bubble-meta-gap, var(--sp-1)); font: var(--bubble-meta-font, var(--type-caption)); color: var(--bubble-meta-ink, var(--card-muted)); }
3814
3824
  .bubble--right > .bubble__meta { justify-self: end; }
3815
- .bubble__meta > .icon { color: var(--accent); }
3816
- .card--3 .bubble__meta { color: var(--card-3-muted); }
3825
+ .bubble__meta > .icon { color: var(--bubble-meta-icon-ink, var(--accent-ink)); }
3817
3826
 
3818
3827
  /* ---- timeline: a hairline with date chips, entries to the right ----
3819
3828
  <ol class="timeline"><li class="timeline__item"><span class="timeline__mark">Wed 14</span><p class="t-body">…</p></li>…</ol> */
@@ -3856,7 +3865,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3856
3865
  /* ---- legend: signal dots with captions ----
3857
3866
  <div class="legend"><span class="legend__item"><span class="dot"></span> very high</span>…</div> */
3858
3867
  .legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font: var(--legend-font, var(--type-caption)); color: var(--legend-ink, var(--card-muted)); }
3859
- .card--3 .legend { color: inherit; opacity: 0.8; }
3860
3868
  .legend__item { display: inline-flex; align-items: center; gap: var(--legend-item-gap, var(--sp-2)); min-height: var(--legend-item-min-h, var(--icon)); }
3861
3869
 
3862
3870
  /* ---- link: "See all ↗" in a card head, big enough to tap ----
@@ -3877,7 +3885,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3877
3885
  .link--muted { color: var(--link-muted-ink, var(--card-muted)); }
3878
3886
  .link:disabled { color: var(--link-disabled-ink, var(--card-muted)); cursor: default; } /* inactive = muted (a Countdown) */
3879
3887
  .link--wait { font-variant-numeric: tabular-nums; } /* the countdown's digits keep their width: the words do not jiggle each second */
3880
- .card--3 .link--muted { color: var(--card-3-muted); }
3881
3888
 
3882
3889
  /* ---- bullets: a list, each item led by a small square picture as tall as a line of its words (Lh 2026-09-28, timebank #153) ----
3883
3890
  <ul class="bullets"><li class="bullets__item"><img class="bullets__pic" alt=""><span class="bullets__text">…</span></li></ul>
@@ -3908,8 +3915,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3908
3915
  only its content box, which stops one fold short of the right and bottom edges; the paper's negative margins run
3909
3916
  back out to the full size. The paper takes the tone by `inherit`. */
3910
3917
  .sticky {
3911
- color: var(--sticky-ink, #141414);
3912
- --_muted: color-mix(in srgb, currentColor 62%, transparent); /* muted words: the ink sunk into the paper (currentColor is read where it is used: there it is still the note's ink) */
3918
+ --dark: var(--sticky-dark, 0); /* a note is pastel paper: a light ground in any palette and any mode, so its words are onlight */
3919
+ color: var(--sticky-ink, var(--card-ink));
3920
+ --_muted: var(--card-muted);
3913
3921
  padding-inline-end: var(--sticky-fold, var(--sp-6)); /* the knob, read by a real property first (the knob test probes that one); --_fold hands the same value to the paper and the flap */
3914
3922
  --_fold: var(--sticky-fold, var(--sp-6));
3915
3923
  position: relative;
@@ -3973,7 +3981,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3973
3981
  .sticky__head:has(> .sticky-date) > .ph { flex: none; width: var(--sticky-pic-size, calc(var(--tap) * 1.5)); min-height: 0; aspect-ratio: 1; } /* beside a date the picture is a small square at the start */
3974
3982
  .sticky__body { flex: 1 1 auto; min-width: 0; min-height: 0; overflow: clip; /* words past the note's room are cut, the foot keeps its place */ font: var(--sticky-font, var(--type-title)); overflow-wrap: anywhere; }
3975
3983
  .sticky--small .sticky__body { font: var(--sticky-small-font, var(--type-label)); } /* the note measured its words and they did not fit at its size (src/sticky/fit.ts) */
3976
- .sticky__paper > :not(.sticky__body) > .chip { border-color: transparent; background-color: var(--sticky-chip-bg, rgb(255 255 255 / 0.85)); color: var(--sticky-ink, #141414); } /* a chip on a note is a label ON the paper (whose note it is): a pale ground nearly opaque (it may lie over words), no edge, the note's ink */
3984
+ .sticky__paper > :not(.sticky__body) > .chip { border-color: transparent; background-color: var(--sticky-chip-bg, rgb(255 255 255 / 0.85)); --dark: inherit; color: var(--sticky-ink, var(--card-ink)); } /* a chip on a note is a label ON the paper (whose note it is): a pale ground nearly opaque (it may lie over words), no edge, the note's ink */
3977
3985
  .sticky__foot { flex: none; font: var(--sticky-foot-font, var(--type-caption)); color: var(--sticky-muted-ink, var(--_muted)); padding-inline-end: var(--_fold); } /* the line stops before the fold */
3978
3986
  /* the flap: a square in the corner — the paper's cut takes its outer half, what is left is the triangle lying on the note */
3979
3987
  .sticky__fold {
@@ -4001,7 +4009,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4001
4009
 
4002
4010
  /* ---- sticky date: the day, big, the month under it — at the end of the note's head (its top-right corner) ----
4003
4011
  <div class="sticky__head">…<time class="sticky-date" datetime="2026-02-14"><span class="sticky-date__day">14</span><span class="sticky-date__month">ก.พ.</span></time></div> */
4004
- .sticky-date { display: grid; justify-items: end; margin-inline-start: auto; text-align: end; color: var(--sticky-ink, #141414); }
4012
+ .sticky-date { display: grid; justify-items: end; margin-inline-start: auto; text-align: end; color: var(--sticky-ink, var(--card-ink)); }
4005
4013
  .sticky-date__day { font: var(--sticky-date-day-font, var(--type-stat)); }
4006
4014
  .sticky-date__month { font: var(--sticky-date-month-font, var(--type-caption)); color: var(--sticky-muted-ink, var(--_muted)); }
4007
4015
 
@@ -4285,7 +4293,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4285
4293
  font: inherit;
4286
4294
  }
4287
4295
  .sticky-heading__title { min-width: 0; max-width: 100%; font: var(--sticky-heading-title-font, var(--type-label)); line-height: 1.35; /* Thai marks stack taller than the label's 1.2, and overflow clips them */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
4288
- .sticky-heading__caption { font: var(--sticky-heading-caption-font, var(--type-caption)); color: color-mix(in srgb, currentColor 62%, transparent); white-space: nowrap; }
4296
+ .sticky-heading__caption { font: var(--sticky-heading-caption-font, var(--type-caption)); color: var(--sticky-heading-caption-ink, var(--card-muted)); white-space: nowrap; } /* the muted words of what it stands on: a note, or the screen */
4289
4297
  /* on a note: across the top of its paper, and the note's words start under it */
4290
4298
  .sticky__stuck > .sticky-heading { position: absolute; inset: var(--sp-2) var(--sp-4) auto; }
4291
4299
  .sticky:has(> .sticky__stuck > .sticky-heading) > .sticky__paper { padding-block-start: var(--sticky-heading-room, calc(var(--sp-12) + var(--sp-4))); }
@@ -4583,7 +4591,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4583
4591
  /* ---- post-paper: the card stock. Its own ground and ink, wherever it lies; its parts read --_muted and --_rule ---- */
4584
4592
  .post-paper {
4585
4593
  background: var(--post-paper-bg, var(--card-1-bg));
4586
- color: var(--post-paper-ink, var(--card-1-ink));
4594
+ --dark: var(--card-1-dark);
4595
+ color: var(--post-paper-ink, var(--card-ink));
4587
4596
  border-radius: var(--post-paper-radius, var(--sp-2));
4588
4597
  box-shadow: var(--post-paper-shadow, var(--shadow-card));
4589
4598
  --_muted: var(--post-paper-muted-ink, var(--card-muted));
@@ -4924,9 +4933,9 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4924
4933
  <div class="row">…meta…</div></article> */
4925
4934
  .card--cover {
4926
4935
  --card-bg: var(--card-3-bg);
4927
- --card-ink: var(--card-3-ink);
4928
- --accent: var(--card-3-ink);
4929
- --on-accent: var(--card-3-bg);
4936
+ --dark: var(--card-3-dark);
4937
+ --accent: var(--card-ink);
4938
+ --accent-dark: calc(1 - var(--dark));
4930
4939
  min-height: var(--_cover-min-h);
4931
4940
  align-content: space-between;
4932
4941
  overflow: hidden;
@@ -4952,8 +4961,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4952
4961
  z-index: -1;
4953
4962
  background: linear-gradient(to bottom, var(--scrim), transparent 40% 60%, var(--scrim));
4954
4963
  }
4955
- .card--cover .t-muted { color: inherit; opacity: 0.75; }
4956
- .card--cover .row__meta { color: inherit; opacity: 0.85; }
4957
4964
 
4958
4965
  /* ---- quote: giant mark, big text, attribution ----
4959
4966
  <blockquote class="quote"><p class="quote__text">It's overcast…</p><footer class="quote__by">Wednesday 15</footer></blockquote> */
@@ -4988,35 +4995,40 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4988
4995
  border: var(--border-w) solid var(--tile-border, var(--outline));
4989
4996
  border-radius: var(--tile-radius, var(--r-card));
4990
4997
  background: var(--tile-bg, var(--action-bg));
4991
- color: var(--tile-ink, var(--action-ink));
4998
+ --dark: var(--action-dark); /* a tile is a surface: the paper's side (a toned tile takes its tone's, below) */
4999
+ color: var(--tile-ink, var(--card-ink));
4992
5000
  font: inherit;
4993
5001
  text-align: left;
4994
5002
  position: relative;
4995
5003
  }
4996
5004
  .tile__label { font: var(--tile-label-font, var(--type-title)); }
4997
5005
  .tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
4998
- .tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: var(--dim); }
5006
+ .tile__sub { font: var(--tile-sub-font, var(--type-caption)); color: var(--tile-sub-ink, var(--card-muted)); } /* the muted words of the tile's ground; --dim faded the full ink to 2.4–3.4:1 */
4999
5007
  .tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
5000
- .tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
5008
+ .tile--on { background: var(--tile-on-bg, var(--accent)); --dark: var(--accent-dark); color: var(--tile-on-ink, var(--card-ink)); border-color: transparent; }
5001
5009
  /* .tile--pick's hidden input and :has(:checked) fill are declared once in choice.css, alongside .chip--pick and .mood__opt */
5002
5010
  .tile--wide { grid-column: 1 / -1; }
5003
5011
  /* 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 */
5004
- .tile--stat { border-color: transparent; background: none; padding-inline: 0; 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; }
5012
+ /* a stat tile has no fill of its own, so it has no ink of its own: it takes the ink of what it stands on (currentColor on `color`
5013
+ = the inherited colour), as the card's heading does. It kept the plain tile's --action-ink, dark on a dark card (timebank, tone 3,
5014
+ Lh 2026-10-18). --tile-ink still overrides; a toned stat tile (.tile--N, below) takes its fill and its ink back */
5015
+ .tile--stat { --dark: inherit; color: var(--tile-ink, currentColor); border-color: transparent; background: none; padding-inline: 0; 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; }
5005
5016
  /* upload: dashed, centred plus + label */
5006
5017
  .tile--upload {
5007
5018
  border-style: dashed;
5008
5019
  background: transparent;
5009
5020
  color: inherit;
5021
+ --dark: inherit;
5010
5022
  align-content: center;
5011
5023
  justify-items: center;
5012
5024
  gap: var(--tile-upload-gap, var(--sp-2));
5013
5025
  text-align: center;
5014
5026
  }
5015
5027
  /* tinted tiles: slot colors inside a card (schedule, notes) */
5016
- .tile--2 { background: var(--tile-2-bg, var(--card-2-bg)); color: var(--tile-2-ink, var(--card-2-ink)); border-color: transparent; }
5017
- .tile--4 { background: var(--tile-4-bg, var(--card-4-bg)); color: var(--tile-4-ink, var(--card-4-ink)); border-color: transparent; }
5018
- .tile--5 { background: var(--tile-5-bg, var(--card-5-bg)); color: var(--tile-5-ink, var(--card-5-ink)); border-color: transparent; }
5019
- .tile--3 { background: var(--tile-3-bg, var(--card-3-bg)); color: var(--tile-3-ink, var(--card-3-ink)); border-color: transparent; }
5028
+ .tile--2 { background: var(--tile-2-bg, var(--card-2-bg)); --dark: var(--card-2-dark); color: var(--tile-2-ink, var(--card-ink)); border-color: transparent; }
5029
+ .tile--4 { background: var(--tile-4-bg, var(--card-4-bg)); --dark: var(--card-4-dark); color: var(--tile-4-ink, var(--card-ink)); border-color: transparent; }
5030
+ .tile--5 { background: var(--tile-5-bg, var(--card-5-bg)); --dark: var(--card-5-dark); color: var(--tile-5-ink, var(--card-ink)); border-color: transparent; }
5031
+ .tile--3 { background: var(--tile-3-bg, var(--card-3-bg)); --dark: var(--card-3-dark); color: var(--tile-3-ink, var(--card-ink)); border-color: transparent; }
5020
5032
 
5021
5033
  /* ---- fold: a sticky-note corner on any card (decorative) ----
5022
5034
  No border. The card's outline follows its border box, so it ran straight
@@ -5139,7 +5151,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5139
5151
  }
5140
5152
  .postcard__turn:focus-visible { outline-offset: calc(-1 * var(--sp-1)); } /* the ring inside the card's edge */
5141
5153
  /* the front: the photo (or the wireframe box) fills it; the band reads on a scrim */
5142
- .postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); color: var(--postcard-front-ink, var(--card-3-ink)); }
5154
+ .postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); --dark: var(--card-3-dark); color: var(--postcard-front-ink, var(--card-ink)); }
5143
5155
  .postcard__front .postcard__turn > img,
5144
5156
  .postcard__front .postcard__turn > .ph {
5145
5157
  position: absolute;
@@ -5162,7 +5174,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5162
5174
  /* the back: paper — the caption and the name packed at the middle, big enough to read at arm's length */
5163
5175
  .postcard__back {
5164
5176
  background: var(--postcard-back-bg, var(--card-2-bg));
5165
- color: var(--postcard-back-ink, var(--card-2-ink));
5177
+ --dark: var(--card-2-dark);
5178
+ color: var(--postcard-back-ink, var(--card-ink));
5166
5179
  transform: rotateY(180deg);
5167
5180
  }
5168
5181
  .postcard__back .postcard__turn { align-content: start; padding-top: var(--postcard-back-top, 20%); gap: var(--postcard-back-gap, var(--sp-5)); } /* the name a fifth down the paper, the words after it */
@@ -5180,7 +5193,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5180
5193
  }
5181
5194
  .postcard__band > * { pointer-events: auto; }
5182
5195
  .postcard__words { display: grid; gap: var(--postcard-words-gap, var(--sp-1)); flex: 1; min-width: 0; pointer-events: none; }
5183
- .postcard__front .postcard__band { --accent: var(--postcard-front-ink, var(--card-3-ink)); --on-accent: var(--postcard-front-bg, var(--card-3-bg)); }
5196
+ .postcard__front .postcard__band { --accent: var(--postcard-front-ink, var(--card-ink)); --accent-dark: calc(1 - var(--dark)); }
5184
5197
  .postcard__band .icon-btn[aria-pressed="true"] .icon { fill: currentColor; }
5185
5198
  .postcard.is-flipped > .postcard__front { transform: rotateY(-180deg); }
5186
5199
  .postcard.is-flipped > .postcard__back { transform: rotateY(0); }
@@ -5265,19 +5278,21 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5265
5278
  gap: var(--modal-drawer-gap, var(--sp-1));
5266
5279
  text-align: center;
5267
5280
  background: var(--modal-drawer-bg, var(--card-3-bg));
5268
- color: var(--modal-drawer-ink, var(--card-3-ink));
5269
- --accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
5270
- --on-accent: var(--card-3-bg);
5281
+ --dark: var(--card-3-dark);
5282
+ color: var(--modal-drawer-ink, var(--card-ink));
5283
+ --accent: var(--card-ink); /* the one solid button inverts on the brand drawer */
5284
+ --accent-dark: calc(1 - var(--dark));
5271
5285
  }
5272
5286
  .modal__drawer--light { /* light: the card's paper, dark words (Lh 2026-09-29) — the one solid button turns dark */
5273
5287
  background: var(--modal-drawer-light-bg, var(--card-bg));
5288
+ --dark: inherit; /* the card's own side */
5274
5289
  color: var(--modal-drawer-light-ink, var(--card-ink));
5275
5290
  --accent: var(--card-ink);
5276
- --on-accent: var(--card-bg);
5291
+ --accent-dark: calc(1 - var(--dark));
5277
5292
  }
5278
5293
  /* the CONFIRM: a Modal with no ModalBody: the ModalAsk IS the card, as tall as its words (no fixed band, no second beat) */
5279
5294
  .modal:not(:has(> .modal__lift)) > .modal__drawer { height: auto; padding-block: var(--modal-confirm-pad-y, var(--sp-6)); animation: none; }
5280
- .modal__drawer .t-caption { opacity: var(--dim); }
5295
+ .modal__drawer .t-caption { color: var(--modal-drawer-caption-ink, var(--card-muted)); }
5281
5296
  .modal__drawer > h2:focus { outline: none; } /* the question takes the cursor when the modal arrives (for a screen reader, the keyboard's way in) — it is not a control: no ring */
5282
5297
  /* the answers as WORD buttons (a BtnRow of Btns; Lh 2026-10-11): the row fills the ask with room over it and each button keeps its words on one
5283
5298
  line — left to its words' width the row squeezed two buttons into round blobs, their words on two lines. A quiet button is an outline only,
@@ -5386,6 +5401,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5386
5401
  background: var(--banner-bg, var(--ink-tint));
5387
5402
  color: var(--banner-ink, inherit);
5388
5403
  border-block: var(--border-w) solid var(--banner-border, var(--hairline));
5404
+ --card-muted: var(--card-ink); /* a tint of the ink is a ground where no lighter shade holds 4.5:1: its secondary words are the full ink */
5389
5405
  /* a band edge to edge of what holds it — the card's or the screen's inset; no radius, no side border: a band, not a card */
5390
5406
  margin-inline: calc(-1 * var(--card-pad, var(--screen-pad)));
5391
5407
  padding-inline: var(--card-pad, var(--screen-pad));
@@ -5439,7 +5455,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5439
5455
  border-radius: var(--float-bar-pill-radius, var(--r-chip));
5440
5456
  border: var(--border-w) solid var(--float-bar-pill-border, var(--action-border));
5441
5457
  background: var(--float-bar-pill-bg, color-mix(in srgb, var(--action-bg) 82%, transparent));
5442
- color: var(--float-bar-pill-ink, var(--action-ink));
5458
+ --dark: var(--action-dark); /* the pill is the items' ground: the paper's side */
5459
+ color: var(--float-bar-pill-ink, var(--card-ink));
5443
5460
  backdrop-filter: blur(var(--sp-3));
5444
5461
  -webkit-backdrop-filter: blur(var(--sp-3));
5445
5462
  box-shadow: var(--float-bar-pill-shadow, var(--shadow-float));
@@ -5455,14 +5472,14 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5455
5472
  border-radius: var(--float-bar-item-radius, var(--r-chip));
5456
5473
  font: var(--float-bar-item-font, var(--type-caption));
5457
5474
  text-decoration: none;
5458
- opacity: var(--rest);
5475
+ color: var(--float-bar-item-rest-ink, var(--card-muted));
5459
5476
  overflow: hidden;
5460
5477
  white-space: nowrap;
5461
5478
  text-overflow: ellipsis;
5462
5479
  }
5463
5480
  /* aria-current="false" is the correct way to mark the OTHER tabs, so the
5464
5481
  bare attribute selector lights every one of them up */
5465
- .float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); opacity: 1; }
5482
+ .float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); }
5466
5483
  .float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
5467
5484
 
5468
5485
  /* ---- callout: a small card with a tail — over a map, a media, a mascot ----
@@ -5499,7 +5516,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5499
5516
  border-left: 0;
5500
5517
  }
5501
5518
  .callout__title { font: var(--callout-title-font, var(--type-title)); }
5502
- .callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: var(--dim); }
5519
+ .callout__sub { font: var(--callout-sub-font, var(--type-caption)); color: var(--callout-sub-ink, var(--action-muted)); }
5503
5520
  /* ---- splash: the app's mark at the human centre while it boots (Lh 2026-09-24) ----
5504
5521
  <section class="screen screen--fill splash"><div class="splash__group"><div class="splash__mark">{the app's lottie/img}</div>
5505
5522
  <p class="splash__text">…</p></div></section>
@@ -5542,7 +5559,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5542
5559
  border: var(--border-w) solid var(--snackbar-border, transparent);
5543
5560
  border-radius: var(--snackbar-radius, var(--r-card));
5544
5561
  background: var(--snackbar-bg, var(--signal-ok-soft));
5545
- color: var(--snackbar-ink, var(--on-surface));
5562
+ --dark: var(--surface-dark); /* the signal's soft ground is a tint of the screen: the screen's side */
5563
+ color: var(--snackbar-ink, var(--card-ink));
5546
5564
  box-shadow: var(--snackbar-shadow, var(--shadow-float));
5547
5565
  font: inherit; text-align: start;
5548
5566
  pointer-events: auto;
@@ -5550,7 +5568,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5550
5568
  .snackbar__body > .icon { flex: none; color: var(--snackbar-icon-ink, var(--signal-ok)); }
5551
5569
  .snackbar__words { display: grid; min-width: 0; overflow-wrap: anywhere; }
5552
5570
  .snackbar__text { font: var(--snackbar-text-font, var(--type-body)); }
5553
- .snackbar__caption { font: var(--snackbar-caption-font, var(--type-caption)); opacity: var(--dim); }
5571
+ .snackbar__caption { font: var(--snackbar-caption-font, var(--type-caption)); color: var(--snackbar-caption-ink, inherit); } /* on the signal's soft ground the full ink: a muted shade there fell under AA */
5554
5572
  @media (prefers-reduced-motion: reduce) { .snackbar { transition: none; } }
5555
5573
 
5556
5574
  /* ---- css/finish.css — light, shadow, edges — only under data-finish="lit" ---- */
@@ -5576,8 +5594,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
5576
5594
  so a finish sits in any palette; the one plain white is the sticker's backing paper and the lit edge's light.
5577
5595
  ============================================================ */
5578
5596
 
5579
- /* every finish: the finish IS the edge, so the wireframe's two borders step back (as [data-palette] in palette.css); ink puts its own line there below */
5580
- [data-finish] { --card-border: transparent; --action-border: transparent; }
5597
+ /* every finish: the finish IS the controls' edge, so the wireframe's control border steps back (as [data-palette] in palette.css). The card
5598
+ keeps its --card-border: lit's ring and sticker's white cut do not reach 3:1 on a light ground (palette.css, THE CARD KEEPS AN EDGE);
5599
+ print and ink draw their own line below */
5600
+ [data-finish] { --action-border: transparent; }
5581
5601
 
5582
5602
  /* ============================================================
5583
5603
  lit — the light comes from above: