Open Source
diff --git a/website/src/components/OverlayCapsule.tsx b/website/src/components/OverlayCapsule.tsx
new file mode 100644
index 0000000..9d8e523
--- /dev/null
+++ b/website/src/components/OverlayCapsule.tsx
@@ -0,0 +1,136 @@
+import { useEffect, useRef } from 'react'
+
+// Faithful recreation of the app's floating overlay capsule.
+// Waveform envelope mirrors WaveformView in OverlayPanelController.swift so the
+// motion reads as the real product, not a generic equalizer.
+
+export type CapsulePhase =
+ | 'idle'
+ | 'listening'
+ | 'transcribing'
+ | 'refining'
+ | 'injecting'
+
+const BAR_WEIGHTS = [0.58, 0.88, 1.0, 0.84, 0.62]
+const BAR_POSITIONS = [-2, -1, 0, 1, 2]
+const SPATIAL_PHASE_STEP = Math.PI / 3.2
+const LOOP_DURATION = 0.84 // seconds, matches the app
+const BAR_MIN = 9
+const BAR_MAX = 30
+
+// Target "loudness" per phase. Listening is lively; processing phases settle.
+function rmsFloor(phase: CapsulePhase): number {
+ switch (phase) {
+ case 'listening':
+ return 0.92
+ case 'transcribing':
+ return 0.34
+ case 'refining':
+ return 0.26
+ case 'injecting':
+ return 0.16
+ default:
+ return 0.18
+ }
+}
+
+// Synthesised speech-like loudness so the bars feel driven by a real voice.
+function liveRms(phase: CapsulePhase, t: number): number {
+ if (phase === 'listening') {
+ const a = (Math.sin(t * 6.1) + 1) * 0.5
+ const b = (Math.sin(t * 11.7 + 1.3) + 1) * 0.5
+ const c = (Math.sin(t * 2.3 + 0.6) + 1) * 0.5
+ const env = 0.45 + 0.55 * (a * 0.5 + b * 0.3 + c * 0.2)
+ return Math.min(1, Math.max(0.4, env))
+ }
+ return rmsFloor(phase)
+}
+
+function barHeight(index: number, time: number, rms: number): number {
+ const cycle = (time % LOOP_DURATION) / LOOP_DURATION
+ const phase = cycle * Math.PI * 2
+ const localPhase = phase - BAR_POSITIONS[index] * SPATIAL_PHASE_STEP
+ const primary = (Math.sin(localPhase) + 1) * 0.5
+ const secondary = (Math.sin(localPhase * 2 - Math.PI / 6) + 1) * 0.5
+ const envelope = primary * 0.82 + secondary * 0.18
+ const activity = Math.max(0.18, Math.min(1, rms))
+ const range = (BAR_MAX - BAR_MIN) * BAR_WEIGHTS[index] * activity
+ return BAR_MIN + range * envelope
+}
+
+function Waveform({ phase }: { phase: CapsulePhase }) {
+ const barsRef = useRef<(HTMLSpanElement | null)[]>([])
+ const barSetters = useRef<((el: HTMLSpanElement | null) => void)[]>([])
+ const phaseRef = useRef(phase)
+ phaseRef.current = phase
+
+ // Stable per-bar ref callback so re-renders don't detach/reattach each node.
+ const setBarRef = (i: number) => {
+ if (!barSetters.current[i]) {
+ barSetters.current[i] = (el: HTMLSpanElement | null) => {
+ barsRef.current[i] = el
+ }
+ }
+ return barSetters.current[i]
+ }
+
+ // Empty deps on purpose: the rAF loop runs for the component's lifetime and
+ // reads the latest phase via phaseRef, so it never needs to restart.
+ useEffect(() => {
+ const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches
+ if (reduce) {
+ barsRef.current.forEach((bar, i) => {
+ if (bar) bar.style.transform = `scaleY(${barHeight(i, 0.42, 0.6) / BAR_MAX})`
+ })
+ return
+ }
+
+ let raf = 0
+ const start = performance.now()
+ const tick = (now: number) => {
+ const t = (now - start) / 1000
+ const rms = liveRms(phaseRef.current, t)
+ for (let i = 0; i < BAR_WEIGHTS.length; i += 1) {
+ const bar = barsRef.current[i]
+ if (bar) bar.style.transform = `scaleY(${barHeight(i, t, rms) / BAR_MAX})`
+ }
+ raf = requestAnimationFrame(tick)
+ }
+ raf = requestAnimationFrame(tick)
+ return () => cancelAnimationFrame(raf)
+ }, [])
+
+ return (
+
+ {BAR_WEIGHTS.map((_, i) => (
+
+ ))}
+
+ )
+}
+
+type OverlayCapsuleProps = {
+ phase: CapsulePhase
+ text: string
+ className?: string
+}
+
+const STATUS_LABEL: Partial
> = {
+ transcribing: 'Transcribing…',
+ refining: 'Refining…',
+ injecting: 'Injecting…',
+}
+
+export function OverlayCapsule({ phase, text, className }: OverlayCapsuleProps) {
+ const display = text || STATUS_LABEL[phase] || (phase === 'listening' ? 'Listening…' : '')
+ const isPlaceholder = !text
+
+ return (
+
+ )
+}
diff --git a/website/src/components/content.ts b/website/src/components/content.ts
index 6a239b4..62c7359 100644
--- a/website/src/components/content.ts
+++ b/website/src/components/content.ts
@@ -1,42 +1,8 @@
-export type DemoExample = {
- app: string
- said: string
- writes: string
-}
-
export type SupportedApp = {
iconPath: string
name: string
}
-export const demoExamples: DemoExample[] = [
- {
- app: 'Slack',
- said: 'tell jason that api bug is fixed\nhe can pull the latest main branch',
- writes:
- 'Hey Jason - the API bug has been fixed.\nPull the latest from main when you get a chance.'
- },
- {
- app: 'Cursor',
- said: 'this function cleans up the user input\nand saves it to the database',
- writes:
- '// Sanitizes raw user input and persists\n// the cleaned result to the database.'
- },
- {
- app: 'Gmail',
- said:
- "tell the client the project is delayed one week\nbecause we're waiting on a third party api",
- writes:
- "Hi - I wanted to give you a quick update on the timeline. We're currently waiting on a third-party API integration, which will push delivery back by approximately one week. I'll keep you posted on any changes."
- },
- {
- app: 'ChatGPT',
- said: 'write a prompt that makes gpt analyze\nthis csv and find outliers',
- writes:
- 'Analyze the attached CSV file. Identify statistical outliers in each numeric column using the IQR method. Flag rows that contain outliers and explain why each value is anomalous.'
- }
-]
-
export const apps: SupportedApp[] = [
{ iconPath: '/app-icons/cursor.svg', name: 'Cursor' },
{ iconPath: '/app-icons/vscode.svg', name: 'VS Code' },
@@ -48,24 +14,6 @@ export const apps: SupportedApp[] = [
{ iconPath: '/app-icons/chatgpt.svg', name: 'ChatGPT' }
]
-export const capabilities = [
- {
- title: 'AI Rewrite',
- copy:
- 'Say it rough, get it polished. Not autocorrect - a full rewrite that matches the tone your context needs.'
- },
- {
- title: 'Refine for the moment',
- copy:
- 'Turn a spoken note into a concise code comment, a professional email, a casual message, or a structured prompt.'
- },
- {
- title: 'Always Ready',
- copy:
- 'One shortcut, anywhere on your Mac. No app switching, no input panel, no copy-pasting.'
- }
-]
-
export const openSourceBenefits = [
{
title: 'Transparent by default',
diff --git a/website/src/components/useDictationScene.ts b/website/src/components/useDictationScene.ts
new file mode 100644
index 0000000..00d277b
--- /dev/null
+++ b/website/src/components/useDictationScene.ts
@@ -0,0 +1,131 @@
+import { useEffect, useRef, useState } from 'react'
+
+import type { CapsulePhase } from './OverlayCapsule'
+
+export type SceneExample = {
+ app: string
+ said: string
+ writes: string
+}
+
+export type SceneView = {
+ phase: CapsulePhase
+ capsuleText: string
+ output: string
+ injected: boolean
+ index: number
+}
+
+// Dictation phase timeline, in ms from the start of each loop. The phases run:
+// listen+type → hold → transcribe → refine → inject(type) → hold, then repeat.
+const TYPE_SAID_MS = 1700 // time to type out the spoken text
+const LISTEN_TYPING_END = 1900 // listening while the typed text settles
+const LISTEN_HOLD_END = 2900 // full spoken text held before processing
+const TRANSCRIBE_END = 3400
+const REFINE_END = 4200
+const INJECT_TYPE_MS = 950 // time to type the injected result
+const INJECT_TYPING_END = 5300 // result fully injected, then held until LOOP_MS
+const LOOP_MS = 7200
+
+// Frozen frame shown when the scene is off-screen or motion is reduced.
+const REDUCED_MOTION_ELAPSED_MS = 5000
+
+// One full dictation cycle expressed as a pure function of elapsed time, so the
+// loop can be paused/resumed and stays in sync with the waveform.
+function viewAt(totalElapsed: number, examples: SceneExample[]): SceneView {
+ if (examples.length === 0) return RESTING
+ const index = Math.floor(totalElapsed / LOOP_MS) % examples.length
+ const t = totalElapsed % LOOP_MS
+ const { said, writes } = examples[index]
+
+ if (t < LISTEN_TYPING_END) {
+ const p = Math.min(1, t / TYPE_SAID_MS)
+ return {
+ phase: 'listening',
+ capsuleText: said.slice(0, Math.ceil(said.length * p)),
+ output: '',
+ injected: false,
+ index,
+ }
+ }
+ if (t < LISTEN_HOLD_END) {
+ return { phase: 'listening', capsuleText: said, output: '', injected: false, index }
+ }
+ if (t < TRANSCRIBE_END) {
+ return { phase: 'transcribing', capsuleText: '', output: '', injected: false, index }
+ }
+ if (t < REFINE_END) {
+ return { phase: 'refining', capsuleText: '', output: '', injected: false, index }
+ }
+ if (t < INJECT_TYPING_END) {
+ const p = Math.min(1, (t - REFINE_END) / INJECT_TYPE_MS)
+ return {
+ phase: 'injecting',
+ capsuleText: '',
+ output: writes.slice(0, Math.ceil(writes.length * p)),
+ injected: true,
+ index,
+ }
+ }
+ // hold the finished result, then fade handled by the consumer
+ return { phase: 'injecting', capsuleText: '', output: writes, injected: true, index }
+}
+
+const RESTING: SceneView = {
+ phase: 'listening',
+ capsuleText: '',
+ output: '',
+ injected: false,
+ index: 0,
+}
+
+const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)'
+
+export function useDictationScene(examples: SceneExample[], active: boolean): SceneView {
+ const [view, setView] = useState(RESTING)
+ const [reducedMotion, setReducedMotion] = useState(
+ () => typeof window !== 'undefined' && window.matchMedia(REDUCED_MOTION_QUERY).matches
+ )
+ const startRef = useRef(null)
+ const lastRef = useRef('')
+
+ // Track the preference reactively so toggling "Reduce motion" takes effect
+ // immediately, not only after the scene scrolls out and back.
+ useEffect(() => {
+ const media = window.matchMedia(REDUCED_MOTION_QUERY)
+ const update = () => setReducedMotion(media.matches)
+ update()
+ media.addEventListener('change', update)
+ return () => media.removeEventListener('change', update)
+ }, [])
+
+ useEffect(() => {
+ if (!active) {
+ startRef.current = null
+ return
+ }
+ if (reducedMotion) {
+ setView(viewAt(REDUCED_MOTION_ELAPSED_MS, examples))
+ return
+ }
+
+ // Re-baseline on every (re)start so an examples swap can't carry a stale
+ // timestamp into the elapsed calculation.
+ startRef.current = null
+ let raf = 0
+ const tick = (now: number) => {
+ if (startRef.current === null) startRef.current = now
+ const next = viewAt(now - startRef.current, examples)
+ const sig = `${next.index}|${next.phase}|${next.capsuleText.length}|${next.output.length}`
+ if (sig !== lastRef.current) {
+ lastRef.current = sig
+ setView(next)
+ }
+ raf = requestAnimationFrame(tick)
+ }
+ raf = requestAnimationFrame(tick)
+ return () => cancelAnimationFrame(raf)
+ }, [active, examples, reducedMotion])
+
+ return view
+}
diff --git a/website/src/styles.css b/website/src/styles.css
index 7abe28f..08be693 100644
--- a/website/src/styles.css
+++ b/website/src/styles.css
@@ -38,7 +38,9 @@ a {
}
.page {
- overflow: hidden;
+ /* overflow-x: clip (not hidden) so the sticky feature nav still works —
+ `hidden` would turn .page into a scroll container and break position: sticky. */
+ overflow-x: clip;
}
.site-header {
@@ -140,12 +142,28 @@ a {
position: relative;
display: grid;
isolation: isolate;
- min-height: 100vh;
- place-items: center;
- padding: 116px 22px 74px;
+ min-height: 88vh;
+ /* Top-anchored (not centred) so the copy sits higher and the empty space
+ above shrinks while the lower density eases up. */
+ place-items: start center;
+ align-content: start;
+ padding: clamp(150px, 19vh, 210px) 22px 0;
text-align: center;
}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ clip-path: inset(50%);
+ white-space: nowrap;
+ border: 0;
+}
+
.dot-field-background {
position: absolute;
inset: 0;
@@ -237,18 +255,6 @@ a {
animation: cue-bounce 1.8s ease-in-out infinite;
}
-.demo-section {
- padding: 96px 22px 72px;
-}
-
-.section-heading {
- width: min(720px, 100%);
- margin: 0 auto 42px;
- text-align: center;
-}
-
-.section-heading h2,
-.capabilities-section h2,
.final-cta h2 {
margin: 0;
font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
@@ -258,158 +264,6 @@ a {
letter-spacing: 0;
}
-.section-heading h2 {
- font-size: 58px;
-}
-
-.section-heading p {
- margin: 18px 0 0;
- color: var(--muted);
- font-size: 18px;
- line-height: 1.65;
-}
-
-.demo-card {
- position: relative;
- width: min(1010px, 100%);
- min-height: 430px;
- margin: 0 auto;
- padding: clamp(24px, 3.3vw, 44px);
- border: 1px solid var(--line);
- border-radius: var(--radius);
- background:
- radial-gradient(circle at 50% 45%, var(--glow), transparent 38%),
- linear-gradient(180deg, #fff, #fafbfc);
- box-shadow: var(--shadow);
- transition: opacity 300ms ease;
-}
-
-.demo-card-fade {
- opacity: 0.34;
-}
-
-.demo-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 18px;
- padding-bottom: 24px;
- border-bottom: 1px solid var(--line);
-}
-
-.demo-app {
- display: inline-flex;
- align-items: center;
- gap: 9px;
- font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-size: 15px;
- font-weight: 750;
-}
-
-.demo-app::before {
- content: '';
- width: 10px;
- height: 10px;
- border-radius: 50%;
- background: var(--blue);
- box-shadow: 0 0 0 7px rgba(24, 119, 255, 0.1);
-}
-
-.demo-status {
- color: #7c8188;
- font-size: 14px;
- font-weight: 650;
-}
-
-.demo-columns {
- display: grid;
- grid-template-columns: 1fr 72px 1fr;
- gap: 0;
- min-height: 270px;
- align-items: stretch;
-}
-
-.demo-panel {
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding: 26px 0;
-}
-
-.demo-label {
- color: #8a8f96;
- font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-size: 13px;
- font-weight: 750;
- letter-spacing: 0.02em;
- text-transform: uppercase;
-}
-
-.demo-panel p {
- margin: 16px 0 0;
- white-space: pre-wrap;
- color: #121212;
- font-size: clamp(20px, 2vw, 28px);
- line-height: 1.45;
- font-weight: 500;
-}
-
-.demo-panel-said p {
- color: #6b7077;
-}
-
-.demo-panel-writes {
- opacity: 0.24;
- transform: translateY(8px);
- transition:
- opacity 260ms ease,
- transform 260ms ease;
-}
-
-.demo-card-writes .demo-panel-writes,
-.demo-card-hold .demo-panel-writes,
-.demo-card-fade .demo-panel-writes {
- opacity: 1;
- transform: translateY(0);
-}
-
-.demo-process {
- display: grid;
- place-items: center;
-}
-
-.demo-process span {
- display: block;
- width: 46px;
- height: 1px;
- background: #d8dbe0;
- transform-origin: center;
-}
-
-.demo-card-processing .demo-process span {
- height: 2px;
- background: var(--blue);
- box-shadow: 0 0 22px var(--glow);
- animation: process-line 900ms ease-in-out infinite;
-}
-
-.demo-dots {
- display: flex;
- justify-content: center;
- gap: 9px;
-}
-
-.demo-dots span {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: #d6d9de;
-}
-
-.demo-dots .is-active {
- background: var(--ink);
-}
-
.works-section {
width: min(1240px, calc(100% - 44px));
margin: 0 auto;
@@ -468,43 +322,6 @@ a {
object-fit: contain;
}
-.capabilities-section {
- width: min(1120px, calc(100% - 44px));
- margin: 0 auto;
- padding: 34px 0 78px;
- text-align: center;
-}
-
-.capability-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 18px;
- margin-top: 38px;
- text-align: left;
-}
-
-.capability-card {
- min-height: 230px;
- padding: 30px;
- border: 1px solid var(--line);
- border-radius: 24px;
- background: #fff;
-}
-
-.capability-card h3 {
- margin: 0;
- font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-size: 24px;
- line-height: 1.18;
-}
-
-.capability-card p {
- margin: 24px 0 0;
- color: var(--muted);
- font-size: 17px;
- line-height: 1.66;
-}
-
.open-source-section {
display: grid;
grid-template-columns: minmax(300px, 0.9fr) minmax(0, 1.35fr);
@@ -651,16 +468,6 @@ a {
}
}
-@keyframes process-line {
- 0%,
- 100% {
- transform: scaleX(0.44);
- }
- 50% {
- transform: scaleX(1);
- }
-}
-
@keyframes app-marquee {
from {
transform: translateX(0);
@@ -707,29 +514,6 @@ a {
font-size: clamp(46px, 11.6vw, 78px);
}
- .demo-columns {
- grid-template-columns: 1fr;
- }
-
- .demo-process {
- min-height: 48px;
- }
-
- .demo-process span {
- width: 1px;
- height: 32px;
- }
-
- .demo-card-processing .demo-process span {
- width: 2px;
- height: 38px;
- animation: process-line-vertical 900ms ease-in-out infinite;
- }
-
- .capability-grid {
- grid-template-columns: 1fr;
- }
-
.open-source-section {
grid-template-columns: 1fr;
min-height: auto;
@@ -800,31 +584,7 @@ a {
bottom: 20px;
}
- .demo-section {
- padding: 74px 18px 54px;
- }
-
- .section-heading h2 {
- font-size: 36px;
- }
-
- .demo-card {
- min-height: 560px;
- padding: 22px;
- border-radius: 22px;
- }
-
- .demo-header {
- align-items: flex-start;
- flex-direction: column;
- }
-
- .demo-panel p {
- font-size: 19px;
- }
-
.works-section,
- .capabilities-section,
.open-source-section {
width: calc(100% - 36px);
}
@@ -849,11 +609,6 @@ a {
height: 28px;
}
- .capability-card {
- min-height: auto;
- padding: 24px;
- }
-
.open-source-section {
gap: 38px;
padding: 70px 0 74px;
@@ -896,12 +651,1088 @@ a {
}
}
-@keyframes process-line-vertical {
- 0%,
- 100% {
- transform: scaleY(0.44);
- }
- 50% {
- transform: scaleY(1);
+/* ============================================================
+ App-true recreation: window chrome, overlay capsule,
+ dashboard showcase, and the sticky feature scroller.
+ ============================================================ */
+
+/* --- macOS window chrome --- */
+.app-window {
+ position: relative;
+ overflow: hidden;
+ background: #fff;
+ border: 1px solid #e6e8ec;
+ border-radius: 16px;
+ box-shadow:
+ 0 44px 110px rgba(15, 20, 28, 0.16),
+ 0 10px 30px rgba(15, 20, 28, 0.06);
+}
+
+.win-toolbar {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ height: 46px;
+ padding: 0 16px;
+ border-bottom: 1px solid #eef0f3;
+ background: #fbfbfc;
+}
+
+.win-lights {
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 8px;
+}
+
+.win-light {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+}
+
+.win-close {
+ background: #ff5f57;
+}
+
+.win-min {
+ background: #febc2e;
+}
+
+.win-zoom {
+ background: #28c840;
+}
+
+.win-body.is-flush {
+ padding: 0;
+}
+
+/* --- Overlay capsule (the real floating overlay) --- */
+.overlay-capsule {
+ display: inline-flex;
+ align-items: center;
+ gap: 12px;
+ min-width: 150px;
+ max-width: 440px;
+ height: 54px;
+ padding: 0 18px;
+ border-radius: 999px;
+ background: #0a0a0b;
+ box-shadow:
+ 0 20px 44px rgba(8, 10, 14, 0.36),
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
+}
+
+.oc-wave {
+ display: inline-flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: center;
+ gap: 3px;
+ width: 46px;
+ height: 32px;
+}
+
+.oc-bar {
+ width: 3px;
+ height: 30px;
+ border-radius: 2px;
+ background: rgba(255, 255, 255, 0.97);
+ transform: scaleY(0.3);
+ transform-origin: center;
+ will-change: transform;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.18);
+}
+
+.oc-text-viewport {
+ display: flex;
+ flex: 1 1 auto;
+ min-width: 0;
+ justify-content: flex-end;
+ overflow: hidden;
+ -webkit-mask-image: linear-gradient(90deg, transparent, #000 18px);
+ mask-image: linear-gradient(90deg, transparent, #000 18px);
+}
+
+.oc-text {
+ white-space: nowrap;
+ color: #fff;
+ font-size: 16px;
+ font-weight: 500;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
+}
+
+.oc-text.is-placeholder {
+ color: rgba(255, 255, 255, 0.66);
+}
+
+/* --- Showcase: dashboard peeking up from the hero --- */
+.showcase {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ place-items: start center;
+ margin-top: clamp(-220px, -13vh, -120px);
+ padding: 0 22px 96px;
+}
+
+.showcase-frame {
+ width: min(1140px, 100%);
+}
+
+.showcase .scroll-cue {
+ position: absolute;
+ top: -42px;
+ bottom: auto;
+}
+
+/* --- Dashboard recreation --- */
+.dash {
+ display: grid;
+ grid-template-columns: 172px 1fr;
+ min-height: 560px;
+}
+
+.dash-sidebar {
+ display: flex;
+ flex-direction: column;
+ padding: 0 12px 16px;
+ background: linear-gradient(180deg, #eef1f5, #e8ecf1);
+ border-right: 1px solid #e3e6ec;
+}
+
+.dash-sidebar-top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 46px;
+}
+
+.dash-sidebar-toggle {
+ color: #9aa0a8;
+}
+
+.dash-brand {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 4px 6px 16px;
+ font-size: 17px;
+ font-weight: 800;
+}
+
+.dash-brand img {
+ width: 24px;
+ height: 24px;
+ border-radius: 7px;
+}
+
+.dash-nav-group {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin-bottom: 14px;
+}
+
+.dash-nav-label {
+ padding: 0 8px 6px;
+ color: #9aa0a8;
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.dash-nav-item {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ padding: 7px 9px;
+ border-radius: 8px;
+ color: #3f444b;
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.dash-nav-item.is-active {
+ background: var(--blue);
+ color: #fff;
+ box-shadow: 0 6px 14px rgba(24, 119, 255, 0.3);
+}
+
+.dash-main {
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+ min-width: 0;
+ padding: 18px 22px 26px;
+}
+
+.dash-topbar {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 16px;
+}
+
+.dash-title h3 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 800;
+}
+
+.dash-title p {
+ margin: 4px 0 0;
+ color: #888d94;
+ font-size: 13px;
+}
+
+.dash-pills {
+ display: flex;
+ flex: 0 0 auto;
+ gap: 8px;
+}
+
+.dash-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ height: 28px;
+ padding: 0 11px;
+ border: 1px solid #e3e6ec;
+ border-radius: 999px;
+ background: #fff;
+ color: #4a4f56;
+ font-size: 12.5px;
+ font-weight: 650;
+}
+
+.dash-pill-dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: #28c840;
+}
+
+.dash-stats {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 12px;
+}
+
+.dash-stat {
+ display: flex;
+ flex-direction: column;
+ padding: 14px 15px;
+ border: 1px solid #ebedf0;
+ border-radius: 14px;
+}
+
+.dash-stat.is-highlight {
+ background: #eef4ff;
+ border-color: #bcd6ff;
+}
+
+.dash-stat-dot {
+ width: 9px;
+ height: 9px;
+ margin-bottom: 11px;
+ border-radius: 50%;
+}
+
+.tone-blue {
+ background: var(--blue);
+}
+
+.tone-green {
+ background: #27c06b;
+}
+
+.tone-amber {
+ background: #ff9f1a;
+}
+
+.tone-pink {
+ background: #ff5d8f;
+}
+
+.dash-stat-label {
+ color: #888d94;
+ font-size: 12.5px;
+ font-weight: 600;
+}
+
+.dash-stat-value {
+ margin: 6px 0 8px;
+ font-size: 26px;
+ font-weight: 800;
+ letter-spacing: -0.01em;
+}
+
+.dash-stat-caption {
+ color: #a6abb2;
+ font-size: 11.5px;
+}
+
+.dash-charts {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+}
+
+.dash-chart {
+ padding: 16px;
+ border: 1px solid #ebedf0;
+ border-radius: 14px;
+}
+
+.dash-chart-head h5 {
+ margin: 0;
+ font-size: 14px;
+ font-weight: 750;
+}
+
+.dash-chart-head p {
+ margin: 3px 0 12px;
+ color: #9aa0a8;
+ font-size: 12px;
+}
+
+/* Scene donut */
+.scene-body {
+ display: flex;
+ align-items: center;
+ gap: 18px;
+}
+
+.scene-donut {
+ position: relative;
+ flex: 0 0 auto;
+ width: 132px;
+ height: 132px;
+}
+
+.scene-donut svg {
+ width: 100%;
+ height: 100%;
+}
+
+.scene-donut-center {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.scene-donut-center strong {
+ font-size: 28px;
+ font-weight: 800;
+ letter-spacing: -0.01em;
+}
+
+.scene-donut-center span {
+ color: #9aa0a8;
+ font-size: 12px;
+}
+
+.scene-legend {
+ display: flex;
+ flex: 1;
+ min-width: 0;
+ flex-direction: column;
+ gap: 9px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.scene-legend li {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ font-size: 13.5px;
+}
+
+.scene-legend-dot {
+ width: 9px;
+ height: 9px;
+ flex: 0 0 auto;
+ border-radius: 50%;
+}
+
+.scene-legend-name {
+ overflow: hidden;
+ color: #3f444b;
+ font-weight: 600;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.scene-legend-pct {
+ margin-left: auto;
+ color: #6a6f76;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+}
+
+/* Hours bars */
+.hours-bars {
+ display: flex;
+ align-items: flex-end;
+ gap: 6px;
+ height: 132px;
+ padding-top: 8px;
+}
+
+.hours-bar {
+ flex: 1;
+ min-height: 6px;
+ border-radius: 5px 5px 3px 3px;
+ background: #1f86ff;
+}
+
+.hours-bar.is-faint {
+ background: #cfe2ff;
+}
+
+.hours-axis {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 8px;
+ color: #b0b5bb;
+ font-size: 11px;
+}
+
+.dash-history {
+ padding: 16px;
+ border: 1px solid #ebedf0;
+ border-radius: 14px;
+}
+
+.dash-history-head h4 {
+ margin: 0;
+ font-size: 14px;
+ font-weight: 750;
+}
+
+.dash-history-head p {
+ margin: 3px 0 12px;
+ color: #9aa0a8;
+ font-size: 12px;
+}
+
+.dash-history-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 13px;
+ border: 1px solid #eef0f3;
+ border-radius: 12px;
+ background: #fafbfc;
+}
+
+.dash-history-body {
+ flex: 1;
+ min-width: 0;
+}
+
+.dash-history-meta {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.dash-history-meta strong {
+ font-size: 13px;
+}
+
+.dash-history-tag {
+ padding: 2px 7px;
+ border-radius: 6px;
+ background: #e7f7ee;
+ color: #1f8f4e;
+ font-size: 10.5px;
+ font-weight: 700;
+}
+
+.dash-history-tag.is-muted {
+ background: #f0f1f4;
+ color: #80858c;
+}
+
+.dash-history-sub {
+ display: block;
+ margin: 3px 0 6px;
+ color: #9aa0a8;
+ font-size: 11.5px;
+}
+
+.dash-history-body p {
+ margin: 0;
+ color: #2a2e34;
+ font-size: 13.5px;
+}
+
+.dash-history-copy {
+ flex: 0 0 auto;
+ color: var(--blue);
+ font-size: 12px;
+ font-weight: 650;
+}
+
+.dashboard-shell {
+ position: relative;
+}
+
+/* Float the overlay capsule over the bottom edge of the window (mostly below),
+ matching the floating-overlay feel and the feature-scene capsules. */
+.dash-overlay-dock {
+ position: absolute;
+ left: 50%;
+ bottom: -27px;
+ z-index: 3;
+ transform: translateX(-50%);
+}
+
+/* --- Sticky feature scroller --- */
+.features {
+ width: min(1320px, calc(100% - 44px));
+ margin: 0 auto;
+ padding: 60px 0;
+}
+
+.features-grid {
+ display: grid;
+ grid-template-columns: 220px 1fr;
+ gap: clamp(32px, 5vw, 84px);
+ align-items: start;
+}
+
+.features-nav {
+ position: sticky;
+ top: 0;
+ align-self: start;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ /* Same padding-top as .feature-block so the nav's first line starts on the
+ exact same row as each section heading — in flow AND once pinned. */
+ padding-top: clamp(104px, 13vh, 150px);
+}
+
+.features-nav-kicker {
+ margin-bottom: 8px;
+ color: #b0b5bb;
+ font-size: 12px;
+ font-weight: 750;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+}
+
+.features-nav ul {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.features-nav li {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: #c2c6cc;
+ font-size: 18px;
+ font-weight: 700;
+ transition: color 0.3s ease;
+}
+
+.features-nav-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #d6d9de;
+ transition:
+ background 0.3s ease,
+ box-shadow 0.3s ease,
+ transform 0.3s ease;
+}
+
+.features-nav li.is-active {
+ color: #0a0a0a;
+}
+
+.features-nav li.is-active .features-nav-dot {
+ background: #0a0a0a;
+ box-shadow: 0 0 0 5px rgba(10, 10, 10, 0.08);
+ transform: scale(1.1);
+}
+
+.features-flow {
+ display: flex;
+ flex-direction: column;
+ gap: clamp(8px, 3vh, 48px);
+ /* Let the 1fr grid track shrink below the scenes' min-content width. */
+ min-width: 0;
+}
+
+.feature-block {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ gap: 30px;
+ min-height: 84vh;
+ /* Keep in sync with .features-nav top. */
+ padding-top: clamp(104px, 13vh, 150px);
+}
+
+.feature-eyebrow {
+ color: var(--blue);
+ font-size: 13px;
+ font-weight: 750;
+ letter-spacing: 0.02em;
+}
+
+.feature-block h3 {
+ margin: 14px 0 0;
+ font-size: clamp(34px, 4.4vw, 58px);
+ line-height: 1.03;
+ font-weight: 780;
+ letter-spacing: -0.015em;
+}
+
+.feature-block p {
+ max-width: 560px;
+ margin: 20px 0 0;
+ color: #5a5f66;
+ font-size: 18px;
+ line-height: 1.62;
+}
+
+/* --- Scene scaffolding --- */
+.scene {
+ position: relative;
+ width: 100%;
+}
+
+.scene-dock {
+ position: absolute;
+ left: 50%;
+ bottom: -26px;
+ z-index: 4;
+ transform: translateX(-50%);
+}
+
+/* Scene 1: Slack-style rewrite */
+.chat-window {
+ width: min(640px, 100%);
+ margin: 0 auto;
+}
+
+.win-toolbar-inner {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ min-width: 0;
+ flex: 1;
+}
+
+.chat-workspace {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ font-size: 13.5px;
+ font-weight: 750;
+}
+
+.chat-workspace-badge {
+ display: grid;
+ place-items: center;
+ width: 20px;
+ height: 20px;
+ border-radius: 6px;
+ background: #4a154b;
+ color: #fff;
+ font-size: 11px;
+ font-weight: 800;
+}
+
+.chat-channel {
+ color: #6a6f76;
+ font-size: 13.5px;
+ font-weight: 650;
+}
+
+.chat-thread {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ min-height: 196px;
+ padding: 20px;
+}
+
+.chat-msg {
+ display: flex;
+ gap: 11px;
+}
+
+.chat-avatar {
+ display: grid;
+ place-items: center;
+ flex: 0 0 auto;
+ width: 34px;
+ height: 34px;
+ border-radius: 9px;
+ color: #fff;
+ font-size: 14px;
+ font-weight: 800;
+}
+
+.chat-name {
+ font-size: 13.5px;
+ font-weight: 750;
+}
+
+.chat-name em {
+ margin-left: 6px;
+ color: #a2a7ae;
+ font-size: 11.5px;
+ font-style: normal;
+ font-weight: 600;
+}
+
+.chat-msg p {
+ margin: 3px 0 0;
+ color: #2a2e34;
+ font-size: 14.5px;
+ line-height: 1.5;
+}
+
+.chat-msg-me {
+ animation: msg-pop 0.4s ease both;
+}
+
+@keyframes msg-pop {
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.chat-compose {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin: 0 20px 20px;
+ padding: 12px 14px;
+ border: 1px solid #e2e5ea;
+ border-radius: 12px;
+}
+
+.chat-input {
+ flex: 1;
+ min-width: 0;
+ color: #1f2329;
+ font-size: 14.5px;
+}
+
+.chat-input.is-empty {
+ color: #aab0b7;
+}
+
+.chat-caret {
+ display: inline-block;
+ width: 2px;
+ height: 16px;
+ margin-left: 1px;
+ vertical-align: -3px;
+ background: var(--blue);
+ animation: caret-blink 1s steps(1) infinite;
+}
+
+@keyframes caret-blink {
+ 50% {
+ opacity: 0;
+ }
+}
+
+.chat-send {
+ display: grid;
+ place-items: center;
+ width: 30px;
+ height: 30px;
+ border-radius: 8px;
+ background: #eceef1;
+ color: #b3b8bf;
+ font-size: 13px;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.chat-send.is-ready {
+ background: var(--blue);
+ color: #fff;
+}
+
+/* Scene 2: context tabs */
+.context-window {
+ width: min(640px, 100%);
+ margin: 0 auto;
+}
+
+.context-app {
+ flex: 0 0 auto;
+ padding: 3px 10px;
+ border-radius: 7px;
+ background: #f1f3f6;
+ color: #3f444b;
+ font-size: 12.5px;
+ font-weight: 750;
+}
+
+.context-said {
+ overflow: hidden;
+ color: #9aa0a8;
+ font-size: 13px;
+ font-style: italic;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.context-tabs {
+ display: flex;
+ gap: 8px;
+ padding: 16px 20px 0;
+}
+
+.context-tab {
+ padding: 6px 12px;
+ border-radius: 999px;
+ color: #8a8f96;
+ font-size: 12.5px;
+ font-weight: 650;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.context-tab.is-active {
+ background: #0a0a0b;
+ color: #fff;
+}
+
+.context-output {
+ min-height: 168px;
+ margin: 16px 20px 24px;
+ padding: 18px;
+ border: 1px solid #eef0f3;
+ border-radius: 12px;
+ background: #fafbfc;
+}
+
+.context-output p {
+ margin: 0;
+ color: #1f2329;
+ font-size: 16px;
+ line-height: 1.55;
+ white-space: pre-wrap;
+}
+
+.context-output.kind-code p {
+ font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+ font-size: 14px;
+ color: #2f6f4f;
+}
+
+.context-output.kind-prompt p {
+ color: #3a3f46;
+}
+
+.context-waiting {
+ color: #aab0b7;
+ font-size: 15px;
+}
+
+/* Scene 3: always-ready desktop */
+.scene-anywhere {
+ padding-bottom: 8px;
+}
+
+.desktop {
+ position: relative;
+ display: grid;
+ place-items: center;
+ width: 100%;
+ min-height: 360px;
+ padding: 40px;
+ border-radius: 20px;
+ overflow: hidden;
+ background:
+ radial-gradient(circle at 30% 20%, var(--glow), transparent 45%),
+ radial-gradient(circle at 75% 75%, rgba(120, 180, 255, 0.18), transparent 50%),
+ linear-gradient(160deg, #1b2330, #0f1620);
+}
+
+.desktop-glow {
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(circle at 50% 120%, rgba(24, 119, 255, 0.28), transparent 55%);
+ pointer-events: none;
+}
+
+.desktop-dock {
+ display: flex;
+ gap: 16px;
+ padding: 12px 18px;
+ border-radius: 18px;
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(14px);
+ border: 1px solid rgba(255, 255, 255, 0.12);
+}
+
+.desktop-dock img {
+ width: 44px;
+ height: 44px;
+}
+
+.desktop-key {
+ position: absolute;
+ top: 28px;
+ right: 30px;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ color: rgba(255, 255, 255, 0.66);
+ font-size: 12px;
+ font-weight: 650;
+ transition: opacity 0.3s ease;
+}
+
+.desktop-key kbd {
+ display: inline-grid;
+ place-items: center;
+ min-width: 30px;
+ height: 26px;
+ padding: 0 8px;
+ border-radius: 7px;
+ background: rgba(255, 255, 255, 0.16);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.25);
+ color: #fff;
+ font-family: inherit;
+ font-size: 12px;
+ transition:
+ transform 0.15s ease,
+ background 0.15s ease;
+}
+
+.desktop-key[data-pressed='true'] kbd {
+ transform: translateY(2px);
+ background: rgba(24, 119, 255, 0.5);
+ box-shadow: 0 0 0 rgba(0, 0, 0, 0.25);
+}
+
+.anywhere-dock {
+ position: absolute;
+ left: 50%;
+ bottom: 28px;
+ transform: translateX(-50%);
+ animation: capsule-rise 0.5s ease both;
+}
+
+@keyframes capsule-rise {
+ from {
+ opacity: 0;
+ transform: translate(-50%, 14px);
+ }
+ to {
+ opacity: 1;
+ transform: translate(-50%, 0);
+ }
+}
+
+/* --- responsive --- */
+@media (max-width: 980px) {
+ .features-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .features-nav {
+ position: static;
+ height: auto;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-bottom: 8px;
+ /* Drop the desktop sticky-alignment offset once the nav stacks above. */
+ padding-top: 0;
+ }
+
+ .features-nav-kicker {
+ width: 100%;
+ margin-bottom: 0;
+ }
+
+ .feature-block {
+ min-height: auto;
+ padding-top: 0;
+ }
+
+ .dash {
+ grid-template-columns: 148px 1fr;
+ }
+
+ .dash-charts {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 720px) {
+ .showcase {
+ margin-top: clamp(-150px, -15vh, -96px);
+ padding-bottom: 64px;
+ }
+
+ .dash {
+ grid-template-columns: 1fr;
+ }
+
+ .dash-sidebar {
+ display: none;
+ }
+
+ .dash-main {
+ padding: 16px 16px 22px;
+ }
+
+ .dash-topbar {
+ flex-direction: column;
+ }
+
+ .dash-title p {
+ max-width: none;
+ }
+
+ .dash-pills {
+ flex-wrap: wrap;
+ }
+
+ .dash-stats {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .feature-block h3 {
+ font-size: clamp(30px, 9vw, 40px);
+ }
+
+ .chat-thread {
+ min-height: 150px;
}
}