chore: run Tailwind v4 upgrade tool (Phase 1)

- Upgraded tailwindcss v3 → v4.2.1, postcss plugin to @tailwindcss/postcss
- Deleted tailwind.config.js, migrated theme to CSS @theme block in index.css
- Replaced @tailwind directives with @import 'tailwindcss'
- Added @custom-variant dark, @utility blocks for custom utilities
- Updated class names across 128 files (shadow-sm → shadow-xs, etc.)
- Removed autoprefixer (built into v4)
- Added migration plan doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-07 20:00:38 -05:00
parent 732ccba966
commit a332a9ebab
129 changed files with 2068 additions and 1536 deletions

View File

@@ -0,0 +1,633 @@
# ResolutionFlow — Tailwind v4 Migration & Feature Guide
**Claude Code Handoff Document · Pre-Investor Pitch Sprint**
---
## Purpose
This document gives Claude Code everything needed to:
1. Execute the mechanical Tailwind v3 → v4 upgrade
2. Know which new v4 features to use going forward so the ResolutionFlow UI looks and feels premium for the investor pitch demo
## Branch
This migration touches nearly every file in the frontend. **Always work on a dedicated branch:**
```bash
git checkout -b feat/tailwind-v4-upgrade
```
Do not merge to `main` until all phases are complete and the full visual QA pass is done.
---
## Stack Context
| | |
|---|---|
| Frontend | React 19.2 + Vite 7 + TypeScript 5.9 |
| Current Tailwind | v3.4.19 |
| Component Library | shadcn/ui (target: new-york style post-upgrade) |
| Canvas Editor | React Flow (@xyflow/react) |
| Brand Color | Cyan — `#06b6d4 → #22d3ee` (hsl 187 72% 43%) |
| Background | `hsl(228 12% 7%)` — dark theme only, no light mode |
| Body Font | IBM Plex Sans |
| Heading Font | Bricolage Grotesque |
| Label/Mono Font | JetBrains Mono |
| Brand Gradient | `linear-gradient(135deg, #06b6d4 0%, #22d3ee 100%)` |
| Animation Library | tailwindcss-animate → **replace with tw-animate-css** |
| Tailwind Plugins | None |
---
---
# PART 1: Mechanical Migration
Execute phases in order. Verify the app builds and renders correctly after each phase before proceeding.
---
## Phase 1 — Update Dependencies
Run the official Tailwind upgrade tool from the `/frontend` directory. It handles the majority of migration automatically:
```bash
# Run from /frontend
npx @tailwindcss/upgrade@latest
```
This tool will:
- Update `tailwindcss` to v4
- Install `@tailwindcss/vite` (replaces the PostCSS plugin)
- Migrate `tailwind.config.js` → CSS `@theme` block
- Update `@tailwind` directives to `@import`
- Handle renamed utility classes in template files
After running, verify `package.json` reflects:
```json
// devDependencies should now include:
"tailwindcss": "^4.x.x",
"@tailwindcss/vite": "^4.x.x"
// These are no longer needed and can be removed:
// "autoprefixer" (bundled in v4 via Lightning CSS)
// "postcss" (unless used for something other than Tailwind)
```
---
## Phase 2 — Update Vite Config
Replace the PostCSS-based Tailwind setup with the first-party Vite plugin:
```ts
// vite.config.ts — BEFORE
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})
// vite.config.ts — AFTER
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
```
Remove `postcss.config.js` if it exists and was only used for Tailwind. Lightning CSS is now bundled inside Tailwind v4.
---
## Phase 3 — Migrate CSS Entry Point
Update `index.css`:
```css
/* BEFORE — v3 directives */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* AFTER — v4 single import */
@import "tailwindcss";
@import "tw-animate-css";
/* ResolutionFlow is dark-only. Replace darkMode: ["class"] from tailwind.config.js
with this custom variant declaration. This is the v4 equivalent. */
@custom-variant dark (&:where(.dark, .dark *));
/* React Flow styles must now live in CSS, not imported in App.tsx */
@layer base {
@import "@xyflow/react/dist/style.css";
}
```
> **Important:** Remove `import '@xyflow/react/dist/style.css'` from `App.tsx` after adding it here.
> **Important:** Remove `darkMode: ["class"]` from `tailwind.config.js` — it is now handled by `@custom-variant dark` above. Since ResolutionFlow is dark-only with no light mode toggle, verify that the `.dark` class is still present on the `<html>` element in `index.html`.
---
## Phase 4 — Migrate Theme Configuration
The upgrade tool migrates `tailwind.config.js` automatically, but verify the ResolutionFlow design tokens are correctly expressed. The `@theme` block lives in `index.css` after the imports.
### OKLCH Color Values
Claude Code flagged that the cyan range benefits from OKLCH for better gradient interpolation. Use these OKLCH equivalents for brand colors inside `@theme` — they render more accurately on wide-gamut displays (MacBooks, modern monitors used in pitch settings):
| Hex | OKLCH |
|---|---|
| `#06b6d4` (brand-from) | `oklch(72% 0.15 195)` |
| `#22d3ee` (brand-to) | `oklch(82% 0.13 195)` |
| `#0891b2` (brand-dark) | `oklch(62% 0.14 195)` |
### Full `@theme` Block
```css
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:where(.dark, .dark *));
@theme {
/* ResolutionFlow Brand — Cyan system (OKLCH for wide-gamut accuracy) */
--color-brand-from: oklch(72% 0.15 195); /* #06b6d4 */
--color-brand-to: oklch(82% 0.13 195); /* #22d3ee */
--color-brand-dark: oklch(62% 0.14 195); /* #0891b2 */
/* Dark surface palette */
--color-dark-DEFAULT: #101114;
--color-dark-card: #14161a;
--color-dark-surface: #14161a;
/* Text palette */
--color-text-primary: #f8fafc;
--color-text-secondary: #8891a0;
--color-text-muted: #5a6170;
/* Typography */
--font-sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
--font-heading: 'Bricolage Grotesque', system-ui, sans-serif;
--font-label: 'JetBrains Mono', monospace;
/* Border radius */
--radius-lg: 0.75rem;
--radius-md: calc(0.75rem - 2px);
--radius-sm: calc(0.75rem - 4px);
/* Keyframe animations — move ALL @keyframes from index.css into @theme */
--animate-fade-in: fade-in 200ms ease-out;
--animate-fade-in-up: fade-in-up 200ms ease-out;
--animate-slide-in-left: slide-in-from-left 200ms ease-out;
--animate-slide-in-bottom: slide-in-from-bottom 200ms ease-out;
--animate-scale-in: scale-in 150ms ease-out;
--animate-breathe-glow: breatheGlow 3s ease-in-out infinite alternate;
--animate-bell-wobble: bellWobble 0.5s ease-in-out;
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slide-in-from-left {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
@keyframes slide-in-from-bottom {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes breatheGlow {
from { box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 20px oklch(72% 0.15 195 / 0.04); }
to { box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 30px oklch(72% 0.15 195 / 0.12); }
}
@keyframes bellWobble {
0% { transform: rotate(0deg); }
20% { transform: rotate(8deg); }
40% { transform: rotate(-6deg); }
60% { transform: rotate(4deg); }
80% { transform: rotate(-2deg); }
100% { transform: rotate(0deg); }
}
}
```
> **Important:** Remove the loose `@keyframes` blocks from `index.css` after moving them into `@theme`. They should not exist in both places.
### shadcn/ui CSS Variable Bridge
Keep the existing `:root` block as-is — shadcn still uses HSL format for its variables:
```css
/* shadcn/ui CSS variable bridge — keep as-is from existing index.css */
@layer base {
:root {
--background: 228 12% 7%;
--foreground: 210 40% 98%;
--card: 220 10% 10%;
--card-foreground: 210 40% 98%;
--popover: 220 10% 10%;
--popover-foreground: 210 40% 98%;
--primary: 187 72% 43%;
--primary-foreground: 228 12% 7%;
--secondary: 220 8% 14%;
--secondary-foreground: 210 40% 98%;
--muted: 220 8% 14%;
--muted-foreground: 215 10% 58%;
--accent: 220 8% 14%;
--accent-foreground: 210 40% 98%;
--destructive: 350 81% 55%;
--destructive-foreground: 210 40% 98%;
--border: 220 8% 14%;
--input: 220 8% 14%;
--ring: 187 72% 43%;
--radius: 0.75rem;
/* ... rest of existing tokens ... */
}
}
---
## Phase 5 Replace Animation Library
```bash
# Remove old plugin
npm uninstall tailwindcss-animate
# Install replacement
npm install -D tw-animate-css
```
Remove any `@plugin 'tailwindcss-animate'` references from CSS files. The `@import "tw-animate-css"` added in Phase 3 replaces it.
---
## Phase 7 — Consolidate Hardcoded Inline Colors
Claude Code identified **128 instances of `style={{}}` inline color values across 44 files**, including atmosphere orbs in `AppLayout.tsx` using hardcoded `rgba(6,182,212,...)`. Now that brand colors are proper CSS variables in `@theme`, replace all hardcoded cyan/brand references with variables.
**Search and replace targets:**
```ts
// FIND these hardcoded patterns:
rgba(6, 182, 212, ...) // brand-from with opacity
rgba(34, 211, 238, ...) // brand-to with opacity
#06b6d4 // brand-from hex
#22d3ee // brand-to hex
#0891b2 // brand-dark hex
// REPLACE with CSS variable equivalents:
oklch(72% 0.15 195 / 0.XX) // brand-from with opacity (XX = your alpha)
oklch(82% 0.13 195 / 0.XX) // brand-to with opacity
var(--color-brand-from) // brand-from solid
var(--color-brand-to) // brand-to solid
var(--color-brand-dark) // brand-dark solid
```
**Specifically in `AppLayout.tsx`** — atmosphere orbs should become:
```tsx
// BEFORE
style={{ background: 'rgba(6, 182, 212, 0.08)' }}
// AFTER — references the theme token, respects any future brand color changes
style={{ background: 'oklch(72% 0.15 195 / 0.08)' }}
// Or better yet, move to a CSS utility class entirely:
// .atmosphere-orb { background: oklch(72% 0.15 195 / 0.08); }
```
> This is the right time to do this cleanup — the theme is already being touched, and it means future brand color changes only require updating `@theme` instead of hunting through 44 files.
---
## Phase 8 — Reinstall shadcn/ui Components
shadcn/ui components have been updated for Tailwind v4 and React 19. Commit any custom component modifications first, then reinstall to get refreshed versions.
```bash
# Re-initialize with v4 defaults
npx shadcn@latest init
# When prompted:
# Style: new-york (more refined than 'default')
# Base color: cyan (matches ResolutionFlow brand)
# Reinstall actively used components:
npx shadcn@latest add button card dialog dropdown-menu
npx shadcn@latest add badge input label select textarea
npx shadcn@latest add table tabs tooltip separator
npx shadcn@latest add sheet command popover
```
**What's new in shadcn v4 components:**
- `forwardRef` removed from all components (React 19 handles ref natively)
- `data-slot` attributes added for easier CSS targeting
- HSL colors converted to OKLCH for wider color gamut
- Dark mode colors revisited for better accessibility
- `tailwindcss-animate` deprecated in favor of `tw-animate-css`
---
## Phase 9 — Visual QA Checklist
Work through the app after Phases 16. These are the most likely regression areas:
- [ ] **Borders** — default border color changed from `gray-200` to `currentColor`. Check all card, table, and input borders against the `hsl(var(--border))` token.
- [ ] **Focus rings** — default ring changed from `3px blue` to `1px currentColor`. Verify form fields and button focus states look intentional.
- [ ] **Placeholder text** — changed from `gray-400` to `currentColor` at 50% opacity. Check all inputs.
- [ ] **Button cursors** — now `cursor:default` (browser standard). Add `cursor-pointer` explicitly where desired.
- [ ] **React Flow canvas** — verify nodes, handles, edges, and controls render correctly with CSS import moved to `@layer base`.
- [ ] **Animations** — check dialog open/close, sheet slide, dropdown appear animations after the animation library swap.
- [ ] **Keyframe animations** — verify `breatheGlow` on stat cards, `bellWobble` on notification bell, and all `fade-in-*` utilities still work after moving keyframes into `@theme`.
- [ ] **Cyan brand gradient** — verify `bg-gradient-brand` still renders correctly. Check it looks at least as good (ideally better) with OKLCH values.
- [ ] **Atmosphere orbs** — verify `AppLayout.tsx` orbs render correctly after inline style replacement with OKLCH variables.
- [ ] **Glass morphism** — verify `.glass-card` and `.glass-card-static` backdrop blur still applies on all surfaces.
- [ ] **JetBrains Mono** — verify label/code elements still use mono font correctly.
- [ ] **Sonner toasts** — verify custom toast styling (card bg, border, icon colors) still applies.
- [ ] **Dark variant** — verify `.dark` class on `<html>` is present and the `@custom-variant dark` replacement behaves identically to the old `darkMode: ["class"]` config.
---
## Commit Strategy
```bash
git checkout -b feat/tailwind-v4-upgrade
git commit -m "chore: upgrade Tailwind v3 → v4, update Vite plugin"
git commit -m "chore: migrate CSS entry point — @import, @custom-variant dark"
git commit -m "chore: migrate theme to @theme block — OKLCH colors, keyframes"
git commit -m "chore: swap tailwindcss-animate for tw-animate-css"
git commit -m "chore: consolidate 128 hardcoded inline colors to CSS variables"
git commit -m "chore: reinstall shadcn/ui components for v4 (new-york style, cyan)"
git commit -m "fix: visual QA pass — borders, rings, placeholders, glass, animations"
git commit -m "feat: apply v4 feature enhancements across components"
# When complete and QA passes:
git checkout main
git merge feat/tailwind-v4-upgrade
```
---
---
# PART 2: Tailwind v4 Feature Guide
**These are new capabilities available after the upgrade. Apply them proactively when building or modifying components — do not default to v3 patterns when a v4 equivalent is listed here.**
---
## Feature 1 — Auto-Resizing Textareas (`field-sizing-content`)
Apply to every multi-line text input in the app. Engineers typing session notes and documentation should never fight a fixed-height box.
**Where to apply:** Session runner documentation field · step notes input · FlowPilot prompt input · any multi-line input in the flow editor
```tsx
// BEFORE — v3, fixed height or JS resize logic
<textarea
className="w-full resize-none h-32 rounded-md border p-3"
/>
// AFTER — v4, grows automatically as the engineer types
<textarea
className="w-full field-sizing-content min-h-[80px] max-h-[400px]
rounded-md border border-input bg-background p-3
text-sm focus-visible:ring-2 focus-visible:ring-ring"
/>
```
---
## Feature 2 — CSS-Native Enter Animations (`@starting-style`)
Use for elements that appear in the session runner — step transitions, panel reveals, status banners. No JavaScript animation library needed for these patterns.
**Where to apply:** Step cards appearing in session runner · status/alert banners · completion state reveals · FlowPilot suggestion panels
```css
/* index.css — define the utility */
@utility fade-in-up {
transition: opacity 300ms ease, transform 300ms ease;
opacity: 1;
transform: translateY(0);
@starting-style {
opacity: 0;
transform: translateY(8px);
}
}
@utility scale-in {
transition: opacity 150ms ease, transform 150ms ease;
opacity: 1;
transform: scale(1);
@starting-style {
opacity: 0;
transform: scale(0.97);
}
}
```
```tsx
// Component usage — animates in on mount with no JS
<div className="fade-in-up">
<StepCard step={currentStep} />
</div>
<div className="scale-in">
<FlowPilotPanel />
</div>
```
---
## Feature 3 — Container Queries (`@container`)
Use for components that need to adapt based on where they are rendered — not the viewport. Critical for ResolutionFlow where the same component may appear in a sidebar, modal, or full-width panel.
**Where to apply:** StepCard (sidebar vs full-width) · FlowPilot panel (collapsed vs expanded) · documentation preview panel · step library items
```tsx
// Wrap the parent with @container
<div className="@container w-full">
<StepCard step={step} />
</div>
// Inside StepCard — responds to its container, not the viewport
function StepCard({ step }: { step: Step }) {
return (
<div className="
flex flex-col gap-2
@sm:flex-row @sm:items-center
@lg:gap-4
p-4 rounded-lg border border-border bg-card
">
<StepIcon type={step.type} />
<StepContent step={step} />
<div className="@sm:ml-auto">
<StepActions step={step} />
</div>
</div>
)
}
```
---
## Feature 4 — Enhanced Gradient APIs
The cyan brand gradient is ResolutionFlow's primary visual identity. v4 unlocks radial gradients, angle control, and OKLCH interpolation for richer rendering.
**Where to apply:** Hero/header sections · card hover states · active step indicators · FlowPilot branding elements · progress indicators
```tsx
// Linear — explicit angle control (v4)
<div className="bg-linear-135 from-[#06b6d4] to-[#22d3ee]">
// Radial — great for glow effects on active/highlighted elements (v4)
<div className="bg-radial from-[#06b6d4]/20 to-transparent">
// Radial with position — spotlight effect (v4)
<div className="bg-radial-[at_30%_50%] from-[#06b6d4]/15 via-[#22d3ee]/8 to-transparent">
// OKLCH interpolation — richer, more accurate gradient transition (v4)
<div className="bg-linear-to-r from-[#06b6d4] to-[#22d3ee] [color-interpolation-method:oklch]">
// Combine with glass card for the premium demo look
<div className="glass-card-static bg-radial-[at_top_left] from-[#06b6d4]/10 to-transparent">
```
---
## Feature 5 — Dynamic Utility Values
In v3, non-standard values required bracket notation. In v4 the scale is continuous — use values directly.
```tsx
// v3 — arbitrary brackets for anything off-scale
<div className="w-[18px] h-[18px] z-[60] grid-cols-[repeat(7,1fr)]">
// v4 — direct values work without brackets
<div className="w-4.5 h-4.5 z-60 grid-cols-7">
// Grid columns support any number directly
<div className="grid grid-cols-7"> // 7-column grid, no config needed
<div className="grid grid-cols-15"> // 15-column, works out of the box
```
---
## Feature 6 — `not-*` Variant
Style elements only when they don't match a condition. Useful in the session runner step list.
```tsx
// Bottom border on all steps except the last
<div className="not-last:border-b border-border pb-4 mb-4">
<StepCard />
</div>
// Dim inactive steps
<div className="not-[.active]:opacity-60 not-[.active]:hover:opacity-80 transition-opacity">
<StepCard />
</div>
```
---
## Feature 7 — Theme Tokens as Native CSS Variables
After the v4 migration, all `@theme` tokens are exposed as native CSS custom properties. This means you can access ResolutionFlow design tokens in TypeScript — useful for React Flow node styling and Recharts chart colors.
```ts
// Access brand colors in JS/TS — no more hardcoded hex values
const style = getComputedStyle(document.documentElement)
const brandColor = style.getPropertyValue('--color-brand-from').trim() // '#06b6d4'
// Use directly in React Flow node styles
const nodeStyles = {
background: 'var(--color-dark-card)',
border: '1px solid var(--color-brand-from)',
boxShadow: '0 0 12px rgba(6, 182, 212, 0.15)',
}
// Use in Recharts chartConfig — no more hsl() wrapper needed
const chartConfig = {
sessions: {
label: 'Sessions',
color: 'var(--color-brand-from)',
},
resolved: {
label: 'Resolved',
color: 'var(--color-brand-to)',
},
}
```
---
## Feature 8 — Color Scheme Utilities
Control native browser UI element theming (scrollbars, form controls) to match the dark theme — previously required custom CSS.
```tsx
// Apply to the root html element in index.html or App.tsx
<html className="scheme-dark">
// This makes native browser scrollbars, select dropdowns,
// date inputs, and form controls render in dark mode automatically.
// ResolutionFlow is dark-only so this should be applied globally.
```
---
## Feature 9 — `inert` Utility
Disable interaction on an entire subtree without JavaScript. Useful for locking the flow editor or step list during FlowPilot AI generation.
```tsx
// Disable the step list while FlowPilot is generating
<div className={isGenerating ? 'inert' : ''}>
<StepList steps={steps} />
</div>
// The inert attribute disables all pointer events, focus, and
// accessibility interaction on the entire subtree at once.
```
---
## General Rules for v4 Going Forward
1. **Use `field-sizing-content` on every `<textarea>`** — no exceptions
2. **Use `@starting-style` animations** instead of adding/removing CSS classes for enter effects
3. **Wrap repeated components in `@container`** when they appear in variable-width contexts
4. **Use `not-last:` instead of custom `:not(:last-child)` selectors** in lists
5. **Reference `var(--color-brand-from)` and `var(--color-brand-to)`** in JS instead of hardcoded `#06b6d4` or `rgba(6,182,212,...)`
6. **Use OKLCH for any new color values**`oklch(72% 0.15 195)` not `#06b6d4`
7. **All new keyframe animations go inside `@theme`** — not loose in `index.css`
8. **Do not use `tailwindcss-animate`** — replaced by `tw-animate-css`
9. **Do not add `@tailwind base/components/utilities` directives** — replaced by `@import "tailwindcss"`
10. **Do not add `darkMode: ["class"]` to any config** — replaced by `@custom-variant dark`
11. **Prefer `bg-linear-135` syntax** over `bg-gradient-to-r` for the brand gradient — more explicit and v4-native
---
*ResolutionFlow · Tailwind v4 Migration Doc · Pre-Investor Pitch Sprint*

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.1", "@eslint/js": "^9.39.1",
"@tailwindcss/postcss": "^4.2.1",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
@@ -50,14 +51,13 @@
"@types/react": "^19.2.5", "@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0", "globals": "^16.5.0",
"jsdom": "^28.0.0", "jsdom": "^28.0.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"tailwindcss": "^3.4.19", "tailwindcss": "^4.2.1",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"typescript-eslint": "^8.46.4", "typescript-eslint": "^8.46.4",
"vite": "^7.2.4", "vite": "^7.2.4",

View File

@@ -1,6 +1,5 @@
export default { export default {
plugins: { plugins: {
tailwindcss: {}, '@tailwindcss/postcss': {},
autoprefixer: {},
}, },
} }

View File

@@ -2,7 +2,7 @@ import { Outlet } from 'react-router-dom'
export function AccountLayout() { export function AccountLayout() {
return ( return (
<div className="container mx-auto max-w-screen-lg px-4 py-6"> <div className="container mx-auto max-w-(--breakpoint-lg) px-4 py-6">
<Outlet /> <Outlet />
</div> </div>
) )

View File

@@ -56,7 +56,7 @@ export function DeleteAccountModal({ onClose }: Props) {
required required
className={cn( className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2', 'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none' 'text-foreground focus:border-primary focus:outline-hidden'
)} )}
/> />
</div> </div>
@@ -69,7 +69,7 @@ export function DeleteAccountModal({ onClose }: Props) {
onClick={onClose} onClick={onClose}
className={cn( className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium', 'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground' 'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)} )}
> >
Cancel Cancel

View File

@@ -45,7 +45,7 @@ export function LeaveAccountModal({ accountName, onClose }: Props) {
onClick={onClose} onClick={onClose}
className={cn( className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium', 'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground' 'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)} )}
> >
Cancel Cancel

View File

@@ -60,7 +60,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
onChange={(e) => setTargetUserId(e.target.value)} onChange={(e) => setTargetUserId(e.target.value)}
className={cn( className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2', 'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none' 'text-foreground focus:border-primary focus:outline-hidden'
)} )}
> >
{nonOwnerMembers.map((m) => ( {nonOwnerMembers.map((m) => (
@@ -77,7 +77,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
required required
className={cn( className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2', 'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none' 'text-foreground focus:border-primary focus:outline-hidden'
)} )}
/> />
</div> </div>
@@ -90,7 +90,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
onClick={onClose} onClick={onClose}
className={cn( className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium', 'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground' 'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)} )}
> >
Cancel Cancel
@@ -100,7 +100,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
disabled={isSubmitting || !password} disabled={isSubmitting || !password}
className={cn( className={cn(
'rounded-[10px] px-4 py-2 text-sm font-semibold', 'rounded-[10px] px-4 py-2 text-sm font-semibold',
'bg-amber-500 text-[#101114] hover:bg-amber-400', 'bg-amber-500 text-brand-dark hover:bg-amber-400',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
> >

View File

@@ -36,7 +36,7 @@ export function AdminLayout() {
return ( return (
<div className="flex h-full"> <div className="flex h-full">
{/* Desktop sidebar */} {/* Desktop sidebar */}
<div className="hidden w-60 flex-shrink-0 border-r border-border bg-card md:block"> <div className="hidden w-60 shrink-0 border-r border-border bg-card md:block">
<AdminSidebar /> <AdminSidebar />
</div> </div>
@@ -44,7 +44,7 @@ export function AdminLayout() {
{mobileOpen && ( {mobileOpen && (
<div className="fixed inset-0 z-40 md:hidden"> <div className="fixed inset-0 z-40 md:hidden">
<div <div
className="absolute inset-0 bg-card/80 backdrop-blur-sm" className="absolute inset-0 bg-card/80 backdrop-blur-xs"
onClick={() => setMobileOpen(false)} onClick={() => setMobileOpen(false)}
/> />
<div className="absolute inset-y-0 left-0 w-60 border-r border-border bg-card shadow-xl"> <div className="absolute inset-y-0 left-0 w-60 border-r border-border bg-card shadow-xl">
@@ -63,7 +63,7 @@ export function AdminLayout() {
{/* Content */} {/* Content */}
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<div className="mx-auto max-w-screen-2xl p-6"> <div className="mx-auto max-w-(--breakpoint-2xl) p-6">
{/* Mobile menu button */} {/* Mobile menu button */}
<button <button
onClick={() => setMobileOpen(true)} onClick={() => setMobileOpen(true)}

View File

@@ -58,7 +58,7 @@ export function CreateCategoryModal({
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg"> <div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg">
{/* Header */} {/* Header */}
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
@@ -98,7 +98,7 @@ export function CreateCategoryModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />
@@ -122,7 +122,7 @@ export function CreateCategoryModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />

View File

@@ -67,7 +67,7 @@ export function EditCategoryModal({
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg"> <div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg">
{/* Header */} {/* Header */}
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
@@ -107,7 +107,7 @@ export function EditCategoryModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />
@@ -131,7 +131,7 @@ export function EditCategoryModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />

View File

@@ -48,7 +48,7 @@ export function SearchInput({ value = '', onSearch, placeholder = 'Search...', c
placeholder={placeholder} placeholder={placeholder}
className={cn( className={cn(
'h-9 w-full rounded-md border border-border bg-card pl-9 pr-8 text-sm text-foreground', 'h-9 w-full rounded-md border border-border bg-card pl-9 pr-8 text-sm text-foreground',
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20' 'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)} )}
/> />
{localValue && ( {localValue && (

View File

@@ -77,7 +77,7 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
<select <select
value={period} value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)} onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-ring" className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-ring"
> >
{PERIOD_OPTIONS.map((opt) => ( {PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}> <option key={opt.value} value={opt.value}>

View File

@@ -29,7 +29,7 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps)
className={`rounded-2xl px-4 py-3 text-[0.875rem] leading-relaxed ${ className={`rounded-2xl px-4 py-3 text-[0.875rem] leading-relaxed ${
role === 'user' role === 'user'
? 'bg-primary/15 text-foreground' ? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]' : 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`} }`}
> >
<MarkdownContent content={content} className="text-[0.875rem] leading-relaxed" /> <MarkdownContent content={content} className="text-[0.875rem] leading-relaxed" />
@@ -38,7 +38,7 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps)
{/* Suggested flows (assistant only) */} {/* Suggested flows (assistant only) */}
{role === 'assistant' && suggestedFlows && suggestedFlows.length > 0 && ( {role === 'assistant' && suggestedFlows && suggestedFlows.length > 0 && (
<div className="space-y-1.5"> <div className="space-y-1.5">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Related Flows Related Flows
</span> </span>
{suggestedFlows.map(flow => ( {suggestedFlows.map(flow => (

View File

@@ -31,7 +31,7 @@ export function ChatSidebar({
<div className="px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--glass-border)' }}> <div className="px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--glass-border)' }}>
<button <button
onClick={onNewChat} onClick={onNewChat}
className="w-full flex items-center justify-center gap-2 bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-4 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all" className="w-full flex items-center justify-center gap-2 bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-4 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
> >
<Plus size={16} /> <Plus size={16} />
New Chat New Chat
@@ -42,7 +42,7 @@ export function ChatSidebar({
<div className="flex-1 overflow-y-auto py-2"> <div className="flex-1 overflow-y-auto py-2">
{pinnedChats.length > 0 && ( {pinnedChats.length > 0 && (
<div className="px-3 mb-1"> <div className="px-3 mb-1">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Pinned Pinned
</span> </span>
</div> </div>

View File

@@ -136,7 +136,7 @@ export function ConcludeSessionModal({
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/60 backdrop-blur-sm" className="absolute inset-0 bg-black/60 backdrop-blur-xs"
onClick={onClose} onClick={onClose}
/> />
@@ -169,7 +169,7 @@ export function ConcludeSessionModal({
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="p-2 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors" className="p-2 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
> >
<X size={18} /> <X size={18} />
</button> </button>
@@ -188,7 +188,7 @@ export function ConcludeSessionModal({
'w-8 h-px', 'w-8 h-px',
step === s || (i === 1 && step === 'summary') || (i === 2 && step === 'summary') step === s || (i === 1 && step === 'summary') || (i === 2 && step === 'summary')
? 'bg-primary/40' ? 'bg-primary/40'
: 'bg-[rgba(255,255,255,0.06)]' : 'bg-brand-border'
)} )}
/> />
)} )}
@@ -196,10 +196,10 @@ export function ConcludeSessionModal({
className={cn( className={cn(
'w-6 h-6 rounded-full flex items-center justify-center text-[0.6875rem] font-label font-medium transition-colors', 'w-6 h-6 rounded-full flex items-center justify-center text-[0.6875rem] font-label font-medium transition-colors',
step === s step === s
? 'bg-gradient-brand text-[#101114]' ? 'bg-gradient-brand text-brand-dark'
: (i < ['select-outcome', 'add-notes', 'summary'].indexOf(step)) : (i < ['select-outcome', 'add-notes', 'summary'].indexOf(step))
? 'bg-primary/20 text-primary' ? 'bg-primary/20 text-primary'
: 'bg-[rgba(255,255,255,0.06)] text-muted-foreground' : 'bg-brand-border text-muted-foreground'
)} )}
> >
{i + 1} {i + 1}
@@ -233,7 +233,7 @@ export function ConcludeSessionModal({
className={cn( className={cn(
'w-full flex items-center gap-4 p-4 rounded-xl border transition-all text-left', 'w-full flex items-center gap-4 p-4 rounded-xl border transition-all text-left',
'hover:scale-[1.01] active:scale-[0.99]', 'hover:scale-[1.01] active:scale-[0.99]',
'bg-[rgba(255,255,255,0.02)] border-[rgba(255,255,255,0.06)]', 'bg-[rgba(255,255,255,0.02)] border-brand-border',
'hover:border-[rgba(255,255,255,0.12)] hover:bg-[rgba(255,255,255,0.04)]' 'hover:border-[rgba(255,255,255,0.12)] hover:bg-[rgba(255,255,255,0.04)]'
)} )}
> >
@@ -268,7 +268,7 @@ export function ConcludeSessionModal({
</div> </div>
<div> <div>
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground block mb-2"> <label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-2">
Additional Notes (optional) Additional Notes (optional)
</label> </label>
<textarea <textarea
@@ -282,7 +282,7 @@ export function ConcludeSessionModal({
: 'What still needs to be done, where you left off...' : 'What still needs to be done, where you left off...'
} }
rows={4} rows={4}
className="w-full resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-none focus:border-[rgba(6,182,212,0.3)]" className="w-full resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }} style={{ borderColor: 'var(--glass-border)' }}
/> />
</div> </div>
@@ -312,7 +312,7 @@ export function ConcludeSessionModal({
style={{ borderColor: 'var(--glass-border)' }} style={{ borderColor: 'var(--glass-border)' }}
> >
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground flex items-center gap-1.5"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Sparkles size={10} className="text-primary" /> <Sparkles size={10} className="text-primary" />
Generated Ticket Notes Generated Ticket Notes
</span> </span>
@@ -335,7 +335,7 @@ export function ConcludeSessionModal({
<div /> <div />
<button <button
onClick={onClose} onClick={onClose}
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all" className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
> >
Cancel Cancel
</button> </button>
@@ -346,14 +346,14 @@ export function ConcludeSessionModal({
<> <>
<button <button
onClick={() => setStep('select-outcome')} onClick={() => setStep('select-outcome')}
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all" className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
> >
Back Back
</button> </button>
<button <button
onClick={handleGenerate} onClick={handleGenerate}
disabled={generating} disabled={generating}
className="flex items-center gap-2 bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50" className="flex items-center gap-2 bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
> >
{generating ? ( {generating ? (
<> <>
@@ -390,7 +390,7 @@ export function ConcludeSessionModal({
'flex items-center gap-2 px-4 py-2.5 rounded-[10px] text-sm font-semibold transition-all', 'flex items-center gap-2 px-4 py-2.5 rounded-[10px] text-sm font-semibold transition-all',
copied copied
? 'bg-emerald-400/15 text-emerald-400 border border-emerald-400/30' ? 'bg-emerald-400/15 text-emerald-400 border border-emerald-400/30'
: 'bg-gradient-brand text-[#101114] hover:opacity-90 active:scale-[0.97]' : 'bg-gradient-brand text-brand-dark hover:opacity-90 active:scale-[0.97]'
)} )}
> >
{copied ? ( {copied ? (
@@ -407,7 +407,7 @@ export function ConcludeSessionModal({
</button> </button>
<button <button
onClick={onClose} onClick={onClose}
className="px-4 py-2.5 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all" className="px-4 py-2.5 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
> >
Done Done
</button> </button>

View File

@@ -87,7 +87,7 @@ export function ContextMenu({ position, items, onClose }: ContextMenuProps) {
'flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors', 'flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
item.variant === 'danger' item.variant === 'danger'
? 'text-rose-400 hover:bg-rose-500/10' ? 'text-rose-400 hover:bg-rose-500/10'
: 'text-foreground hover:bg-[rgba(255,255,255,0.06)]' : 'text-foreground hover:bg-brand-border'
)} )}
> >
{item.icon && ( {item.icon && (

View File

@@ -74,7 +74,7 @@ export function CreateFlowDropdown({
{showMenu && ( {showMenu && (
<> <>
<div className="fixed inset-0 z-10" onClick={() => setShowMenu(false)} /> <div className="fixed inset-0 z-10" onClick={() => setShowMenu(false)} />
<div className="absolute right-0 z-20 mt-1 w-64 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm"> <div className="absolute right-0 z-20 mt-1 w-64 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-xs">
{/* Troubleshooting */} {/* Troubleshooting */}
<Link <Link
to="/trees/new" to="/trees/new"

View File

@@ -125,7 +125,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
> >
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/80 backdrop-blur-sm" className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose} onClick={onClose}
aria-hidden="true" aria-hidden="true"
/> />
@@ -139,13 +139,13 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
isFullScreen isFullScreen
? 'fixed inset-4 max-w-none w-auto h-auto rounded-2xl' ? 'fixed inset-4 max-w-none w-auto h-auto rounded-2xl'
: cn( : cn(
'max-h-[100vh] rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl', 'max-h-screen rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl',
sizeClasses[size] sizeClasses[size]
) )
)} )}
> >
{/* Header - Fixed at top */} {/* Header - Fixed at top */}
<div className="flex flex-shrink-0 items-center justify-between border-b border-border px-4 py-3 sm:px-6 sm:py-4"> <div className="flex shrink-0 items-center justify-between border-b border-border px-4 py-3 sm:px-6 sm:py-4">
<h2 id="modal-title" className="text-lg font-semibold text-foreground"> <h2 id="modal-title" className="text-lg font-semibold text-foreground">
{title} {title}
</h2> </h2>
@@ -168,7 +168,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
className={cn( className={cn(
'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1', 'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1',
'hover:bg-accent hover:text-foreground', 'hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20' 'focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)} )}
aria-label="Close modal" aria-label="Close modal"
> >
@@ -184,7 +184,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
{/* Footer - Fixed at bottom */} {/* Footer - Fixed at bottom */}
{footer && ( {footer && (
<div className="flex-shrink-0 border-t border-border px-4 py-3 sm:px-6 sm:py-4"> <div className="shrink-0 border-t border-border px-4 py-3 sm:px-6 sm:py-4">
{footer} {footer}
</div> </div>
)} )}

View File

@@ -186,7 +186,7 @@ export function TagInput({
className={cn( className={cn(
'flex-1 min-w-[80px] border-0 bg-transparent px-1 py-0.5 text-sm text-foreground', 'flex-1 min-w-[80px] border-0 bg-transparent px-1 py-0.5 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:outline-none focus:ring-0' 'focus:outline-hidden focus:ring-0'
)} )}
/> />
)} )}

View File

@@ -108,7 +108,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="p-1.5 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors" className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
> >
<X size={16} /> <X size={16} />
</button> </button>
@@ -122,7 +122,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${ className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
msg.role === 'user' msg.role === 'user'
? 'bg-primary/15 text-foreground' ? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]' : 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`} }`}
> >
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" /> <MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
@@ -131,7 +131,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
))} ))}
{loading && ( {loading && (
<div className="flex justify-start"> <div className="flex justify-start">
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-xl px-3.5 py-2.5"> <div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-xl px-3.5 py-2.5">
<Loader2 size={16} className="animate-spin text-primary" /> <Loader2 size={16} className="animate-spin text-primary" />
</div> </div>
</div> </div>
@@ -140,7 +140,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
{/* Suggested flows */} {/* Suggested flows */}
{suggestedFlows.length > 0 && ( {suggestedFlows.length > 0 && (
<div className="space-y-2 pt-2"> <div className="space-y-2 pt-2">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Related Flows Related Flows
</span> </span>
{suggestedFlows.map(flow => ( {suggestedFlows.map(flow => (
@@ -162,14 +162,14 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Ask about this step..." placeholder="Ask about this step..."
rows={1} rows={1}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]" className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }} style={{ borderColor: 'var(--glass-border)' }}
disabled={loading || initializing} disabled={loading || initializing}
/> />
<button <button
onClick={handleSend} onClick={handleSend}
disabled={!input.trim() || loading || initializing} disabled={!input.trim() || loading || initializing}
className="bg-gradient-brand text-[#101114] p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40" className="bg-gradient-brand text-brand-dark p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
> >
<Send size={16} /> <Send size={16} />
</button> </button>

View File

@@ -11,7 +11,7 @@ export function CopilotToggle({ isOpen, onToggle }: CopilotToggleProps) {
return ( return (
<button <button
onClick={onToggle} onClick={onToggle}
className="fixed bottom-6 right-6 z-40 bg-gradient-brand text-[#101114] p-3.5 rounded-full shadow-lg shadow-primary/30 hover:opacity-90 active:scale-[0.97] transition-all" className="fixed bottom-6 right-6 z-40 bg-gradient-brand text-brand-dark p-3.5 rounded-full shadow-lg shadow-primary/30 hover:opacity-90 active:scale-[0.97] transition-all"
title="Open AI Copilot" title="Open AI Copilot"
> >
<MessageCircle size={22} /> <MessageCircle size={22} />

View File

@@ -21,7 +21,7 @@ export function QuickStats({ stats }: QuickStatsProps) {
className={cn('glass-card p-4 fade-in', i === 0 && 'active-glow')} className={cn('glass-card p-4 fade-in', i === 0 && 'active-glow')}
style={{ animationDelay: `${50 + i * 30}ms` }} style={{ animationDelay: `${50 + i * 30}ms` }}
> >
<p className="font-label text-[0.625rem] font-medium uppercase tracking-[0.1em] text-muted-foreground"> <p className="font-label text-[0.625rem] font-medium uppercase tracking-widest text-muted-foreground">
{stat.label} {stat.label}
</p> </p>
<p <p

View File

@@ -56,7 +56,7 @@ export function WeeklyCalendar({ events = {} }: WeeklyCalendarProps) {
borderBottom: day.isToday ? '2px solid #06b6d4' : '1px solid var(--glass-border)', borderBottom: day.isToday ? '2px solid #06b6d4' : '1px solid var(--glass-border)',
}} }}
> >
<span className={`font-label text-[0.625rem] uppercase tracking-[0.1em] ${day.isToday ? 'text-cyan-400' : 'text-muted-foreground'}`}> <span className={`font-label text-[0.625rem] uppercase tracking-widest ${day.isToday ? 'text-cyan-400' : 'text-muted-foreground'}`}>
{day.label} {day.label}
</span> </span>
<div className={`text-sm font-heading font-bold ${day.isToday ? 'text-foreground' : 'text-muted-foreground'}`}> <div className={`text-sm font-heading font-bold ${day.isToday ? 'text-foreground' : 'text-muted-foreground'}`}>

View File

@@ -45,7 +45,7 @@ export function AIPromptDialog({
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/60 backdrop-blur-sm" className="absolute inset-0 bg-black/60 backdrop-blur-xs"
onClick={() => !isGenerating && onClose()} onClick={() => !isGenerating && onClose()}
/> />
@@ -68,7 +68,7 @@ export function AIPromptDialog({
placeholder={`Example: "A flow for troubleshooting VPN connectivity issues when users can't connect to the corporate network"`} placeholder={`Example: "A flow for troubleshooting VPN connectivity issues when users can't connect to the corporate network"`}
rows={4} rows={4}
disabled={isGenerating} disabled={isGenerating}
className="w-full rounded-xl border border-border bg-background px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none disabled:opacity-50" className="w-full rounded-xl border border-border bg-background px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden resize-none disabled:opacity-50"
autoFocus autoFocus
/> />
@@ -83,14 +83,14 @@ export function AIPromptDialog({
<button <button
onClick={onClose} onClick={onClose}
disabled={isGenerating} disabled={isGenerating}
className="rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 text-sm text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50" className="rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-brand-border px-4 py-2 text-sm text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
> >
Cancel Cancel
</button> </button>
<button <button
onClick={handleGenerate} onClick={handleGenerate}
disabled={!prompt.trim() || isGenerating} disabled={!prompt.trim() || isGenerating}
className="flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50" className="flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
> >
{isGenerating ? ( {isGenerating ? (
<> <>

View File

@@ -50,7 +50,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${ className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
msg.role === 'user' msg.role === 'user'
? 'bg-primary/15 text-foreground' ? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]' : 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`} }`}
> >
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" /> <MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
@@ -59,7 +59,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
))} ))}
{isLoading && ( {isLoading && (
<div className="flex justify-start"> <div className="flex justify-start">
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-xl px-3.5 py-2.5"> <div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-xl px-3.5 py-2.5">
<Loader2 size={16} className="animate-spin text-primary" /> <Loader2 size={16} className="animate-spin text-primary" />
</div> </div>
</div> </div>
@@ -77,14 +77,14 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Ask AI to help..." placeholder="Ask AI to help..."
rows={1} rows={1}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]" className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }} style={{ borderColor: 'var(--glass-border)' }}
disabled={isLoading} disabled={isLoading}
/> />
<button <button
onClick={onSend} onClick={onSend}
disabled={!input.trim() || isLoading} disabled={!input.trim() || isLoading}
className="bg-gradient-brand text-[#101114] p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40" className="bg-gradient-brand text-brand-dark p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
> >
<Send size={16} /> <Send size={16} />
</button> </button>

View File

@@ -65,7 +65,7 @@ export function EditorAIPanel({
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="p-1.5 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors" className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
> >
<X size={16} /> <X size={16} />
</button> </button>

View File

@@ -29,7 +29,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
{flowName || 'Untitled Flow'} {flowName || 'Untitled Flow'}
</span> </span>
</div> </div>
<div className="mt-1 flex items-center gap-3 font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <div className="mt-1 flex items-center gap-3 font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
<span>{flowType || 'flow'}</span> <span>{flowType || 'flow'}</span>
{nodeCount !== undefined && <span>{nodeCount} nodes</span>} {nodeCount !== undefined && <span>{nodeCount} nodes</span>}
</div> </div>
@@ -44,7 +44,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
<div className="border-b px-3 py-2.5" style={{ borderColor: 'var(--glass-border)' }}> <div className="border-b px-3 py-2.5" style={{ borderColor: 'var(--glass-border)' }}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Icon className={`h-3.5 w-3.5 ${colorClass}`} /> <Icon className={`h-3.5 w-3.5 ${colorClass}`} />
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{node.type} {node.type}
</span> </span>
</div> </div>

View File

@@ -28,7 +28,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
return ( return (
<div key={s.id} className="rounded-lg border border-border bg-[rgba(255,255,255,0.02)] px-3 py-2"> <div key={s.id} className="rounded-lg border border-border bg-[rgba(255,255,255,0.02)] px-3 py-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{s.action_type.replace(/_/g, ' ')} {s.action_type.replace(/_/g, ' ')}
</span> </span>
<span className={`flex items-center gap-1 text-xs ${config.color}`}> <span className={`flex items-center gap-1 text-xs ${config.color}`}>
@@ -39,7 +39,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
{s.target_node_id && ( {s.target_node_id && (
<p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p> <p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p>
)} )}
<p className="mt-0.5 font-label text-[0.625rem] text-[#5a6170]"> <p className="mt-0.5 font-label text-[0.625rem] text-brand-text-muted">
{new Date(s.created_at).toLocaleDateString()} {new Date(s.created_at).toLocaleDateString()}
</p> </p>
</div> </div>

View File

@@ -24,7 +24,7 @@ export function GuideCard({ guide }: GuideCardProps) {
<p className="text-xs text-muted-foreground leading-relaxed"> <p className="text-xs text-muted-foreground leading-relaxed">
{guide.summary} {guide.summary}
</p> </p>
<span className="mt-2 inline-block font-label text-[0.625rem] uppercase tracking-[0.1em] text-primary"> <span className="mt-2 inline-block font-label text-[0.625rem] uppercase tracking-widest text-primary">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'} {guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span> </span>
</div> </div>

View File

@@ -87,7 +87,7 @@ export function AppLayout() {
}} }}
/> />
<div className={cn('app-shell relative z-[1]', sidebarCollapsed && 'app-shell--collapsed')}> <div className={cn('app-shell relative z-1', sidebarCollapsed && 'app-shell--collapsed')}>
{/* Top Bar - spans full width */} {/* Top Bar - spans full width */}
<TopBar /> <TopBar />
@@ -109,7 +109,7 @@ export function AppLayout() {
{mobileMenuOpen && ( {mobileMenuOpen && (
<div className="fixed inset-0 z-50 md:hidden"> <div className="fixed inset-0 z-50 md:hidden">
<div <div
className="absolute inset-0 bg-black/80 backdrop-blur-sm animate-fade-in" className="absolute inset-0 bg-black/80 backdrop-blur-xs animate-fade-in"
onClick={() => setMobileMenuOpen(false)} onClick={() => setMobileMenuOpen(false)}
aria-hidden="true" aria-hidden="true"
/> />

View File

@@ -124,10 +124,10 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
if (!open) return null if (!open) return null
return ( return (
<div className="fixed inset-0 z-[100] flex items-start justify-center pt-[20vh]"> <div className="fixed inset-0 z-100 flex items-start justify-center pt-[20vh]">
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/60 backdrop-blur-sm animate-fade-in" className="absolute inset-0 bg-black/60 backdrop-blur-xs animate-fade-in"
onClick={onClose} onClick={onClose}
/> />
@@ -143,7 +143,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
onChange={e => { setQuery(e.target.value); setSelectedIndex(0) }} onChange={e => { setQuery(e.target.value); setSelectedIndex(0) }}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Search flows, sessions…" placeholder="Search flows, sessions…"
className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none" className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-hidden"
/> />
<kbd className="rounded border border-border bg-background px-1.5 py-0.5 font-label text-[0.625rem] text-muted-foreground"> <kbd className="rounded border border-border bg-background px-1.5 py-0.5 font-label text-[0.625rem] text-muted-foreground">
ESC ESC

View File

@@ -33,7 +33,7 @@ export function EmailVerificationBanner() {
return ( return (
<div className="flex items-center gap-3 border-b border-amber-400/20 bg-amber-400/5 px-4 py-2 text-sm"> <div className="flex items-center gap-3 border-b border-amber-400/20 bg-amber-400/5 px-4 py-2 text-sm">
<AlertTriangle className="h-4 w-4 flex-shrink-0 text-amber-400" /> <AlertTriangle className="h-4 w-4 shrink-0 text-amber-400" />
<span className="text-amber-200"> <span className="text-amber-200">
Your email is not verified. Your email is not verified.
</span> </span>

View File

@@ -70,8 +70,8 @@ export function QuickLaunch({ open, onClose }: QuickLaunchProps) {
if (!open) return null if (!open) return null
return ( return (
<div className="fixed inset-0 z-[100] flex items-start justify-center pt-[15vh]"> <div className="fixed inset-0 z-100 flex items-start justify-center pt-[15vh]">
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm animate-fade-in" onClick={onClose} /> <div className="absolute inset-0 bg-black/60 backdrop-blur-xs animate-fade-in" onClick={onClose} />
<div ref={containerRef} className="relative w-full max-w-md rounded-xl border border-border bg-card shadow-2xl animate-scale-in"> <div ref={containerRef} className="relative w-full max-w-md rounded-xl border border-border bg-card shadow-2xl animate-scale-in">
<div className="flex items-center justify-between border-b border-border px-4 py-3"> <div className="flex items-center justify-between border-b border-border px-4 py-3">
<h3 className="text-sm font-heading font-semibold text-foreground">Quick Launch</h3> <h3 className="text-sm font-heading font-semibold text-foreground">Quick Launch</h3>

View File

@@ -102,7 +102,7 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
<div <div
className={cn( className={cn(
'absolute right-0 top-full z-20 mt-1 w-48 rounded-md border border-border', 'absolute right-0 top-full z-20 mt-1 w-48 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg' 'bg-card backdrop-blur-xs py-1 shadow-lg'
)} )}
> >
{isLoading ? ( {isLoading ? (

View File

@@ -174,7 +174,7 @@ export function FolderEditModal({
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */} {/* Backdrop */}
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} /> <div className="absolute inset-0 bg-black/80 backdrop-blur-xs" onClick={onClose} />
{/* Modal */} {/* Modal */}
<div className="relative z-10 w-full max-w-md bg-card border border-border rounded-2xl p-6 shadow-lg"> <div className="relative z-10 w-full max-w-md bg-card border border-border rounded-2xl p-6 shadow-lg">
@@ -202,7 +202,7 @@ export function FolderEditModal({
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'border-border' 'border-border'
)} )}
autoFocus autoFocus
@@ -221,7 +221,7 @@ export function FolderEditModal({
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground', 'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'border-border' 'border-border'
)} )}
> >

View File

@@ -162,7 +162,7 @@ function FolderItem({
<div <div
className={cn( className={cn(
'absolute right-0 top-full z-10 mt-1 w-40 rounded-md border border-border', 'absolute right-0 top-full z-10 mt-1 w-40 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg' 'bg-card backdrop-blur-xs py-1 shadow-lg'
)} )}
> >
<button <button
@@ -362,7 +362,7 @@ export function FolderSidebar({
{/* Mobile backdrop */} {/* Mobile backdrop */}
{mobileOpen && ( {mobileOpen && (
<div <div
className="fixed inset-0 z-40 bg-black/80 backdrop-blur-sm md:hidden" className="fixed inset-0 z-40 bg-black/80 backdrop-blur-xs md:hidden"
onClick={onMobileClose} onClick={onMobileClose}
aria-hidden="true" aria-hidden="true"
/> />
@@ -461,7 +461,7 @@ export function FolderSidebar({
<div <div
className={cn( className={cn(
'fixed z-50 w-44 rounded-md border border-border', 'fixed z-50 w-44 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg' 'bg-card backdrop-blur-xs py-1 shadow-lg'
)} )}
style={{ left: contextMenu.x, top: contextMenu.y }} style={{ left: contextMenu.x, top: contextMenu.y }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}

View File

@@ -87,7 +87,7 @@ export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) {
maxLength={255} maxLength={255}
className={cn( className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -105,7 +105,7 @@ export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) {
placeholder="e.g. customizing for a specific client…" placeholder="e.g. customizing for a specific client…"
className={cn( className={cn(
'w-full resize-none rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full resize-none rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>

View File

@@ -143,7 +143,7 @@ export function ImportFlowModal({ onClose }: ImportFlowModalProps) {
/> />
{parseError && ( {parseError && (
<div className="flex items-start gap-2 rounded-lg border border-rose-500/20 bg-rose-500/5 px-3 py-2"> <div className="flex items-start gap-2 rounded-lg border border-rose-500/20 bg-rose-500/5 px-3 py-2">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-rose-400" /> <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-rose-400" />
<p className="text-xs text-rose-400">{parseError}</p> <p className="text-xs text-rose-400">{parseError}</p>
</div> </div>
)} )}
@@ -165,7 +165,7 @@ export function ImportFlowModal({ onClose }: ImportFlowModalProps) {
maxLength={255} maxLength={255}
className={cn( className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>

View File

@@ -114,7 +114,7 @@ export function ShareTreeModal({ tree, isOpen, onClose }: ShareTreeModalProps) {
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/80 backdrop-blur-sm" className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose} onClick={onClose}
/> />
@@ -221,7 +221,7 @@ export function ShareTreeModal({ tree, isOpen, onClose }: ShareTreeModalProps) {
type="text" type="text"
value={activeShare.share_url} value={activeShare.share_url}
readOnly readOnly
className="flex-1 bg-transparent text-sm text-foreground outline-none" className="flex-1 bg-transparent text-sm text-foreground outline-hidden"
/> />
<button <button
onClick={handleCopyLink} onClick={handleCopyLink}

View File

@@ -30,7 +30,7 @@ export function SortDropdown({ value, onChange, className }: SortDropdownProps)
onChange={(e) => onChange(e.target.value as SortBy)} onChange={(e) => onChange(e.target.value as SortBy)}
className={cn( className={cn(
'rounded-md border border-border bg-card px-3 py-1.5 text-sm', 'rounded-md border border-border bg-card px-3 py-1.5 text-sm',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
> >
{sortOptions.map((option) => ( {sortOptions.map((option) => (

View File

@@ -37,20 +37,20 @@ export function TreeListView({
{trees.map((tree) => ( {trees.map((tree) => (
<div <div
key={tree.id} key={tree.id}
className="flex items-center gap-4 bg-card border border-border rounded-2xl p-4 transition-all hover:border-primary/30 hover:shadow-sm" className="flex items-center gap-4 bg-card border border-border rounded-2xl p-4 transition-all hover:border-primary/30 hover:shadow-xs"
> >
{/* Left: Name and Description */} {/* Left: Name and Description */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-1">
<h3 className="font-semibold text-foreground truncate">{tree.name}</h3> <h3 className="font-semibold text-foreground truncate">{tree.name}</h3>
{tree.status === 'draft' && ( {tree.status === 'draft' && (
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 flex-shrink-0"> <span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 shrink-0">
<FileText className="h-3 w-3" /> <FileText className="h-3 w-3" />
Draft Draft
</span> </span>
)} )}
{tree.tree_type === 'maintenance' && ( {tree.tree_type === 'maintenance' && (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 flex-shrink-0"> <span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 shrink-0">
<Wrench className="h-3 w-3" /> <Wrench className="h-3 w-3" />
Maintenance Maintenance
</span> </span>
@@ -62,11 +62,11 @@ export function TreeListView({
)} )}
{tree.is_public ? ( {tree.is_public ? (
<span title="Public tree"> <span title="Public tree">
<Globe className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" /> <Globe className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span> </span>
) : ( ) : (
<span title="Private tree"> <span title="Private tree">
<Lock className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" /> <Lock className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span> </span>
)} )}
</div> </div>
@@ -90,7 +90,7 @@ export function TreeListView({
</div> </div>
{/* Right: Metadata and Actions */} {/* Right: Metadata and Actions */}
<div className="flex items-center gap-3 flex-shrink-0"> <div className="flex items-center gap-3 shrink-0">
<div className="hidden sm:flex flex-col items-end text-xs text-muted-foreground"> <div className="hidden sm:flex flex-col items-end text-xs text-muted-foreground">
<span>v{tree.version}</span> <span>v{tree.version}</span>
<span>{tree.usage_count} uses</span> <span>{tree.usage_count} uses</span>

View File

@@ -173,13 +173,13 @@ export function TreeTableView({
{tree.name} {tree.name}
</span> </span>
{tree.status === 'draft' && ( {tree.status === 'draft' && (
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 flex-shrink-0"> <span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 shrink-0">
<FileText className="h-3 w-3" /> <FileText className="h-3 w-3" />
Draft Draft
</span> </span>
)} )}
{tree.tree_type === 'maintenance' && ( {tree.tree_type === 'maintenance' && (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 flex-shrink-0"> <span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 shrink-0">
<Wrench className="h-3 w-3" /> <Wrench className="h-3 w-3" />
Maintenance Maintenance
</span> </span>
@@ -191,11 +191,11 @@ export function TreeTableView({
)} )}
{tree.is_public ? ( {tree.is_public ? (
<span title="Public tree"> <span title="Public tree">
<Globe className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" /> <Globe className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span> </span>
) : ( ) : (
<span title="Private tree"> <span title="Private tree">
<Lock className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" /> <Lock className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span> </span>
)} )}
</div> </div>

View File

@@ -76,7 +76,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
] ]
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs">
<div className="w-full max-w-lg rounded-xl border border-border bg-card shadow-2xl"> <div className="w-full max-w-lg rounded-xl border border-border bg-card shadow-2xl">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4"> <div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -116,7 +116,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
Server names (one per line) Server names (one per line)
</label> </label>
<textarea <textarea
className="h-40 w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="h-40 w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
placeholder={"RDS-01\nRDS-02\nRDS-03"} placeholder={"RDS-01\nRDS-02\nRDS-03"}
value={manualInput} value={manualInput}
onChange={e => setManualInput(e.target.value)} onChange={e => setManualInput(e.target.value)}

View File

@@ -132,7 +132,7 @@ export function BatchStatusCard({ session, targetLabel, treeId, batchId }: Batch
{isNotStarted && ( {isNotStarted && (
<button <button
onClick={handleStart} onClick={handleStart}
className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-3 py-1.5 text-[0.8125rem] font-medium text-white shadow-sm shadow-primary/20 hover:opacity-90 transition-opacity" className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-3 py-1.5 text-[0.8125rem] font-medium text-white shadow-xs shadow-primary/20 hover:opacity-90 transition-opacity"
> >
<Play className="h-3.5 w-3.5" /> <Play className="h-3.5 w-3.5" />
Start Start

View File

@@ -36,13 +36,13 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.label} value={field.label}
onChange={(e) => onUpdate({ label: e.target.value })} onChange={(e) => onUpdate({ label: e.target.value })}
placeholder="Field label" placeholder="Field label"
className="min-w-0 flex-1 rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="min-w-0 flex-1 rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
<select <select
value={field.field_type} value={field.field_type}
onChange={(e) => onUpdate({ field_type: e.target.value as IntakeFieldType })} onChange={(e) => onUpdate({ field_type: e.target.value as IntakeFieldType })}
className="rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
{FIELD_TYPE_OPTIONS.map((opt) => ( {FIELD_TYPE_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option> <option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -84,7 +84,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.variable_name} value={field.variable_name}
onChange={(e) => onUpdate({ variable_name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '') })} onChange={(e) => onUpdate({ variable_name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '') })}
placeholder="e.g. server_name" placeholder="e.g. server_name"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
<p className="mt-0.5 text-[10px] text-muted-foreground">Used as [VAR:{field.variable_name}]</p> <p className="mt-0.5 text-[10px] text-muted-foreground">Used as [VAR:{field.variable_name}]</p>
</div> </div>
@@ -96,7 +96,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.placeholder || ''} value={field.placeholder || ''}
onChange={(e) => onUpdate({ placeholder: e.target.value || undefined })} onChange={(e) => onUpdate({ placeholder: e.target.value || undefined })}
placeholder="Hint text" placeholder="Hint text"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -107,7 +107,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.help_text || ''} value={field.help_text || ''}
onChange={(e) => onUpdate({ help_text: e.target.value || undefined })} onChange={(e) => onUpdate({ help_text: e.target.value || undefined })}
placeholder="Description or instructions" placeholder="Description or instructions"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -118,7 +118,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.default_value || ''} value={field.default_value || ''}
onChange={(e) => onUpdate({ default_value: e.target.value || undefined })} onChange={(e) => onUpdate({ default_value: e.target.value || undefined })}
placeholder="Pre-filled value" placeholder="Pre-filled value"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -129,7 +129,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.group_name || ''} value={field.group_name || ''}
onChange={(e) => onUpdate({ group_name: e.target.value || undefined })} onChange={(e) => onUpdate({ group_name: e.target.value || undefined })}
placeholder="e.g. Network Settings" placeholder="e.g. Network Settings"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -144,7 +144,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
}} }}
placeholder="Option 1&#10;Option 2&#10;Option 3" placeholder="Option 1&#10;Option 2&#10;Option 3"
rows={3} rows={3}
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
)} )}

View File

@@ -18,7 +18,7 @@ export function IntakeFormBuilder() {
</div> </div>
{intakeForm.length === 0 ? ( {intakeForm.length === 0 ? (
<div className="rounded-lg border border-dashed border-border bg-white/[0.02] py-8 text-center"> <div className="rounded-lg border border-dashed border-border bg-white/2 py-8 text-center">
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" /> <FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<p className="text-sm text-muted-foreground">No intake form fields yet</p> <p className="text-sm text-muted-foreground">No intake form fields yet</p>
<p className="mt-1 text-xs text-muted-foreground"> <p className="mt-1 text-xs text-muted-foreground">

View File

@@ -154,7 +154,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select <select
value={frequency} value={frequency}
onChange={(e) => setFrequency(e.target.value)} onChange={(e) => setFrequency(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
{FREQUENCY_OPTIONS.map(opt => ( {FREQUENCY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option> <option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -172,7 +172,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
max={23} max={23}
value={hour} value={hour}
onChange={(e) => setHour(Number(e.target.value))} onChange={(e) => setHour(Number(e.target.value))}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
<div> <div>
@@ -183,7 +183,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
max={59} max={59}
value={minute} value={minute}
onChange={(e) => setMinute(Number(e.target.value))} onChange={(e) => setMinute(Number(e.target.value))}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
</div> </div>
@@ -194,7 +194,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select <select
value={timezone} value={timezone}
onChange={(e) => setTimezone(e.target.value)} onChange={(e) => setTimezone(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
{TIMEZONE_OPTIONS.map(tz => ( {TIMEZONE_OPTIONS.map(tz => (
<option key={tz} value={tz}>{tz}</option> <option key={tz} value={tz}>{tz}</option>
@@ -209,7 +209,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select <select
value={selectedTargetListId} value={selectedTargetListId}
onChange={(e) => setSelectedTargetListId(e.target.value)} onChange={(e) => setSelectedTargetListId(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
<option value="">None manual targets only</option> <option value="">None manual targets only</option>
{targetLists.map(tl => ( {targetLists.map(tl => (

View File

@@ -41,7 +41,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.title} value={step.title}
onChange={(e) => onUpdate({ title: e.target.value })} onChange={(e) => onUpdate({ title: e.target.value })}
placeholder="Section title" placeholder="Section title"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
</div> </div>
@@ -74,7 +74,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
type="text" type="text"
value={step.title} value={step.title}
onChange={(e) => onUpdate({ title: e.target.value })} onChange={(e) => onUpdate({ title: e.target.value })}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -90,7 +90,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ estimated_minutes: e.target.value ? parseInt(e.target.value) : undefined })} onChange={(e) => onUpdate({ estimated_minutes: e.target.value ? parseInt(e.target.value) : undefined })}
placeholder="—" placeholder="—"
min={1} min={1}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -102,7 +102,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ description: e.target.value })} onChange={(e) => onUpdate({ description: e.target.value })}
placeholder="Step instructions. Use [VAR:name] for variables." placeholder="Step instructions. Use [VAR:name] for variables."
rows={4} rows={4}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
{availableVariables.length > 0 && ( {availableVariables.length > 0 && (
<div className="mt-1 flex flex-wrap gap-1"> <div className="mt-1 flex flex-wrap gap-1">
@@ -131,7 +131,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ commands: e.target.value || undefined })} onChange={(e) => onUpdate({ commands: e.target.value || undefined })}
placeholder="Install-WindowsFeature AD-Domain-Services -IncludeManagementTools" placeholder="Install-WindowsFeature AD-Domain-Services -IncludeManagementTools"
rows={3} rows={3}
className="w-full rounded border border-border bg-card px-3 py-2 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -181,7 +181,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ warning_text: e.target.value || undefined })} onChange={(e) => onUpdate({ warning_text: e.target.value || undefined })}
placeholder="Caution: This will restart the service..." placeholder="Caution: This will restart the service..."
rows={2} rows={2}
className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-yellow-400/30 focus:outline-none focus:ring-1 focus:ring-yellow-400/20" className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-yellow-400/30 focus:outline-hidden focus:ring-1 focus:ring-yellow-400/20"
/> />
</div> </div>
)} )}
@@ -194,7 +194,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.expected_outcome || ''} value={step.expected_outcome || ''}
onChange={(e) => onUpdate({ expected_outcome: e.target.value || undefined })} onChange={(e) => onUpdate({ expected_outcome: e.target.value || undefined })}
placeholder="Server should respond with..." placeholder="Server should respond with..."
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -210,7 +210,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.verification_prompt || ''} value={step.verification_prompt || ''}
onChange={(e) => onUpdate({ verification_prompt: e.target.value || undefined })} onChange={(e) => onUpdate({ verification_prompt: e.target.value || undefined })}
placeholder="Confirm the role was installed" placeholder="Confirm the role was installed"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
<div> <div>
@@ -218,7 +218,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
<select <select
value={step.verification_type || ''} value={step.verification_type || ''}
onChange={(e) => onUpdate({ verification_type: e.target.value as 'checkbox' | 'text_input' || undefined })} onChange={(e) => onUpdate({ verification_type: e.target.value as 'checkbox' | 'text_input' || undefined })}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
<option value="">None</option> <option value="">None</option>
<option value="checkbox">Checkbox (confirm done)</option> <option value="checkbox">Checkbox (confirm done)</option>
@@ -239,7 +239,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.reference_url || ''} value={step.reference_url || ''}
onChange={(e) => onUpdate({ reference_url: e.target.value || undefined })} onChange={(e) => onUpdate({ reference_url: e.target.value || undefined })}
placeholder="https://learn.microsoft.com/..." placeholder="https://learn.microsoft.com/..."
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
<div className="flex items-end pb-1"> <div className="flex items-end pb-1">
@@ -266,7 +266,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ onChange={(e) => onUpdate({
library_visibility: e.target.value === '' ? undefined : e.target.value as 'team' | 'public' library_visibility: e.target.value === '' ? undefined : e.target.value as 'team' | 'public'
})} })}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
<option value="">Inherit from flow</option> <option value="">Inherit from flow</option>
<option value="team">Team only</option> <option value="team">Team only</option>

View File

@@ -145,7 +145,7 @@ export function StepList({ onStepContextMenu }: StepListProps) {
type="text" type="text"
value={step.title} value={step.title}
onChange={(e) => updateStep(step.id, { title: e.target.value })} onChange={(e) => updateStep(step.id, { title: e.target.value })}
className="flex-1 bg-transparent text-sm text-muted-foreground focus:outline-none" className="flex-1 bg-transparent text-sm text-muted-foreground focus:outline-hidden"
placeholder="Procedure Complete" placeholder="Procedure Complete"
/> />
<span className="text-[10px] text-muted-foreground">END</span> <span className="text-[10px] text-muted-foreground">END</span>
@@ -239,7 +239,7 @@ export function StepList({ onStepContextMenu }: StepListProps) {
className={cn( className={cn(
'group flex flex-col rounded-xl border border-border px-3 py-2.5 transition-colors', 'group flex flex-col rounded-xl border border-border px-3 py-2.5 transition-colors',
'hover:border-primary/30 hover:bg-accent/50', 'hover:border-primary/30 hover:bg-accent/50',
isGhost && 'border-l-2 border-dashed !border-l-primary/40 opacity-60' isGhost && 'border-l-2 border-dashed border-l-primary/40! opacity-60'
)} )}
onContextMenu={(e) => onStepContextMenu?.(e, step.id)} onContextMenu={(e) => onStepContextMenu?.(e, step.id)}
> >

View File

@@ -71,7 +71,7 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
const error = errors[field.variable_name] const error = errors[field.variable_name]
const baseInputClass = cn( const baseInputClass = cn(
'w-full rounded-lg border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1', 'w-full rounded-lg border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-hidden focus:ring-1',
error error
? 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20' ? 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20'
: 'border-border focus:border-primary focus:ring-primary/20' : 'border-border focus:border-primary focus:ring-primary/20'
@@ -211,7 +211,7 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs">
<div className="mx-4 w-full max-w-lg rounded-2xl border border-border bg-[#0a0a0a] shadow-xl"> <div className="mx-4 w-full max-w-lg rounded-2xl border border-border bg-[#0a0a0a] shadow-xl">
{/* Header */} {/* Header */}
<div className="border-b border-border px-6 py-4"> <div className="border-b border-border px-6 py-4">

View File

@@ -154,7 +154,7 @@ export function StepDetail({
{/* Expected outcome */} {/* Expected outcome */}
{'expected_outcome' in step && step.expected_outcome && ( {'expected_outcome' in step && step.expected_outcome && (
<div className="rounded-lg border border-border bg-white/[0.02] p-3"> <div className="rounded-lg border border-border bg-white/2 p-3">
<h4 className="mb-1 text-xs font-medium text-muted-foreground">Expected Outcome</h4> <h4 className="mb-1 text-xs font-medium text-muted-foreground">Expected Outcome</h4>
<p className="text-sm text-muted-foreground">{resolve(step.expected_outcome)}</p> <p className="text-sm text-muted-foreground">{resolve(step.expected_outcome)}</p>
</div> </div>
@@ -162,7 +162,7 @@ export function StepDetail({
{/* Verification */} {/* Verification */}
{verificationPrompt && ( {verificationPrompt && (
<div className="rounded-lg border border-border bg-white/[0.02] p-3"> <div className="rounded-lg border border-border bg-white/2 p-3">
<h4 className="mb-2 text-xs font-medium text-muted-foreground">Verification</h4> <h4 className="mb-2 text-xs font-medium text-muted-foreground">Verification</h4>
{verificationType === 'checkbox' ? ( {verificationType === 'checkbox' ? (
<label className="flex items-center gap-2 text-sm text-muted-foreground"> <label className="flex items-center gap-2 text-sm text-muted-foreground">
@@ -184,7 +184,7 @@ export function StepDetail({
onChange={(e) => onVerificationChange(e.target.value)} onChange={(e) => onVerificationChange(e.target.value)}
disabled={isCompleted} disabled={isCompleted}
placeholder="Enter observed value..." placeholder="Enter observed value..."
className="w-full rounded border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 disabled:opacity-50" className="w-full rounded border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20 disabled:opacity-50"
/> />
</div> </div>
)} )}
@@ -200,7 +200,7 @@ export function StepDetail({
onChange={(e) => onNotesChange(e.target.value)} onChange={(e) => onNotesChange(e.target.value)}
placeholder="Add notes for this step..." placeholder="Add notes for this step..."
rows={2} rows={2}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
)} )}

View File

@@ -74,7 +74,7 @@ export function CSATModal({ isOpen, onClose, sessionId }: CSATModalProps) {
placeholder="Any comments? (optional)" placeholder="Any comments? (optional)"
maxLength={500} maxLength={500}
rows={3} rows={3}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 resize-none" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20 resize-none"
/> />
{/* Actions */} {/* Actions */}

View File

@@ -62,7 +62,7 @@ export function ContinuationModal({
'hover:border-border hover:bg-accent' 'hover:border-border hover:bg-accent'
)} )}
> >
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-accent"> <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent">
{nodeTypeIcons[node.type]} {nodeTypeIcons[node.type]}
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
@@ -71,7 +71,7 @@ export function ContinuationModal({
{nodeTypeLabels[node.type]} {nodeTypeLabels[node.type]}
</p> </p>
</div> </div>
<ArrowRight className="h-4 w-4 flex-shrink-0 text-muted-foreground" /> <ArrowRight className="h-4 w-4 shrink-0 text-muted-foreground" />
</button> </button>
))} ))}
</div> </div>
@@ -98,7 +98,7 @@ export function ContinuationModal({
'hover:border-amber-500 hover:bg-amber-500/10' 'hover:border-amber-500 hover:bg-amber-500/10'
)} )}
> >
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-amber-500/10"> <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-amber-500/10">
<GitBranch className="h-5 w-5 text-amber-500" /> <GitBranch className="h-5 w-5 text-amber-500" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
@@ -111,7 +111,7 @@ export function ContinuationModal({
{/* Warning */} {/* Warning */}
<div className="mt-3 flex items-start gap-2 rounded-md bg-yellow-400/10 p-3"> <div className="mt-3 flex items-start gap-2 rounded-md bg-yellow-400/10 p-3">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" /> <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
<p className="text-sm text-yellow-400"> <p className="text-sm text-yellow-400">
You'll need to complete this branch manually or mark the issue as resolved. You'll need to complete this branch manually or mark the issue as resolved.
Custom branches can be saved as a personal tree when your session ends. Custom branches can be saved as a personal tree when your session ends.

View File

@@ -141,7 +141,7 @@ export function ExportPreviewModal({
className={cn( className={cn(
'h-96 w-full resize-y rounded-md border border-border bg-card p-4', 'h-96 w-full resize-y rounded-md border border-border bg-card p-4',
'font-mono text-sm text-foreground', 'font-mono text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
@@ -153,7 +153,7 @@ export function ExportPreviewModal({
className={cn( className={cn(
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium', 'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium',
'text-muted-foreground hover:bg-accent hover:text-foreground', 'text-muted-foreground hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20' 'focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)} )}
> >
{copied ? ( {copied ? (
@@ -173,7 +173,7 @@ export function ExportPreviewModal({
onClick={handleDownload} onClick={handleDownload}
className={cn( className={cn(
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium', 'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
'hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/20' 'hover:opacity-90 focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)} )}
> >
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />

View File

@@ -83,7 +83,7 @@ export function ForkTreeModal({
<Modal isOpen={isOpen} onClose={onClose} title="Save Custom Tree?" footer={footer}> <Modal isOpen={isOpen} onClose={onClose} title="Save Custom Tree?" footer={footer}>
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-start gap-3 rounded-lg bg-accent/50 p-4"> <div className="flex items-start gap-3 rounded-lg bg-accent/50 p-4">
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-accent"> <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent">
<GitFork className="h-5 w-5 text-foreground" /> <GitFork className="h-5 w-5 text-foreground" />
</div> </div>
<div> <div>
@@ -107,7 +107,7 @@ export function ForkTreeModal({
placeholder="My Custom Tree" placeholder="My Custom Tree"
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20' 'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -124,7 +124,7 @@ export function ForkTreeModal({
rows={3} rows={3}
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20', 'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
'resize-none' 'resize-none'
)} )}
/> />

View File

@@ -33,7 +33,7 @@ export function SaveSessionAsTreeModal({
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-lg rounded-2xl p-6 shadow-lg"> <div className="bg-card border border-border w-full max-w-lg rounded-2xl p-6 shadow-lg">
{/* Header */} {/* Header */}
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
@@ -70,7 +70,7 @@ export function SaveSessionAsTreeModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />
@@ -91,7 +91,7 @@ export function SaveSessionAsTreeModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />

View File

@@ -141,7 +141,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
{/* Mobile backdrop */} {/* Mobile backdrop */}
{!isCollapsed && ( {!isCollapsed && (
<div <div
className="fixed inset-0 z-30 bg-black/80 backdrop-blur-sm sm:hidden" className="fixed inset-0 z-30 bg-black/80 backdrop-blur-xs sm:hidden"
onClick={() => setIsCollapsed(true)} onClick={() => setIsCollapsed(true)}
aria-hidden="true" aria-hidden="true"
/> />
@@ -203,7 +203,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
className={cn( className={cn(
'h-full min-h-[200px] w-full resize-none rounded-md border-0 bg-transparent p-0 text-sm', 'h-full min-h-[200px] w-full resize-none rounded-md border-0 bg-transparent p-0 text-sm',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:outline-none focus:ring-0' 'focus:outline-hidden focus:ring-0'
)} )}
/> />
)} )}

View File

@@ -102,7 +102,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn( className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3', 'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -118,7 +118,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn( className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3', 'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -129,7 +129,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
onChange={(e) => handleFilterChange('treeName', e.target.value)} onChange={(e) => handleFilterChange('treeName', e.target.value)}
className={cn( className={cn(
'rounded-md border border-border bg-card px-3 py-2', 'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'sm:min-w-[200px]' 'sm:min-w-[200px]'
)} )}
> >

View File

@@ -111,7 +111,7 @@ export function SessionOutcomeModal({
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2', 'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground', 'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -126,7 +126,7 @@ export function SessionOutcomeModal({
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2', 'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground', 'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>

View File

@@ -135,7 +135,7 @@ export function SessionTimeline({
{decisions.map((decision, index) => ( {decisions.map((decision, index) => (
<div key={index} className="ml-1 border-l-2 border-border pl-6"> <div key={index} className="ml-1 border-l-2 border-border pl-6">
<div className="relative"> <div className="relative">
<span className="absolute -left-[1.625rem] top-1 h-2 w-2 rounded-full bg-muted-foreground" /> <span className="absolute -left-6.5 top-1 h-2 w-2 rounded-full bg-muted-foreground" />
<div className="bg-card border border-border rounded-xl p-4"> <div className="bg-card border border-border rounded-xl p-4">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<div className="flex-1"> <div className="flex-1">

View File

@@ -181,7 +181,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */} {/* Backdrop */}
<div <div
className="absolute inset-0 bg-black/80 backdrop-blur-sm" className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose} onClick={onClose}
/> />
@@ -265,7 +265,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
placeholder="e.g. Training link, Customer escalation" placeholder="e.g. Training link, Customer escalation"
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground placeholder-muted-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground placeholder-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
maxLength={100} maxLength={100}
/> />
@@ -299,8 +299,8 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
onChange={(e) => setCustomDatetime(e.target.value)} onChange={(e) => setCustomDatetime(e.target.value)}
className={cn( className={cn(
'mt-2 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'mt-2 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'[color-scheme:dark]' 'scheme-dark'
)} )}
/> />
)} )}

View File

@@ -76,7 +76,7 @@ export function SharedSessionTreePreview({
return ( return (
<div className="bg-card border border-border rounded-2xl"> <div className="bg-card border border-border rounded-2xl">
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur"> <div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur-sm">
<h3 className="text-sm font-semibold text-foreground">Tree Structure</h3> <h3 className="text-sm font-semibold text-foreground">Tree Structure</h3>
</div> </div>
<div className="max-h-[600px] overflow-y-auto py-2"> <div className="max-h-[600px] overflow-y-auto py-2">

View File

@@ -77,7 +77,7 @@ export function StepRatingModal({
const getRating = (stepId: string) => ratings.get(stepId) const getRating = (stepId: string) => ratings.get(stepId)
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="bg-card border border-border w-full max-w-2xl max-h-[90vh] flex flex-col rounded-2xl shadow-lg"> <div className="bg-card border border-border w-full max-w-2xl max-h-[90vh] flex flex-col rounded-2xl shadow-lg">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4"> <div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -174,7 +174,7 @@ export function StepRatingModal({
className={cn( className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50' 'disabled:opacity-50'
)} )}
/> />

View File

@@ -21,7 +21,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-md rounded-2xl p-6 shadow-lg"> <div className="bg-card border border-border w-full max-w-md rounded-2xl p-6 shadow-lg">
<h2 className="mb-1 text-lg font-semibold text-foreground">Input Required</h2> <h2 className="mb-1 text-lg font-semibold text-foreground">Input Required</h2>
<p className="mb-4 text-sm text-muted-foreground"> <p className="mb-4 text-sm text-muted-foreground">
@@ -40,7 +40,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
autoFocus autoFocus
className={cn( className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', 'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />

View File

@@ -46,7 +46,7 @@ export function PinnedFlowsSection({ flows, onUnpin }: PinnedFlowsSectionProps)
</button> </button>
<div <div
className="overflow-hidden transition-[max-height] duration-[250ms] ease-out" className="overflow-hidden transition-[max-height] duration-250 ease-out"
style={{ style={{
maxHeight: collapsed ? 0 : showAll maxHeight: collapsed ? 0 : showAll
? `${flows.length * 36 + 40}px` ? `${flows.length * 36 + 40}px`

View File

@@ -65,7 +65,7 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
} }
return ( return (
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black/80 backdrop-blur-sm sm:items-center sm:p-4"> <div className="fixed inset-0 z-50 flex items-end justify-center bg-black/80 backdrop-blur-xs sm:items-center sm:p-4">
<div className="relative flex h-[95vh] w-full max-w-full flex-col border border-border bg-card shadow-lg sm:h-[90vh] sm:max-w-4xl sm:rounded-2xl"> <div className="relative flex h-[95vh] w-full max-w-full flex-col border border-border bg-card shadow-lg sm:h-[90vh] sm:max-w-4xl sm:rounded-2xl">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between border-b border-border p-4"> <div className="flex items-center justify-between border-b border-border p-4">
@@ -133,7 +133,7 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
{/* Loading Overlay */} {/* Loading Overlay */}
{isSubmitting && ( {isSubmitting && (
<div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<Spinner className="border-t-foreground" /> <Spinner className="border-t-foreground" />
<p className="text-sm text-muted-foreground">Creating step...</p> <p className="text-sm text-muted-foreground">Creating step...</p>

View File

@@ -75,7 +75,7 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
const visibleReviews = showAllReviews ? allTextReviews : allTextReviews.slice(0, 3) const visibleReviews = showAllReviews ? allTextReviews : allTextReviews.slice(0, 3)
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="relative flex h-[90vh] w-full max-w-3xl flex-col bg-card border border-border rounded-2xl shadow-lg"> <div className="relative flex h-[90vh] w-full max-w-3xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */} {/* Header */}
<div className="flex items-start justify-between border-b border-border p-6 pb-4"> <div className="flex items-start justify-between border-b border-border p-6 pb-4">

View File

@@ -179,7 +179,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
placeholder="Enter step title" placeholder="Enter step title"
className={cn( className={cn(
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20', 'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
errors.title ? 'border-red-400/50' : 'border-border' errors.title ? 'border-red-400/50' : 'border-border'
)} )}
/> />
@@ -201,7 +201,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
placeholder="Describe what to do in this step..." placeholder="Describe what to do in this step..."
rows={6} rows={6}
className={cn( className={cn(
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20', 'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
errors.instructions ? 'border-red-400/50' : 'border-border' errors.instructions ? 'border-red-400/50' : 'border-border'
)} )}
/> />
@@ -221,7 +221,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setHelpText(e.target.value)} onChange={(e) => setHelpText(e.target.value)}
placeholder="Additional context or tips..." placeholder="Additional context or tips..."
rows={3} rows={3}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -296,7 +296,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
id="category" id="category"
value={categoryId} value={categoryId}
onChange={(e) => setCategoryId(e.target.value)} onChange={(e) => setCategoryId(e.target.value)}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="">None</option> <option value="">None</option>
{categories.map(cat => ( {categories.map(cat => (
@@ -318,7 +318,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setTagInput(e.target.value)} onChange={(e) => setTagInput(e.target.value)}
onKeyDown={handleTagInputKeyDown} onKeyDown={handleTagInputKeyDown}
placeholder="Type tag and press Enter" placeholder="Type tag and press Enter"
className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/> />
<button <button
type="button" type="button"
@@ -359,7 +359,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
id="visibility" id="visibility"
value={visibility} value={visibility}
onChange={(e) => setVisibility(e.target.value as 'private' | 'team' | 'public')} onChange={(e) => setVisibility(e.target.value as 'private' | 'team' | 'public')}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="private">Private (only me)</option> <option value="private">Private (only me)</option>
<option value="team">Team (my team members)</option> <option value="team">Team (my team members)</option>

View File

@@ -49,7 +49,7 @@ export function StepFormModal({ isOpen, onClose, onSuccess, editingStep }: StepF
} : undefined } : undefined
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="relative flex h-[90vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg"> <div className="relative flex h-[90vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between border-b border-border p-6 pb-4"> <div className="flex items-center justify-between border-b border-border p-6 pb-4">

View File

@@ -151,7 +151,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
placeholder="Search steps..." placeholder="Search steps..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="w-full rounded-md border border-border bg-card py-2 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="w-full rounded-md border border-border bg-card py-2 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/> />
</div> </div>
@@ -162,7 +162,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by category" aria-label="Filter by category"
value={selectedCategoryId || ''} value={selectedCategoryId || ''}
onChange={(e) => setSelectedCategoryId(e.target.value || undefined)} onChange={(e) => setSelectedCategoryId(e.target.value || undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="">All Categories</option> <option value="">All Categories</option>
{categories.map(cat => ( {categories.map(cat => (
@@ -175,7 +175,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by step type" aria-label="Filter by step type"
value={selectedStepType || ''} value={selectedStepType || ''}
onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)} onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="">All Types</option> <option value="">All Types</option>
<option value="decision">Decision</option> <option value="decision">Decision</option>
@@ -188,7 +188,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by minimum rating" aria-label="Filter by minimum rating"
value={minRating?.toString() || ''} value={minRating?.toString() || ''}
onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : undefined)} onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="">Any Rating</option> <option value="">Any Rating</option>
<option value="4">4+ Stars</option> <option value="4">4+ Stars</option>
@@ -201,7 +201,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Sort steps by" aria-label="Sort steps by"
value={sortBy} value={sortBy}
onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')} onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20" className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
> >
<option value="recent">Most Recent</option> <option value="recent">Most Recent</option>
<option value="popular">Most Popular</option> <option value="popular">Most Popular</option>

View File

@@ -39,7 +39,7 @@ export function AIFixReviewModal({ fixes, onApply, onApplyAll, onClose }: AIFixR
const allHandled = pendingFixes.length === 0 const allHandled = pendingFixes.length === 0
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="relative flex h-[80vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg"> <div className="relative flex h-[80vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4"> <div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -127,7 +127,7 @@ export function AIFixReviewModal({ fixes, onApply, onApplyAll, onClose }: AIFixR
<div className="mt-3 flex gap-2"> <div className="mt-3 flex gap-2">
<button <button
onClick={() => handleApply(fix)} onClick={() => handleApply(fix)}
className="flex items-center gap-1 rounded-md bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-sm shadow-primary/20 hover:opacity-90" className="flex items-center gap-1 rounded-md bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-xs shadow-primary/20 hover:opacity-90"
> >
<Check className="h-3 w-3" /> <Check className="h-3 w-3" />
Apply Apply

View File

@@ -140,13 +140,13 @@ function FlowCanvasInner({ selectedNodeId, onNodeSelect, onSelectAnswerType, onN
className="dark bg-accent/30" className="dark bg-accent/30"
> >
<Background variant={BackgroundVariant.Dots} gap={20} size={1.5} color="hsl(var(--muted-foreground) / 0.25)" /> <Background variant={BackgroundVariant.Dots} gap={20} size={1.5} color="hsl(var(--muted-foreground) / 0.25)" />
<Controls showInteractive={false} className="!bg-card !border-border !shadow-lg" /> <Controls showInteractive={false} className="bg-card! border-border! shadow-lg!" />
{minimapVisible && ( {minimapVisible && (
<MiniMap <MiniMap
pannable pannable
zoomable zoomable
nodeColor={minimapNodeColor} nodeColor={minimapNodeColor}
className="!bg-card !border-border" className="bg-card! border-border!"
nodeStrokeWidth={2} nodeStrokeWidth={2}
/> />
)} )}

View File

@@ -16,7 +16,7 @@ function FlowCanvasAnswerNodeComponent({ data, selected }: NodeProps) {
return ( return (
<> <>
<Handle type="target" position={Position.Top} className="!bg-border !w-2 !h-2 !border-0" /> <Handle type="target" position={Position.Top} className="bg-border! w-2! h-2! border-0!" />
<div <div
className={cn( className={cn(
@@ -61,7 +61,7 @@ function FlowCanvasAnswerNodeComponent({ data, selected }: NodeProps) {
)} )}
</div> </div>
<Handle type="source" position={Position.Bottom} className="!bg-border !w-2 !h-2 !border-0" /> <Handle type="source" position={Position.Bottom} className="bg-border! w-2! h-2! border-0!" />
</> </>
) )
} }

View File

@@ -62,15 +62,15 @@ function FlowCanvasNodeComponent({ data, selected }: NodeProps) {
return ( return (
<> <>
{/* Target handle at top */} {/* Target handle at top */}
<Handle type="target" position={Position.Top} className="!bg-border !w-2 !h-2 !border-0" /> <Handle type="target" position={Position.Top} className="bg-border! w-2! h-2! border-0!" />
<div <div
onContextMenu={(e) => onContextMenu?.(e, node.id)} onContextMenu={(e) => onContextMenu?.(e, node.id)}
className={cn( className={cn(
'w-[280px] rounded-xl border border-border bg-card shadow-sm cursor-pointer transition-all', 'w-[280px] rounded-xl border border-border bg-card shadow-xs cursor-pointer transition-all',
config.borderClass, config.borderClass,
selected && 'ring-1 ring-primary shadow-md', selected && 'ring-1 ring-primary shadow-md',
isGhost && 'border-dashed !border-primary/40 opacity-60' isGhost && 'border-dashed border-primary/40! opacity-60'
)} )}
> >
{/* Header */} {/* Header */}
@@ -175,7 +175,7 @@ function FlowCanvasNodeComponent({ data, selected }: NodeProps) {
</div> </div>
{/* Source handle at bottom */} {/* Source handle at bottom */}
<Handle type="source" position={Position.Bottom} className="!bg-border !w-2 !h-2 !border-0" /> <Handle type="source" position={Position.Bottom} className="bg-border! w-2! h-2! border-0!" />
</> </>
) )
} }

View File

@@ -28,7 +28,7 @@ export function MetadataSidePanel({ isOpen, onClose }: MetadataSidePanelProps) {
<> <>
{/* Backdrop — click to close */} {/* Backdrop — click to close */}
<div <div
className="fixed inset-0 z-40 bg-background/40 backdrop-blur-sm" className="fixed inset-0 z-40 bg-background/40 backdrop-blur-xs"
onClick={onClose} onClick={onClose}
aria-hidden="true" aria-hidden="true"
/> />

View File

@@ -62,7 +62,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
titleError ? 'border-red-400' : 'border-border' titleError ? 'border-red-400' : 'border-border'
)} )}
/> />
@@ -107,7 +107,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
)} )}
@@ -134,7 +134,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn( className={cn(
'block w-full rounded-md border border-border px-3 py-2 font-mono text-sm', 'block w-full rounded-md border border-border px-3 py-2 font-mono text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
)} )}
@@ -154,7 +154,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
</div> </div>
@@ -195,7 +195,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground', 'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
> >
<option value="">Link to existing node...</option> <option value="">Link to existing node...</option>

View File

@@ -104,7 +104,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
questionError ? 'border-red-400' : 'border-border' questionError ? 'border-red-400' : 'border-border'
)} )}
/> />
@@ -126,7 +126,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
</div> </div>
@@ -187,7 +187,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn( className={cn(
'block w-full rounded-md border px-3 py-2 text-sm', 'block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary',
optionLabelError ? 'border-red-400' : 'border-border' optionLabelError ? 'border-red-400' : 'border-border'
)} )}
/> />

View File

@@ -59,7 +59,7 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
titleError ? 'border-red-400' : 'border-border' titleError ? 'border-red-400' : 'border-border'
)} )}
/> />
@@ -103,7 +103,7 @@ Document what was done and the outcome.
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
)} )}
@@ -134,7 +134,7 @@ Document what was done and the outcome.
className={cn( className={cn(
'block w-full rounded-md border border-border px-3 py-2 text-sm', 'block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)} )}
/> />
</div> </div>

View File

@@ -184,7 +184,7 @@ function NodeListItem({
'group flex items-center gap-1 rounded-md px-2 py-1.5 text-sm transition-colors cursor-pointer', 'group flex items-center gap-1 rounded-md px-2 py-1.5 text-sm transition-colors cursor-pointer',
isRootNode isRootNode
? isSelected ? isSelected
? 'bg-blue-500/20 ring-2 ring-blue-500 shadow-sm' ? 'bg-blue-500/20 ring-2 ring-blue-500 shadow-xs'
: 'bg-blue-500/10 border border-blue-500/30 hover:bg-blue-500/15' : 'bg-blue-500/10 border border-blue-500/30 hover:bg-blue-500/15'
: isSelected : isSelected
? 'bg-primary/10 ring-1 ring-primary' ? 'bg-primary/10 ring-1 ring-primary'
@@ -581,7 +581,7 @@ export function NodeList() {
{/* Add Node Type Selector */} {/* Add Node Type Selector */}
{addingToParent && ( {addingToParent && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-xs">
<div className="w-full max-w-xs rounded-lg border border-border bg-card p-4 shadow-lg"> <div className="w-full max-w-xs rounded-lg border border-border bg-card p-4 shadow-lg">
<h3 className="mb-3 text-sm font-semibold">Select Node Type</h3> <h3 className="mb-3 text-sm font-semibold">Select Node Type</h3>
<div className="space-y-2"> <div className="space-y-2">

View File

@@ -167,7 +167,7 @@ export function NodePicker({
className={cn( className={cn(
'flex-1 rounded-md border border-border px-2 py-1 text-sm', 'flex-1 rounded-md border border-border px-2 py-1 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -201,7 +201,7 @@ export function NodePicker({
className={cn( className={cn(
'block w-full rounded-md border px-3 py-2 text-sm', 'block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground', 'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
error ? 'border-red-400' : 'border-border' error ? 'border-red-400' : 'border-border'
)} )}
> >

View File

@@ -64,7 +64,7 @@ interface AddNodePickerProps {
function AddNodePicker({ onSelect, onCancel }: AddNodePickerProps) { function AddNodePicker({ onSelect, onCancel }: AddNodePickerProps) {
return ( return (
<div className="flex items-center gap-2 rounded-xl border border-dashed border-primary/40 bg-card px-3 py-2 shadow-sm"> <div className="flex items-center gap-2 rounded-xl border border-dashed border-primary/40 bg-card px-3 py-2 shadow-xs">
<span className="text-xs text-muted-foreground shrink-0">Add:</span> <span className="text-xs text-muted-foreground shrink-0">Add:</span>
<button <button

View File

@@ -168,7 +168,7 @@ export function TreeCanvasNode({
return ( return (
<div <div
className={cn( className={cn(
'relative rounded-xl border border-border bg-card shadow-sm transition-all duration-150', 'relative rounded-xl border border-border bg-card shadow-xs transition-all duration-150',
config.borderClass, config.borderClass,
isExpanded && 'ring-1 ring-primary shadow-md', isExpanded && 'ring-1 ring-primary shadow-md',
isSelected && !isExpanded && 'ring-1 ring-primary/50', isSelected && !isExpanded && 'ring-1 ring-primary/50',

View File

@@ -73,7 +73,7 @@ export function TreeMetadataForm() {
className={cn( className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
nameError ? 'border-red-400' : 'border-border' nameError ? 'border-red-400' : 'border-border'
)} )}
/> />
@@ -94,7 +94,7 @@ export function TreeMetadataForm() {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
</div> </div>
@@ -112,7 +112,7 @@ export function TreeMetadataForm() {
className={cn( className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm', 'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground', 'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
> >
<option value="">No category</option> <option value="">No category</option>
@@ -134,7 +134,7 @@ export function TreeMetadataForm() {
className={cn( className={cn(
'block min-w-0 flex-1 rounded-md border border-border px-3 py-2 text-sm', 'block min-w-0 flex-1 rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground', 'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
autoFocus autoFocus
/> />

View File

@@ -76,7 +76,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
'flex items-center gap-1.5 rounded-md px-3 py-1 text-xs font-medium transition-colors', 'flex items-center gap-1.5 rounded-md px-3 py-1 text-xs font-medium transition-colors',
isFixing isFixing
? 'bg-primary/10 text-primary cursor-wait' ? 'bg-primary/10 text-primary cursor-wait'
: 'bg-gradient-brand text-white shadow-sm shadow-primary/20 hover:opacity-90' : 'bg-gradient-brand text-white shadow-xs shadow-primary/20 hover:opacity-90'
)} )}
> >
{isFixing ? ( {isFixing ? (
@@ -109,7 +109,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
: 'cursor-default' : 'cursor-default'
)} )}
> >
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-red-400" /> <AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-400" />
<div className="flex-1"> <div className="flex-1">
<p className="text-red-400">{error.message}</p> <p className="text-red-400">{error.message}</p>
{error.nodeId && ( {error.nodeId && (
@@ -133,7 +133,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
: 'cursor-default' : 'cursor-default'
)} )}
> >
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" /> <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
<div className="flex-1"> <div className="flex-1">
<p className="text-yellow-400">{warning.message}</p> <p className="text-yellow-400">{warning.message}</p>
{warning.nodeId && ( {warning.nodeId && (

View File

@@ -186,7 +186,7 @@ export function TreePreviewNode({
{/* Solution path indicator - shows when this branch leads to a solution */} {/* Solution path indicator - shows when this branch leads to a solution */}
{leadsTosolution && ( {leadsTosolution && (
<div <div
className="absolute -top-1.5 -right-1.5 flex items-center justify-center rounded-full bg-green-500/90 p-0.5 shadow-sm" className="absolute -top-1.5 -right-1.5 flex items-center justify-center rounded-full bg-green-500/90 p-0.5 shadow-xs"
title="This branch leads to a solution" title="This branch leads to a solution"
> >
<CheckCircle className="h-3 w-3 text-foreground" /> <CheckCircle className="h-3 w-3 text-foreground" />

View File

@@ -3,14 +3,14 @@ import { cn } from '@/lib/utils'
import { Spinner } from '@/components/common/Spinner' import { Spinner } from '@/components/common/Spinner'
const buttonVariants = cva( const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 active:scale-[0.97]', 'inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 active:scale-[0.97]',
{ {
variants: { variants: {
variant: { variant: {
primary: primary:
'bg-gradient-brand text-[#101114] font-semibold shadow-lg shadow-primary/20 hover:opacity-90', 'bg-gradient-brand text-brand-dark font-semibold shadow-lg shadow-primary/20 hover:opacity-90',
secondary: secondary:
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground hover:border-[rgba(255,255,255,0.12)] hover:bg-[rgba(255,255,255,0.06)]', 'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground hover:border-[rgba(255,255,255,0.12)] hover:bg-brand-border',
destructive: destructive:
'bg-red-400/10 text-red-400 border border-red-400/20 hover:bg-red-400/20', 'bg-red-400/10 text-red-400 border border-red-400/20 hover:bg-red-400/20',
ghost: ghost:

View File

@@ -12,7 +12,7 @@ export function Input({ className, error, id, ...props }: InputProps) {
className={cn( className={cn(
'flex h-9 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'flex h-9 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:cursor-not-allowed disabled:opacity-50', 'disabled:cursor-not-allowed disabled:opacity-50',
error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20', error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20',
className className

View File

@@ -6,7 +6,7 @@ export function Skeleton({ className, ...props }: SkeletonProps) {
return ( return (
<div <div
className={cn( className={cn(
'animate-pulse rounded-lg bg-[rgba(255,255,255,0.06)]', 'animate-pulse rounded-lg bg-brand-border',
className className
)} )}
{...props} {...props}

View File

@@ -12,7 +12,7 @@ export function Textarea({ className, error, id, ...props }: TextareaProps) {
className={cn( className={cn(
'flex w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'flex w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground', 'placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:cursor-not-allowed disabled:opacity-50', 'disabled:cursor-not-allowed disabled:opacity-50',
error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20', error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20',
className className

View File

@@ -1,6 +1,289 @@
@tailwind base; @import 'tailwindcss';
@tailwind components;
@tailwind utilities; @custom-variant dark (&:is(.dark *));
@theme {
--color-brand-gradient-from: #06b6d4;
--color-brand-gradient-to: #22d3ee;
--color-brand-dark: #101114;
--color-brand-dark-card: #14161a;
--color-brand-dark-surface: #14161a;
--color-brand-text-primary: #f8fafc;
--color-brand-text-secondary: #8891a0;
--color-brand-text-muted: #5a6170;
--color-brand-border: rgba(255, 255, 255, 0.06);
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--font-sans:
IBM Plex Sans, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI,
Roboto, sans-serif;
--font-heading: Bricolage Grotesque, system-ui, sans-serif;
--font-label: JetBrains Mono, monospace;
--background-image-gradient-brand: linear-gradient(
135deg,
#06b6d4 0%,
#22d3ee 100%
);
--background-image-gradient-brand-hover: linear-gradient(
135deg,
#0891b2 0%,
#06b6d4 100%
);
}
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@utility animate-fade-in {
animation: fade-in 200ms ease-out;
}
@utility animate-fade-in-up {
animation: fade-in-up 200ms ease-out;
}
@utility animate-slide-in-left {
animation: slide-in-from-left 200ms ease-out;
}
@utility animate-slide-in-bottom {
animation: slide-in-from-bottom 200ms ease-out;
}
@utility animate-scale-in {
animation: scale-in 150ms ease-out;
}
@utility btn-press {
/* Button press feedback */
@apply active:scale-[0.98] transition-transform;
}
@utility text-gradient-brand {
/* Brand gradient text */
@apply bg-gradient-brand bg-clip-text text-transparent;
}
@utility glass-card {
/* Glass card — interactive with hover lift */
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
transition:
transform 200ms var(--ease-out-smooth),
border-color 200ms var(--ease-out-smooth),
box-shadow 200ms var(--ease-out-smooth);
&:hover {
transform: scale(1.02);
border-color: var(--glass-border-hover);
box-shadow: var(--shadow-float-hover);
}
}
@utility glass-card-static {
/* Glass card — static, no hover transform */
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
}
@utility active-glow {
/* Breathing glow for highlighted stat cards */
animation: breatheGlow 3s ease-in-out infinite alternate;
}
@utility rdp-custom {
@apply text-foreground;
& .rdp-month {
@apply w-full;
}
& .rdp-caption {
@apply flex justify-center items-center mb-4;
}
& .rdp-caption_label {
@apply text-sm font-medium;
}
& .rdp-nav {
@apply flex gap-1;
}
& .rdp-nav_button {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
& .rdp-table {
@apply w-full border-collapse;
}
& .rdp-head_cell {
@apply text-muted-foreground font-normal text-xs;
}
& .rdp-cell {
@apply text-center text-sm p-0;
}
& .rdp-day {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
& .rdp-day_selected {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
& .rdp-day_today {
@apply bg-accent text-accent-foreground;
}
& .rdp-day_outside {
@apply text-muted-foreground opacity-50;
}
& .rdp-day_disabled {
@apply text-muted-foreground opacity-50;
}
& .rdp-day_range_middle {
@apply bg-accent text-accent-foreground;
}
& .rdp-day_hidden {
@apply invisible;
}
}
@utility rdp-month {
.rdp-custom & {
@apply w-full;
}
}
@utility rdp-caption {
.rdp-custom & {
@apply flex justify-center items-center mb-4;
}
}
@utility rdp-caption_label {
.rdp-custom & {
@apply text-sm font-medium;
}
}
@utility rdp-nav {
.rdp-custom & {
@apply flex gap-1;
}
}
@utility rdp-nav_button {
.rdp-custom & {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
}
@utility rdp-table {
.rdp-custom & {
@apply w-full border-collapse;
}
}
@utility rdp-head_cell {
.rdp-custom & {
@apply text-muted-foreground font-normal text-xs;
}
}
@utility rdp-cell {
.rdp-custom & {
@apply text-center text-sm p-0;
}
}
@utility rdp-day {
.rdp-custom & {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
}
@utility rdp-day_selected {
.rdp-custom & {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
}
@utility rdp-day_today {
.rdp-custom & {
@apply bg-accent text-accent-foreground;
}
}
@utility rdp-day_outside {
.rdp-custom & {
@apply text-muted-foreground opacity-50;
}
}
@utility rdp-day_disabled {
.rdp-custom & {
@apply text-muted-foreground opacity-50;
}
}
@utility rdp-day_range_middle {
.rdp-custom & {
@apply bg-accent text-accent-foreground;
}
}
@utility rdp-day_hidden {
.rdp-custom & {
@apply invisible;
}
}
@layer base { @layer base {
:root { :root {
@@ -86,289 +369,233 @@
} }
} }
/* App Shell Grid Layout */ @layer utilities {
.app-shell { /* App Shell Grid Layout */
display: grid;
grid-template-columns: var(--sidebar-w) 1fr;
grid-template-rows: 56px 1fr;
height: 100vh;
overflow: hidden;
transition: grid-template-columns 200ms ease;
}
.app-shell--collapsed {
grid-template-columns: 56px 1fr;
}
.topbar {
grid-column: 1 / -1;
}
.sidebar {
min-height: 0;
overflow-y: auto;
}
.main-content {
min-height: 0;
min-width: 0;
overflow-y: auto;
}
/* Mobile: single column */
@media (max-width: 767px) {
.app-shell { .app-shell {
grid-template-columns: 1fr; display: grid;
grid-template-columns: var(--sidebar-w) 1fr;
grid-template-rows: 56px 1fr;
height: 100vh;
overflow: hidden;
transition: grid-template-columns 200ms ease;
} }
}
/* Staggered fade-in for page sections */ .app-shell--collapsed {
.fade-in { grid-template-columns: 56px 1fr;
animation: fadeIn 0.3s ease forwards; }
}
@keyframes fadeIn { .topbar {
from { opacity: 0; transform: translateY(6px); } grid-column: 1 / -1;
to { opacity: 1; transform: translateY(0); } }
}
/* Animations */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-in-up { .sidebar {
from { opacity: 0; transform: translateY(4px); } min-height: 0;
to { opacity: 1; transform: translateY(0); } overflow-y: auto;
} }
@keyframes slide-in-from-left { .main-content {
from { transform: translateX(-100%); } min-height: 0;
to { transform: translateX(0); } min-width: 0;
} overflow-y: auto;
}
@keyframes slide-in-from-bottom { /* Mobile: single column */
from { opacity: 0; transform: translateY(16px); } @media (max-width: 767px) {
to { opacity: 1; transform: translateY(0); } .app-shell {
} grid-template-columns: 1fr;
}
}
@keyframes scale-in { /* Staggered fade-in for page sections */
from { opacity: 0; transform: scale(0.95); } .fade-in {
to { opacity: 1; transform: scale(1); } animation: fadeIn 0.3s ease forwards;
} }
@keyframes slideDown { @keyframes fadeIn {
from { transform: translateY(-100%); opacity: 0; } from {
to { transform: translateY(0); opacity: 1; } opacity: 0;
} transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Animations */
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideInRight { @keyframes fade-in-up {
from { transform: translateX(100%); } from {
to { transform: translateX(0); } opacity: 0;
} transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInRight { @keyframes slide-in-from-left {
from { transform: translateX(30px); opacity: 0; } from {
to { transform: translateX(0); opacity: 1; } transform: translateX(-100%);
} }
to {
transform: translateX(0);
}
}
@keyframes breatheGlow { @keyframes slide-in-from-bottom {
from { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 0 20px rgba(6, 182, 212, 0.04); } from {
to { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 0 30px rgba(6, 182, 212, 0.12); } opacity: 0;
} transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes bellWobble { @keyframes scale-in {
0% { transform: rotate(0deg); } from {
20% { transform: rotate(8deg); } opacity: 0;
40% { transform: rotate(-6deg); } transform: scale(0.95);
60% { transform: rotate(4deg); } }
80% { transform: rotate(-2deg); } to {
100% { transform: rotate(0deg); } opacity: 1;
transform: scale(1);
}
}
@keyframes slideDown {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes fadeInRight {
from {
transform: translateX(30px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes breatheGlow {
from {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 20px rgba(6, 182, 212, 0.04);
}
to {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 30px rgba(6, 182, 212, 0.12);
}
}
@keyframes bellWobble {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(8deg);
}
40% {
transform: rotate(-6deg);
}
60% {
transform: rotate(4deg);
}
80% {
transform: rotate(-2deg);
}
100% {
transform: rotate(0deg);
}
}
} }
@layer utilities { @layer utilities {
.animate-fade-in { /* Sonner Toast Customization — outside @layer for higher specificity */
animation: fade-in 200ms ease-out; [data-sonner-toast] {
background-color: hsl(var(--card)) !important;
color: hsl(var(--card-foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3) !important;
border-radius: 0.75rem;
font-family: 'IBM Plex Sans', system-ui, sans-serif;
} }
.animate-fade-in-up { [data-sonner-toast] [data-title] {
animation: fade-in-up 200ms ease-out; font-family: 'IBM Plex Sans', system-ui, sans-serif;
font-weight: 600;
} }
.animate-slide-in-left { [data-sonner-toast][data-type='success'] {
animation: slide-in-from-left 200ms ease-out; border-color: rgba(52, 211, 153, 0.3) !important;
}
[data-sonner-toast][data-type='success'] [data-icon] {
color: #34d399;
} }
.animate-slide-in-bottom { [data-sonner-toast][data-type='error'] {
animation: slide-in-from-bottom 200ms ease-out; border-color: rgba(248, 113, 113, 0.3) !important;
}
[data-sonner-toast][data-type='error'] [data-icon] {
color: #f87171;
} }
.animate-scale-in { [data-sonner-toast][data-type='info'] {
animation: scale-in 150ms ease-out; border-color: hsl(var(--border)) !important;
}
[data-sonner-toast][data-type='info'] [data-icon] {
color: hsl(var(--muted-foreground));
} }
/* Button press feedback */ [data-sonner-toast][data-type='warning'] {
.btn-press { border-color: rgba(251, 191, 36, 0.3) !important;
@apply active:scale-[0.98] transition-transform; }
[data-sonner-toast][data-type='warning'] [data-icon] {
color: #fbbf24;
} }
/* Brand gradient text */ [data-sonner-toast] [data-close-button] {
.text-gradient-brand { color: hsl(var(--muted-foreground));
@apply bg-gradient-brand bg-clip-text text-transparent; border-radius: 0.375rem;
transition:
color 150ms,
background-color 150ms;
}
[data-sonner-toast] [data-close-button]:hover {
background-color: hsl(var(--accent));
color: hsl(var(--accent-foreground));
} }
/* Glass card — interactive with hover lift */ /* React Day Picker Customization */
.glass-card {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
transition: transform 200ms var(--ease-out-smooth),
border-color 200ms var(--ease-out-smooth),
box-shadow 200ms var(--ease-out-smooth);
}
.glass-card:hover {
transform: scale(1.02);
border-color: var(--glass-border-hover);
box-shadow: var(--shadow-float-hover);
}
/* Glass card — static, no hover transform */
.glass-card-static {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
}
/* Breathing glow for highlighted stat cards */
.active-glow {
animation: breatheGlow 3s ease-in-out infinite alternate;
}
}
/* Sonner Toast Customization — outside @layer for higher specificity */
[data-sonner-toast] {
background-color: hsl(var(--card)) !important;
color: hsl(var(--card-foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3) !important;
border-radius: 0.75rem;
font-family: 'IBM Plex Sans', system-ui, sans-serif;
}
[data-sonner-toast] [data-title] {
font-family: 'IBM Plex Sans', system-ui, sans-serif;
font-weight: 600;
}
[data-sonner-toast][data-type="success"] {
border-color: rgba(52, 211, 153, 0.3) !important;
}
[data-sonner-toast][data-type="success"] [data-icon] {
color: #34d399;
}
[data-sonner-toast][data-type="error"] {
border-color: rgba(248, 113, 113, 0.3) !important;
}
[data-sonner-toast][data-type="error"] [data-icon] {
color: #f87171;
}
[data-sonner-toast][data-type="info"] {
border-color: hsl(var(--border)) !important;
}
[data-sonner-toast][data-type="info"] [data-icon] {
color: hsl(var(--muted-foreground));
}
[data-sonner-toast][data-type="warning"] {
border-color: rgba(251, 191, 36, 0.3) !important;
}
[data-sonner-toast][data-type="warning"] [data-icon] {
color: #fbbf24;
}
[data-sonner-toast] [data-close-button] {
color: hsl(var(--muted-foreground));
border-radius: 0.375rem;
transition: color 150ms, background-color 150ms;
}
[data-sonner-toast] [data-close-button]:hover {
background-color: hsl(var(--accent));
color: hsl(var(--accent-foreground));
}
/* React Day Picker Customization */
@layer components {
.rdp-custom {
@apply text-foreground;
}
.rdp-custom .rdp-month {
@apply w-full;
}
.rdp-custom .rdp-caption {
@apply flex justify-center items-center mb-4;
}
.rdp-custom .rdp-caption_label {
@apply text-sm font-medium;
}
.rdp-custom .rdp-nav {
@apply flex gap-1;
}
.rdp-custom .rdp-nav_button {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
.rdp-custom .rdp-table {
@apply w-full border-collapse;
}
.rdp-custom .rdp-head_cell {
@apply text-muted-foreground font-normal text-xs;
}
.rdp-custom .rdp-cell {
@apply text-center text-sm p-0;
}
.rdp-custom .rdp-day {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
.rdp-custom .rdp-day_selected {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
.rdp-custom .rdp-day_today {
@apply bg-accent text-accent-foreground;
}
.rdp-custom .rdp-day_outside {
@apply text-muted-foreground opacity-50;
}
.rdp-custom .rdp-day_disabled {
@apply text-muted-foreground opacity-50;
}
.rdp-custom .rdp-day_range_middle {
@apply bg-accent text-accent-foreground;
}
.rdp-custom .rdp-day_hidden {
@apply invisible;
}
} }
/* React Flow dark theme overrides */ /* React Flow dark theme overrides */

View File

@@ -189,7 +189,7 @@ export function AccountSettingsPage() {
className={cn( className={cn(
'flex-1 rounded-md border border-border bg-card px-3 py-2', 'flex-1 rounded-md border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
autoFocus autoFocus
onKeyDown={(e) => { onKeyDown={(e) => {
@@ -367,7 +367,7 @@ export function AccountSettingsPage() {
}} }}
className={cn( className={cn(
'rounded-md border border-border bg-card px-2 py-0.5 text-xs', 'rounded-md border border-border bg-card px-2 py-0.5 text-xs',
'text-foreground focus:border-primary focus:outline-none' 'text-foreground focus:border-primary focus:outline-hidden'
)} )}
> >
<option value="engineer">engineer</option> <option value="engineer">engineer</option>
@@ -415,7 +415,7 @@ export function AccountSettingsPage() {
className={cn( className={cn(
'flex-1 rounded-md border border-border bg-card px-3 py-2', 'flex-1 rounded-md border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
/> />
<select <select
@@ -423,7 +423,7 @@ export function AccountSettingsPage() {
onChange={(e) => setInviteRole(e.target.value)} onChange={(e) => setInviteRole(e.target.value)}
className={cn( className={cn(
'rounded-md border border-border bg-card px-3 py-2', 'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
> >
<option value="engineer">Engineer</option> <option value="engineer">Engineer</option>
@@ -609,7 +609,7 @@ export function AccountSettingsPage() {
className={cn( className={cn(
'mt-2 block w-full max-w-xs rounded-xl border border-border bg-card px-3 py-2', 'mt-2 block w-full max-w-xs rounded-xl border border-border bg-card px-3 py-2',
'text-sm text-foreground', 'text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)} )}
> >
<option value="markdown">Markdown (.md)</option> <option value="markdown">Markdown (.md)</option>

View File

@@ -223,7 +223,7 @@ export default function AssistantChatPage() {
<div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center"> <div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center">
<Sparkles size={14} className="text-primary" /> <Sparkles size={14} className="text-primary" />
</div> </div>
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-2xl px-4 py-3"> <div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-2xl px-4 py-3">
<Loader2 size={16} className="animate-spin text-primary" /> <Loader2 size={16} className="animate-spin text-primary" />
</div> </div>
</div> </div>
@@ -242,7 +242,7 @@ export default function AssistantChatPage() {
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Ask about IT, networking, troubleshooting..." placeholder="Ask about IT, networking, troubleshooting..."
rows={3} rows={3}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-none focus:border-[rgba(6,182,212,0.3)]" className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }} style={{ borderColor: 'var(--glass-border)' }}
disabled={loading} disabled={loading}
/> />
@@ -250,7 +250,7 @@ export default function AssistantChatPage() {
<button <button
onClick={handleSend} onClick={handleSend}
disabled={!input.trim() || loading} disabled={!input.trim() || loading}
className="bg-gradient-brand text-[#101114] p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40" className="bg-gradient-brand text-brand-dark p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
title="Send message" title="Send message"
> >
<Send size={18} /> <Send size={18} />
@@ -286,7 +286,7 @@ export default function AssistantChatPage() {
</p> </p>
<button <button
onClick={handleNewChat} onClick={handleNewChat}
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all" className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
> >
Start a Conversation Start a Conversation
</button> </button>

View File

@@ -97,7 +97,7 @@ export function ChangePasswordPage() {
className={cn( className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2', 'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
/> />
@@ -116,7 +116,7 @@ export function ChangePasswordPage() {
className={cn( className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2', 'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
placeholder="At least 10 characters" placeholder="At least 10 characters"
@@ -139,7 +139,7 @@ export function ChangePasswordPage() {
className={cn( className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2', 'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
/> />
@@ -151,7 +151,7 @@ export function ChangePasswordPage() {
className={cn( className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press', 'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90', 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black', 'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
'disabled:cursor-not-allowed disabled:opacity-50', 'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all' 'transition-all'
)} )}

View File

@@ -171,7 +171,7 @@ export function FeedbackPage() {
onChange={e => setEmail(e.target.value)} onChange={e => setEmail(e.target.value)}
placeholder="your@email.com" placeholder="your@email.com"
required required
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden"
/> />
<p className="mt-1 text-xs text-muted-foreground">We'll reply to this address if we need more details.</p> <p className="mt-1 text-xs text-muted-foreground">We'll reply to this address if we need more details.</p>
</div> </div>
@@ -193,7 +193,7 @@ export function FeedbackPage() {
onClick={() => setTypeDropdownOpen(!typeDropdownOpen)} onClick={() => setTypeDropdownOpen(!typeDropdownOpen)}
onKeyDown={handleDropdownKeyDown} onKeyDown={handleDropdownKeyDown}
className={cn( className={cn(
"w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none", "w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden",
feedbackType ? "text-foreground" : "text-muted-foreground" feedbackType ? "text-foreground" : "text-muted-foreground"
)} )}
> >
@@ -244,7 +244,7 @@ export function FeedbackPage() {
required required
minLength={10} minLength={10}
rows={6} rows={6}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none resize-y" className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden resize-y"
/> />
<p className="mt-1 text-xs text-muted-foreground"> <p className="mt-1 text-xs text-muted-foreground">
{message.trim().length < 10 {message.trim().length < 10

View File

@@ -75,7 +75,7 @@ export function ForgotPasswordPage() {
className={cn( className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2', 'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
placeholder="you@example.com" placeholder="you@example.com"
@@ -88,7 +88,7 @@ export function ForgotPasswordPage() {
className={cn( className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press', 'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90', 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background', 'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'disabled:cursor-not-allowed disabled:opacity-50', 'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all' 'transition-all'
)} )}

View File

@@ -14,7 +14,7 @@ export default function GuideDetailPage() {
<p className="text-sm text-muted-foreground mb-4">The guide you're looking for doesn't exist.</p> <p className="text-sm text-muted-foreground mb-4">The guide you're looking for doesn't exist.</p>
<Link <Link
to="/guides" to="/guides"
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all" className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all"
> >
Back to Guides Back to Guides
</Link> </Link>
@@ -47,10 +47,10 @@ export default function GuideDetailPage() {
</div> </div>
</div> </div>
<div className="flex items-center gap-4 mt-4 pt-4 border-t" style={{ borderColor: 'var(--glass-border)' }}> <div className="flex items-center gap-4 mt-4 pt-4 border-t" style={{ borderColor: 'var(--glass-border)' }}>
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'} {guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span> </span>
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.reduce((acc, s) => acc + s.steps.length, 0)} steps {guide.sections.reduce((acc, s) => acc + s.steps.length, 0)} steps
</span> </span>
</div> </div>

View File

@@ -106,7 +106,7 @@ export function LoginPage() {
className={cn( className={cn(
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5', 'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
placeholder="you@example.com" placeholder="you@example.com"
@@ -127,7 +127,7 @@ export function LoginPage() {
className={cn( className={cn(
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5', 'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground', 'text-foreground placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20', 'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors' 'transition-colors'
)} )}
placeholder="••••••••••" placeholder="••••••••••"
@@ -145,8 +145,8 @@ export function LoginPage() {
disabled={isLoading} disabled={isLoading}
className={cn( className={cn(
'w-full rounded-[10px] px-4 py-2.5 text-sm font-semibold', 'w-full rounded-[10px] px-4 py-2.5 text-sm font-semibold',
'bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]', 'bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background', 'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'disabled:cursor-not-allowed disabled:opacity-50', 'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all' 'transition-all'
)} )}

View File

@@ -90,7 +90,7 @@ export default function MyAnalyticsPage() {
<select <select
value={period} value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)} onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary/20" className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20"
> >
{PERIOD_OPTIONS.map((opt) => ( {PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}> <option key={opt.value} value={opt.value}>
@@ -298,7 +298,7 @@ export default function MyAnalyticsPage() {
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="h-2.5 w-2.5 rounded-full flex-shrink-0" className="h-2.5 w-2.5 rounded-full shrink-0"
style={{ style={{
backgroundColor: backgroundColor:
OUTCOME_COLORS[outcome] ?? '#94a3b8', OUTCOME_COLORS[outcome] ?? '#94a3b8',

View File

@@ -136,7 +136,7 @@ export function MyTreesPage() {
{showCreateMenu && ( {showCreateMenu && (
<> <>
<div className="fixed inset-0 z-10" onClick={() => setShowCreateMenu(false)} /> <div className="fixed inset-0 z-10" onClick={() => setShowCreateMenu(false)} />
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm"> <div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-xs">
<Link <Link
to="/trees/new" to="/trees/new"
onClick={() => setShowCreateMenu(false)} onClick={() => setShowCreateMenu(false)}

Some files were not shown because too many files have changed in this diff Show More