Commit Graph

15 Commits

Author SHA1 Message Date
62e795174c v0.3 Phase 5g: quietware default-on for branch builds
The headless screenshots from Phases 4 and 5 had VITE_LUMOTIA_QUIETWARE=1
set at capture time; the running Tauri dev/release builds did not. Result:
21 commits of design work were invisible in the actual app even though the
emailed screenshots showed v0.3.

Fix is one-line: invert the env-var check in src/routes/+layout.svelte so
data-design="quietware" is the default on this branch. Explicit opt-out via
VITE_LUMOTIA_QUIETWARE=0 still works for anyone who wants to see the v0.2
surface for comparison.

Behaviour:
  unset → quietware ON
  "1"   → quietware ON (same as unset)
  "0"   → v0.2 surface forced

Branch-only default for now. Merge to main can decide whether to keep this
or require explicit opt-in once v0.3 stabilises.
2026-05-15 20:32:07 +01:00
4de3acd426 v0.3 Phase 5c: Dictation layout migration toward the mockup structure
Layout work, not a colour pass. DictationPage's existing 1 282-LOC
structure stays untouched for v0.2; under quietware a new layout
renders via LumotiaPageSkeleton with five zones matching the mockup:

  Capture header   large Record + status + secondary support + timer + pill
  Slim notice      info-classed browser-preview or danger-classed errors
  Primary surface  calm transcript canvas with empty-state typography
  Action bar       Copy / Save / Extract Tasks / Template / Open Viewer
  Mono metadata    Smart · model · Local-only or Browser-preview

Implementation pattern. Gated {#if isQuietware} branch inside the
existing {#if needsDownload}{:else} structure. The new layout
subscribes to ALL existing state bindings (page.recording,
page.timerText, transcript, error, tauriRuntimeAvailable, modelLoading,
settings.*, etc.) and reuses every existing handler:
  toggleRecording, copyAll, saveTypedText, manualExtractTasks,
  applyTemplate, invoke("open_viewer_window")

v0.2 surface unchanged — when data-design="quietware" is absent the
existing control-strip / toolbar / post-capture-card layout renders
as before.

Capture-header.

  - Record button (72 px circular) carries the role-correct
    --button-record-bg (red). Recording state pulses; modelLoading
    shows spinner; non-Tauri state neutral-disabled.
  - Status text uses Young Serif italic. Four states map to
    "Ready to capture" / "Loading model" / "Desktop preview mode"
    / "Recording…".
  - Secondary support text in Work Sans body weight, adapts per state.
  - Timer in JetBrains Mono tabular-nums, right aligned.
  - Status pill appears when useful (recording / transcribing / ready).

Slim notice.

  - Browser-preview renders LumotiaNotice tone="info" slim dismissible
    with the friendly Phase 5b copy.
  - Real errors render tone="danger" slim dismissible.
  - Slot stays empty otherwise — no permanent banner clutter.

Transcript surface.

  - Empty state: 56-px Mic icon, Young Serif italic
    "Talk now, think later.", Work Sans support "Press record, or
    Ctrl+Shift+R." Centred, low-emphasis tertiary text on the calm
    card surface.
  - Populated: scrollable transcript at the user's accessibility
    transcript-size preference.

Bottom action bar.

  - Copy (tertiary), Save (primary blue), Extract Tasks (secondary),
    Template (secondary, applies templates[0] when present), Open
    Viewer (secondary, gated on Tauri).
  - All disabled until transcript has content. Disabled controls
    render neutral with no wireline per the Phase 4k token contract.

Mono metadata footer.

  - Left: "Smart · {model} · Local only" or "Browser preview".
  - Right: "{formatMode} · {activeProfile}".
  - JetBrains Mono, text-tertiary, single line, below the action bar.

What's preserved (no behaviour change).

  - Recording events, hotkey listeners (lumotia:toggle-recording).
  - Tauri runtime branching (tauriRuntimeAvailable, browser-preview state).
  - Live preview hooks (emit("preview-listening") etc.).
  - Task extraction (manualExtractTasks -> extractTasksForTranscript).
  - Template apply (applyTemplate(template)).
  - Copy / Save paths (copyAll, saveTypedText).
  - All existing state bindings on page.*, settings.*, prefs.*.

Out of scope for this commit (queued for follow-up).

  - Files / Tasks / History page migrations (Phases 5d-5f).
  - Real Lumotia wordmark / icon integration (Phase 6).
  - Motion audit (Phase 7).
  - Full Template-picker menu (currently auto-applies templates[0]).

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.
  - Headless-Chromium screenshots confirm the new layout renders
    correctly in quietware-dark and quietware-light with the
    disabled-record state (Tauri APIs absent in browser preview),
    slim info notice, and empty-state typography.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:14:12 +01:00
b9dd598937 v0.3 Phase 4k: palette architecture hardening — scales + component tokens + contrast check
Token-hardening pass per round-9 redirect. No new visual decisions —
just plumbing the tokens correctly so the colour grammar locked in
Phase 4j has a system underneath instead of hand-tuned hex.

Scales.

  NEW: src/design-system/v0.3-quietware-scales.css. 11-step tonal
  scales (50, 100, ..., 900, 950) for five source hues and their five
  complement sources. Saturation drops at extremes to avoid neon
  highs and washed-out lows. Hue-stable lightness curve picked to
  mimic Material 3 / IBM Carbon tonal scales.

  Generated by scripts/generate-quietware-scales.py (committed) so
  values are reproducible. To regenerate:
    python3 scripts/generate-quietware-scales.py > scales.css

  Wired into src/app.css directly before v0.3-quietware-tokens.css.

Component tokens consume scale references only.

  All --button-{role}-{tier} and --progress-{state}-fill tokens now
  read from var(--{hue}-{step}) instead of raw hex literals. Future
  scale tuning happens in one place; component callers never change.

    --button-record-bg:    var(--red-600)
    --button-primary-bg:   var(--blue-700)
    --button-success-bg:   var(--green-800)   /* darkened so white
                                                  text clears AA */
    --button-caution-bg:   var(--yellow-400)
    --button-neutral-bg:   var(--color-bg-elevated)
    --button-disabled-bg:  var(--color-bg-elevated)
    --brand-accent:        var(--orange-500)

Progress component tokens.

  New family: --progress-{download,transcribing,success,caution,danger,
  disk,disk-caution,disk-danger}-fill plus --progress-track. Each
  mode tunes the shade step so the bar reads on its track:

    Dark:    blue-400 / green-500 / yellow-500 / red-500
    Light:   blue-700 / green-800 / yellow-600 / red-700
    HC-light: blue-700 / green-800 / yellow-700 / red-700
    HC-dark:  blue-300 / green-400 / yellow-400 / red-400

  LumotiaProgress.svelte gains tone="transcribing", "disk",
  "disk-caution", "disk-danger" so callers map progress role to
  token explicitly.

Border / wireline / focus-ring discipline.

  Three distinct token families:
    --button-primary-border    structural component edge
    --button-primary-wire      complementary identity detail
    --focus-ring-color         keyboard interaction

  Documented as separate concerns in the tokens file.

Contrast check script.

  NEW: scripts/check-colour-contrast.mjs. Parses scales + tokens
  CSS, builds per-scope token tables for dark / light / HC-light /
  HC-dark, resolves var() chains and color-mix() expressions, then
  verifies twelve load-bearing component pairs against WCAG 2.2
  minima (4.5:1 text, 3:1 UI). Exit code 1 on failure.

  Current state: 0 failures across 4 modes.

  Two pairs reported as "warn" rather than "fail": caution progress
  on cream + caution notice border on cream. The yellow source colour
  cannot clear 3:1 on a near-white surface without losing its
  identity. Role recognition is carried by left-bar + icon + label,
  not by surface contrast.

Verified.

  - node scripts/check-colour-contrast.mjs: 0 failures, 4 modes, 12
    load-bearing pairs + 2 known-limitation warns.
  - npm run check: 0 errors, 0 warnings across 5707 files.

Phase 5c (Dictation layout migration) is the natural next focused
sitting. This commit gives Phase 5c the systematic colour grammar to
build on, and the contrast check gates future palette regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:00:17 +01:00
2c6f5ca230 v0.3 Phase 4j: colour grammar correction — record red, brand orange separate, wirelines bumped
Major correction per round-8 redirect.

Problem.

  - Brown/copper --color-accent #C97845/#9D5F32 was dragging the UI
    back into the warm-brutalist palette and competing with semantic
    yellow.
  - Record button about to become blue (Lumotia primary). Wrong —
    record is universally red across products and OSes.
  - Brand orange conflated with primary action. Orange should be
    brand mark only.

Solution.

  Source tokens — identity-only, never used directly in components:
    --source-red    #FF0700
    --source-yellow #FFCD00
    --source-green  #00FF56
    --source-blue   #000AFF
    --source-orange-brand #F0620A
    --{role}-complement-source for the wireline layer

  Component tokens — what components subscribe to. Hand-tuned hex
  values verified at WCAG AA on the foreground each button pairs with.
  Tonal scales (red-50..red-950 etc.) reserved for a focused future
  commit; component names stay stable through that upgrade:

    --button-record-bg     #DC2626  (red)
    --button-record-fg     #FFFFFF
    --button-record-border #B91C1C
    --button-record-wire   #00D8E0  (cyan, red's complement)

    --button-primary-bg    #2563EB  (blue)
    --button-primary-fg    #FFFFFF
    --button-primary-wire  #F6E600  (yellow, blue's complement)

    --button-danger-bg     same as record (red, fg white, cyan wire)

    --button-success-bg    #15803D dark / #166534 light  (green)
    --button-success-wire  #E600A0  (magenta, green's complement)

    --button-caution-bg    #FACC15  (yellow, fg #1A1500 near-black)
    --button-caution-wire  #1850D8  (blue, yellow's complement)

    --brand-accent          #F0620A (orange, LOGO ONLY)
    --brand-wire            #0A98F0 (cyan-blue, orange's complement)

  --color-accent repointed:
    quietware dark:   #4A7BFF  (was #C97845 copper)
    quietware light:  #1D4ED8  (was #9D5F32 copper)
    HC:               #005FCC  (existing, unchanged)
    v0.2 fallback:    var(--color-accent) preserved → v0.2 stays amber.

  New LumotiaButton variant: "record". Used by DictationPage's
  record control. Existing v0.2 record button class swapped from
  bg-accent (amber) to bg-[var(--button-record-bg)] (red). v0.2
  fallback at :root maps --button-record-bg to var(--color-danger)
  so non-quietware surface keeps its red-when-recording / amber-when-
  idle behaviour.

Wirelines (renamed from counterlines).

    --wire-width 1px / --wire-width-active 2px / --wire-width-focus 2px
    --wire-opacity-dark 0.75 (was 0.32; bumped per round-8 spec)
    --wire-opacity-light 0.6 (was 0.22)
    --wire-opacity-active 0.9
    --wire-opacity-focus 1
    --wire-opacity is a mode-aware resolver (-dark dark / -light light)
    --wire-style solid (dotted reserved for design-preview / drag-drop)

  HC contract: --wire-width 0, --wire-width-focus 2px kept.
  --focus-ring-color #005FCC (strong blue), not a decorative complement.

  Legacy --counterline-* tokens kept as aliases pointing at the new
  --wire-* set so Phase 4h/4i opt-in code still works.

Design-system-v2 preview now shows the new record variant alongside
primary so both red and blue button identities are visible.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Phase 5c (Dictation layout migration) still queued for the next
focused sitting. This commit gives Phase 5c the correct grammar to
build on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:39:21 +01:00
001d8fe83d v0.3 Phase 4i: counterlines as interaction-affordance identity
Round-7 redirect (Option D): move counterlines from notices (where
they're invisible) to interactive controls (where they add identity).

Three-level model:

  Level 0  no counterline   panels, notices (default), content, disabled
  Level 1  quiet            controls, chips, active nav
                            1 px, 48% opacity dark / 34% light
  Level 2  focus / strong   keyboard focus, recording state
                            2 px, 85% opacity

Notices retire from default counterline rendering. Notice already
carries enough semantic language: bar + icon + border + bg + label.
A complementary inset on top was adding nothing. Notice can opt in
via <LumotiaNotice counterline ... /> when it deliberately mimics
control identity.

Token deltas vs Phase 4h.

  --counterline-width: 1px               unchanged
  --counterline-width-focus: 2px         new (Level-2 width)
  --counterline-opacity-dark: 0.48       was 0.32 (raised so Level-1
                                          actually reads on controls)
  --counterline-opacity-light: 0.34      was 0.22
  --counterline-opacity-focus: 0.85      new
  --role-brand-counterline: #3A6BFF      new (blue counter for the
                                          orange brand accent so
                                          primary buttons get an
                                          identity edge like the
                                          other roles)
  --button-{role}-counterline            new (per-role component tokens
                                          subscribing to role-counterline
                                          via color-mix)
  --focus-ring-color                     new (defaults to brand accent;
                                          HC overrides to #005FCC)

LumotiaButton applies counterlines to primary + destructive variants
only. Secondary + tertiary stay clean. All variants get a Level-2
focus-visible ring at --focus-ring-color * opacity-focus, with the
existing ring-offset-2 ring-offset-bg pattern.

LumotiaNotice gains a `counterline` boolean prop, default false. When
set, the previous Phase 4h inset shadow re-applies.

HC contract: counterline-width 0 + all -counterline tokens transparent.
Focus width stays 2 px because the HC focus ring IS the tactile
detail in that mode. focus-ring-color forces to #005FCC.

Honest evaluation of the visible result.

  Notices: visibly cleaner without the default inset shadow. Reads
  with more confidence. Good change.

  Buttons: counterline applied at 1 px / 48% dark / 34% light is
  perceptible at standard viewing but screenshot-zoom subtle. Focus
  state implemented but not visible in static screenshots; requires
  manual tab-test in the dev server.

  Recommend keeping the architecture even if the visual is restrained,
  because the focus-state work is genuinely valuable accessibility
  scaffolding regardless of how the Level-1 reads.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Phase 5c (Dictation layout migration) still queued for a focused
fresh sitting. This commit does not block it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:07:26 +01:00
74bcc293bb v0.3 Phase 4h: counterlines test on LumotiaNotice
Test commit for Jake's round-6 spec. Subtle 1-px inset shadow in a
softened complementary hue, applied to LumotiaNotice only. Other
test surfaces (Button.destructive, status pills, record ring, active
nav) reserved for after evaluation.

Tokens (quietware scope only, v0.2 surface unchanged):

  --counterline-width: 1px

  Raw complementary hues per Jake's table:
    --role-info-counterline    var(--color-accent)  /* copper for blue */
    --role-danger-counterline  #6EA8FF              /* soft blue for red */
    --role-success-counterline #B891FF              /* soft violet for green */
    --role-caution-counterline #3A6BFF              /* soft blue for yellow */

  Tinted per-mode via color-mix:
    Dark mode:   32% opacity (slightly stronger against dark surface)
    Light mode:  22% opacity (whisper-quiet against cream)
    HC mode:     all transparent, --counterline-width: 0

  Per-usage notice tokens emitted:
    --notice-{role}-counterline

LumotiaNotice applies via Tailwind arbitrary value:

  shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-{tone}-counterline,transparent)]

HC contract zeroes both the width and the colour token so the 2-px
HC border carries the tactile detail instead.

Honest evaluation of the visible result.

  At 1px and 22%/32% opacity the counterline is implemented
  correctly but reads as whisper-quiet to the point of being almost
  invisible at standard viewing zoom. Doesn't look noisy (the risk
  Jake flagged), but also doesn't add perceptible tactility.

  Pending Jake's call:
    (a) raise opacity to ~45% dark / ~35% light for visibility,
    (b) keep at current opacity for the quietness,
    (c) revert and proceed Phase 5c.

  Phase 5c proceeds either way; this commit does NOT block the
  layout migration.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:50:55 +01:00
2f11f493c5 v0.3 Phase 4g: four-tier semantic tokens + notice usage layer
Round-5 feedback flagged the muddy result of forcing one --color-caution
to be both bright yellow (signal) and accessible text (ochre). The
single-token-per-role model collapsed the visible identity. Caution
notices ended up reading as brown/khaki surfaces because color-mix(
in oklab, var(--color-caution) 8%, transparent) over cream produced
muddy results whatever the source hex.

New architecture: four-tier per-role tokens, hand-tuned -bg surfaces,
plus usage-specific notice tokens on top.

Tier model — each semantic role publishes four tokens:

  signal  Brand-true colour. Left bars, large icons, dots, focus rings.
          Looks like the role at a glance.
  ink     Accessible text variant. Used ONLY when text itself must
          carry the role colour. WCAG AA 4.5:1 on its surface.
  border  Middle value. Visible chip/notice outline; not text-safe.
  bg      Hand-tuned pale tint surface. NOT a color-mix derivation.

Example, light-mode caution:
  --color-caution-signal #FFCD00  (bright yellow)
  --color-caution-ink    #7A5D00  (ochre, for small text only)
  --color-caution-border #D9A900  (mid gold)
  --color-caution-bg     #FFF7D6  (clean pale yellow tint)

--color-{role} retained as a backward-compat alias pointing at -signal.
v0.2 callers keep working; new code reaches for the specific tier.

Usage layer — components subscribe to usage-specific tokens, not
role-tier tokens directly:

  --notice-{role}-bar     left bar, signal-tier
  --notice-{role}-icon    signal in dark, ink in light, signal in HC
  --notice-{role}-border  outer border colour
  --notice-{role}-bg      hand-tuned pale surface

LumotiaNotice reads these via Tailwind arbitrary-value classes
(bg-[var(--notice-info-bg)] etc). v0.2 fallback values defined at
:root (outside quietware blocks) using color-mix from v0.2 --color-
{role} tokens so the Notice keeps rendering sensibly when quietware
is off.

HC contract — every -bg token forces to transparent. HC notices
render text + icon + 2-px border + left bar, no body tint. Brand
atmosphere steps fully aside.

Per Jake's round-5 spec: yellow stays yellow as the visible signal,
ochre only appears as small-text ink, and brown/khaki never
represents the caution role's primary visual identity again. Same
discipline applies to red/blue/green — each role has a clean
hand-tuned -bg pale tint per theme.

Verified — visible result.

  Light caution notice: bright #FFCD00 bar + signal icon + ink-darkened
  small icon + pale #FFF7D6 surface + #D9A900 border. Clean,
  semantically clear, no mud.

  Dark caution notice: bright #FFCD00 bar + signal icon + dark warm
  #29210A surface + bright yellow border. Reads as yellow at a glance.

  HC modes: no body tint, strong bordered notices, semantic colours
  preserved as primary identifier alongside icon + label.

  npm run check: 0 errors, 0 warnings across 5707 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:27:46 +01:00
2fcb5aff78 v0.3 Phase 4f/5a/5b: token architecture + skeleton + browser-preview reclass
Three small phases land together for Jake's round-4 feedback. Each is
focused and reviewable. Larger Dictation/Settings/Tasks/History page
migrations are deferred to focused future sittings.

Phase 4f — semantic source / derived token architecture.

  Added --semantic-{red,blue,green,yellow}-source raw-palette tokens.
  These encode the role's hue identity from Jake's color.adobe.com
  palette (#FF0700 / #FFCD00 / #00FF56 / #000AFF) and stay constant
  across all themes. The existing --color-{danger,caution,success,info}
  tokens are now documented as theme-tuned derivations meeting WCAG
  AA in context. Source tokens make the brand-to-runtime relationship
  traceable in code.

  Added --color-{role}-bg subtle-fill tokens derived via
  color-mix(in oklab, var(--color-{role}) 8%, transparent). Used by
  Notice backgrounds, hover tints and focus halos. HC mode forces
  -bg tokens to transparent so HC notices read as text + icon +
  2-px border + left bar with no body tint.

Phase 5a — page skeleton primitive + slim notice variant.

  NEW src/lib/ui/LumotiaPageSkeleton.svelte. Six-zone layout:
    header (optional)
    notice (optional, slim ambient state)
    primary work surface (required, primaryBleed for full-bleed)
    right rail (optional, lg-screen drawer)
    action bar (optional, bottom)
    metadata (optional, mono micro-footer)
  Every zone except primary is opt-in. Foundation for the
  page-skeleton sweep without forcing any page migration yet.

  LumotiaNotice gained a slim prop. When true: padding collapses
  (px-3 py-2 vs px-4 py-3), icon shrinks (14 vs 16), title block is
  suppressed. For ambient page-level notices like browser-preview.

Phase 5b — browser-preview reclassification.

  src/lib/pages/DictationPage.svelte: the browser-preview state used
  to render as a red error block. That is an environment limitation,
  not a failed action. Reclassified as a slim info notice with
  friendlier copy:

    "You're in a browser preview. Local transcription only works in
     the Lumotia desktop app."

  Other error states (transcription failed, needs review) still
  render through the original danger-tinted block with the
  technical-details affordance preserved. Only the browser-preview
  path branches into LumotiaNotice tone="info" slim.

The larger Dictation page restructure (header recomposition,
Template/Extract Tasks demotion to bottom action bar, empty-state
typography) is reserved for a focused Phase 5c sitting since
DictationPage.svelte is 1 262 LOC of state-heavy code.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:12:33 +01:00
dab9c7b5a6 v0.3 Phase 4d/4e: palette revision + HC rendering contract
Round-4 feedback (2026-05-15) pulled the system away from the
color.adobe.com primaries toward muted-Material values, separated
brand accent from semantic caution, and reframed high-contrast as a
behavioural rendering contract rather than a palette override.

Two phases land together because the palette revision and the HC
contract are inherently linked — HC mode needs both palette overrides
AND behavioural-token overrides to deliver "different rendering
contract" semantics.

Phase 4d palette revision.

  Dark mode (was wine-aubergine #2A1620, now brown-charcoal #12100E):
    --color-bg            #12100E
    --color-bg-elevated   #1A1713
    --color-bg-card       #211D18
    --color-bg-input      #181510
    --color-sidebar       #0F0E0C
    --color-border        #4A4035
    --color-border-subtle #2C261F

  Brand accent (new — was unset, fell through to v0.2 amber that
  competed with semantic yellow):
    Dark:  --color-accent #C97845 (copper)
    Light: --color-accent #9D5F32 (deeper copper)

  Semantic colours (muted-Material, both modes):
    Dark:  danger #FF8A8A  caution #F2C94C  success #79D59B  info #8AB4F8
    Light: danger #B3261E  caution #7A5D00  success #1B6B3A  info #2457A6

  Light mode work surface (slight paper tone, not pure white):
    --color-bg-card #FFFDF8 (was #FFFFFF)
    --color-bg-elevated #F6F1EA

  Caution fill-only-on-cream rule retires. Ochre #7A5D00 passes AA
  as foreground text on cream under the muted palette, so the single-
  token-per-role convention applies across all four semantic colours
  in both modes.

  LumotiaNotice opacity rebalance:
    Background tint  10% -> 8%
    Outer border     neutral subtle -> role at 40%
    Icon             --color-text -> role colour
    Left bar         role at 100% (unchanged)
    Title / body     --color-text (unchanged)

Phase 4e HC rendering contract.

  Behavioural tokens published alongside palette:
    --grain-opacity            0.06 dark / 0.07 light / 0 in HC
    --shadow-strength          1 default / 0 in HC
    --border-width-control     1px default / 2px in HC
    --focus-ring-width         2px default / 3px in HC
    --panel-radius             8px (reserved for future tightening)

  --quietware-texture-opacity retained as a legacy alias pointing at
  --grain-opacity so Phase 4c slider keeps working through the rename.

  HC palette: pure-black or pure-white base, the other for borders,
  single strong-focus blue #005FCC replaces accent. Brand atmosphere
  steps aside. Selector splits via :not([data-theme="light"]) so
  HC-light and HC-dark variants diverge cleanly.

Verified.

  - npm run check: 0 errors, 0 warnings across 5706 files.
  - Plan doc updated with full before/after tables, behavioural-token
    contract table, and Phase 4c / 4d / 4e log sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:50:11 +01:00
4dca802f0b v0.3 Phase 4c: HC toggle + texture-intensity slider + grain overlay
Three additions land together so the slider is meaningful from day
one (instead of recording a preference that nothing consumes yet).

Static grain overlay.

  - src/design-system/v0.3-quietware-tokens.css adds a fixed-position
    body::after element that ships an SVG feTurbulence noise tile
    repeated across the viewport. Opacity reads from
    --quietware-texture-opacity. mix-blend-mode: overlay produces the
    soft notebook-paper feel without obscuring content.
  - pointer-events: none so clicks pass through. Active only under
    html[data-design="quietware"]. High-contrast forces opacity to 0
    via the existing HC palette block.

QuietwareAccessibilityControls component.

  - NEW: src/lib/components/QuietwareAccessibilityControls.svelte.
    Two controls, both quietware-only:
      1. High-contrast toggle wires html[data-contrast="high"].
         Overrides the OS prefers-contrast: more query when explicitly
         on. Built on LumotiaToggle so it inherits the accessible
         label + description pattern.
      2. Texture-intensity slider, range 0.05 to 0.08, step 0.005,
         default 0.06. Sets --quietware-texture-opacity inline on
         <html>, overriding the per-mode default in the tokens CSS.
         Disabled while high-contrast is on. Reset button restores
         0.06.
  - Persistence: localStorage keys
      lumotia:quietware:high-contrast
      lumotia:quietware:texture-opacity
    so choices survive dev rebuilds and full restarts. Full
    integration with the central Preferences store is a follow-up;
    localStorage keeps Phase 4c contained.

SettingsPage Accessibility section.

  - Imports the new QuietwareAccessibilityControls component.
  - Renders {#if isQuietware} <QuietwareAccessibilityControls />
    {/if} immediately after the existing AccessibilityControls block.
  - Search filter copy extended to cover the new control vocabulary.

Verified.

  - npm run check: 0 errors, 0 warnings across 5706 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:43:58 +01:00
091780086b v0.3 Phase 4a: Settings tab shell
Discovers that v0.2 SettingsPage.svelte was already structured as
eight numbered sections aligning almost 1:1 with the v0.3 plan. The
cheapest path to "one section visible at a time" is therefore a
tab-shell overlay rather than a file split. Behaviour stays identical
to v0.2 when quietware is off; with quietware on, only the active
tab's section renders.

Changes.

  - NEW: src/lib/ui/LumotiaSettingsTabs.svelte. Tab list following
    the WAI-ARIA "tabs (automatic activation)" pattern. Arrow / Home /
    End keyboard navigation. Active tab uses the brand left-bar
    accent (border-l-caution) — same grammar as the LumotiaNotice
    refactor from Phase 3.

  - MODIFIED: src/lib/pages/SettingsPage.svelte. Added an isQuietware
    reactive flag, a MutationObserver watching <html data-design>,
    an activeTab state, and eight {#if !isQuietware || activeTab ===
    'X'} wrappers around the existing top-level SettingsGroup
    sections. The settings tree is unchanged; only its top-level
    rendering gate changed.

  - v0.2 fallback: when data-design="quietware" is absent the
    tablist does not render and all eight sections stack as before.
    The search filter spans every section in v0.2 mode. Smallest-
    possible behavioural change for "one section at a time".

Tab IDs in this commit map 1:1 to existing v0.2 section names
(start-here / transcription / models / tasks / accessibility /
privacy / advanced / help). Phase 4b will restructure to the plan's
canonical names (carving Output from Advanced and Vocabulary from
Transcription).

Phases 4b and 4c are logged as pending in the plan doc:
  4b. Restructure to canonical tab names (Output, Vocabulary).
  4c. Texture-opacity slider + high-contrast toggle in Accessibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:22:13 +01:00
db9c50c548 v0.3 Phase 3: status grammar — LumotiaNotice left-bar refactor
Phase 3 audit found that both LumotiaStatusPill and LumotiaNotice
already shipped in v0.2 Phase 5. Phase 3 reduced to an audit-and-adapt
pass.

LumotiaStatusPill ($lib/components/StatusPill.svelte) is already
correct for quietware. Pattern: neutral pill background + neutral
label text + role colour confined to a 6x6 px dot. Colour is
supplementary, label is always perceivable. Works unchanged across
all three quietware modes. No changes shipped.

LumotiaNotice ($lib/ui/LumotiaNotice.svelte) refactored to the
left-bar accent pattern. Previously the role colour rendered on the
icon outline and on the entire surround border at 30% opacity; in
quietware light mode that left the bright #FFCD00 caution colour
faded against cream paper. New pattern:

  - 4-px solid LEFT border in the tone colour, 100% opacity.
  - Soft tone-tinted background (10% opacity) for ambient cue.
  - Subtle neutral outer border on the other three sides.
  - Icon, title and body text all use --color-text.

Visible signal lives on the large bar (works at any contrast level);
foreground text uses the always-legible neutral. Matches Material
Design "banner with leading accent" and IBM Carbon "inline
notification" guidance. ARIA semantics (role="alert" for danger,
role="status" for the rest) preserved.

Design-system-v2 preview route already imports both primitives. The
updated Notice renders correctly with the existing preview content;
no additional wiring required.

Caution remains a fill-only convention on cream by design. Any
darkened yellow reads as olive ("looks like poop", per Jake), so the
fill-only rule stays in force and this pattern delivers it cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:03:00 +01:00
a9972f76bb v0.3 Phase 2: build flag wiring
Wires VITE_LUMOTIA_QUIETWARE=1 to set <html data-design="quietware">
via a $effect in src/routes/+layout.svelte. Without the flag the
runtime app behaves exactly as v0.2; with the flag the v0.3 token
overrides in src/design-system/v0.3-quietware-tokens.css activate.

Hot-reload safety: if the env var is unset between dev rebuilds, the
attribute is cleared so devs do not see a stuck quietware surface.

Activation paths:

  VITE_LUMOTIA_QUIETWARE=1 npm run dev          (development)
  VITE_LUMOTIA_QUIETWARE=1 npm run build        (production)
  Or manually via dev tools: <html data-design="quietware">

Combine with data-theme="light" (existing v0.2 theme wiring) and
data-contrast="high" (existing v0.2 contrast wiring) to reach the
light and high-contrast quietware modes.

Smallest possible PR. No new dependencies. No bundle-size impact in
the off path. No component changes. Plan doc updated to mark Phase 2
as landed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 12:53:56 +01:00
7075c205fc v0.3 Phase 1: palette amendment to color.adobe.com primaries
Third palette iteration. Source: Jake's color.adobe.com primaries
attached to the Phase 1 review reply 2026-05-15:

    #FF0700  red     -> --color-danger
    #000AFF  blue    -> --color-info
    #00FF56  green   -> --color-success
    #FFCD00  yellow  -> --color-caution

Each primary is used literally where it clears WCAG AA on the
relevant background. Where it does not, a text-safe sibling at the
same hue and saturation is derived.

  LIGHT MODE on cream paper
    --info     #000AFF  Jake literal       7.96 AAA
    --danger   #E60600  sibling            4.51 AA
    --success  #00852D  sibling            4.51 AA
    --caution  #FFCD00  Jake literal       1.42 (fill-only by convention)

  DARK MODE on lifted Coffee Bean #2A1620
    --info     #7076FF  lifted from blue   4.64 AA
    --danger   #FF2A24  lifted from red    4.54 AA
    --success  #00FF56  Jake literal       12.51 AAA
    --caution  #FFCD00  Jake literal       11.33 AAA

  HIGH CONTRAST on black, S=80% L=70%
    --info     #757AF0  5.80 AA
    --danger   #F07975  7.70 AAA
    --success  #75F09F  14.72 AAA
    --caution  #F0D875  14.78 AAA

Caution remains a fill-only convention on cream. Any darkened yellow
reads as olive or mustard, which Jake refused at the round-2 stage
(see capture 2026-05-15-re-lumotia-v03-renders-revised-palette-).

No component changes. No behavioural changes. Phase 1 still inert
without html[data-design="quietware"]. Supersedes the cobalt-pegged
square palette in commit f718ade. Geometry geometry argument retired;
canonical brand primaries take its place.

Plan doc updated with the new palette tables and the round-3
refinement reference. Phase 1 log notes the three-iteration journey.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 12:51:38 +01:00
f718aded16 v0.3 Phase 1: Tactile Quietware tokens
Foundation for the v0.3 Tactile Quietware release. Tokens only. No
component refactors, no layout changes, no behavioural shifts. The new
palette and typography activate only when html[data-design="quietware"]
is set; the runtime v0.2 surface is unaffected without the attribute.

What landed.

  - Three-mode palette (light, dark, high-contrast) at
    src/design-system/v0.3-quietware-tokens.css. Strict 90deg square
    colour harmony pegged at cobalt blue #0047AB. Hues: 215 / 305 / 35
    / 125. Saturation locked at 100%. Lightness adjusted per role for
    WCAG AA 4.5:1 on the relevant background; computed values
    documented in the plan doc.

  - V4 font stack self-hosted as WOFF2:
        Work Sans (variable, roman + italic) for body, UI, controls.
        Young Serif for brand moments only.
        JetBrains Mono stays via existing src/fonts/jetbrains-mono.woff2.

  - Import wired in src/app.css directly after the tailwind import.
    The new tokens are inert until data-design="quietware" is set on
    <html>.

  - Reduced-motion override scoped to quietware so prefers-reduced-motion
    actually bites inside the new design system from day one.

  - Plan doc at docs/release/v0.3-tactile-quietware.md with the full
    seven-phase plan (Phase 1 logged), four high-priority persona test
    gates (Pawel, Simone, Chris, Ron), and the Phase 0 gov.uk DfE
    accessibility tools checklist.

Activation paths.

  - Manual now: dev tools, set <html data-design="quietware">.
  - Phase 2 will wire VITE_LUMOTIA_QUIETWARE=1 through +layout.svelte.

Verified.

  - npm run check produced 26 errors but all 26 are pre-existing in
    files this PR does not touch (LumotiaTooltip.svelte missing
    bits-ui import; tests/e2e/smoke.spec.ts missing playwright types).
    Tracked separately, not blocking v0.3 Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 12:15:07 +01:00