diff --git a/docs/release/v0.3-tactile-quietware.md b/docs/release/v0.3-tactile-quietware.md
index 7789b52..41042d6 100644
--- a/docs/release/v0.3-tactile-quietware.md
+++ b/docs/release/v0.3-tactile-quietware.md
@@ -236,6 +236,77 @@ Architectural finding: the v0.2 `SettingsPage.svelte` (2 891 LOC after Phase 4a)
Rationale: phase 4a already delivers "one section at a time" under quietware. Renaming the eight tabs to match the canonical plan (Output / Vocabulary carved out as top-level) is labelling polish, not user-value. Page-skeleton sweep (Phase 5) delivers more visible improvement per LOC. Phase 4b will return after Phase 5 lands.
+### Phase 4d — Palette revision per 2026-05-15 round 4 feedback. Landed 2026-05-15.
+
+The color.adobe.com primaries (#FF0700 / #000AFF / #00FF56 / #FFCD00) earned vividness at the cost of brand identity and accent-vs-caution discipline. Round-4 feedback pulled the system back toward muted-Material values with a clear architectural split:
+
+```
+Atmosphere = warm tactile surface
+Status = red / yellow / green / blue
+Brand = copper-clay amber accent (--color-accent, distinct from caution)
+Accessibility = high-contrast override is a different rendering contract
+```
+
+Dark mode (revised, brown-charcoal instead of wine-aubergine):
+
+| Token | Before (4c) | After (4d) |
+|---|---|---|
+| `--color-bg` | `#2A1620` | `#12100E` |
+| `--color-bg-elevated` | `#34222B` | `#1A1713` |
+| `--color-bg-card` | `#3E2A33` | `#211D18` |
+| `--color-sidebar` | `#1F0F18` | `#0F0E0C` |
+| `--color-accent` | (unset, fell through to v0.2) | `#C97845` |
+| `--color-danger` | `#FF2A24` | `#FF8A8A` |
+| `--color-caution` | `#FFCD00` | `#F2C94C` |
+| `--color-success` | `#00FF56` | `#79D59B` |
+| `--color-info` | `#7076FF` | `#8AB4F8` |
+
+Light mode (revised, semantic values back to Material AA-friendly hues):
+
+| Token | Before (4c) | After (4d) |
+|---|---|---|
+| `--color-bg-card` | `#FFFFFF` | `#FFFDF8` (warm white) |
+| `--color-bg-elevated` | `#F3EEE6` | `#F6F1EA` |
+| `--color-accent` | (unset) | `#9D5F32` |
+| `--color-danger` | `#C54B1F` | `#B3261E` |
+| `--color-caution` | `#FFCD00` (fill-only) | `#7A5D00` (passes as text) |
+| `--color-success` | `#00852D` | `#1B6B3A` |
+| `--color-info` | `#0047AB` | `#2457A6` |
+
+The "caution fill-only on cream" rule retires under the muted palette. Ochre `#7A5D00` passes AA as foreground text against cream, so the same single-token-per-role convention applies across all four semantic colours in light mode.
+
+LumotiaNotice opacity rebalance:
+
+| Surface | Before | After |
+|---|---|---|
+| Background tint | role at 10% | role at 8% |
+| Outer border | neutral subtle | role at 40% |
+| Left bar | role full | role full (unchanged) |
+| Icon | `--color-text` | role colour |
+| Title / body | `--color-text` | `--color-text` (unchanged) |
+
+### Phase 4e — High-contrast rendering contract. Landed 2026-05-15.
+
+High-contrast is not a colour variant — it's a different rendering contract that the entire UI subscribes to. The tokens block now publishes behavioural variables alongside the palette overrides, so components can opt into the contract by reading them.
+
+Behavioural tokens (defaults + HC values):
+
+| Token | Default | HC |
+|---|---|---|
+| `--grain-opacity` | 0.06 (dark) / 0.07 (light) | 0 |
+| `--shadow-strength` | 1 | 0 |
+| `--border-width-control` | 1px | 2px |
+| `--focus-ring-width` | 2px | 3px |
+| `--panel-radius` | 8px | 8px (unchanged for now) |
+
+`--quietware-texture-opacity` retained as a legacy alias pointing at `--grain-opacity` so Phase 4c controls keep working through the rename.
+
+HC palette tweaks (round-4 feedback):
+
+- **HC light** is now true pure-white background (`#FFFFFF`) + true pure-black text/borders (`#000000`). Brand atmosphere steps aside completely. Single strong-focus blue `#005FCC` replaces the accent.
+- **HC dark** stays pure black `#000000` with white borders, semantic muted hues that read clearly. Matches the v0.3 dark feel but at maximum legibility.
+- Hue-based selectors: `[data-design="quietware"][data-contrast="high"]` matches both base themes; the dark-base variant is scoped via `:not([data-theme="light"])` so light-base HC and dark-base HC can diverge.
+
### Phase 4c — Texture-opacity slider + high-contrast toggle. Landed 2026-05-15.
- New static grain overlay defined in `src/design-system/v0.3-quietware-tokens.css`. Fixed-position `body::after`, `mix-blend-mode: overlay`, opacity driven by `--quietware-texture-opacity`. Pattern generated inline via SVG `feTurbulence` (`baseFrequency=0.85`, `numOctaves=2`, `stitchTiles=stitch`). Tile size 240×240, repeated across viewport. `pointer-events: none` so the overlay never intercepts clicks. Active only under `html[data-design="quietware"]` and only on the dark and light modes — high-contrast forces opacity to 0.
diff --git a/src/design-system/v0.3-quietware-tokens.css b/src/design-system/v0.3-quietware-tokens.css
index e7e0246..92993d1 100644
--- a/src/design-system/v0.3-quietware-tokens.css
+++ b/src/design-system/v0.3-quietware-tokens.css
@@ -10,29 +10,26 @@
Activation paths:
1. Manual: add data-design="quietware" to via dev tools.
- 2. Build flag (Phase 2): VITE_LUMOTIA_QUIETWARE=1 will wire the
+ 2. Build flag (Phase 2): VITE_LUMOTIA_QUIETWARE=1 wires the
attribute through +layout.svelte automatically.
--------------------------------------------------------------
- Palette source — Jake's color.adobe.com primaries, locked 2026-05-15:
+ Palette — Phase 4d revision (2026-05-15 round 4 feedback).
- #FF0700 red (--danger)
- #000AFF blue (--info)
- #00FF56 green (--success)
- #FFCD00 yellow (--caution)
+ Atmosphere is warm brown-charcoal. Semantic colours are
+ muted-Material hues, NOT max-saturation primaries. The brand
+ accent (copper-clay) is intentionally distinct from caution
+ (yellow) so the two warm attention colours do not collide.
- Each primary is used literally where it clears WCAG AA on the
- relevant background. Where it does not (typically yellow and green
- on cream paper, blue on dark Coffee Bean) a text-safe sibling at
- the same hue and saturation is derived by adjusting lightness
- until 4.5:1 clears.
+ Light-mode caution returns to ochre #7A5D00; this passes AA as
+ text on cream and works with the left-bar Notice pattern. The
+ earlier "fill-only on cream" rule applied to the bright #FFCD00
+ primary; under muted-Material values that constraint dissolves.
- Caution stays a fill-only role on cream paper. Any darkened yellow
- reads as olive or mustard ("looks like poop", per Jake), which we
- refuse to ship. Pill backgrounds, dots, left-border accents and
- icons take --color-caution. Foreground text inside caution-coloured
- pills stays --color-text. This matches Material, GOV.UK and IBM
- Carbon practice.
+ High contrast is a rendering CONTRACT, not just a palette. Phase
+ 4e behavioural tokens (--grain-opacity, --shadow-strength,
+ --border-width-control, --focus-ring-width, --panel-radius)
+ accompany the palette override.
Typography stack (V4 pairing, locked 2026-05-15):
Work Sans -> body, UI, controls, headers
@@ -70,55 +67,69 @@
/* ============================================================
Quietware — dark mode default.
- Lifted Coffee Bean base. Jake's primaries used directly where
- they clear AA on the dark surface; lifted siblings for the rest.
+ Warm brown-charcoal atmosphere. Reads as warm when you notice
+ it, not coloured at first glance. Semantic and brand-accent
+ palettes are deliberately separate.
============================================================ */
:root[data-design="quietware"] {
- --color-bg: #2A1620;
- --color-bg-elevated: #34222B;
- --color-bg-card: #3E2A33;
- --color-bg-input: #281620;
- --color-border: #6B4D58;
- --color-border-subtle: #4E3742;
+ --color-bg: #12100E;
+ --color-bg-elevated: #1A1713;
+ --color-bg-card: #211D18;
+ --color-bg-input: #181510;
+ --color-border: #4A4035;
+ --color-border-subtle: #2C261F;
--color-text: #F3EFE7;
--color-text-secondary: #BDB6A9;
- --color-text-tertiary: #8D8577;
+ --color-text-tertiary: #908779;
- /* --info and --danger lifted from primaries; pure red/blue too dark on dark bg. */
- --color-info: #7076FF; /* lifted from #000AFF, 4.64 AA */
- --color-danger: #FF2A24; /* lifted from #FF0700, 4.54 AA */
- /* --success and --caution use Jake's primaries directly; both already AAA on dark bg. */
- --color-success: #00FF56; /* Jake literal, 12.51 AAA */
- --color-caution: #FFCD00; /* Jake literal, 11.33 AAA */
+ /* Brand accent. Copper-clay, distinct from semantic yellow. */
+ --color-accent: #C97845;
+ --color-accent-hover: #B96638;
+ --color-accent-subtle: #C9784514;
+ --color-accent-glow: #C9784525;
+
+ /* Semantic palette. Muted-Material hues. */
+ --color-danger: #FF8A8A;
+ --color-caution: #F2C94C;
--color-warning: var(--color-caution);
+ --color-success: #79D59B;
+ --color-info: #8AB4F8;
- --color-sidebar: #1F0F18;
- --color-nav-active: #3E2A33;
- --color-hover: #34222B;
- --color-overlay-dim: rgba(31, 8, 18, 0.7);
+ --color-sidebar: #0F0E0C;
+ --color-nav-active: #211D18;
+ --color-hover: #1A1713;
+ --color-overlay-dim: rgba(15, 14, 12, 0.7);
--font-family-body: 'Work Sans', system-ui, sans-serif;
--font-family-display: 'Young Serif', serif;
--font-family-mono: 'JetBrains Mono', monospace;
- /* Texture intensity. 0.06 baseline, slider range 0.05 to 0.08 in advanced. */
- --quietware-texture-opacity: 0.06;
+ /* Phase 4e behavioural tokens — components opt into the rendering
+ contract by reading these. Defaults preserve existing v0.3 feel. */
+ --grain-opacity: 0.06;
+ --shadow-strength: 1;
+ --border-width-control: 1px;
+ --focus-ring-width: 2px;
+ --panel-radius: 8px;
+
+ /* Legacy alias — earlier Phase 4c name. Kept for transition. */
+ --quietware-texture-opacity: var(--grain-opacity);
--quietware-texture-color: rgba(243, 239, 231, 1);
}
/* ============================================================
Quietware — light mode.
- Cream paper base. Jake's blue passes AAA on cream as text and is
- used literally. The other three roles use text-safe siblings
- derived at the same hue and saturation (lightness shifted for AA).
- Caution is fill-only on cream by convention.
+ Warm paper base. Slight tone on the main work surface so the
+ page does not read as a generic web app. Caution returns to
+ ochre #7A5D00 — passes AA as text on cream under the muted
+ palette.
============================================================ */
:root[data-design="quietware"][data-theme="light"] {
--color-bg: #FBF8F2;
- --color-bg-elevated: #F3EEE6;
- --color-bg-card: #FFFFFF;
- --color-bg-input: #F3EEE6;
+ --color-bg-elevated: #F6F1EA;
+ --color-bg-card: #FFFDF8;
+ --color-bg-input: #F4EFE7;
--color-border: #897D70;
--color-border-subtle: #D8D0C4;
@@ -126,70 +137,106 @@
--color-text-secondary: #5E574C;
--color-text-tertiary: #74685B;
- --color-info: #000AFF; /* Jake literal, 7.96 AAA on cream */
- --color-danger: #E60600; /* text-safe derived from #FF0700, 4.51 AA */
- --color-success: #00852D; /* text-safe derived from #00FF56, 4.51 AA */
- /* Caution is FILL ONLY on cream. Text inside caution pills uses --color-text. */
- --color-caution: #FFCD00; /* Jake literal, fill-only by convention */
+ --color-accent: #9D5F32;
+ --color-accent-hover: #854E29;
+ --color-accent-subtle: #9D5F3214;
+ --color-accent-glow: #9D5F3225;
+
+ --color-danger: #B3261E;
+ --color-caution: #7A5D00;
--color-warning: var(--color-caution);
+ --color-success: #1B6B3A;
+ --color-info: #2457A6;
--color-sidebar: #F3EEE6;
--color-nav-active: #E8E2D6;
--color-hover: #EAE4D8;
--color-overlay-dim: rgba(29, 27, 24, 0.55);
- --quietware-texture-opacity: 0.07;
+ --grain-opacity: 0.07;
+ --quietware-texture-opacity: var(--grain-opacity);
--quietware-texture-color: rgba(29, 27, 24, 1);
}
/* ============================================================
- Quietware — high-contrast mode.
- Same four hues retained so role recognition does not flip between
- modes. Saturation 80%, lightness 70%. Zero texture, no translucent
- overlays.
+ Quietware — high-contrast rendering contract.
+ Different rendering CONTRACT, not just a colour override. The
+ tactile brand steps aside entirely. No grain, no shadows,
+ thicker borders, sharper focus rings. Pure black/white with a
+ single strong-blue focus colour.
============================================================ */
-:root[data-design="quietware"][data-contrast="high"] {
- --color-bg: #000000;
- --color-bg-elevated: #0A0A0A;
- --color-bg-card: #111111;
- --color-bg-input: #050505;
- --color-border: #FFFFFF;
- --color-border-subtle: #888888;
+:root[data-design="quietware"][data-contrast="high"],
+:root[data-design="quietware"][data-theme="light"][data-contrast="high"] {
+ --color-bg: #FFFFFF;
+ --color-bg-elevated: #FFFFFF;
+ --color-bg-card: #FFFFFF;
+ --color-bg-input: #FFFFFF;
+ --color-border: #000000;
+ --color-border-subtle: #000000;
- --color-text: #FFFFFF;
- --color-text-secondary: #DADADA;
- --color-text-tertiary: #C8C8C8;
+ --color-text: #000000;
+ --color-text-secondary: #000000;
+ --color-text-tertiary: #000000;
- --color-info: #757AF0; /* 5.80 AA on black */
- --color-danger: #F07975; /* 7.70 AAA on black */
- --color-success: #75F09F; /* 14.72 AAA on black */
- --color-caution: #F0D875; /* 14.78 AAA on black */
+ /* Accent collapses to the strong focus blue in HC. Brand
+ identity steps aside. */
+ --color-accent: #005FCC;
+ --color-accent-hover: #004BA0;
+ --color-accent-subtle: transparent;
+ --color-accent-glow: transparent;
+
+ --color-danger: #B3261E;
+ --color-caution: #7A5D00;
--color-warning: var(--color-caution);
+ --color-success: #1B6B3A;
+ --color-info: #005FCC;
- --color-sidebar: #000000;
- --color-nav-active: #1A1A1A;
- --color-hover: #1A1A1A;
+ --color-sidebar: #FFFFFF;
+ --color-nav-active: #000000;
+ --color-hover: #F0F0F0;
--color-overlay-dim: rgba(0, 0, 0, 0.85);
- /* No texture in high contrast. Clarity over atmosphere. */
+ /* Behavioural overrides. The contract bites here. */
+ --grain-opacity: 0;
+ --shadow-strength: 0;
+ --border-width-control: 2px;
+ --focus-ring-width: 3px;
+ --panel-radius: 8px;
--quietware-texture-opacity: 0;
--quietware-texture-color: rgba(0, 0, 0, 0);
}
+/* Dark-base high-contrast — black background instead of white.
+ Used when the user explicitly wants HC inside the dark theme. */
+:root[data-design="quietware"][data-contrast="high"]:not([data-theme="light"]) {
+ --color-bg: #000000;
+ --color-bg-elevated: #000000;
+ --color-bg-card: #000000;
+ --color-bg-input: #000000;
+ --color-border: #FFFFFF;
+ --color-border-subtle: #FFFFFF;
+
+ --color-text: #FFFFFF;
+ --color-text-secondary: #FFFFFF;
+ --color-text-tertiary: #FFFFFF;
+
+ --color-accent: #75A8F0;
+ --color-accent-hover: #5C92E0;
+
+ --color-danger: #FF8A8A;
+ --color-caution: #F2C94C;
+ --color-success: #79D59B;
+ --color-info: #8AB4F8;
+
+ --color-sidebar: #000000;
+ --color-nav-active: #FFFFFF;
+ --color-hover: #1A1A1A;
+}
+
/* ============================================================
- Static grain overlay. Atmospheric texture across the whole viewport.
- Fixed position so it does not scroll. Tile pattern generated inline
- via SVG feTurbulence. Opacity driven by --quietware-texture-opacity
- (default 0.06 dark / 0.07 light, slider range 0.05 to 0.08 in
- advanced settings). mix-blend-mode overlay darkens shadows and
- brightens highlights, producing the soft notebook-paper effect.
-
- pointer-events: none so clicks pass through. z-index 9999 places it
- above all content, but content remains fully interactive thanks to
- the pointer-events override.
-
- No texture in high-contrast mode — the [data-contrast="high"] block
- above sets --quietware-texture-opacity to 0.
+ Static grain overlay. Reads --grain-opacity. Zero in HC mode by
+ contract. Fixed-position so it does not scroll. mix-blend-mode
+ overlay produces the soft notebook-paper effect.
============================================================ */
:root[data-design="quietware"] body::after {
content: "";
@@ -198,7 +245,7 @@
pointer-events: none;
z-index: 9999;
background-image: url("data:image/svg+xml;utf8,");
- opacity: var(--quietware-texture-opacity);
+ opacity: var(--grain-opacity);
mix-blend-mode: overlay;
}
diff --git a/src/lib/ui/LumotiaNotice.svelte b/src/lib/ui/LumotiaNotice.svelte
index 63bd1dd..dc86136 100644
--- a/src/lib/ui/LumotiaNotice.svelte
+++ b/src/lib/ui/LumotiaNotice.svelte
@@ -4,19 +4,24 @@
// scattered across pages. Four tones map to the semantic tokens.
//
// v0.3 Phase 3 update — left-bar accent pattern. The visible status
- // signal now lives on a 4-px solid left border in the tone colour
- // (100% opacity, fill-grade chroma); the surrounding container uses
- // a soft tone-tinted background and a subtle outer border. Icon,
- // title and body text all use --color-text so they remain legible
- // across every theme combination, including quietware light mode
- // where --color-caution is the bright #FFCD00 fill (would fail as
- // text on cream). Same accessibility role semantics retained.
+ // signal lives on a 4-px solid left border in the tone colour, full
+ // opacity. The surround uses a soft tone-tinted background + outer
+ // border in the tone colour. Title and body text use --color-text;
+ // icon uses the tone colour. Same accessibility role semantics
+ // retained.
+ //
+ // v0.3 Phase 4d opacity rebalance (2026-05-15 round 4 feedback):
+ // Background role colour at 8% (was 10%, less heavy)
+ // Outer border role colour at 40% (was neutral subtle)
+ // Left bar role colour at 100% (unchanged)
+ // Icon role colour (reverted from --color-text once the
+ // muted-Material palette landed; previous fill-only-
+ // on-cream concern dissolved with the new ochre
+ // #7A5D00 light-mode caution).
+ // Title + body --color-text (unchanged)
//
// Pattern matches Material Design "banner with leading accent" and
- // IBM Carbon "inline notification" guidance: status colour on a
- // large fill surface (the bar), neutral foreground text. Works for
- // any tone in any theme regardless of WCAG-text contrast on the
- // role colour itself.
+ // IBM Carbon "inline notification" guidance.
import { Info, AlertTriangle, AlertOctagon, CheckCircle, X } from "lucide-svelte";
import type { Snippet } from "svelte";
@@ -40,25 +45,33 @@
classes = "",
}: Props = $props();
- const palette: Record = {
+ const palette: Record = {
info: {
bar: "border-l-info",
- bg: "bg-info/10",
+ bg: "bg-info/8",
+ border: "border-info/40",
+ iconColor: "text-info",
icon: Info,
},
caution: {
bar: "border-l-caution",
- bg: "bg-caution/10",
+ bg: "bg-caution/8",
+ border: "border-caution/40",
+ iconColor: "text-caution",
icon: AlertTriangle,
},
danger: {
bar: "border-l-danger",
- bg: "bg-danger/10",
+ bg: "bg-danger/8",
+ border: "border-danger/40",
+ iconColor: "text-danger",
icon: AlertOctagon,
},
success: {
bar: "border-l-success",
- bg: "bg-success/10",
+ bg: "bg-success/8",
+ border: "border-success/40",
+ iconColor: "text-success",
icon: CheckCircle,
},
};
@@ -73,10 +86,10 @@