diff --git a/.gitignore b/.gitignore index e6cfa118..758ce880 100644 --- a/.gitignore +++ b/.gitignore @@ -233,3 +233,4 @@ package.json package-lock.json .worktrees/ .gstack/ +.gitnexus diff --git a/CLAUDE.md b/CLAUDE.md index 3ebb8970..4ce6373a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md - Patherly / ResolutionFlow Project Context -> **Last Updated:** March 27, 2026 +> **Last Updated:** April 6, 2026 --- @@ -16,7 +16,8 @@ | Context | Name Used | |---------|-----------| -| Repository / directory / database / Docker | `patherly` / `patherly_postgres` | +| Repository / directory / database | `patherly` (internal name) | +| Docker containers | `resolutionflow_postgres`, `resolutionflow_frontend`, `resolutionflow_backend` | | Backend, frontend UI, production URLs | **ResolutionFlow** | - **Design system:** [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) — THE source of truth for all design decisions @@ -44,7 +45,7 @@ - **Phase:** Go-to-Market Validation (Pre-PMF) - **Backend:** Complete (55+ API endpoints, 100+ integration tests) - **Frontend:** Core features complete, Tree Editor functional -- **Database:** PostgreSQL with Docker, 98 migrations +- **Database:** PostgreSQL with Docker, 101 migrations - **Detailed status:** [CURRENT-STATE.md](CURRENT-STATE.md) ### What's In Progress @@ -96,7 +97,7 @@ patherly/ │ │ ├── services/knowledge_flywheel.py # AI session analysis → flow proposals │ │ ├── services/knowledge_flywheel_scheduler.py # APScheduler job for batch analysis │ │ └── services/knowledge_gap_service.py # Weak options & escalation signal detection -│ ├── alembic/ # Database migrations (001-029+) +│ ├── alembic/ # Database migrations (001-070 sequential, then hash IDs) │ ├── scripts/ # seed_data.py, seed_trees.py │ └── tests/ # pytest integration tests ├── frontend/ @@ -188,8 +189,8 @@ Official ConnectWise developer guides live in `docs/connectwise/best-practices/` ## Development Commands ```powershell -# Start PostgreSQL -docker start patherly_postgres +# Start PostgreSQL (run from VPS SSH — docker not available inside code-server, see Lesson 103) +docker start resolutionflow_postgres # Backend (from backend/) source venv/bin/activate # Linux/Mac @@ -203,21 +204,19 @@ npm run dev pytest --override-ini="addopts=" # First time only: create test database -docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;" +docker exec -it resolutionflow_postgres psql -U postgres -c "CREATE DATABASE resolutionflow_test;" # Frontend build (IMPORTANT: stricter than tsc --noEmit — always use as final check) cd frontend && npm run build # Database migrations cd backend && alembic upgrade head -alembic revision --autogenerate -m "Description" --rev-id=NNN # NNN = next sequential number -# IMPORTANT: Migrations use sequential 3-digit IDs (001, 002, ..., 068, 069). -# Check the latest: ls backend/alembic/versions/ | grep -E '^\d{3}_' | sort | tail -1 -# The revision ID and filename prefix MUST match (e.g., revision="068", file=068_description.py). -# down_revision MUST point to the previous sequential number. Never use hex hash IDs for new migrations. +alembic revision --autogenerate -m "Description" +# Sequential 3-digit IDs (001–070) were used historically. New migrations use Alembic's default hex hash IDs. +# Do NOT pass --rev-id — let Alembic generate the hash automatically. -# Access PostgreSQL -docker exec -it patherly_postgres psql -U postgres -d patherly +# Access PostgreSQL (run from VPS SSH — docker not available inside code-server, see Lesson 103) +docker exec -it resolutionflow_postgres psql -U postgres -d resolutionflow # Seed data cd backend && pip install httpx && python -m scripts.seed_trees @@ -292,7 +291,7 @@ gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi **62. Playwright strict mode — scope selectors to avoid ambiguity:** Step titles appear in both the sidebar checklist and main content heading. Use `getByRole('heading', { name })` for the main content, or scope with `page.locator('.animate-scale-in')` for command palette items. `getByText()` frequently matches multiple elements due to the sidebar + main content layout. -**63. Node 20 required for frontend builds:** Vite 7+ requires Node 20.19+. The system Node may be v18; use nvm: `export NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh" && nvm use 20`. For direct binary access without nvm sourcing: `PATH="/home/michaelchihlas/.nvm/versions/node/v20.19.0/bin:$PATH"`. +**63. Node 20 required for frontend builds:** Vite 7+ requires Node 20.19+. The system Node may be v18; use nvm: `export NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh" && nvm use 20`. For direct binary access without nvm sourcing: `PATH="$HOME/.nvm/versions/node/v20.19.0/bin:$PATH"`. **64. PostHog product analytics:** Initialized via `PostHogProvider` in `main.tsx` with explicit `posthog.init()` + `client` prop pattern. Event helpers in `lib/analytics.ts` — use `analytics.eventName(props)` to track. `identifyUser()` called in `authStore.fetchUser()`, `resetAnalytics()` on logout. Env vars: `VITE_PUBLIC_POSTHOG_KEY`, `VITE_PUBLIC_POSTHOG_HOST`. Autocapture enabled. @@ -332,7 +331,7 @@ gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi **82. `bun` requires PATH setup on devserver01:** `export BUN_INSTALL="$HOME/.bun" && export PATH="$BUN_INSTALL/bin:$PATH"`. The gstack browse binary and Playwright need this. Chromium system deps: `libatk1.0-0 libatk-bridge2.0-0 libcups2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2`. -**83. FlowPilot ActionBar is `position: fixed; bottom: 0`:** Any UI element placed in normal document flow below the session content will be hidden behind it. New fixed-position elements (like the message bar) must use `bottom: 68px` (action bar height) and the same `left: var(--sidebar-w)` pattern. The conversation column uses `pb-32` for clearance. +**83. ~~FlowPilot ActionBar fixed bottom~~ (Superseded by Lesson 93):** Actions moved to the page header. `FlowPilotActionBar` component exists but is no longer used in the main session flow. The only fixed-bottom element is the message input. **84. AI session `abandoned` status is fully wired:** `POST /ai-sessions/{id}/abandon` sets status to `abandoned` with optional `reason` param. Frontend: `aiSessionsApi.abandonSession()`, `useFlowPilotSession().abandonSession()`, "Close" button in `FlowPilotActionBar`. Redirects to `/sessions` after closing. @@ -344,6 +343,7 @@ gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi **88. Charcoal palette — sidebar-darkest approach:** Sidebar `#0e1016`, page `#16181f`, cards `#1e2028`, borders `#2a2e3a`. This gives more contrast range than true-dark. All colors via CSS variables in `index.css` `@theme` block. Accent is electric blue (#60a5fa), not orange or cyan. +*(Lessons 89–91 were retracted.)* **92. `tsc -b` in Dockerfile is stricter than `npx tsc --noEmit`:** The production build (`tsc -b && vite build`) enforces `noUnusedLocals` and `noUnusedParameters` as hard errors. After any refactor that moves logic between components or removes features, trace every import and destructured prop to remove orphans. IDE warnings (yellow squiggles) flag these — check them before pushing. @@ -353,7 +353,7 @@ gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi **95. Image upload → AI vision pipeline:** Paste/attach images → upload to Railway S3 bucket via `uploadsApi.upload()` → send `upload_ids` with chat message → backend fetches from S3 via `storage_service.download_file()` → resized via `storage_service.resize_image_for_vision()` (Pillow, 1568px max, PNG→JPEG) → base64-encoded → sent as Claude multimodal content blocks. Max 3 images/message. Images are NOT stored in conversation history (text-only). Vision helpers live in `storage_service.py`. -**96. `bg-accent` is ember orange — never use for code/kbd elements:** In Tailwind v4, `bg-accent` maps to `--color-accent: #f97316`. Use `bg-code` for code blocks, `bg-white/[0.12] border border-white/[0.06]` for inline code/badges, `bg-white/[0.08]` for kbd shortcuts. Orange is reserved for interactive elements only (buttons, active nav, links). +**96. `bg-accent` is electric blue — never use for code/kbd elements:** In Tailwind v4, `bg-accent` maps to `--color-accent: #60a5fa` (dark) / `#2563eb` (light). Use `bg-code` for code blocks, `bg-white/[0.12] border border-white/[0.06]` for inline code/badges, `bg-white/[0.08]` for kbd shortcuts. Blue accent is reserved for interactive elements only (buttons, active nav, links). Ember orange (#f97316) is deprecated — do not use. **97. Railway Object Storage (S3 bucket) is provisioned:** Bucket `resolutionflow-uploads` on Railway canvas. Variables: `STORAGE_ENDPOINT`, `STORAGE_ACCESS_KEY`, `STORAGE_SECRET_KEY`, `STORAGE_BUCKET_NAME`, `STORAGE_REGION` — mapped via variable references on the `patherly` backend service. Accessed via boto3 in `storage_service.py`. Pillow (`Pillow>=10.0.0`) + `libjpeg-dev`/`zlib1g-dev` in Dockerfile for image resize. @@ -390,16 +390,16 @@ gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi **Source of truth:** [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) — always read this before making visual or UI decisions. -- **Theme:** Flat, high-contrast dark theme (Sentry/PostHog-inspired). No glass morphism, no backdrop blur, no ambient orbs, no gradient backgrounds on surfaces. Light mode planned. -- **Backgrounds:** `bg-page` (`#1a1c23`), `bg-sidebar` (`#10121a`), `bg-card` (`#22252e`), `bg-elevated` (`#2e3140`) -- **Cards:** `bg-card` with 1px `border-default` (`#2e3240`), 8px radius. No shadows, no blur, no gradients. Hover: `border-hover` (`#3d4252`) -- **Buttons:** Primary: solid `accent` (#f97316), white text, 5px radius. Ghost: transparent + 1px border, hover `bg-elevated` -- **Inputs:** `bg-input` (`#282b35`) with 1px `border-default`, 5px radius. Focus: `border-color: accent` + `box-shadow: 0 0 0 2px accent-dim` -- **Text:** `text-heading` (`#f0f2f5`) → `text-primary` (`#e2e5eb`) → `text-muted-foreground` (`#848b9b`) → `text-muted` (`#4f5666`). NEVER use `text-secondary` — in Tailwind v4 it maps to a surface color (#2e3140), not a text color. -- **Borders:** `border-default` (`#2e3240`), `border-hover` (`#3d4252`) -- **Functional colors:** `#34d399` (success), `#eab308` (warning), `#f87171` (danger) — each with `-dim` variant at 10% opacity -- **Accent:** Ember orange `#f97316` — used sparingly (≤5% of UI). `accent-dim` = `rgba(249,115,22,0.10)`, `accent-text` = `#fdba74` -- **Deprecated:** Do NOT use `glass-card`, `glass-stat`, `bg-gradient-brand`, `text-gradient-brand`, `backdrop-filter: blur()`, ambient orbs, purple gradients, or cyan accent (`#22d3ee`) +- **Theme:** Flat, high-contrast dark theme (Sentry/PostHog-inspired). No glass morphism, no backdrop blur, no ambient orbs, no gradient backgrounds on surfaces. Light mode fully specified (v6). +- **Backgrounds:** `bg-page` (`#16181f`), `bg-sidebar` (`#0e1016`), `bg-card` (`#1e2028`), `bg-elevated` (`#2a2d38`) +- **Cards:** `bg-card` with 1px `border-default` (`#2a2e3a`), 8px radius. No shadows, no blur, no gradients. Hover: `border-hover` (`#3d4252`) +- **Buttons:** Primary: solid `accent` (#60a5fa dark / #2563eb light), white text, 5px radius. Ghost: transparent + 1px border, hover `bg-elevated` +- **Inputs:** `bg-input` (`#252830`) with 1px `border-default`, 5px radius. Focus: `border-color: accent` + `box-shadow: 0 0 0 2px accent-dim` +- **Text:** `text-heading` (`#f0f2f5`) → `text-primary` (`#e2e5eb`) → `text-muted-foreground` (`#848b9b`) → `text-muted` (`#4f5666`). NEVER use `text-secondary` — in Tailwind v4 it maps to a surface color, not a text color. +- **Borders:** `border-default` (`#2a2e3a`), `border-hover` (`#3d4252`) +- **Functional colors:** `#34d399` (success), `#fbbf24` (warning/amber), `#f87171` (danger), `#67e8f9` (info/cyan) — each with `-dim` variant at 10% opacity +- **Accent:** Electric blue `#60a5fa` (dark) / `#2563eb` (light) — used sparingly (≤5% of UI). `accent-dim` = `rgba(96,165,250,0.10)`, `accent-text` = `#93c5fd` +- **Deprecated:** Do NOT use `glass-card`, `glass-stat`, `bg-gradient-brand`, `text-gradient-brand`, `backdrop-filter: blur()`, ambient orbs, purple gradients, ember orange (`#f97316`), or cyan (`#22d3ee`) as accent — cyan is now the info color only --- @@ -518,3 +518,105 @@ When a feature, fix, or significant piece of work is finished and merged/committ | Bugs & Fixes | CLAUDE.md → Critical Lessons Learned section | | Design System | [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) | | Dev Environment | [DEV-ENV.md](DEV-ENV.md) — 46.202.92.250 setup, Docker, CORS, networking | + + +# GitNexus — Code Intelligence + +This project is indexed by GitNexus as **resolutionflow** (14787 symbols, 31366 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. + +> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. + +## Always Do + +- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. +- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. +- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. +- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. +- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`. + +## When Debugging + +1. `gitnexus_query({query: ""})` — find execution flows related to the issue +2. `gitnexus_context({name: ""})` — see all callers, callees, and process participation +3. `READ gitnexus://repo/resolutionflow/process/{processName}` — trace the full execution flow step by step +4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed + +## When Refactoring + +- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`. +- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code. +- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed. + +## Never Do + +- NEVER edit a function, class, or method without first running `gitnexus_impact` on it. +- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. +- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph. +- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope. + +## Tools Quick Reference + +| Tool | When to use | Command | +|------|-------------|---------| +| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` | +| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` | +| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` | +| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` | +| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` | +| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` | + +## Impact Risk Levels + +| Depth | Meaning | Action | +|-------|---------|--------| +| d=1 | WILL BREAK — direct callers/importers | MUST update these | +| d=2 | LIKELY AFFECTED — indirect deps | Should test | +| d=3 | MAY NEED TESTING — transitive | Test if critical path | + +## Resources + +| Resource | Use for | +|----------|---------| +| `gitnexus://repo/resolutionflow/context` | Codebase overview, check index freshness | +| `gitnexus://repo/resolutionflow/clusters` | All functional areas | +| `gitnexus://repo/resolutionflow/processes` | All execution flows | +| `gitnexus://repo/resolutionflow/process/{name}` | Step-by-step execution trace | + +## Self-Check Before Finishing + +Before completing any code modification task, verify: +1. `gitnexus_impact` was run for all modified symbols +2. No HIGH/CRITICAL risk warnings were ignored +3. `gitnexus_detect_changes()` confirms changes match expected scope +4. All d=1 (WILL BREAK) dependents were updated + +## Keeping the Index Fresh + +After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it: + +```bash +npx gitnexus analyze +``` + +If the index previously included embeddings, preserve them by adding `--embeddings`: + +```bash +npx gitnexus analyze --embeddings +``` + +To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.** + +> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`. + +## CLI + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | +| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | +| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | + + diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index ba49969e..aaff9d10 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -180,57 +180,6 @@ export default function LandingPage() { Built by a 15-year MSP veteran who got tired of empty ticket notes.

-
-
-
-
-
- 🔒 - app.resolutionflow.com/pilot -
-
-
-
-
-
- You - User can't access shared drive after password reset -
-
-
-
- - FlowPilot is thinking… -
-
-
-
- FlowPilot - Likely a cached credential issue. Let's check: -
-
-
-
- FlowPilot - 1. Run klist purge to clear Kerberos tickets -
-
-
-
- FlowPilot - 2. Credential Manager → remove saved share entries -
-
-
-
- Auto-doc - 3 steps captured ✓ -
-
-
-
-
-
diff --git a/frontend/src/styles/landing.css b/frontend/src/styles/landing.css index 56f36fc1..313b4d95 100644 --- a/frontend/src/styles/landing.css +++ b/frontend/src/styles/landing.css @@ -74,9 +74,8 @@ } .landing-nav.scrolled { - background: rgba(20, 22, 29, 0.95); + background: #0d0f15; border-bottom: 1px solid var(--lp-border); - backdrop-filter: blur(8px); } .landing-nav-inner { @@ -230,19 +229,44 @@ /* ---- HERO ---- */ .landing-hero { - padding: 9rem 2rem 5rem; + padding: 10rem 2rem 8rem; + position: relative; + overflow: hidden; + min-height: 580px; +} + +/* Full-bleed image layer — positioned lower so terrain fills, hub upper-right */ +.landing-hero::before { + content: ''; + position: absolute; + inset: 0; + background: url('/images/hero_001.jpg') 58% 38% / cover no-repeat; + opacity: 0.72; + z-index: 0; +} + +/* Left-to-right bleed: solid dark where text lives, dissolves into raw image on the right */ +.landing-hero::after { + content: ''; + position: absolute; + inset: 0; + background: + linear-gradient(to right, #14161d 22%, rgba(20, 22, 29, 0.80) 38%, rgba(20, 22, 29, 0.20) 58%, transparent 78%), + linear-gradient(to top, #14161d 0%, rgba(20, 22, 29, 0) 16%); + z-index: 1; } .landing-hero-inner { max-width: 1200px; margin: 0 auto; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 4rem; - align-items: center; + display: flex; + align-items: flex-start; + position: relative; + z-index: 2; } .landing-hero-content { + max-width: 520px; animation: landingFadeInUp 0.8s ease-out; } @@ -286,7 +310,7 @@ } .landing-hero-accent { - color: var(--lp-accent-text); + color: var(--lp-accent); } .landing-hero-sub { @@ -359,72 +383,205 @@ animation: landingPreviewEntrance 1s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both; } -/* ---- PREVIEW WINDOW ---- */ -.landing-preview-window { +/* ---- TICKET COMPARISON (hero visual) ---- */ +.landing-ticket-comparison { + display: flex; + align-items: stretch; + gap: 0; + width: 100%; +} + +.landing-tc-col { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; + min-width: 0; +} + +.landing-tc-label { + font-size: 0.6rem; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + padding: 0 0.25rem; +} + +.landing-tc-label.before-label { + color: var(--lp-text-dim); +} + +.landing-tc-label.after-label { + color: var(--lp-accent); +} + +.landing-tc-card { border-radius: 8px; border: 1px solid var(--lp-border); background: var(--lp-card); - overflow: hidden; - box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5); + padding: 0.875rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + flex: 1; } -.landing-preview-titlebar { +.landing-tc-card.before-card { + opacity: 0.55; +} + +.landing-tc-card.after-card { + border-color: rgba(96, 165, 250, 0.28); + box-shadow: 0 0 36px rgba(96, 165, 250, 0.10), 0 16px 48px rgba(0, 0, 0, 0.55); +} + +.landing-tc-header { display: flex; align-items: center; - gap: 12px; - padding: 10px 14px; - background: rgba(255, 255, 255, 0.02); + justify-content: space-between; + padding-bottom: 0.5rem; border-bottom: 1px solid var(--lp-border); } -.landing-preview-dots { - display: flex; - gap: 6px; +.tc-ticket-id { + font-size: 0.65rem; + font-weight: 700; + color: var(--lp-text-secondary); + font-family: 'IBM Plex Sans', sans-serif; } -.landing-preview-dots span { - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--lp-elevated); +.tc-status { + font-size: 0.58rem; + font-weight: 600; + padding: 2px 7px; + border-radius: 100px; + letter-spacing: 0.02em; } -.landing-preview-dots span:first-child { - background: #ef4444; +.tc-status.open { + background: rgba(248, 113, 113, 0.1); + color: #f87171; + border: 1px solid rgba(248, 113, 113, 0.2); } -.landing-preview-dots span:nth-child(2) { - background: #eab308; -} - -.landing-preview-dots span:last-child { - background: #22c55e; -} - -.landing-preview-url { - display: flex; - align-items: center; - gap: 6px; - padding: 3px 10px; - border-radius: 6px; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--lp-border); - font-size: 0.6rem; - color: var(--lp-text-dim); - flex: 1; - max-width: 260px; -} - -.landing-lock-icon { +.tc-status.resolved { + background: rgba(52, 211, 153, 0.1); color: var(--lp-success); - font-size: 0.55rem; + border: 1px solid rgba(52, 211, 153, 0.2); } -.landing-preview-body { - padding: 1.25rem; - min-height: 260px; +.landing-tc-subject { + font-size: 0.7rem; + font-weight: 600; + color: var(--lp-text-heading); + line-height: 1.4; +} + +.landing-tc-notes-heading { + font-size: 0.58rem; + font-weight: 600; + color: var(--lp-text-dim); + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.landing-tc-notes { + display: flex; + flex-direction: column; + gap: 0.3rem; + flex: 1; +} + +.landing-tc-notes.before-notes { + padding: 0.6rem 0.75rem; + border-radius: 5px; + background: rgba(255, 255, 255, 0.02); + border: 1px dashed rgba(255, 255, 255, 0.06); + font-size: 0.65rem; + color: var(--lp-text-dim); + font-style: italic; + min-height: 80px; + align-items: flex-start; + justify-content: flex-start; +} + +.tc-note { + display: flex; + align-items: baseline; + gap: 5px; + font-size: 0.62rem; + color: var(--lp-text-secondary); + line-height: 1.5; + animation: tcNoteIn 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; + animation-delay: calc(1.2s + var(--note-i, 0) * 0.55s); + opacity: 0; +} + +@keyframes tcNoteIn { + from { opacity: 0; transform: translateY(5px); } + to { opacity: 1; transform: translateY(0); } +} + +.tc-note.resolution-note { + margin-top: 0.15rem; + padding-top: 0.35rem; + border-top: 1px solid var(--lp-border); +} + +.tc-time { + font-size: 0.55rem; + color: var(--lp-text-dim); + font-family: 'JetBrains Mono', monospace; + flex-shrink: 0; + min-width: 28px; +} + +.tc-check { + color: var(--lp-success); + flex-shrink: 0; + font-size: 0.6rem; +} + +.tc-resolution-tag { + font-size: 0.52rem; + font-weight: 700; + padding: 1px 5px; + border-radius: 3px; + background: rgba(96, 165, 250, 0.12); + color: var(--lp-accent-text); + flex-shrink: 0; + letter-spacing: 0.02em; +} + +.landing-tc-footer { + font-size: 0.58rem; + padding-top: 0.4rem; + border-top: 1px solid var(--lp-border); + line-height: 1.4; +} + +.landing-tc-footer.before-footer { + color: var(--lp-text-dim); +} + +.landing-tc-footer.after-footer { + color: var(--lp-accent-text); + font-weight: 500; +} + +.landing-tc-divider { display: flex; align-items: center; + justify-content: center; + flex-shrink: 0; + width: 32px; + padding-top: 1.4rem; + color: var(--lp-text-dim); +} + +.landing-tc-divider svg { + width: 14px; + height: 14px; } /* ---- MOCK ELEMENTS ---- */ @@ -1255,79 +1412,6 @@ to { opacity: 1; transform: translateY(0) scale(1); } } -/* ---- CHAT ANIMATION ---- */ -.landing-chat-animated { - opacity: 0; - transform: translateX(-16px); - animation: landingChatSlideIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) both; - animation-delay: calc(1.5s + var(--chat-index, 0) * 1.2s); -} - -.landing-chat-animated:nth-child(2) { - animation: landingTypingLifecycle 3s ease both; - animation-delay: 2.7s; -} - -.landing-chat-animated:nth-child(3) { animation-delay: 5.7s; } -.landing-chat-animated:nth-child(4) { animation-delay: 6.7s; } -.landing-chat-animated:nth-child(5) { animation-delay: 7.7s; } -.landing-chat-animated:nth-child(6) { animation-delay: 9s; } - -@keyframes landingChatSlideIn { - from { opacity: 0; transform: translateX(-16px); } - to { opacity: 1; transform: translateX(0); } -} - -@keyframes landingTypingLifecycle { - 0% { opacity: 0; transform: translateX(-16px); } - 10% { opacity: 1; transform: translateX(0); } - 75% { opacity: 1; transform: translateX(0); } - 90% { opacity: 0; transform: translateX(0); } - 100% { opacity: 0; height: 0; padding: 0; margin: 0; overflow: hidden; } -} - -.landing-typing-indicator { - display: flex; - align-items: center; - gap: 5px; - padding: 6px 10px; - border-radius: 6px; - background: var(--lp-accent-soft); - border: 1px solid rgba(96, 165, 250, 0.1); - width: fit-content; -} - -.landing-typing-indicator span { - display: block; - width: 5px; - height: 5px; - border-radius: 50%; - background: var(--lp-accent); - opacity: 0.5; - animation: landingTypingBounce 1s ease-in-out infinite; -} - -.landing-typing-indicator span:nth-child(2) { animation-delay: 0.15s; } -.landing-typing-indicator span:nth-child(3) { animation-delay: 0.3s; } - -.landing-typing-label { - font-size: 0.6rem; - color: var(--lp-accent); - font-weight: 500; - white-space: nowrap; - /* Override dot styles */ - width: auto !important; - height: auto !important; - border-radius: 0 !important; - background: transparent !important; - opacity: 1 !important; - animation: none !important; -} - -@keyframes landingTypingBounce { - 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } - 30% { transform: translateY(-4px); opacity: 1; } -} /* ---- SCROLL REVEAL ---- */ .landing-reveal { @@ -1617,7 +1701,6 @@ /* ---- REDUCED MOTION ---- */ @media (prefers-reduced-motion: reduce) { - .landing-chat-animated, .landing-hero-visual, .landing-hero-content { opacity: 1; @@ -1625,13 +1708,10 @@ animation: none; } - .landing-typing-indicator span { + .tc-note { + opacity: 1; + transform: none; animation: none; - opacity: 0.6; - } - - .landing-chat-animated:nth-child(2) { - display: none; } .landing-reveal {