diff --git a/index.html b/index.html
index 4c61e60f2..4b9554832 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,7 @@
-
+
@@ -11,6 +11,7 @@
+
Scripter — Write Faster
diff --git a/public/manifest.json b/public/manifest.json
index 2699782b8..b51dd49c5 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,21 +1,27 @@
{
- "name": "Scripter",
+ "name": "Scripter — Write Faster",
"short_name": "Scripter",
"description": "Professional screenplay editor with real-time collaboration",
- "start_url": "/",
+ "start_url": "/app/dashboard",
"display": "standalone",
- "background_color": "#1a1a2e",
- "theme_color": "#1a1a2e",
+ "background_color": "#0a0a0a",
+ "theme_color": "#0a0a0a",
"orientation": "any",
"icons": [
{
- "src": "/icon-192.png",
+ "src": "/src-tauri/128x128.png",
+ "sizes": "128x128",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/src-tauri/128x128.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
- "src": "/icon-512.png",
+ "src": "/src-tauri/128x128.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
diff --git a/public/sw.js b/public/sw.js
new file mode 100644
index 000000000..5c1fe6bd2
--- /dev/null
+++ b/public/sw.js
@@ -0,0 +1,65 @@
+const CACHE_NAME = 'scripter-v1';
+const API_CACHE = 'scripter-api-v1';
+const STATIC_ASSETS = [
+ '/',
+ '/index.html',
+ '/manifest.json',
+ '/src/App.tsx',
+ '/src/index.css',
+];
+
+self.addEventListener('install', (event) => {
+ event.waitUntil(
+ caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
+ );
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', (event) => {
+ event.waitUntil(
+ caches.keys().then((keys) =>
+ Promise.all(
+ keys.filter((key) => key !== CACHE_NAME && key !== API_CACHE).map((key) => caches.delete(key))
+ )
+ )
+ );
+ self.clients.claim();
+});
+
+self.addEventListener('fetch', (event) => {
+ const { request } = event;
+ const url = new URL(request.url);
+
+ if (url.pathname.startsWith('/trpc/')) {
+ event.respondWith(
+ fetch(request)
+ .then((response) => {
+ const clonedResponse = response.clone();
+ caches.open(API_CACHE).then((cache) => cache.put(request, clonedResponse));
+ return response;
+ })
+ .catch(() => caches.match(request))
+ );
+ return;
+ }
+
+ event.respondWith(
+ caches.match(request).then((cached) => {
+ const fetchPromise = fetch(request).then((response) => {
+ if (response.status === 200) {
+ const clonedResponse = response.clone();
+ caches.open(CACHE_NAME).then((cache) => cache.put(request, clonedResponse));
+ }
+ return response;
+ });
+
+ return cached || fetchPromise;
+ })
+ );
+});
+
+self.addEventListener('message', (event) => {
+ if (event.data === 'skipWaiting') {
+ self.skipWaiting();
+ }
+});
diff --git a/src/App.tsx b/src/App.tsx
index 5a80b76fe..bef566b53 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,6 +4,14 @@ import { ClerkProvider } from './lib/auth/clerk-provider';
import { routes } from './routes';
import './index.css';
+if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/sw.js').catch((err) => {
+ console.warn('Service worker registration failed:', err);
+ });
+ });
+}
+
render(
() => (
diff --git a/src/index.css b/src/index.css
index 13db93aa5..1afe52378 100644
--- a/src/index.css
+++ b/src/index.css
@@ -101,3 +101,625 @@ input:focus, textarea:focus, select:focus {
--sidebar-width: 0px;
}
}
+
+/* Waitlist Page Styles */
+.waitlist-page {
+ min-height: 100vh;
+ background: var(--color-bg-primary);
+}
+
+.waitlist-hero {
+ min-height: calc(100vh - 80px);
+ display: flex;
+ flex-direction: column;
+ padding: 40px 20px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.waitlist-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+}
+
+.waitlist-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 12px;
+ background: var(--color-bg-tertiary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-full);
+ font-size: 0.875rem;
+ color: var(--color-text-secondary);
+}
+
+.badge-dot {
+ width: 8px;
+ height: 8px;
+ background: var(--color-accent);
+ border-radius: 50%;
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+.waitlist-headline {
+ font-size: 2.75rem;
+ font-weight: 700;
+ line-height: 1.2;
+ background: linear-gradient(135deg, #fff 0%, #a3a3a3 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ margin-bottom: 16px;
+}
+
+.waitlist-subheadline {
+ font-size: 1.25rem;
+ color: var(--color-text-secondary);
+ max-width: 600px;
+ line-height: 1.6;
+}
+
+.waitlist-form {
+ background: var(--color-bg-tertiary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ padding: 32px;
+ margin: 24px 0;
+}
+
+.waitlist-form .form-group {
+ margin-bottom: 20px;
+}
+
+.waitlist-form label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 500;
+ color: var(--color-text-primary);
+ font-size: 0.9375rem;
+}
+
+.waitlist-form .optional {
+ color: var(--color-text-muted);
+ font-size: 0.8125rem;
+ margin-left: 4px;
+}
+
+.waitlist-form input {
+ width: 100%;
+ padding: 14px 16px;
+ font-size: 1rem;
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ color: var(--color-text-primary);
+ transition: all var(--transition-fast);
+}
+
+.waitlist-form input:focus {
+ border-color: var(--color-accent);
+ outline: none;
+ box-shadow: 0 0 0 3px var(--color-accent-muted);
+}
+
+.waitlist-form input::placeholder {
+ color: var(--color-text-muted);
+}
+
+.submit-btn {
+ width: 100%;
+ padding: 16px;
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: white;
+ background: var(--color-accent);
+ border: none;
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+
+.submit-btn:hover:not(:disabled) {
+ background: var(--color-accent-hover);
+ transform: translateY(-1px);
+ box-shadow: var(--shadow-md);
+}
+
+.submit-btn:disabled {
+ opacity: 0.7;
+ cursor: not-allowed;
+}
+
+.error-message {
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid var(--color-error);
+ color: var(--color-error);
+ padding: 12px 16px;
+ border-radius: var(--radius-md);
+ font-size: 0.9375rem;
+ margin-top: 16px;
+}
+
+.privacy-note {
+ text-align: center;
+ color: var(--color-text-muted);
+ font-size: 0.875rem;
+ margin-top: 20px;
+}
+
+.waitlist-count {
+ text-align: center;
+ color: var(--color-text-muted);
+ font-size: 0.875rem;
+ margin-top: 16px;
+}
+
+.waitlist-footer {
+ padding: 24px 0;
+ border-top: 1px solid var(--color-border);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 0.875rem;
+ color: var(--color-text-muted);
+}
+
+.footer-links {
+ display: flex;
+ gap: 20px;
+}
+
+.footer-links a {
+ color: var(--color-text-secondary);
+}
+
+.footer-links a:hover {
+ color: var(--color-accent);
+}
+
+.waitlist-features {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 24px;
+ margin-top: 32px;
+}
+
+.waitlist-features .feature {
+ text-align: center;
+ padding: 24px;
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ transition: all var(--transition-fast);
+}
+
+.waitlist-features .feature:hover {
+ border-color: var(--color-border-hover);
+ transform: translateY(-2px);
+}
+
+.waitlist-features .feature-icon {
+ font-size: 2rem;
+ margin-bottom: 12px;
+}
+
+.waitlist-features .feature h3 {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--color-text-primary);
+ margin-bottom: 8px;
+}
+
+.waitlist-features .feature p {
+ color: var(--color-text-secondary);
+ font-size: 0.9375rem;
+ line-height: 1.5;
+}
+
+.waitlist-social-proof {
+ margin-top: 48px;
+ padding: 32px;
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+}
+
+.waitlist-social-proof .social-proof-header h3 {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--color-text-primary);
+ text-align: center;
+ margin-bottom: 24px;
+}
+
+.testimonials {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 24px;
+}
+
+.testimonial {
+ padding: 20px;
+ background: var(--color-bg-tertiary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+}
+
+.testimonial-quote {
+ font-size: 0.9375rem;
+ color: var(--color-text-secondary);
+ font-style: italic;
+ margin-bottom: 16px;
+ line-height: 1.6;
+}
+
+.testimonial-author {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.testimonial-author .author-name {
+ font-weight: 600;
+ color: var(--color-text-primary);
+ font-size: 0.9375rem;
+}
+
+.testimonial-author .author-role {
+ font-size: 0.8125rem;
+ color: var(--color-text-muted);
+}
+
+.waitlist-success {
+ text-align: center;
+ padding: 24px 0;
+}
+
+.waitlist-success h2 {
+ font-size: 1.5rem;
+ color: var(--color-success);
+ margin-bottom: 12px;
+}
+
+.waitlist-success p {
+ color: var(--color-text-secondary);
+ margin-bottom: 24px;
+}
+
+.referral-info {
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ padding: 20px;
+ margin-top: 20px;
+}
+
+.referral-label {
+ font-size: 0.875rem;
+ color: var(--color-text-muted);
+ margin-bottom: 8px;
+}
+
+.referral-code {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.code-display {
+ font-family: 'Monaco', 'Menlo', monospace;
+ font-size: 1.25rem;
+ font-weight: 700;
+ background: var(--color-bg-tertiary);
+ padding: 12px 20px;
+ border-radius: var(--radius-md);
+ letter-spacing: 2px;
+ color: var(--color-accent);
+}
+
+.copy-btn {
+ padding: 10px 16px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--color-text-primary);
+ background: var(--color-bg-tertiary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+
+.copy-btn:hover {
+ background: var(--color-bg-elevated);
+ border-color: var(--color-border-hover);
+}
+
+.referral-hint {
+ font-size: 0.8125rem;
+ color: var(--color-text-muted);
+ text-align: center;
+}
+
+/* Team Management */
+.freno-teams {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 24px;
+}
+
+.freno-team-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 16px;
+ margin-top: 24px;
+}
+
+.freno-team-card {
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ transition: all var(--transition-fast);
+ position: relative;
+}
+
+.freno-team-card:hover {
+ border-color: var(--color-border-hover);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-md);
+}
+
+.freno-team-card-new {
+ border-style: dashed;
+ align-items: center;
+ justify-content: center;
+ min-height: 140px;
+ cursor: pointer;
+ text-align: center;
+}
+
+.freno-team-card-new:hover {
+ border-color: var(--color-accent);
+ background: var(--color-accent-muted);
+}
+
+.freno-team-card-link {
+ text-decoration: none;
+ color: inherit;
+}
+
+.freno-team-icon {
+ font-size: 2rem;
+ margin-bottom: 4px;
+}
+
+.freno-team-card h3 {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--color-text-primary);
+}
+
+.freno-team-card-actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 8px;
+ padding-top: 12px;
+ border-top: 1px solid var(--color-border);
+}
+
+/* Team Detail */
+.freno-team-detail {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 24px;
+}
+
+.freno-back-link {
+ font-size: 0.875rem;
+ color: var(--color-text-secondary);
+ margin-bottom: 8px;
+ display: inline-block;
+}
+
+.freno-back-link:hover {
+ color: var(--color-accent);
+}
+
+.freno-team-meta {
+ color: var(--color-text-muted);
+ font-size: 0.875rem;
+ margin-top: 4px;
+}
+
+.freno-header-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.freno-team-members-section {
+ margin-top: 32px;
+}
+
+.freno-team-members-section h2 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 16px;
+}
+
+.freno-members-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.freno-member-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 16px;
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+}
+
+.freno-member-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.freno-member-id {
+ font-weight: 500;
+ color: var(--color-text-primary);
+}
+
+.freno-member-joined {
+ font-size: 0.8125rem;
+ color: var(--color-text-muted);
+}
+
+.freno-member-actions {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+.freno-select {
+ background: var(--color-bg-tertiary);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-md);
+ padding: 6px 12px;
+ color: var(--color-text-primary);
+ font-size: 0.875rem;
+ cursor: pointer;
+}
+
+.freno-select:focus {
+ border-color: var(--color-accent);
+ outline: none;
+}
+
+/* Alert */
+.freno-alert {
+ padding: 12px 16px;
+ border-radius: var(--radius-md);
+ margin-bottom: 16px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 0.875rem;
+}
+
+.freno-alert-error {
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ color: #fca5a5;
+}
+
+.freno-alert-dismiss {
+ background: none;
+ border: none;
+ color: inherit;
+ cursor: pointer;
+ font-size: 1.125rem;
+ padding: 0 4px;
+ opacity: 0.7;
+}
+
+.freno-alert-dismiss:hover {
+ opacity: 1;
+}
+
+/* Danger button */
+.freno-btn-danger {
+ background: var(--color-error);
+ color: white;
+ padding: 8px 16px;
+ border-radius: var(--radius-md);
+ font-size: 0.875rem;
+ font-weight: 500;
+ transition: opacity var(--transition-fast);
+}
+
+.freno-btn-danger:hover {
+ opacity: 0.9;
+}
+
+.freno-btn-danger:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.freno-btn-danger-sm {
+ color: var(--color-error);
+ font-size: 0.8125rem;
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ transition: background var(--transition-fast);
+}
+
+.freno-btn-danger-sm:hover {
+ background: rgba(239, 68, 68, 0.1);
+}
+
+/* Loading state */
+.freno-loading {
+ text-align: center;
+ padding: 48px;
+ color: var(--color-text-muted);
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .freno-teams,
+ .freno-team-detail {
+ padding: 16px;
+ }
+
+ .freno-team-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .freno-page-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+
+ .freno-header-actions {
+ width: 100%;
+ flex-direction: column;
+ }
+
+ .freno-header-actions button {
+ width: 100%;
+ }
+
+ .freno-member-row {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+
+ .freno-member-actions {
+ width: 100%;
+ justify-content: flex-end;
+ }
+}