diff --git a/docs/README.md b/docs/README.md index 170405e..ef71cba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,3 +20,7 @@ npm run build ``` The production build is written to `out/` and can be hosted by any static web server. + +Set `NEXT_PUBLIC_SITE_URL` to the canonical production origin when building for deployment so +Open Graph and Twitter image URLs resolve to the public site. Vercel deployment URLs are detected +automatically. diff --git a/docs/app/base.css b/docs/app/base.css index b678efb..aad950a 100644 --- a/docs/app/base.css +++ b/docs/app/base.css @@ -4,27 +4,19 @@ --brand-500: #a855f7; --brand-400: #c084fc; --brand-300: #d8b4fe; - --brand-200: #ead4ff; - --brand-100: #f5eaff; --ink: #171419; --ink-soft: #4f4854; --ink-muted: #786f7d; --paper: #fbfafc; --paper-warm: #f7f4f8; --surface: #ffffff; - --surface-soft: #f3eff5; --line: #e5dfe8; --line-strong: #d4cad8; - --dark: #17131a; - --dark-raised: #211a25; --dark-line: #362c3c; - --success: #24a66b; --warning: #f59e0b; --font-sans: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; --font-display: "Avenir Next", Avenir, "Segoe UI", ui-sans-serif, sans-serif; --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - --shadow-sm: 0 8px 30px rgba(41, 25, 48, 0.07); - --shadow-lg: 0 30px 90px rgba(49, 25, 60, 0.14); --shell: 1220px; } @@ -163,13 +155,6 @@ p { height: 18px; } -.button-small { - min-height: 44px; - padding-inline: 16px; - border-radius: 11px; - font-size: 13px; -} - .button-primary { background: linear-gradient(135deg, var(--brand-500), var(--brand-700)); box-shadow: 0 12px 28px rgba(132, 47, 238, 0.24), inset 0 1px rgba(255, 255, 255, 0.35); @@ -180,35 +165,6 @@ p { box-shadow: 0 16px 34px rgba(132, 47, 238, 0.3), inset 0 1px rgba(255, 255, 255, 0.35); } -.button-ghost { - border-color: var(--line); - background: rgba(255, 255, 255, 0.72); - color: var(--ink); -} - -.button-ghost:hover { - border-color: var(--line-strong); - background: white; - box-shadow: var(--shadow-sm); -} - -.button-dark { - background: var(--ink); - color: white; -} - -.button-light { - background: white; - color: #211925; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18); -} - -.button-dark-ghost { - border-color: rgba(255, 255, 255, 0.2); - background: rgba(255, 255, 255, 0.06); - color: white; -} - .motion-ready .reveal { opacity: 0; transform: translateY(28px); diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx index 93fe3aa..7e52e0e 100644 --- a/docs/app/layout.tsx +++ b/docs/app/layout.tsx @@ -10,7 +10,18 @@ import "./privacy/privacy-document.css"; import "./responsive.css"; import "./privacy/privacy-responsive.css"; +const configuredSiteUrl = + process.env.NEXT_PUBLIC_SITE_URL ?? + process.env.VERCEL_PROJECT_PRODUCTION_URL ?? + process.env.VERCEL_URL ?? + "http://localhost:3000"; + +const metadataBase = new URL( + configuredSiteUrl.startsWith("http") ? configuredSiteUrl : `https://${configuredSiteUrl}`, +); + export const metadata: Metadata = { + metadataBase, title: { default: "VniDrop — Direct file transfer, on your terms", template: "%s · VniDrop", @@ -31,12 +42,26 @@ export const metadata: Metadata = { title: "VniDrop — Direct file transfer, on your terms", description: "Move files from your device to theirs, with no account and no hosted transfer copy.", + images: [ + { + url: "/og.png", + width: 1200, + height: 630, + alt: "VniDrop — Your files, a straight line between devices.", + }, + ], }, twitter: { - card: "summary", + card: "summary_large_image", title: "VniDrop — Direct file transfer, on your terms", description: "Move files from your device to theirs, with no account and no hosted transfer copy.", + images: [ + { + url: "/og.png", + alt: "VniDrop — Your files, a straight line between devices.", + }, + ], }, }; diff --git a/docs/app/og.png/route.tsx b/docs/app/og.png/route.tsx new file mode 100644 index 0000000..46b40aa --- /dev/null +++ b/docs/app/og.png/route.tsx @@ -0,0 +1,424 @@ +import { ImageResponse } from "next/og"; + +export const dynamic = "force-static"; + +const imageSize = { + width: 1200, + height: 630, +}; + +function BrandMark() { + return ( + + + + + + + + + + + + + + + + ); +} + +function FileGlyph() { + return ( + + + + + ); +} + +function CheckGlyph() { + return ( + + + + ); +} + +function LockGlyph() { + return ( + + + + + ); +} + +function TransferIllustration() { + return ( +
+
+ +
+ DIRECT · ENCRYPTED +
+ + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ +
+
+
Project files
+
12 items · 284 MB
+
+
+
+
+
+
+
Sending directly
+
72%
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ +
+
Received
+
Verified on arrival
+
+
+
+ ); +} + +export function GET() { + return new ImageResponse( +
+
+ +
+
+
+
+ +
+ +
+
VniDrop
+
+ OPEN SOURCE · LOCAL P2P +
+
+
+ +
+
Your files.
+
A straight line
+
between devices.
+
+ +
+ Send files directly. Stay in control. +
+ + +
, + imageSize, + ); +} diff --git a/docs/components/approval-demo.tsx b/docs/components/approval-demo.tsx deleted file mode 100644 index 74ffb18..0000000 --- a/docs/components/approval-demo.tsx +++ /dev/null @@ -1,86 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Icon } from "@/components/icons"; - -type ApprovalState = "pending" | "approved" | "refused"; - -export function ApprovalDemo() { - const [state, setState] = useState("pending"); - - return ( -
-
- - - - Receiver requests -
-
-
-
- - -
-
- - {state === "pending" ? "NEW REQUEST" : state === "approved" ? "IN PROGRESS" : "REQUEST CLOSED"} - -

- {state === "pending" && "Maya’s phone wants to receive"} - {state === "approved" && "Sending securely to Maya"} - {state === "refused" && "Request refused"} -

-
-
- -
- - - - - Summer photos - 42 files · 1.8 GB - - {state === "approved" && 68%} -
- - {state === "pending" && ( -
- - -
- )} - - {state === "approved" && ( -
-
- -
-
- Direct connection - -
-
- )} - - {state === "refused" && ( -
- -

This device was not given access.

- -
- )} -
-
- - Approval is required by default -
-
- ); -} diff --git a/docs/components/approval-scene.css b/docs/components/approval-scene.css deleted file mode 100644 index 2edd2e0..0000000 --- a/docs/components/approval-scene.css +++ /dev/null @@ -1,267 +0,0 @@ -/* Interactive receiver approval */ -.approval-demo { - overflow: hidden; - border: 1px solid #ded6e2; - border-radius: 24px; - background: #fbf9fc; - box-shadow: 0 35px 90px rgba(49, 29, 58, 0.16); - transform: rotate(1deg); - transition: transform 280ms ease, border-color 280ms ease; -} - -.approval-demo:hover { transform: rotate(0deg) translateY(-3px); } -.approval-demo.state-approved { border-color: rgba(45, 185, 117, 0.4); } -.approval-demo.state-refused { border-color: rgba(221, 92, 75, 0.35); } - -.approval-window-bar { - display: flex; - height: 48px; - align-items: center; - gap: 6px; - padding: 0 16px; - border-bottom: 1px solid #ded6e2; - background: #f0ecf2; -} - -.approval-window-bar > span { - width: 8px; - height: 8px; - border-radius: 50%; - background: #d1c7d5; -} - -.approval-window-bar > span:nth-child(1) { background: #f1a191; } -.approval-window-bar > span:nth-child(2) { background: #f1ca72; } -.approval-window-bar > span:nth-child(3) { background: #72cf9b; } - -.approval-window-bar small { - margin-left: auto; - color: #8a7f8f; - font-family: var(--font-mono); - font-size: 8px; - font-weight: 700; - letter-spacing: 0.06em; -} - -.approval-body { - min-height: 385px; - padding: 35px; - background: - radial-gradient(circle at 90% 0%, rgba(192, 132, 252, 0.16), transparent 27%), - white; -} - -.approval-heading { - display: flex; - align-items: center; - gap: 17px; - margin-bottom: 30px; -} - -.device-avatar { - position: relative; - width: 52px; - height: 60px; - flex: 0 0 auto; - border: 4px solid #352e39; - border-radius: 13px; - background: #211d24; - box-shadow: 0 10px 25px rgba(42, 30, 48, 0.16); -} - -.device-avatar::before { - position: absolute; - top: 4px; - left: 50%; - width: 16px; - height: 3px; - border-radius: 999px; - background: #0c0a0d; - content: ""; - transform: translateX(-50%); -} - -.device-avatar-screen { - position: absolute; - inset: 14px 8px 8px; - border-radius: 7px; - background: linear-gradient(145deg, #b55efa, #6f27e9); -} - -.device-avatar-glow { - position: absolute; - inset: -5px; - z-index: -1; - border-radius: 16px; - box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.25); - animation: avatar-pulse 2.7s ease-out infinite; -} - -.approval-heading h3 { - max-width: 360px; - margin: 5px 0 0; - font-size: 21px; - letter-spacing: -0.035em; - line-height: 1.22; -} - -.approval-file { - display: flex; - align-items: center; - gap: 13px; - padding: 16px; - border: 1px solid #e5dde8; - border-radius: 14px; - background: #f8f5f9; -} - -.approval-file-icon { - display: grid; - width: 42px; - height: 42px; - flex: 0 0 auto; - place-items: center; - border-radius: 11px; - background: var(--brand-100); - color: var(--brand-600); -} - -.approval-file-icon svg { width: 21px; } -.approval-file strong, -.approval-file small { display: block; } -.approval-file strong { font-size: 12px; } -.approval-file small { color: var(--ink-muted); font-size: 9px; } - -.approval-percent { - margin-left: auto; - color: var(--brand-600); - font-family: var(--font-mono); - font-size: 11px; - font-weight: 800; -} - -.approval-actions { - display: grid; - grid-template-columns: 1fr 1.4fr; - gap: 10px; - margin-top: 22px; -} - -.approval-actions button, -.approval-refused button { - min-height: 47px; - border: 1px solid #ded4e2; - border-radius: 12px; - font-size: 12px; - font-weight: 750; - cursor: pointer; - transition: transform 160ms ease, background 160ms ease, box-shadow 160ms ease; -} - -.approval-actions button:hover, -.approval-refused button:hover { transform: translateY(-2px); } -.approval-refuse { background: white; color: #625769; } -.approval-accept { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - border-color: transparent !important; - background: linear-gradient(135deg, var(--brand-500), var(--brand-700)); - box-shadow: 0 10px 24px rgba(132, 47, 238, 0.22); - color: white; -} - -.approval-accept svg { width: 17px; } - -.approval-progress { - margin-top: 25px; - animation: approval-state-enter 350ms ease; -} - -.approval-progress-track { - overflow: hidden; - height: 8px; - border-radius: 999px; - background: #eee8f1; -} - -.approval-progress-track span { - display: block; - width: 68%; - height: 100%; - border-radius: inherit; - background: linear-gradient(90deg, var(--brand-500), var(--brand-700)); - box-shadow: 0 0 15px rgba(168, 85, 247, 0.38); - animation: progress-fill 900ms cubic-bezier(0.25, 0.8, 0.25, 1) both; -} - -.approval-progress-meta { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 13px; - color: var(--ink-muted); - font-size: 9px; -} - -.approval-progress-meta > span { - display: flex; - align-items: center; - gap: 9px; -} - -.approval-progress-meta button, -.approval-refused button { - border: 0; - background: transparent; - color: var(--brand-600); - font-size: 9px; - font-weight: 750; - cursor: pointer; -} - -.approval-refused { - display: flex; - align-items: center; - gap: 12px; - margin-top: 23px; - padding: 14px; - border: 1px solid #f0d5d0; - border-radius: 12px; - background: #fff8f7; - animation: approval-state-enter 350ms ease; -} - -.approval-refused > span { - display: grid; - width: 30px; - height: 30px; - flex: 0 0 auto; - place-items: center; - border-radius: 50%; - background: #fde7e3; - color: #d85848; -} - -.approval-refused svg { width: 15px; } -.approval-refused p { margin: 0; color: #705c59; font-size: 10px; } -.approval-refused button { margin-left: auto; } - -.approval-footer { - display: flex; - height: 50px; - align-items: center; - justify-content: center; - gap: 8px; - border-top: 1px solid #ded6e2; - background: #f2eef4; - color: #766b7b; - font-family: var(--font-mono); - font-size: 8px; - font-weight: 700; - letter-spacing: 0.04em; -} - -.approval-footer svg { width: 15px; color: var(--brand-600); } - diff --git a/docs/components/brand.tsx b/docs/components/brand.tsx index 67a0303..7954873 100644 --- a/docs/components/brand.tsx +++ b/docs/components/brand.tsx @@ -20,28 +20,14 @@ export function BrandMark({ className, title }: BrandAssetProps) { ); } -export function AppIcon({ className, title }: BrandAssetProps) { - return ( - {title - ); -} - -export function Brand({ compact = false }: { compact?: boolean }) { +export function Brand() { return ( - {!compact && VniDrop} + VniDrop - {!compact && Send files directly. Stay in control.} + Send files directly. Stay in control. ); } diff --git a/docs/components/hero-device.css b/docs/components/hero-device.css deleted file mode 100644 index 66a75e8..0000000 --- a/docs/components/hero-device.css +++ /dev/null @@ -1,490 +0,0 @@ -/* Receiver hardware, status overlays, and responsive stage composition */ -.receiver-phone-shell { - position: absolute; - z-index: 5; - top: 17%; - right: 1.5%; - width: 23.5%; - aspect-ratio: 0.49; - transform: translate( - calc(var(--pointer-x) * 7px), - calc(var(--pointer-y) * 5px) - ) - rotateY(calc(var(--pointer-x) * -1.6deg)) - rotateX(calc(var(--pointer-y) * 1.3deg)); - transform-origin: center; - transition: transform 180ms ease-out; -} - -.receiver-phone-frame { - position: absolute; - inset: 0; - padding: 7px; - border: 1px solid #bdb7c1; - border-radius: 38px; - background: linear-gradient(145deg, #e7e4e9, #aaa4ae 42%, #f3f1f4 72%, #aaa3ae); - box-shadow: - 0 38px 85px rgba(43, 27, 51, 0.22), - 0 9px 25px rgba(43, 27, 51, 0.12), - inset 0 0 0 1px rgba(255, 255, 255, 0.85); -} - -.receiver-phone-frame::before { - position: absolute; - z-index: 3; - inset: 2px; - border: 1px solid rgba(255, 255, 255, 0.7); - border-radius: 35px; - content: ""; - pointer-events: none; -} - -.phone-side-button { - position: absolute; - right: -3px; - width: 3px; - border-radius: 0 2px 2px 0; - background: #98929d; -} - -.phone-side-button-one { top: 24%; height: 14%; } -.phone-side-button-two { top: 42%; height: 21%; } - -.phone-screen { - position: relative; - width: 100%; - height: 100%; - overflow: hidden; - border-radius: 31px; - background: - radial-gradient(circle at 80% 18%, rgba(192, 132, 252, 0.12), transparent 28%), - #faf9fb; - box-shadow: inset 0 0 0 1px rgba(26, 22, 29, 0.18); -} - -.phone-screen::after { - position: absolute; - top: -28%; - left: -42%; - width: 82%; - height: 130%; - background: linear-gradient(110deg, transparent 35%, rgba(255, 255, 255, 0.32), transparent 66%); - content: ""; - pointer-events: none; - transform: rotate(8deg); -} - -.phone-status-bar { - position: relative; - z-index: 2; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - align-items: center; - height: 24px; - padding: 0 10px; - color: #2e2931; - font-size: 6px; - font-weight: 760; -} - -.phone-island { - width: 42px; - height: 12px; - justify-self: center; - border-radius: 999px; - background: #171419; -} - -.phone-signal { - justify-self: end; - font-size: 5px; - letter-spacing: 1px; -} - -.phone-app-bar { - display: grid; - grid-template-columns: 26px 1fr 25px; - align-items: center; - padding: 5px 11px 8px; -} - -.phone-brand-mark { - width: 26px; - height: 26px; - object-fit: contain; -} - -.phone-app-bar strong { - color: #29242c; - font-size: 9px; - letter-spacing: -0.02em; -} - -.phone-app-bar > span { - display: grid; - width: 23px; - height: 23px; - place-items: center; - border: 1px solid #e9e3ec; - border-radius: 8px; - background: white; - color: var(--brand-700); -} - -.phone-app-bar svg { width: 11px; } - -.phone-transfer-card { - display: grid; - margin: 6px 10px 0; - padding: 11px 9px 10px; - border: 1px solid #e6e0e9; - border-radius: 14px; - background: white; - box-shadow: 0 9px 25px rgba(48, 33, 55, 0.055); - text-align: center; -} - -.phone-file-art { - position: relative; - display: grid; - width: 46px; - height: 46px; - margin: 0 auto 8px; - place-items: center; - overflow: hidden; - border-radius: 13px; - background: - linear-gradient(145deg, rgba(255, 255, 255, 0.2), transparent 48%), - linear-gradient(135deg, #c084fc, #7c2aef); - color: white; - box-shadow: 0 10px 22px rgba(132, 47, 238, 0.22); -} - -.phone-file-art::after { - position: absolute; - top: -8px; - right: -8px; - width: 24px; - height: 24px; - background: rgba(235, 211, 255, 0.65); - content: ""; - transform: rotate(45deg); -} - -.phone-file-art svg { width: 21px; } - -.phone-transfer-card small { - color: #9a8fa0; - font-family: var(--font-mono); - font-size: 5px; - font-weight: 800; - letter-spacing: 0.09em; -} - -.phone-transfer-card strong { - margin-top: 3px; - color: #29232d; - font-size: 9px; - letter-spacing: -0.02em; -} - -.phone-transfer-card > span { - color: #8d8391; - font-size: 6px; -} - -.phone-state-area { - position: relative; - height: 88px; - margin: 9px 10px 0; - overflow: hidden; - border-radius: 13px; - background: #f2eff4; -} - -.phone-state { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - text-align: center; -} - -.state-symbol { - display: grid; - width: 31px; - height: 31px; - margin-bottom: 6px; - place-items: center; - border-radius: 10px; - background: #e5d3f3; - color: var(--brand-700); -} - -.state-symbol.is-success { - background: #d8f4e7; - color: #188457; -} - -.state-symbol svg { width: 15px; } - -.phone-state strong { - color: #39313d; - font-size: 7px; -} - -.phone-state small { - color: #8d8291; - font-size: 5.5px; -} - -.transfer-percentage { - margin-bottom: 3px; - color: var(--brand-700); - font-family: var(--font-display); - font-size: 24px; - font-weight: 740; - line-height: 1; - letter-spacing: -0.08em; -} - -.transfer-percentage span { - margin-left: 1px; - font-size: 10px; -} - -.phone-home-indicator { - position: absolute; - bottom: 5px; - left: 50%; - width: 42px; - height: 3px; - border-radius: 999px; - background: #27222a; - transform: translateX(-50%); -} - -.hero-route-card, -.hero-verified-card { - position: absolute; - z-index: 8; - display: flex; - align-items: center; - border: 1px solid rgba(214, 204, 220, 0.92); - background: rgba(255, 255, 255, 0.92); - box-shadow: 0 15px 45px rgba(49, 27, 61, 0.12); - backdrop-filter: blur(16px); -} - -.hero-route-card { - top: 3%; - left: 36%; - gap: 8px; - min-width: 176px; - padding: 9px 10px; - border-radius: 13px; -} - -.route-pulse { - width: 8px; - height: 8px; - flex: 0 0 auto; - border: 2px solid #def7eb; - border-radius: 50%; - background: #2fbc7a; - box-shadow: 0 0 0 4px rgba(47, 188, 122, 0.1); -} - -.hero-route-card > span:nth-child(2), -.hero-verified-card > span:nth-child(2) { - display: grid; -} - -.hero-route-card small { - color: #978d9b; - font-family: var(--font-mono); - font-size: 5px; - font-weight: 800; - letter-spacing: 0.12em; -} - -.hero-route-card strong, -.hero-verified-card strong { - color: #342d37; - font-size: 7.5px; -} - -.route-latency { - margin-left: auto; - color: var(--brand-700); - font-family: var(--font-mono); - font-size: 6px; - font-weight: 800; -} - -.hero-verified-card { - right: 0; - bottom: 12%; - gap: 8px; - padding: 9px 11px 9px 8px; - border-radius: 13px; -} - -.hero-verified-card > span:first-child { - display: grid; - width: 31px; - height: 31px; - place-items: center; - border-radius: 10px; - background: #dcf6e9; - color: #1b8d5d; -} - -.hero-verified-card svg { width: 16px; } - -.hero-verified-card small { - color: #8e8492; - font-size: 6px; -} - -.hero-privacy-rail { - position: absolute; - z-index: 6; - bottom: 3.5%; - left: 8%; - display: inline-flex; - align-items: center; - gap: 9px; - padding: 7px 10px; - border: 1px solid rgba(217, 208, 222, 0.82); - border-radius: 999px; - background: rgba(255, 255, 255, 0.7); - color: #786e7c; - font-family: var(--font-mono); - font-size: 5.5px; - font-weight: 750; - letter-spacing: 0.035em; - text-transform: uppercase; - backdrop-filter: blur(12px); -} - -.hero-privacy-rail span { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.hero-privacy-rail svg { - width: 9px; - color: var(--brand-700); -} - -.hero-privacy-rail i { - width: 3px; - height: 3px; - border-radius: 50%; - background: #c4b9c8; -} - -@media (max-width: 680px) { - .hero-stage { - aspect-ratio: 1.02; - } - - .hero-stage-grid { - inset: 2% -2% 2%; - border-radius: 30px; - background-size: 22px 22px, 22px 22px, auto, auto; - } - - .desktop-product-shell { - top: 12%; - left: -2%; - width: 70%; - height: 56%; - } - - .receiver-phone-shell { - top: 22%; - right: -1%; - width: 26%; - } - - .desktop-app-layout { grid-template-columns: 42px 1fr; } - .desktop-transfer-view { padding: 11px 10px 9px; } - .desktop-sidebar { gap: 4px; padding-top: 7px; } - .product-brand-mark { width: 29px; height: 29px; } - .desktop-sidebar-item { width: 26px; height: 26px; } - .desktop-sidebar-item svg { width: 12px; } - .file-collection-card { grid-template-columns: 51px 1fr; gap: 7px; } - .file-collage { width: 51px; height: 45px; } - .collection-menu { display: none; } - .receiver-state-stack { width: 72px; } - .desktop-transfer-foot { display: none; } - - .approval-knock-card { - right: -6%; - bottom: -22%; - width: 200px; - } - - .hero-route-card { - top: 1%; - left: 27%; - min-width: 156px; - } - - .hero-verified-card { right: -1%; bottom: 8%; } - .hero-privacy-rail { bottom: 0; left: 3%; } - .stage-coordinate-side { display: none; } -} - -@media (max-width: 480px) { - .desktop-window-bar { - grid-template-columns: 1fr auto; - height: 28px; - } - - .window-title { display: none; } - .desktop-app-layout { height: calc(100% - 28px); } - .desktop-view-heading { margin-bottom: 7px; } - .desktop-view-heading strong { font-size: 12px; } - .file-collection-card { padding: 7px; } - .receiver-section-heading { margin-top: 8px; } - .desktop-receiver-row { grid-template-columns: 27px 1fr; } - .receiver-avatar { width: 26px; height: 26px; } - .receiver-state-stack { display: none; } - .receiver-progress-track { left: 43px; } - .approval-knock-card { bottom: -27%; width: 184px; padding: 9px; } - .approval-knock-card p { margin: 6px 0; } - - .phone-app-bar { grid-template-columns: 22px 1fr 22px; padding-inline: 7px; } - .phone-brand-mark { width: 22px; height: 22px; } - .phone-transfer-card { margin-inline: 7px; padding-inline: 7px; } - .phone-file-art { width: 38px; height: 38px; } - .phone-state-area { height: 73px; margin-inline: 7px; } - .state-symbol { width: 27px; height: 27px; margin-bottom: 3px; } - .transfer-percentage { font-size: 20px; } - - .hero-route-card { - left: 20%; - min-width: 142px; - padding: 7px 8px; - } - - .route-latency { display: none; } - .hero-verified-card { padding: 7px; } - .hero-verified-card > span:first-child { width: 27px; height: 27px; } - - .hero-privacy-rail { - gap: 6px; - font-size: 5px; - } - - .hero-privacy-rail span:nth-of-type(3), - .hero-privacy-rail i:nth-of-type(2) { - display: none; - } -} diff --git a/docs/components/hero-illustration.tsx b/docs/components/hero-illustration.tsx deleted file mode 100644 index 270328e..0000000 --- a/docs/components/hero-illustration.tsx +++ /dev/null @@ -1,386 +0,0 @@ -"use client"; - -import type { CSSProperties, PointerEvent } from "react"; -import { useRef } from "react"; -import { motion, useReducedMotion } from "motion/react"; -import { BrandMark } from "@/components/brand"; -import { Icon } from "@/components/icons"; - -type StageStyle = CSSProperties & { - "--pointer-x": string; - "--pointer-y": string; -}; - -const cycle = 10.5; -const rest = 1.2; - -export function HeroIllustration() { - const stageRef = useRef(null); - const reduceMotion = useReducedMotion(); - - const updateParallax = (event: PointerEvent) => { - const stage = stageRef.current; - if (!stage || reduceMotion) return; - const bounds = stage.getBoundingClientRect(); - const x = ((event.clientX - bounds.left) / bounds.width - 0.5) * 2; - const y = ((event.clientY - bounds.top) / bounds.height - 0.5) * 2; - stage.style.setProperty("--pointer-x", x.toFixed(3)); - stage.style.setProperty("--pointer-y", y.toFixed(3)); - }; - - const resetParallax = () => { - stageRef.current?.style.setProperty("--pointer-x", "0"); - stageRef.current?.style.setProperty("--pointer-y", "0"); - }; - - const repeating = { duration: cycle, repeat: Infinity, repeatDelay: rest }; - - return ( -
-
- ); -} diff --git a/docs/components/hero-scene.css b/docs/components/hero-scene.css deleted file mode 100644 index f0beacf..0000000 --- a/docs/components/hero-scene.css +++ /dev/null @@ -1,606 +0,0 @@ -/* Coordinated product transfer stage */ -.hero-visual { - position: relative; - width: min(100%, 760px); - margin: 0; -} - -.hero-stage { - --pointer-x: 0; - --pointer-y: 0; - position: relative; - width: 100%; - aspect-ratio: 1.17; - isolation: isolate; - perspective: 1400px; -} - -.hero-stage-grid { - position: absolute; - z-index: -3; - inset: 3% 0 4% 1%; - overflow: hidden; - border: 1px solid rgba(159, 132, 173, 0.15); - border-radius: 44px; - background: - linear-gradient(rgba(110, 86, 122, 0.055) 1px, transparent 1px), - linear-gradient(90deg, rgba(110, 86, 122, 0.055) 1px, transparent 1px), - radial-gradient(circle at 72% 35%, rgba(192, 132, 252, 0.13), transparent 38%), - rgba(255, 255, 255, 0.2); - background-size: 28px 28px, 28px 28px, auto, auto; - mask-image: linear-gradient(to bottom, transparent 0, black 9%, black 88%, transparent 100%); -} - -.hero-stage-grid::before, -.hero-stage-grid::after { - position: absolute; - width: 32px; - height: 32px; - border-style: solid; - border-color: rgba(168, 85, 247, 0.35); - content: ""; -} - -.hero-stage-grid::before { - top: 18px; - left: 18px; - border-width: 1px 0 0 1px; -} - -.hero-stage-grid::after { - right: 18px; - bottom: 18px; - border-width: 0 1px 1px 0; -} - -.hero-stage-glow { - position: absolute; - z-index: -2; - top: 18%; - right: 9%; - width: 45%; - aspect-ratio: 1; - border-radius: 50%; - background: rgba(168, 85, 247, 0.16); - filter: blur(70px); - transform: translate( - calc(var(--pointer-x) * 9px), - calc(var(--pointer-y) * 9px) - ); -} - -.stage-coordinate { - position: absolute; - z-index: 1; - color: rgba(87, 70, 95, 0.48); - font-family: var(--font-mono); - font-size: 7px; - font-weight: 750; - letter-spacing: 0.13em; -} - -.stage-coordinate-top { - top: 1.5%; - left: 5%; -} - -.stage-coordinate-side { - top: 27%; - right: -3%; - transform: rotate(90deg); -} - -.desktop-product-shell { - position: absolute; - z-index: 3; - top: 11%; - left: 1.5%; - width: 64%; - height: 55%; - transform: translate( - calc(var(--pointer-x) * -5px), - calc(var(--pointer-y) * -4px) - ) - rotateY(calc(var(--pointer-x) * 1.4deg)) - rotateX(calc(var(--pointer-y) * -1.2deg)); - transform-origin: center; - transition: transform 180ms ease-out; -} - -.desktop-product-window { - position: relative; - width: 100%; - height: 100%; - overflow: visible; - border: 1px solid rgba(195, 186, 200, 0.95); - border-radius: 20px; - background: #fcfbfc; - box-shadow: - 0 38px 90px rgba(52, 34, 62, 0.16), - 0 8px 24px rgba(52, 34, 62, 0.08), - inset 0 1px white; -} - -.desktop-product-window::after { - position: absolute; - inset: 0; - border-radius: inherit; - background: linear-gradient(118deg, rgba(255, 255, 255, 0.5), transparent 24%, transparent 72%, rgba(168, 85, 247, 0.025)); - content: ""; - pointer-events: none; -} - -.desktop-window-bar { - position: relative; - z-index: 2; - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: center; - height: 34px; - padding: 0 11px; - border-bottom: 1px solid #e9e5eb; - border-radius: 20px 20px 0 0; - background: rgba(248, 247, 249, 0.92); -} - -.window-controls { - display: flex; - gap: 5px; -} - -.window-controls i { - width: 7px; - height: 7px; - border-radius: 50%; - background: #d8d2dc; -} - -.window-controls i:first-child { background: #c084fc; } - -.window-title { - color: #777078; - font-size: 8px; - font-weight: 650; -} - -.window-security { - display: inline-flex; - align-items: center; - justify-self: end; - gap: 4px; - color: #7b707f; - font-family: var(--font-mono); - font-size: 6px; - font-weight: 750; - letter-spacing: 0.04em; - text-transform: uppercase; -} - -.window-security svg { - width: 9px; - height: 9px; - color: var(--brand-600); -} - -.desktop-app-layout { - position: relative; - z-index: 1; - display: grid; - grid-template-columns: 50px 1fr; - height: calc(100% - 34px); - overflow: hidden; - border-radius: 0 0 20px 20px; -} - -.desktop-sidebar { - display: flex; - flex-direction: column; - align-items: center; - gap: 7px; - padding: 9px 0 10px; - border-right: 1px solid #ebe7ed; - background: #f7f6f8; -} - -.product-brand-mark { - width: 34px; - height: 34px; - margin-bottom: 2px; - object-fit: contain; -} - -.desktop-sidebar-item { - display: grid; - width: 30px; - height: 30px; - place-items: center; - border-radius: 9px; - color: #8c848f; -} - -.desktop-sidebar-item.is-active { - background: linear-gradient(135deg, #ead5fe, #f5eafe); - color: var(--brand-700); - box-shadow: inset 0 0 0 1px rgba(168, 85, 247, 0.1); -} - -.desktop-sidebar-item svg { - width: 14px; - height: 14px; -} - -.desktop-sidebar-spacer { flex: 1; } - -.desktop-profile { - display: grid; - width: 26px; - height: 26px; - place-items: center; - border-radius: 9px; - background: #232027; - color: white; - font-size: 7px; - font-weight: 780; -} - -.desktop-transfer-view { - display: flex; - min-width: 0; - flex-direction: column; - padding: 14px 16px 12px; - background: #fdfcfd; -} - -.desktop-view-heading { - display: flex; - align-items: flex-start; - justify-content: space-between; - margin-bottom: 11px; -} - -.desktop-view-heading div { - display: grid; - gap: 2px; -} - -.desktop-view-heading small { - color: #9a919e; - font-family: var(--font-mono); - font-size: 6px; - font-weight: 800; - letter-spacing: 0.11em; -} - -.desktop-view-heading strong { - color: #211d24; - font-family: var(--font-display); - font-size: 15px; - line-height: 1.2; - letter-spacing: -0.035em; -} - -.sharing-pill { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 4px 7px; - border: 1px solid #e2d2ee; - border-radius: 999px; - background: #f6edfe; - color: #6f27e9; - font-size: 7px; - font-weight: 750; -} - -.sharing-pill i { - width: 5px; - height: 5px; - border-radius: 50%; - background: var(--brand-500); - box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.1); -} - -.file-collection-card { - position: relative; - display: grid; - grid-template-columns: 64px 1fr auto; - align-items: center; - gap: 11px; - padding: 9px; - border: 1px solid #e9e5eb; - border-radius: 14px; - background: #f7f6f8; -} - -.file-collage { - display: grid; - width: 64px; - height: 52px; - grid-template-columns: 1.15fr 0.85fr; - grid-template-rows: repeat(2, 1fr); - gap: 2px; - overflow: hidden; - border-radius: 9px; - background: #e9e5eb; -} - -.file-collage span:first-child { - grid-row: 1 / 3; - background: - radial-gradient(circle at 35% 23%, #ffe6b6 0 8%, transparent 9%), - linear-gradient(145deg, #94b8d5 0 38%, #5a8b8f 39% 57%, #efbc7b 58%); -} - -.file-collage span:nth-child(2) { - background: linear-gradient(150deg, #8a76a8 0 35%, #d4a985 36% 68%, #5c4356 69%); -} - -.file-collage span:nth-child(3) { - background: linear-gradient(140deg, #ceddca 0 38%, #6d9b77 39% 61%, #d7bd92 62%); -} - -.file-collage span:nth-child(4) { display: none; } - -.file-collection-copy { - display: grid; - min-width: 0; -} - -.file-collection-copy strong { - overflow: hidden; - color: #27222a; - font-size: 10px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.file-collection-copy > span { - color: #7f7683; - font-size: 7px; -} - -.file-collection-copy small { - display: flex; - align-items: center; - gap: 4px; - margin-top: 5px; - color: #85798a; - font-size: 6px; -} - -.file-collection-copy svg { - width: 9px; - color: var(--brand-600); -} - -.collection-menu { - align-self: start; - padding: 0 2px; - color: #9c929f; - font-size: 9px; - letter-spacing: 1px; -} - -.receiver-section-heading { - display: flex; - align-items: baseline; - justify-content: space-between; - margin: 11px 2px 6px; -} - -.receiver-section-heading span { - color: #373039; - font-size: 8px; - font-weight: 750; -} - -.receiver-section-heading small { - color: #938a96; - font-size: 6px; -} - -.desktop-receiver-row { - position: relative; - display: grid; - grid-template-columns: 31px 1fr auto; - align-items: center; - gap: 8px; - min-height: 48px; - padding: 8px 9px 10px; - border: 1px solid #e8e3ea; - border-radius: 12px; - background: white; - box-shadow: 0 5px 15px rgba(49, 36, 55, 0.035); -} - -.receiver-avatar { - display: grid; - width: 29px; - height: 29px; - place-items: center; - border-radius: 10px; - background: linear-gradient(135deg, #ead2fe, #c084fc); - color: #5c1bb8; - font-size: 9px; - font-weight: 800; -} - -.receiver-identity { - display: grid; - min-width: 0; -} - -.receiver-identity strong { - overflow: hidden; - color: #302a33; - font-size: 8px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.receiver-identity small { - color: #938a96; - font-size: 6px; -} - -.receiver-state-stack { - position: relative; - width: 86px; - height: 16px; -} - -.receiver-row-state { - position: absolute; - inset: 0; - display: inline-flex; - align-items: center; - justify-content: flex-end; - gap: 3px; - font-size: 6px; - font-weight: 720; - white-space: nowrap; -} - -.receiver-row-state.is-waiting { color: #9a6716; } -.receiver-row-state.is-sending { color: var(--brand-700); } -.receiver-row-state.is-complete { color: #23855a; } -.receiver-row-state svg { width: 9px; } - -.receiver-progress-track { - position: absolute; - right: 9px; - bottom: 5px; - left: 48px; - height: 2px; - overflow: hidden; - border-radius: 999px; - background: #ede8ef; -} - -.receiver-progress-track span { - display: block; - width: 100%; - height: 100%; - border-radius: inherit; - background: linear-gradient(90deg, var(--brand-400), var(--brand-700)); - transform: scaleX(0); - transform-origin: left; -} - -.desktop-transfer-foot { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: auto; - padding: 8px 2px 0; - color: #8e8492; - font-size: 6px; -} - -.desktop-transfer-foot span { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.desktop-transfer-foot svg { - width: 9px; - color: var(--brand-600); -} - -.approval-knock-card { - position: absolute; - z-index: 8; - right: -8%; - bottom: -19%; - width: 238px; - padding: 12px; - border: 1px solid rgba(215, 202, 222, 0.95); - border-radius: 16px; - background: rgba(255, 255, 255, 0.96); - box-shadow: 0 24px 65px rgba(55, 31, 66, 0.2); - backdrop-filter: blur(18px); -} - -.approval-knock-top { - display: grid; - grid-template-columns: 30px 1fr 27px; - align-items: center; - gap: 8px; -} - -.approval-knock-top > span:nth-child(2) { - display: grid; -} - -.approval-knock-top small { - color: #9a8da0; - font-family: var(--font-mono); - font-size: 5.5px; - font-weight: 800; - letter-spacing: 0.09em; -} - -.approval-knock-top strong { - color: #27212b; - font-size: 8px; -} - -.approval-lock { - display: grid; - width: 27px; - height: 27px; - place-items: center; - border-radius: 9px; - background: var(--brand-100); - color: var(--brand-700); -} - -.approval-lock svg { width: 13px; } - -.approval-knock-card p { - margin: 9px 0; - color: #756c79; - font-size: 7px; -} - -.approval-knock-card p strong { color: #3b323e; } - -.approval-knock-actions { - display: grid; - grid-template-columns: 1fr 1.2fr; - gap: 7px; -} - -.approval-knock-actions > span { - display: inline-flex; - min-height: 28px; - align-items: center; - justify-content: center; - gap: 4px; - border: 1px solid #e6e0e9; - border-radius: 8px; - color: #685f6c; - font-size: 7px; - font-weight: 720; -} - -.approval-knock-actions .approval-accept { - border-color: transparent; - background: linear-gradient(135deg, var(--brand-500), var(--brand-700)); - box-shadow: 0 8px 18px rgba(132, 47, 238, 0.22); - color: white; -} - -.approval-accept svg { width: 10px; } - -.hero-conduit { - position: absolute; - z-index: 2; - inset: 0; - width: 100%; - height: 100%; - overflow: visible; - pointer-events: none; -} - diff --git a/docs/components/icons.tsx b/docs/components/icons.tsx index 9979320..778ff8d 100644 --- a/docs/components/icons.tsx +++ b/docs/components/icons.tsx @@ -2,24 +2,13 @@ import type { SVGProps } from "react"; export type IconName = | "arrow" - | "check" - | "chevron" - | "close" - | "code" | "devices" - | "file" | "folder" | "github" - | "globe" | "lock" - | "menu" - | "nfc" | "qr" | "shield" - | "spark" - | "stop" - | "verified" - | "x"; + | "verified"; type IconProps = SVGProps & { name: IconName; @@ -33,21 +22,6 @@ export function Icon({ name, ...props }: IconProps) { ), - check: , - chevron: , - close: ( - <> - - - - ), - code: ( - <> - - - - - ), devices: ( <> @@ -55,12 +29,6 @@ export function Icon({ name, ...props }: IconProps) { ), - file: ( - <> - - - - ), folder: ( ), @@ -72,30 +40,12 @@ export function Icon({ name, ...props }: IconProps) { fill="currentColor" /> ), - globe: ( - <> - - - - ), lock: ( <> ), - menu: ( - <> - - - ), - nfc: ( - <> - - - - - ), qr: ( <> @@ -110,22 +60,12 @@ export function Icon({ name, ...props }: IconProps) { ), - spark: ( - - ), - stop: , verified: ( <> ), - x: ( - <> - - - - ), }; return ( diff --git a/docs/components/illustration-motion.css b/docs/components/illustration-motion.css deleted file mode 100644 index f1b0e90..0000000 --- a/docs/components/illustration-motion.css +++ /dev/null @@ -1,277 +0,0 @@ -@keyframes scene-orbits { to { transform: rotate(360deg); } } -@keyframes laptop-files { - 0%, 100% { transform: translateY(0) rotate(0); } - 50% { transform: translateY(-5px) rotate(1deg); } -} -@keyframes conduit-energy { to { stroke-dashoffset: -38; } } -@keyframes gate-hover { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-6px); } -} -@keyframes received-file { - 0%, 9% { opacity: 0.55; transform: translateY(12px) scale(0.92); } - 17%, 70% { opacity: 1; transform: translateY(0) scale(1); } - 83%, 100% { opacity: 0.55; transform: translateY(12px) scale(0.92); } -} -@keyframes hero-packet-one { - 0%, 6% { opacity: 0; transform: translate(245px, 320px) scale(0.55) rotate(-8deg); } - 14% { opacity: 1; } - 43% { opacity: 1; transform: translate(380px, 427px) scale(0.82) rotate(5deg); } - 54% { opacity: 1; transform: translate(415px, 430px) scale(0.82) rotate(2deg); } - 84% { opacity: 1; transform: translate(556px, 296px) scale(0.65) rotate(10deg); } - 92%, 100% { opacity: 0; transform: translate(585px, 270px) scale(0.35) rotate(14deg); } -} -@keyframes hero-packet-two { - 0%, 6% { opacity: 0; transform: translate(255px, 327px) scale(0.5) rotate(8deg); } - 15% { opacity: 0.85; } - 45% { opacity: 0.9; transform: translate(382px, 440px) scale(0.75) rotate(-4deg); } - 56% { opacity: 0.9; transform: translate(414px, 436px) scale(0.75) rotate(-2deg); } - 85% { opacity: 0.8; transform: translate(560px, 308px) scale(0.55) rotate(-10deg); } - 93%, 100% { opacity: 0; transform: translate(588px, 278px) scale(0.25) rotate(-15deg); } -} -@keyframes hero-spark-one { - 0%, 12% { opacity: 0; transform: translate(266px, 346px) scale(0); } - 19% { opacity: 1; transform: translate(285px, 360px) scale(1); } - 29%, 100% { opacity: 0; transform: translate(310px, 380px) scale(0); } -} -@keyframes hero-spark-two { - 0%, 77% { opacity: 0; transform: translate(545px, 307px) scale(0); } - 87% { opacity: 1; transform: translate(575px, 279px) scale(1); } - 97%, 100% { opacity: 0; transform: translate(603px, 260px) scale(0); } -} -@keyframes status-hover { - 0%, 100% { margin-top: 0; } - 50% { margin-top: -7px; } -} -@keyframes status-pulse { - 0%, 100% { box-shadow: 0 0 0 3px rgba(43, 196, 119, 0.1); } - 50% { box-shadow: 0 0 0 7px rgba(43, 196, 119, 0); } -} -@keyframes flow-glow { - 0%, 100% { transform: scale(0.8); opacity: 0.6; } - 50% { transform: scale(1.2); opacity: 1; } -} -@keyframes flow-drop { - 0%, 7% { offset-distance: 0%; opacity: 0; } - 13% { opacity: 1; } - 47% { offset-distance: 51%; opacity: 1; } - 57% { offset-distance: 51%; opacity: 1; } - 91% { offset-distance: 100%; opacity: 1; } - 97%, 100% { offset-distance: 100%; opacity: 0; } -} -@keyframes gate-breathe { - 0%, 100% { transform: scale(0.98); } - 50% { transform: scale(1.04); } -} -@keyframes complete-ring { - 0%, 100% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.18); } - 50% { box-shadow: 0 0 0 13px rgba(168, 85, 247, 0); } -} -@keyframes invite-panel-enter { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: none; } -} -@keyframes qr-card-float { - 0%, 100% { transform: rotate(2deg) translateY(0); } - 50% { transform: rotate(-1deg) translateY(-8px); } -} -@keyframes qr-cell { - from { opacity: 0; transform: scale(0.2); } - to { opacity: 1; transform: scale(1); } -} -@keyframes qr-scan { - 0%, 100% { top: 40px; opacity: 0; } - 12%, 88% { opacity: 1; } - 50% { top: 222px; } -} -@keyframes nfc-dot { - 0%, 100% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.2); } - 50% { box-shadow: 0 0 0 19px rgba(168, 85, 247, 0); } -} -@keyframes nfc-wave { - 0%, 100% { opacity: 0.25; transform: translateY(-50%) scale(0.88); } - 50% { opacity: 1; transform: translateY(-50%) scale(1.04); } -} -@keyframes vnd-drop { - 0%, 8% { transform: translateX(-50%) translateY(-16px) rotate(-5deg); } - 20%, 78% { transform: translateX(-50%) translateY(0) rotate(-2deg); } - 88%, 100% { transform: translateX(-50%) translateY(-16px) rotate(-5deg); } -} -@keyframes avatar-pulse { - 0% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.24); } - 75%, 100% { box-shadow: 0 0 0 14px rgba(168, 85, 247, 0); } -} -@keyframes approval-state-enter { - from { opacity: 0; transform: translateY(7px); } - to { opacity: 1; transform: none; } -} -@keyframes progress-fill { - from { width: 0; } - to { width: 68%; } -} -@keyframes network-dash { to { stroke-dashoffset: -44; } } -@keyframes relay-ring { - 0% { opacity: 0.6; transform: translateX(-50%) scale(0.72); } - 85%, 100% { opacity: 0; transform: translateX(-50%) scale(1.2); } -} -@keyframes network-packet-top { - 0%, 8% { left: 15%; top: 40%; opacity: 0; transform: rotate(-7deg) scale(0.7); } - 15% { opacity: 1; } - 48% { left: 47%; top: 20%; opacity: 1; transform: rotate(3deg) scale(0.9); } - 85% { left: 82%; top: 40%; opacity: 1; transform: rotate(8deg) scale(0.72); } - 94%, 100% { left: 85%; top: 42%; opacity: 0; transform: rotate(10deg) scale(0.4); } -} -@keyframes network-packet-bottom { - 0%, 8% { left: 16%; top: 50%; opacity: 0; transform: rotate(8deg) scale(0.65); } - 15% { opacity: 0.8; } - 48% { left: 48%; top: 69%; opacity: 0.8; transform: rotate(-3deg) scale(0.8); } - 84% { left: 82%; top: 50%; opacity: 0.8; transform: rotate(-8deg) scale(0.7); } - 94%, 100% { left: 85%; top: 48%; opacity: 0; transform: rotate(-10deg) scale(0.4); } -} -@keyframes orbit-spin { - from { transform: translate(-50%, -50%) rotate(0deg); } - to { transform: translate(-50%, -50%) rotate(360deg); } -} -@keyframes platform-glow { - 0%, 100% { opacity: 0.45; transform: scale(0.82); } - 50% { opacity: 1; transform: scale(1.15); } -} -@keyframes platform-chip-float { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-8px); } -} - -@media (max-width: 1120px) { - .flow-layout { - grid-template-columns: minmax(450px, 1fr) minmax(350px, 0.8fr); - gap: 45px; - } - - .flow-device { transform: scale(0.88); } - .flow-device-sender { transform: scale(0.88) rotate(-6deg); transform-origin: top left; } - .flow-device-receiver { transform: scale(0.88) rotate(5deg); transform-origin: bottom right; } - - .invite-panel { - grid-template-columns: minmax(0, 0.85fr) minmax(420px, 1.15fr); - } - - .invite-copy { padding: 45px; } - .network-visual { min-height: 500px; } -} - -@media (max-width: 920px) { - .hero-visual { - max-width: 720px; - margin-inline: auto; - } - - .flow-layout { - grid-template-columns: 1fr; - } - - .flow-visual { - position: relative; - top: auto; - } - - .invite-panel { - grid-template-columns: 1fr; - } - - .invite-art { - min-height: 450px; - border-top: 1px solid var(--line); - border-left: 0; - } - - .network-visual { - min-height: 520px; - } - - .platform-orbit { - max-width: 620px; - } -} - -@media (max-width: 680px) { - .hero-stage { - width: 112%; - margin-left: -6%; - } - - .hero-status-secure { - top: 0; - right: 2%; - transform: scale(0.88) rotate(2deg); - transform-origin: top right; - } - - .hero-status-route { left: 4%; transform: scale(0.88) rotate(-3deg); transform-origin: top left; } - .hero-file-label { bottom: 3%; left: 4%; transform: scale(0.88) rotate(-2deg); transform-origin: bottom left; } - - .flow-visual { - height: 540px; - border-radius: 21px; - } - - .flow-device { - width: 137px; - height: 215px; - } - - .flow-device-sender { top: 24px; left: 16px; } - .flow-device-receiver { right: 14px; bottom: 20px; } - .flow-track { inset: 0 -2%; width: 104%; height: 100%; } - .flow-annotation { display: none; } - - .flow-step { - grid-template-columns: auto 1fr; - padding: 20px; - } - - .invite-tabs button { - min-height: 59px; - flex-direction: column; - gap: 3px; - font-size: 10px; - } - - .invite-tabs svg { width: 17px; } - .invite-copy { padding: 42px 24px; } - .invite-art { min-height: 390px; } - .invite-qr-art { transform: scale(0.86) rotate(2deg); } - .invite-nfc-art { transform: scale(0.82); } - .invite-file-art { transform: scale(0.85); } - - .approval-body { min-height: 370px; padding: 28px 20px; } - .approval-heading h3 { font-size: 18px; } - .approval-actions { grid-template-columns: 1fr 1.2fr; } - - .network-visual { - min-height: 430px; - border-radius: 21px; - } - - .network-device { - top: 150px; - width: 77px; - height: 112px; - } - - .network-device-left { left: 13px; } - .network-device-right { right: 13px; } - .network-device svg { width: 24px; } - .network-relay-node { bottom: 29px; } - .network-relay-node small { max-width: 160px; text-align: center; } - .network-direct-label { top: 43px; } - - .platform-orbit { width: 112%; margin-left: -6%; } - .platform-center { width: 112px; height: 112px; border-radius: 30px; } - .platform-center img { width: 68px; height: 68px; } - .platform-chip { width: 48px; height: 48px; padding: 0; } - .platform-chip .platform-glyph { width: 25px; height: 25px; } - .platform-chip-1 { left: 10%; } - .platform-chip-2 { right: 0; } - .platform-chip-3 { right: -2%; } - .platform-chip-4 { left: 17%; } -} diff --git a/docs/components/invitation-scene.css b/docs/components/invitation-scene.css deleted file mode 100644 index e12aa8b..0000000 --- a/docs/components/invitation-scene.css +++ /dev/null @@ -1,405 +0,0 @@ -/* Invitation methods */ -.invite-showcase { - overflow: hidden; - border: 1px solid var(--line); - border-radius: 25px; - background: white; - box-shadow: var(--shadow-lg); -} - -.invite-tabs { - display: grid; - grid-template-columns: repeat(3, 1fr); - border-bottom: 1px solid var(--line); - background: #f7f4f8; -} - -.invite-tabs button { - display: inline-flex; - min-height: 66px; - align-items: center; - justify-content: center; - gap: 9px; - border: 0; - border-right: 1px solid var(--line); - background: transparent; - color: var(--ink-muted); - font-size: 13px; - font-weight: 700; - cursor: pointer; - transition: color 180ms ease, background 180ms ease; -} - -.invite-tabs button:last-child { border-right: 0; } -.invite-tabs button:hover { color: var(--ink); } -.invite-tabs button.is-active { - position: relative; - background: white; - color: var(--brand-600); -} - -.invite-tabs button.is-active::after { - position: absolute; - right: 20%; - bottom: -1px; - left: 20%; - height: 2px; - border-radius: 999px 999px 0 0; - background: var(--brand-500); - content: ""; -} - -.invite-tabs svg { width: 19px; } - -.invite-panel { - display: grid; - min-height: 490px; - grid-template-columns: minmax(0, 0.83fr) minmax(480px, 1.17fr); - animation: invite-panel-enter 420ms cubic-bezier(0.2, 0.8, 0.2, 1); -} - -.invite-copy { - display: flex; - align-items: flex-start; - flex-direction: column; - justify-content: center; - padding: 60px; -} - -.mono-label { - color: var(--brand-600); - font-family: var(--font-mono); - font-size: 9px; - font-weight: 800; - letter-spacing: 0.11em; -} - -.invite-copy h3 { - max-width: 430px; - margin: 12px 0 18px; - font-size: clamp(2rem, 3vw, 3.15rem); - letter-spacing: -0.055em; - line-height: 1.06; -} - -.invite-copy > p { - max-width: 440px; - margin-bottom: 30px; - color: var(--ink-soft); - font-size: 15px; - line-height: 1.7; -} - -.invite-detail { - display: flex; - gap: 11px; - padding-top: 20px; - border-top: 1px solid var(--line); -} - -.invite-detail > svg { - width: 19px; - flex: 0 0 auto; - color: var(--brand-600); -} - -.invite-detail strong, -.invite-detail small { - display: block; -} - -.invite-detail strong { - margin-bottom: 3px; - color: #3c3440; - font-size: 11px; -} - -.invite-detail small { - color: var(--ink-muted); - font-size: 9px; - line-height: 1.5; -} - -.invite-art { - position: relative; - display: grid; - overflow: hidden; - place-items: center; - border-left: 1px solid var(--line); - background: - linear-gradient(rgba(116, 86, 130, 0.05) 1px, transparent 1px), - linear-gradient(90deg, rgba(116, 86, 130, 0.05) 1px, transparent 1px), - radial-gradient(circle at center, #fbf7fd, #f2edf5); - background-size: 24px 24px, 24px 24px, auto; -} - -.invite-art::before { - position: absolute; - width: 330px; - aspect-ratio: 1; - border-radius: 50%; - background: rgba(168, 85, 247, 0.14); - content: ""; - filter: blur(55px); -} - -.invite-qr-art { - position: relative; - display: grid; - width: 265px; - height: 265px; - place-items: center; - border: 1px solid #e0d5e5; - border-radius: 25px; - background: rgba(255, 255, 255, 0.95); - box-shadow: 0 30px 80px rgba(59, 35, 70, 0.18); - transform: rotate(2deg); - animation: qr-card-float 6s ease-in-out infinite; -} - -.invite-qr-grid { - display: grid; - width: 178px; - height: 178px; - grid-template-columns: repeat(9, 1fr); - grid-template-rows: repeat(9, 1fr); - gap: 4px; -} - -.invite-qr-grid span { - border-radius: 2px; - background: transparent; -} - -.invite-qr-grid span.is-filled { - background: #2b2230; - animation: qr-cell 600ms ease backwards; -} - -.invite-qr-grid span:nth-child(3n) { animation-delay: 80ms; } -.invite-qr-grid span:nth-child(4n) { animation-delay: 160ms; } -.invite-qr-grid span:nth-child(7n) { animation-delay: 230ms; } - -.qr-scan-line { - position: absolute; - right: 34px; - left: 34px; - height: 2px; - background: linear-gradient(90deg, transparent, var(--brand-500), transparent); - box-shadow: 0 0 13px var(--brand-400); - animation: qr-scan 3.2s ease-in-out infinite; -} - -.qr-corner { - position: absolute; - width: 25px; - height: 25px; - border-color: var(--brand-500); - border-style: solid; -} - -.qr-corner-one { top: 21px; left: 21px; border-width: 2px 0 0 2px; border-radius: 7px 0 0; } -.qr-corner-two { top: 21px; right: 21px; border-width: 2px 2px 0 0; border-radius: 0 7px 0 0; } -.qr-corner-three { right: 21px; bottom: 21px; border-width: 0 2px 2px 0; border-radius: 0 0 7px; } -.qr-corner-four { bottom: 21px; left: 21px; border-width: 0 0 2px 2px; border-radius: 0 0 0 7px; } - -.invite-nfc-art { - position: relative; - width: 440px; - height: 330px; -} - -.nfc-device { - position: absolute; - top: 10px; - left: 10px; - width: 155px; - height: 300px; - border: 7px solid #d7cfdc; - border-radius: 30px; - background: #211c25; - box-shadow: var(--shadow-lg); - transform: rotate(-8deg); -} - -.nfc-device::after { - position: absolute; - top: 11px; - left: 50%; - width: 50px; - height: 7px; - border-radius: 999px; - background: #09070a; - content: ""; - transform: translateX(-50%); -} - -.nfc-screen-dot { - position: absolute; - top: 50%; - left: 50%; - width: 54px; - height: 54px; - border: 1px solid rgba(192, 132, 252, 0.55); - border-radius: 50%; - background: rgba(168, 85, 247, 0.2); - transform: translate(-50%, -50%); - animation: nfc-dot 2.5s ease-in-out infinite; -} - -.nfc-tag { - position: absolute; - right: 25px; - bottom: 30px; - display: grid; - width: 138px; - height: 138px; - place-items: center; - border: 1px solid #d9cfe0; - border-radius: 28px; - background: white; - box-shadow: 0 25px 70px rgba(52, 31, 62, 0.18); - color: var(--brand-600); - transform: rotate(8deg); -} - -.nfc-tag svg { width: 46px; } -.nfc-tag small { - position: absolute; - bottom: 18px; - color: var(--ink-muted); - font-family: var(--font-mono); - font-size: 8px; - font-weight: 700; - letter-spacing: 0.08em; -} - -.nfc-waves { - position: absolute; - top: 75px; - left: 145px; - width: 185px; - height: 160px; -} - -.nfc-waves span { - position: absolute; - top: 50%; - left: 0; - border: 2px solid var(--brand-500); - border-top-color: transparent; - border-bottom-color: transparent; - border-left-color: transparent; - border-radius: 50%; - transform: translateY(-50%); - animation: nfc-wave 2.4s ease-in-out infinite; -} - -.nfc-waves span:nth-child(1) { width: 55px; height: 70px; } -.nfc-waves span:nth-child(2) { width: 98px; height: 112px; animation-delay: 180ms; } -.nfc-waves span:nth-child(3) { width: 142px; height: 154px; animation-delay: 360ms; } -.nfc-waves i { - position: absolute; - top: 50%; - left: 15px; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--brand-500); - box-shadow: 0 0 0 8px rgba(168, 85, 247, 0.13); - transform: translateY(-50%); -} - -.invite-file-art { - position: relative; - width: 410px; - height: 350px; -} - -.vnd-file { - position: absolute; - z-index: 2; - top: 13px; - left: 50%; - display: flex; - width: 218px; - height: 270px; - align-items: center; - flex-direction: column; - justify-content: center; - border: 1px solid #d8cbe0; - border-radius: 23px; - background: linear-gradient(145deg, #fff, #f7f0fb); - box-shadow: 0 30px 80px rgba(59, 35, 70, 0.2); - transform: translateX(-50%) rotate(-2deg); - animation: vnd-drop 4.8s cubic-bezier(0.3, 0.75, 0.3, 1) infinite; -} - -.vnd-fold { - position: absolute; - top: -1px; - right: -1px; - width: 58px; - height: 58px; - border-bottom: 1px solid #d1c1dc; - border-left: 1px solid #d1c1dc; - border-radius: 0 23px 0 16px; - background: #e7cdf9; - clip-path: polygon(0 0, 100% 100%, 0 100%); -} - -.vnd-file > svg { - width: 58px; - margin-bottom: 20px; - color: var(--brand-600); -} - -.vnd-file strong { - font-family: var(--font-display); - font-size: 16px; -} - -.vnd-file small { - color: var(--ink-muted); - font-size: 10px; -} - -.vnd-file code { - margin-top: 18px; - padding: 5px 10px; - border-radius: 7px; - background: var(--brand-100); - color: var(--brand-600); - font-family: var(--font-mono); - font-size: 10px; - font-weight: 800; -} - -.file-tray { - position: absolute; - right: 37px; - bottom: 7px; - left: 37px; - height: 73px; - border: 2px solid #cabbd2; - border-radius: 19px; - background: rgba(237, 228, 242, 0.85); - transform: perspective(250px) rotateX(55deg); -} - -.file-tray span { - position: absolute; - inset: 14px; - border: 1px dashed #aa8cbc; - border-radius: 10px; -} - -.file-art-orbit { - position: absolute; - border: 1px solid rgba(168, 85, 247, 0.2); - border-radius: 50%; -} - -.file-art-orbit-one { inset: 14px 32px 44px; animation: scene-orbits 24s linear infinite; } -.file-art-orbit-two { inset: 52px 70px 80px; border-style: dashed; animation: scene-orbits 18s linear infinite reverse; } - diff --git a/docs/components/invitation-showcase.tsx b/docs/components/invitation-showcase.tsx deleted file mode 100644 index 312b902..0000000 --- a/docs/components/invitation-showcase.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Icon, type IconName } from "@/components/icons"; - -type Method = "qr" | "nfc" | "file"; - -const methods: Array<{ - id: Method; - icon: IconName; - label: string; - eyebrow: string; - title: string; - text: string; -}> = [ - { - id: "qr", - icon: "qr", - label: "QR code", - eyebrow: "IN THE SAME ROOM", - title: "Point. Scan. Connected.", - text: "Show the invitation on one screen and scan it from the other. No contact exchange required.", - }, - { - id: "nfc", - icon: "nfc", - label: "NFC", - eyebrow: "THE QUICKEST TAP", - title: "Bring devices together.", - text: "Write or open an invitation with an NFC tag for a physical, close-range handoff.", - }, - { - id: "file", - icon: "file", - label: ".vnd file", - eyebrow: "SEND IT ANYWHERE", - title: "An invitation that travels.", - text: "Save the small invitation as a .vnd file, then pass it through the channel you already use.", - }, -]; - -function QrArt() { - const filled = new Set([ - 0, 1, 2, 3, 4, 6, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 29, 30, 31, 32, - 36, 38, 40, 42, 44, 48, 49, 50, 51, 52, 54, 56, 58, 60, 62, 64, 66, 67, 69, - 72, 73, 74, 76, 78, 79, 80, - ]); - return ( -
-
- {Array.from({ length: 81 }, (_, index) => ( - - ))} -
- - - - - -
- ); -} - -function NfcArt() { - return ( -
-
- -
-
- - - - -
-
- - VniDrop -
-
- ); -} - -function FileArt() { - return ( -
- - -
- - - Weekend trip - VniDrop invitation - .vnd -
-
- -
-
- ); -} - -export function InvitationShowcase() { - const [active, setActive] = useState("qr"); - const current = methods.find((method) => method.id === active) ?? methods[0]; - - return ( -
-
- {methods.map((method) => ( - - ))} -
-
-
- {current.eyebrow} -

{current.title}

-

{current.text}

-
- - - The invitation is a private access link. - Only share it with people you intend to receive the transfer. - -
-
- -
-
- ); -} diff --git a/docs/components/network-illustration.tsx b/docs/components/network-illustration.tsx deleted file mode 100644 index 5251913..0000000 --- a/docs/components/network-illustration.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Icon } from "@/components/icons"; - -export function NetworkIllustration() { - return ( -