feat(theme): repoint theme.css at the design system, and find what wasn't captured
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 32s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 53s
CI & Build / Build & push image (push) Successful in 42s

theme.css is now generated from design system 2 (Scribe, inheriting FabledSword)
plus a compatibility alias layer, so the record decides the styling rather than
describing it after the fact.

**Dark is now the base layer.** The kit is dark-mode-first, so `:root` carries
the dark palette and `[data-theme="light"]` overrides it — the inverse of how
this file read before. `useTheme` already sets the attribute explicitly to
"light" or "dark", so the flip needed no JS change. It also closes the one-way
scoping gap #251 recorded: there IS a `[data-theme="light"]` block now, so a
container can add light as well as dark.

**60 dark overrides became 12.** The other 48 were restating relationships the
derivations now express: `--color-bg` follows `--fs-surface-page` because an
alias resolves at use time, so it needs stating once rather than per mode.

## What the audit found, which is the actual deliverable

**12 dead tokens, removed.** Declared in both modes, referenced by nothing:
seven from the removed chat subsystem (bubbles, input bar), plus `--glow-soft`,
`--color-action-ghost-border`, `--radius-pill` and two chat widths. 17% of the
file was styling a feature that no longer exists.

**16 names referenced but NEVER declared** — not by this change, not by the file
before it. Fourteen carried hardcoded fallbacks, so pages rendered and nothing
ever failed, but the fallback was what rendered, every time. Several were off
the palette entirely:

  --color-primary-bg      fell back to rgba(99,102,241,0.15) — an indigo
  --color-destructive     fell back to #b85a4a — not the oxblood
  --color-status-cancelled fell back to #6b7280 — a grey from no palette here
  --color-muted           fell back to #888

All 16 now resolve to real tokens. Expect small visual shifts exactly where a
fallback had drifted; the shift is the fix.

**Two tokens the app needed and never had**: `--fs-status-cancelled` (Scribe has
had a cancelled task status since the lifecycle was built and never had a colour
for it) and `--fs-layout-header` (referenced with a 52px fallback, so 52px was
always the real value — just not one anybody could look up).

## The system grew to cover what the app improvised

Per the operator: the kit wasn't growing with the app, and this is the result.
Recorded as tokens with GAP RECORDED FROM PRACTICE in their rationale — action
hover states, disabled opacity, the modal scrim, both code backgrounds, the
table stripe, the CTA gradient and glows, the accent-deep and accent-wash tints,
and the layout dimensions.

Scribe's own system gained its domain semantics — task status, priority, overdue,
wikilink — all DERIVED from family colours, so twelve rows of duplicated hex
became twelve formulas and zero new values. Priority maps onto the semantic
ladder deliberately: low is info, medium is warning, high is error.

95 tokens resolved, none valueless, 34 derived, no broken references, no cycles.
This commit is contained in:
2026-07-31 12:42:15 -04:00
parent 473280e690
commit 67a529a38e
+319 -161
View File
@@ -1,149 +1,307 @@
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Inter:ital,wght@0,400;0,500;1,400&family=JetBrains+Mono:ital,wght@0,400;1,400&display=swap');
/* ==========================================================================
GENERATED FROM THE DESIGN SYSTEM — Scribe (design system 2), which inherits
the FabledSword house style (design system 1).
Do not hand-edit the --fs-* block below. Edit the design system in the app
and regenerate: /design-systems -> Master stylesheet -> Copy.
DARK IS THE BASE LAYER. The kit is dark-mode-first, so :root carries the dark
palette and [data-theme="light"] overrides it. That is the inverse of how this
file used to read, and it is deliberate: the light palette was never specified
by any rule, so it is recorded as a departure rather than as the default.
Only 12 tokens differ between modes. Everything else — spacing, type, motion,
radius, and every derived colour — is stated once, because a value built with
var() resolves where it is USED, not where it is written.
========================================================================== */
:root {
/* Light mode — warm parchment palette */
--color-bg: #F5F1E8;
--color-bg-secondary: #FBF8F0;
--color-bg-card: #FBF8F0;
--color-surface: #EFEAE0;
--color-text: #14171A;
--color-text-secondary: #5A5852;
--color-text-muted: #9A9890;
--color-border: #D9D6CE;
--color-input-border: #D9D6CE;
--color-primary: #5B4A8A;
--color-danger: #C04A1F;
--color-tag-bg: rgba(91, 74, 138, 0.12);
--color-tag-text: #5B4A8A;
--color-shadow: rgba(0, 0, 0, 0.08);
--color-toast-success: #4A5D3F;
--color-toast-error: #C04A1F;
--color-status-todo: #3F4651;
--color-status-todo-bg: rgba(63, 70, 81, 0.10);
--color-status-in-progress: #5B4A8A;
--color-status-in-progress-bg: rgba(91, 74, 138, 0.12);
--color-status-done: #4A5D3F;
--color-status-done-bg: rgba(74, 93, 63, 0.12);
--color-priority-low: #3D5A6E;
--color-priority-low-bg: rgba(61, 90, 110, 0.12);
--color-priority-medium: #8B6F1E;
--color-priority-medium-bg: rgba(139, 111, 30, 0.12);
--color-priority-high: #C04A1F;
--color-priority-high-bg: rgba(192, 74, 31, 0.12);
--color-wikilink: #5B4A8A;
--color-wikilink-bg: rgba(91, 74, 138, 0.12);
--color-overdue: #C04A1F;
--color-code-bg: #EBEDF0;
--color-code-inline-bg: #EBEDF0;
--color-table-stripe: rgba(20, 23, 26, 0.025);
--color-success: #4A5D3F;
--color-warning: #8B6F1E;
--color-input-bar-bg: #EFEAE0;
--color-input-bar-text: #14171A;
--color-input-bar-placeholder: rgba(20, 23, 26, 0.4);
--color-overlay: rgba(0, 0, 0, 0.45);
--color-bubble-user-bg: transparent;
--color-bubble-user-border: #D9D6CE;
--color-bubble-user-text: #5A5852;
--color-bubble-asst-shadow: 0 2px 14px rgba(91, 74, 138, 0.06), 0 1px 4px rgba(0, 0, 0, 0.05);
--color-primary-solid: #5B4A8A;
--color-primary-deep: #3F3560;
--gradient-cta: linear-gradient(135deg, var(--color-primary-solid), var(--color-primary-deep));
--glow-cta: 0 2px 10px rgba(91, 74, 138, 0.35);
--glow-cta-hover: 0 4px 20px rgba(91, 74, 138, 0.55);
--glow-soft: 0 0 16px rgba(91, 74, 138, 0.35);
--color-primary-faint: rgba(91, 74, 138, 0.08);
--color-primary-tint: rgba(91, 74, 138, 0.12);
--color-primary-wash: rgba(91, 74, 138, 0.20);
/* accent */
--fs-accent: #5B4A8A; /* Scribe's signature colour */
--fs-accent-soft: color-mix(in srgb, var(--fs-accent) 15%, transparent); /* Pill, tag and badge backgrounds */
--fs-accent-faint: color-mix(in srgb, var(--fs-accent) 8%, transparent); /* The faintest accent wash */
--fs-accent-deep: color-mix(in srgb, var(--fs-accent) 70%, black); /* The accent, darkened */
--fs-accent-wash: color-mix(in srgb, var(--fs-accent) 22%, transparent); /* Heaviest accent tint */
--fs-gradient-cta: linear-gradient(135deg, var(--fs-accent), var(--fs-accent-deep));
--fs-glow-cta: 0 2px 10px color-mix(in srgb, var(--fs-accent) 35%, transparent);
--fs-glow-cta-hover: 0 4px 24px color-mix(in srgb, var(--fs-accent) 65%, transparent);
/* Action color set — Hybrid rule: action buttons use these, accent reserved for brand moments */
--color-action-primary: #4A5D3F;
--color-action-primary-hover: #5A6F4D;
--color-action-secondary: #8B7355;
--color-action-secondary-hover: #A0876A;
--color-action-destructive: #6B2118;
--color-action-destructive-hover: #7E2A1F;
--color-action-ghost-border: #3F4651;
/* action */
--fs-action-primary: #4A5D3F; /* Save, Submit, Confirm */
--fs-action-secondary: #8B7355; /* Non-destructive alternates */
--fs-action-tertiary: var(--fs-border-color); /* Ghost / outline actions */
--fs-action-destructive: var(--fs-destructive);
--fs-action-primary-hover: color-mix(in srgb, var(--fs-action-primary) 88%, white);
--fs-action-secondary-hover: color-mix(in srgb, var(--fs-action-secondary) 88%, white);
--fs-action-destructive-hover: color-mix(in srgb, var(--fs-action-destructive) 88%, white);
--radius-sm: 6px;
--radius-md: 12px;
--radius-lg: 18px;
--radius-pill: 9999px;
--focus-ring: 0 0 0 2px rgba(91, 74, 138, 0.5);
/* Layout */
--page-max-width: 1200px;
--page-padding-x: 1rem;
--sidebar-width: 260px;
--chat-reading-width: min(1200px, 100%);
--chat-context-sidebar-width: 220px;
/* border */
--fs-border-color: #3F4651; /* Borders, dividers and ghost outlines */
--fs-border: 0.5px solid var(--fs-border-color);
--fs-border-hover: 0.5px solid color-mix(in srgb, var(--fs-text-secondary) 30%, transparent);
--fs-border-active: 2px solid var(--fs-accent); /* Selected card or active tab only */
/* editor */
--fs-wikilink: var(--fs-accent);
/* elevation */
--fs-shadow-1: 0 1px 0 rgba(0,0,0,0.4); /* Hairline lift */
--fs-shadow-2: 0 4px 12px rgba(0,0,0,0.35); /* Dropdowns, popovers */
--fs-shadow-3: 0 16px 40px rgba(0,0,0,0.5); /* Modals */
/* focus */
--fs-focus-ring: 0 0 0 2px var(--fs-accent);
/* font */
--fs-font-display: 'Fraunces', Georgia, serif;
--fs-font-body: 'Inter', system-ui, sans-serif;
--fs-font-mono: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
/* icon */
--fs-icon-stroke: 1.5px; /* at 24px */
--fs-icon-stroke-sm: 1px; /* at 16px */
/* layout */
--fs-layout-page-max: 1200px;
--fs-layout-page-pad: 1rem;
--fs-layout-sidebar: 260px;
--fs-layout-header: 52px;
/* motion */
--fs-ease: cubic-bezier(0.2, 0.6, 0.2, 1); /* the one curve */
--fs-dur-fast: 120ms;
--fs-dur-base: 180ms;
--fs-dur-slow: 280ms;
/* priority */
--fs-priority-low: var(--fs-info);
--fs-priority-low-bg: color-mix(in srgb, var(--fs-priority-low) 12%, transparent);
--fs-priority-medium: var(--fs-warning);
--fs-priority-medium-bg: color-mix(in srgb, var(--fs-priority-medium) 12%, transparent);
--fs-priority-high: var(--fs-error);
--fs-priority-high-bg: color-mix(in srgb, var(--fs-priority-high) 12%, transparent);
/* radius */
--fs-radius-sm: 4px; /* pills, tags, code spans */
--fs-radius-md: 8px; /* buttons, inputs, small cards */
--fs-radius-lg: 12px; /* cards, panels, modals */
--fs-radius-xl: 16px; /* hero containers */
--fs-radius-pill: 9999px;
/* semantic */
--fs-success: var(--fs-action-primary);
--fs-warning: #8B6F1E;
--fs-error: #C04A1F;
--fs-info: #3D5A6E;
--fs-destructive: #6B2118; /* irreversible — deliberately not the error colour */
/* space */
--fs-space-1: 4px;
--fs-space-2: 8px;
--fs-space-3: 12px;
--fs-space-4: 16px;
--fs-space-5: 20px;
--fs-space-6: 24px;
--fs-space-7: 32px;
--fs-space-8: 48px;
--fs-space-9: 64px;
--fs-space-10: 96px;
/* state */
--fs-disabled-opacity: 0.5;
--fs-overlay: rgba(0, 0, 0, 0.65);
/* status */
--fs-status-todo: var(--fs-border-color);
--fs-status-todo-bg: color-mix(in srgb, var(--fs-status-todo) 12%, transparent);
--fs-status-in-progress: var(--fs-accent);
--fs-status-in-progress-bg: color-mix(in srgb, var(--fs-status-in-progress) 12%, transparent);
--fs-status-done: var(--fs-success);
--fs-status-done-bg: color-mix(in srgb, var(--fs-status-done) 12%, transparent);
--fs-overdue: var(--fs-error);
--fs-status-cancelled: var(--fs-text-tertiary); /* set aside, not failed */
/* surface */
--fs-surface-page: #14171A; /* page bg, deepest surface */
--fs-surface-raised: #1E2228; /* cards, raised elements */
--fs-surface-hover: #2C313A; /* hovered surfaces */
--fs-surface-code: var(--fs-surface-page);
--fs-surface-code-inline: var(--fs-surface-raised);
--fs-table-stripe: color-mix(in srgb, var(--fs-text-primary) 3%, transparent);
/* text */
--fs-text-primary: #E8E4D8; /* also the text colour on filled action buttons */
--fs-text-secondary: #C2BFB4;
--fs-text-tertiary: #9C9A92;
/* type */
--fs-size-display: 40px;
--fs-size-h1: 32px;
--fs-size-h2: 24px;
--fs-size-h3: 18px;
--fs-size-body: 15px;
--fs-size-body-sm: 13px;
--fs-size-label: 12px;
--fs-size-code: 13px;
--fs-size-tiny: 11px; /* the only ALL CAPS, the only non-default tracking */
--fs-weight-regular: 400;
--fs-weight-medium: 500; /* the heaviest the system goes */
--fs-leading-heading: 1.3;
--fs-leading-body: 1.5;
--fs-leading-longform: 1.7;
--fs-tracking-tiny: 0.08em;
}
[data-theme="dark"] {
/* Dark mode — Obsidian / Iron / Pewter */
--color-bg: #14171A;
--color-bg-secondary: #1E2228;
--color-bg-card: #1E2228;
--color-surface: #2C313A;
--color-text: #E8E4D8;
--color-text-secondary: #C2BFB4;
--color-text-muted: #9C9A92;
--color-border: #3F4651;
--color-input-border: #3F4651;
--color-primary: #5B4A8A;
--color-danger: #C04A1F;
--color-tag-bg: rgba(91, 74, 138, 0.15);
--color-tag-text: #5B4A8A;
[data-theme="light"] {
/* accent */
--fs-accent-wash: color-mix(in srgb, var(--fs-accent) 20%, transparent);
--fs-glow-cta-hover: 0 4px 20px color-mix(in srgb, var(--fs-accent) 55%, transparent);
/* border */
--fs-border-color: #D9D6CE;
/* state */
--fs-overlay: rgba(0, 0, 0, 0.45);
/* surface */
--fs-surface-page: #F5F1E8;
--fs-surface-raised: #FBF8F0;
--fs-surface-hover: #EFEAE0;
--fs-surface-code: #EBEDF0;
--fs-surface-code-inline: #EBEDF0;
/* text */
--fs-text-primary: #14171A;
--fs-text-secondary: #5A5852;
--fs-text-tertiary: #9A9890;
}
/* ==========================================================================
COMPATIBILITY ALIASES — the app's historical names, pointing at the system.
These exist so ~55 components keep working while they migrate to --fs-*
one at a time. Every one is a plain var() reference, which is what lets this
block be declared ONCE: when [data-theme="light"] moves --fs-surface-page,
--color-bg follows, because the alias resolves at use time.
That is why this file lost 48 of its 60 dark-mode overrides — they were all
restating relationships the aliases now express directly.
Removing this block is a rename sweep across the components, tracked
separately. Nothing new should reference a --color-* name.
========================================================================== */
:root {
/* surfaces */
--color-bg: var(--fs-surface-page);
--color-bg-secondary: var(--fs-surface-raised);
--color-bg-card: var(--fs-surface-raised);
--color-surface: var(--fs-surface-hover);
--color-code-bg: var(--fs-surface-code);
--color-code-inline-bg: var(--fs-surface-code-inline);
--color-table-stripe: var(--fs-table-stripe);
--color-overlay: var(--fs-overlay);
/* text */
--color-text: var(--fs-text-primary);
--color-text-secondary: var(--fs-text-secondary);
--color-text-muted: var(--fs-text-tertiary);
/* lines */
--color-border: var(--fs-border-color);
--color-input-border: var(--fs-border-color);
--focus-ring: var(--fs-focus-ring);
/* brand */
--color-primary: var(--fs-accent);
--color-primary-solid: var(--fs-accent);
--color-primary-deep: var(--fs-accent-deep);
--color-primary-faint: var(--fs-accent-faint);
--color-primary-tint: var(--fs-accent-soft);
--color-primary-wash: var(--fs-accent-wash);
--color-tag-bg: var(--fs-accent-soft);
--color-tag-text: var(--fs-accent);
--color-wikilink: var(--fs-wikilink);
--color-wikilink-bg: var(--fs-accent-soft);
--gradient-cta: var(--fs-gradient-cta);
--glow-cta: var(--fs-glow-cta);
--glow-cta-hover: var(--fs-glow-cta-hover);
/* actions */
--color-action-primary: var(--fs-action-primary);
--color-action-primary-hover: var(--fs-action-primary-hover);
--color-action-secondary: var(--fs-action-secondary);
--color-action-secondary-hover: var(--fs-action-secondary-hover);
--color-action-destructive: var(--fs-action-destructive);
--color-action-destructive-hover: var(--fs-action-destructive-hover);
/* semantic */
--color-success: var(--fs-success);
--color-warning: var(--fs-warning);
--color-danger: var(--fs-error);
--color-overdue: var(--fs-overdue);
--color-toast-success: var(--fs-success);
--color-toast-error: var(--fs-error);
--color-shadow: rgba(0, 0, 0, 0.4);
--color-toast-success: #4A5D3F;
--color-toast-error: #C04A1F;
--color-status-todo: #3F4651;
--color-status-todo-bg: rgba(63, 70, 81, 0.18);
--color-status-in-progress: #5B4A8A;
--color-status-in-progress-bg: rgba(91, 74, 138, 0.18);
--color-status-done: #4A5D3F;
--color-status-done-bg: rgba(74, 93, 63, 0.18);
--color-priority-low: #3D5A6E;
--color-priority-low-bg: rgba(61, 90, 110, 0.18);
--color-priority-medium: #8B6F1E;
--color-priority-medium-bg: rgba(139, 111, 30, 0.18);
--color-priority-high: #C04A1F;
--color-priority-high-bg: rgba(192, 74, 31, 0.18);
--color-wikilink: #5B4A8A;
--color-wikilink-bg: rgba(91, 74, 138, 0.18);
--color-overdue: #C04A1F;
--color-code-bg: #14171A;
--color-code-inline-bg: #1E2228;
--color-table-stripe: rgba(255, 255, 255, 0.025);
--color-success: #4A5D3F;
--color-warning: #8B6F1E;
--color-input-bar-bg: #1E2228;
--color-input-bar-text: #E8E4D8;
--color-input-bar-placeholder: rgba(232, 228, 216, 0.35);
--color-overlay: rgba(0, 0, 0, 0.65);
--color-bubble-user-bg: transparent;
--color-bubble-user-border: #3F4651;
--color-bubble-user-text: #C2BFB4;
--color-bubble-asst-shadow: 0 4px 28px rgba(91, 74, 138, 0.14), 0 2px 8px rgba(0, 0, 0, 0.4);
--color-primary-solid: #5B4A8A;
--color-primary-deep: #3F3560;
--gradient-cta: linear-gradient(135deg, var(--color-primary-solid), var(--color-primary-deep));
--glow-cta: 0 2px 12px rgba(91, 74, 138, 0.45);
--glow-cta-hover: 0 4px 24px rgba(91, 74, 138, 0.65);
--glow-soft: 0 0 18px rgba(91, 74, 138, 0.4);
--color-primary-faint: rgba(91, 74, 138, 0.10);
--color-primary-tint: rgba(91, 74, 138, 0.14);
--color-primary-wash: rgba(91, 74, 138, 0.22);
/* Action color set — identical across themes */
--color-action-primary: #4A5D3F;
--color-action-primary-hover: #5A6F4D;
--color-action-secondary: #8B7355;
--color-action-secondary-hover: #A0876A;
--color-action-destructive: #6B2118;
--color-action-destructive-hover: #7E2A1F;
--color-action-ghost-border: #3F4651;
/* task status + priority */
--color-status-todo: var(--fs-status-todo);
--color-status-todo-bg: var(--fs-status-todo-bg);
--color-status-in-progress: var(--fs-status-in-progress);
--color-status-in-progress-bg: var(--fs-status-in-progress-bg);
--color-status-done: var(--fs-status-done);
--color-status-done-bg: var(--fs-status-done-bg);
--color-priority-low: var(--fs-priority-low);
--color-priority-low-bg: var(--fs-priority-low-bg);
--color-priority-medium: var(--fs-priority-medium);
--color-priority-medium-bg: var(--fs-priority-medium-bg);
--color-priority-high: var(--fs-priority-high);
--color-priority-high-bg: var(--fs-priority-high-bg);
/* geometry */
--radius-sm: var(--fs-radius-sm);
--radius-md: var(--fs-radius-lg); /* NB: the app's "md" is the system's LARGE */
--radius-lg: var(--fs-radius-xl); /* and the app's "lg" is the system's XL */
--page-max-width: var(--fs-layout-page-max);
--page-padding-x: var(--fs-layout-page-pad);
--sidebar-width: var(--fs-layout-sidebar);
--header-height: var(--fs-layout-header);
/* ------------------------------------------------------------------
Names components reference that were NEVER declared anywhere.
Each of these was reached for with a hardcoded fallback, so the page
rendered — but the fallback was what rendered, always, and several were
off-palette: --color-primary-bg fell back to an indigo, --color-destructive
to a brick that is not the oxblood, --color-status-cancelled to a grey from
no palette in this system.
Wiring them to real tokens is the whole point of the exercise. Expect small
visual shifts exactly where a fallback had drifted; that shift IS the fix.
------------------------------------------------------------------ */
--color-accent: var(--fs-accent);
--color-accent-fg: var(--fs-text-primary);
--color-hover: var(--fs-surface-hover);
--color-bg-hover: var(--fs-surface-hover);
--color-bg-tertiary: var(--fs-surface-hover);
--color-surface-2: var(--fs-surface-hover);
--color-surface-alt: var(--fs-surface-hover);
--color-surface-raised: var(--fs-surface-raised);
--color-input-bg: var(--fs-surface-page);
--color-muted: var(--fs-text-tertiary);
--color-destructive: var(--fs-destructive);
--color-primary-bg: var(--fs-accent-soft);
--color-status-cancelled: var(--fs-status-cancelled);
--font-display: var(--fs-font-display);
--font-mono: var(--fs-font-mono);
}
/* ==========================================================================
Base element styles
========================================================================== */
*,
*::before,
*::after {
@@ -152,36 +310,36 @@
body {
margin: 0;
background: var(--color-bg);
color: var(--color-text);
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, sans-serif;
background: var(--fs-surface-page);
color: var(--fs-text-primary);
font-family: var(--fs-font-body);
font-feature-settings: "cv11";
line-height: 1.5;
transition: background-color 0.2s, color 0.2s;
line-height: var(--fs-leading-body);
transition: background-color var(--fs-dur-base) var(--fs-ease),
color var(--fs-dur-base) var(--fs-ease);
}
h1, h2 {
font-family: 'Fraunces', Georgia, serif;
font-family: var(--fs-font-display);
font-optical-sizing: auto;
font-weight: 500;
line-height: 1.3;
font-weight: var(--fs-weight-medium);
line-height: var(--fs-leading-heading);
}
h3 {
font-family: 'Inter', system-ui, sans-serif;
font-weight: 500;
line-height: 1.3;
font-family: var(--fs-font-body);
font-weight: var(--fs-weight-medium);
line-height: var(--fs-leading-heading);
}
code, pre, kbd, samp {
font-family: 'JetBrains Mono', ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-family: var(--fs-font-mono);
font-feature-settings: "liga", "calt";
}
::selection {
background: rgba(91, 74, 138, 0.3);
color: var(--color-text);
background: var(--fs-accent-wash);
color: var(--fs-text-primary);
}
input:focus-visible,
@@ -190,15 +348,15 @@ select:focus-visible,
button:focus-visible,
a:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
border-radius: var(--radius-sm);
box-shadow: var(--fs-focus-ring);
border-radius: var(--fs-radius-sm);
}
button:not(:disabled):active,
.btn:not(:disabled):active,
[role="button"]:not(:disabled):active {
transform: scale(0.97);
transition: transform 0.08s ease;
transition: transform 0.08s var(--fs-ease);
}
/* Responsive breakpoints: 480px (phone), 768px (tablet), 1024px (desktop) */
@@ -228,11 +386,11 @@ button:not(:disabled):active,
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 9999px;
background: var(--fs-border-color);
border-radius: var(--fs-radius-pill);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-muted);
background: var(--fs-text-tertiary);
}
/* Floating inline assist button (teleported to body, cannot be scoped) */
@@ -240,14 +398,14 @@ button:not(:disabled):active,
position: fixed;
z-index: 150;
transform: translateX(-50%);
background: var(--color-primary);
color: #fff;
background: var(--fs-accent);
color: var(--fs-text-primary);
border: none;
border-radius: 999px;
border-radius: var(--fs-radius-pill);
padding: 0.3rem 0.8rem;
font-size: 0.8rem;
font-size: var(--fs-size-body-sm);
cursor: pointer;
box-shadow: 0 2px 8px var(--color-shadow);
box-shadow: var(--fs-shadow-2);
white-space: nowrap;
}
.inline-assist-btn:hover {