# VREMLY brand system — the LLM file # Version 2 · September 2026 · source of truth: https://vremly.com/brand # Hand this whole file to an AI model and it can build a Vremly page, section, asset or animation on brand. # Every value below is read from the site's code, and the file it lives in is named beside it (paths, not line numbers — lines drift). Nothing is approximate. > This file: https://vremly.com/vremly-brand.md · the same bytes, rendered in a browser tab: https://vremly.com/vremly-brand.txt (generated from the .md by `npm run brand:txt`, so the two cannot drift). > This file governs how Vremly LOOKS. What Vremly may CLAIM lives in https://vremly.com/llms.txt and https://vremly.com/llms-full.txt — writing copy needs both. > Vremly is a registered trade name of 2817230 Alberta Ltd. Questions and corrections: support@vremly.com ## 0. How to use this file - Read section 1 (what Vremly is, and the hard rules) before designing anything. Then pick a page recipe (section 12) and compose it from the components (section 8) and the window / console kit (section 9) with the tokens (section 2) and type (section 3). - The emerald gradient (section 5) is the ONLY texture: five forms plus the page grain. Its vector files are in Appendix A — paste them, never re-pick the stops. - The V (sections 6 and 7) is fixed vector artwork. Its paths are in Appendix B. Place the files; never redraw the V and never set "VREMLY" in a font as a logo. - Every dashboard, console and product graphic (section 9) is built from ONE window + console kit — arrange its parts, never invent a new chart or window style. - Motion (section 11) is a few entrances, a few loops and one console clock. The timings are exact. - Copy (section 13) is verbatim. Never invent slogans, testimonials, customers or numbers. Product-capability claims in marketing copy come only from https://vremly.com/llms-full.txt. The kit's console (section 9) depicts the Vremly MCP connector as the product's own connector screen describes it; the connector is not in llms-full.txt, so copy makes no capability, plan or availability claim about it. - Every downloadable file (logos, marks, gradients, grain) is listed with its URL in sections 5, 6 and 7. The fonts are Google Fonts (section 3). - The CSS for every `.vb-*` class named here is in Appendix C, verbatim; the phone rules are in Appendix D. - `RULE SET HERE` marks a rule that is a decision recorded in this file, not a behaviour read from running code. - Where this file and an older Vremly document disagree, this file wins. The page and this file number their sections differently: | https://vremly.com/brand | This file | |---|---| | Hero | section 0 | | 01 · Logo | section 6 | | 02 · Colour | section 2 | | 03 · Type | section 3 | | 04 · The gradient | section 5 + Appendix A | | 05 · The mark | section 7 + Appendix B | | 06 · Components | section 8 | | 07 · Dashboards & consoles | section 9 | | 08 · Motion | section 11 | | 09 · For AI | this file | ## 1. What Vremly is, and the design language Vremly is the operating system for real-estate media production: one platform where agents book media, teams run operations, and providers find work (owner-asserted, https://vremly.com/llms-full.txt). The loop it closes: an agent books, a technician shoots, an editor delivers, the agent rebooks. Design for real-estate photographers, videographers, media-company owners and the agents and brokers who book them, and design for a phone first (`RULE SET HERE`). The design language: a near-white `#FCFCFC` ground with `#FFFFFF` surfaces, ink `#1A1A1A` headlines in IBM Plex Sans, Inter for reading, `#E5E5E5` hairlines, pill buttons, and ONE emerald accent — a single IBM Plex Serif italic word per headline, filled with an emerald gradient (`.accent-word`). The product appears in light or dark windows using the matching app palette. The only texture is the emerald gradient. Calm, technical, legible. There is no mascot, no pattern, no illustration style and no drawn icon family. Hard rules 1. Emerald `#059669` is the only chromatic ACCENT. The emerald family (`#047857` `#34D399` `#6EE7B7` `#D1FAE5` `#ECFDF5` `#A7F3D0` `#064E3B` `#042F2E`) exists to draw gradients and small emerald text. The product status hues (eight pipeline lanes plus Cancelled) belong to product art, as state: the chips inside product windows, and — as their Tailwind hue families — the aurora a home-page replica sits on (`components/landing/product-ui/AuroraGradient.tsx`). Live one-offs are NOT system values and must never be reused: `.accent-gold` (a gold gradient, `app/globals.css`, on `components/landing/BuiltByOperators.tsx`, with gold `ShineBorder` lines on that band's top and bottom edges), `.clause-mark` (an amber highlighter, only on /switch-from-aryeo) and the raw Tailwind emerald utilities listed in section 10. 2. One accent word per headline — never two, never in body copy, never on a function word, avoid names except for the explicitly approved /brand hero, where “Vremly” is the accent. Most headlines have none. 3. 700 is the heaviest real weight. IBM Plex Sans loads 400/500/600/700, IBM Plex Serif 400 and 700 (`app/layout.tsx`). A `font-black` (900) heading is a browser-synthesised smear of the 700. Never. 4. The ground is `#FCFCFC`, not `#FFFFFF`. White is a surface that sits on it: cards, windows, the nav pill, alternating white bands. 5. The product app has light and dark themes. Both may appear in product art; /brand documents both and offers a local preview toggle. Marketing pages and /brand page chrome stay light. Their dark bands are hand-painted `#0C0C0E` with `#F5F5F7` headlines (`components/landing/Enterprise.tsx`, `BuiltByOperators.tsx`, `product-story/index.tsx`), not a token-swapped site theme. Keep the app's dark palette scoped to product windows. 6. Buttons are pills (`rounded-full`), ink or outline. Never a square button. 7. Place the logo files. The letters in them are drawn, not typeset. 8. Never invent an outcome, customer, testimonial, metric, count or delta. Figures inside product windows are illustrative STATE (counts, amounts, statuses), never improvement. 9. Every section must work at 390px. The phone rules are in Appendix D. 10. `prefers-reduced-motion: reduce` gets the finished frame: loops pin their last step, gradients freeze, nothing is removed and no colour changes. 11. Never sample colour from a raster (a PNG, a JPG, a screenshot). Take hex from section 2. ## 2. Tokens The kit tokens, verbatim from `app/brand/brand.css` (scoped to the `.vb` page wrapper on /brand; copy them onto your own root): ```css .vb { /* grounds */ --vb-ground: #fcfcfc; --vb-surface: #ffffff; --vb-raised: #fafafa; --vb-recessed: #f5f5f5; --vb-fill: #f0f0f0; --vb-wash: #ecfdf5; --vb-wash-tint: #d1fae5; /* ink + text */ --vb-ink: #1a1a1a; --vb-ink-hover: #333333; --vb-lede: #525252; --vb-body: #737373; --vb-label: #a3a3a3; --vb-quiet: #d4d4d4; /* lines */ --vb-hair: #e5e5e5; --vb-divider: #f0f0f0; /* accent */ --vb-accent: #059669; --vb-accent-deep: #047857; --vb-accent-mid: #34d399; --vb-accent-light: #6ee7b7; --vb-accent-edge: rgba(5, 150, 105, 0.14); --vb-accent-hover: rgba(5, 150, 105, 0.35); --vb-accent-hover-strong: rgba(5, 150, 105, 0.45); /* dark surfaces (site dark band) */ --vb-dark: #0c0c0e; --vb-dark-text: #f5f5f7; --vb-dark-body: #a1a1aa; --vb-dark-label: #8b8b93; /* shadows */ --vb-shadow-window: 0 26px 60px -30px rgba(16, 32, 24, 0.35); --vb-shadow-console: 0 26px 60px -30px rgba(16, 32, 24, 0.5); --vb-shadow-card: 0 12px 34px -26px rgba(16, 32, 24, 0.35); --vb-shadow-quote: 0 6px 18px rgba(16, 32, 24, 0.06); /* faces */ --vb-font-body: var(--font-inter), ui-sans-serif, system-ui, sans-serif; --vb-font-head: var(--font-ibm-plex-sans), ui-sans-serif, system-ui, sans-serif; --vb-font-serif: var(--font-ibm-plex-serif), ui-serif, Georgia, serif; --vb-font-mono: var(--font-ibm-plex-mono), ui-monospace, SFMono-Regular, Menlo, monospace; font-family: var(--vb-font-body); color: var(--vb-ink); background-color: var(--vb-ground); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } ``` The site-wide `:root`, verbatim from `app/globals.css` (the `--duration` and five `--color-*` HSL triples that drive two decorative effects are omitted). The emerald family and most greys have no custom property on the marketing site — they are written as literal hex in class strings, so a find-and-replace on a token will not find them. ```css :root { --radius: 0.5rem; --background: #fcfcfc; --foreground: #1a1a1a; --border: #e5e5e5; --surface: #ffffff; --surface-hover: #f5f5f5; --muted: #525252; --muted-light: #737373; } ``` The palette — names, hexes and roles as the swatches on https://vremly.com/brand print them (`components/brand/sections/Colour.tsx`); the RGB and CMYK columns, and any role text in parentheses, are added here. RGB is exact; CMYK is the naive screen conversion (`K = 1 − max(R,G,B)/255`), not a press profile — have print work converted against the press's own profile. Grounds | Name | Hex | RGB | CMYK | Role | |---|---|---|---|---| | Ground | `#FCFCFC` | 252 252 252 | 0 0 0 1 | page background (`--background`) | | Surface | `#FFFFFF` | 255 255 255 | 0 0 0 0 | cards, white bands, nav pill (`--surface`) | | Raised | `#FAFAFA` | 250 250 250 | 0 0 0 2 | tile wells, panels | | Recessed | `#F5F5F5` | 245 245 245 | 0 0 0 4 | grey bands, hover fills (`--surface-hover`) | | Fill | `#F0F0F0` | 240 240 240 | 0 0 0 6 | pill fills, table rules | | Wash | `#ECFDF5` | 236 253 245 | 7 0 3 1 | palest green in the hero wash | | Dark band | `#0C0C0E` | 12 12 14 | 14 14 0 95 | dark sections | | Black | `#0A0A0A` | 10 10 10 | 0 0 0 96 | the black logo files | Ink & text | Name | Hex | RGB | CMYK | Role | |---|---|---|---|---| | Ink | `#1A1A1A` | 26 26 26 | 0 0 0 90 | headlines, dark buttons (`--foreground`; hover `#333333`) | | Lede | `#525252` | 82 82 82 | 0 0 0 68 | ledes, nav and footer links (`--muted`) | | Body | `#737373` | 115 115 115 | 0 0 0 55 | section copy, captions (`--muted-light`; 4.7:1 on white, the smallest caption grey) | | Label | `#A3A3A3` | 163 163 163 | 0 0 0 36 | eyebrows, table heads (only for uppercase labels that repeat information) | | Quiet | `#D4D4D4` | 212 212 212 | 0 0 0 17 | inactive marks, underlines | | Hairline | `#E5E5E5` | 229 229 229 | 0 0 0 10 | borders and card edges (`--border`) | Accent | Name | Hex | RGB | CMYK | Role | |---|---|---|---|---| | Emerald | `#059669` | 5 150 105 | 97 0 30 41 | THE accent — once per headline | | Emerald mid | `#34D399` | 52 211 153 | 75 0 27 17 | gradient midpoint | | Emerald light | `#6EE7B7` | 110 231 183 | 52 0 21 9 | gradient light stop (never carries type on white) | | Deep emerald | `#047857` | 4 120 87 | 97 0 27 53 | small emerald text, hovers | Emerald text at 14px and under takes `#047857` (5.5:1 on white); `#059669` is 3.8:1 there, under WCAG AA for small text. `#059669` stays for everything that is not small text: bullets, bars, legend squares, borders, the accent word (`app/brand/brand.css`). Product status · product art only (`lib/product-ui.ts` `STATUS`, mirrored from the app's light theme; pipeline order) | Status | Hex | RGB | CMYK | Role | |---|---|---|---|---| | Pending | `#C97A2A` | 201 122 42 | 0 39 79 21 | unassigned jobs | | Assigned | `#4F6CC5` | 79 108 197 | 60 45 0 23 | booked with a technician | | In Progress | `#7C5FCE` | 124 95 206 | 40 54 0 19 | media capture underway | | Uploaded | `#0E9AA0` | 14 154 160 | 91 4 0 37 | media handed to editors | | Editing | `#C92E34` | 201 46 52 | 0 77 74 21 | being edited | | In Review | `#B8860B` | 184 134 11 | 0 27 94 28 | quality control before delivery | | Delivered | `#4E9F5D` | 78 159 93 | 51 0 42 38 | sent to the client | | In Revision | `#D6336C` | 214 51 108 | 0 76 50 16 | changes requested after delivery | | Cancelled | `#64748B` | 100 116 139 | 28 17 0 45 | cancelled jobs; not a pipeline lane | Also real, not swatches: the gradient stops `#D1FAE5` (tint), `#A7F3D0`, `#064E3B`, `#042F2E` (section 5); on dark bands, text `#F5F5F7`, body `#A1A1AA`, label `#8B8B93` (the `--vb-dark-*` tokens); the /pricing headline's grey second sentence `#404040`. Product tokens — inside a product window only, never on the marketing page (`lib/product-ui.ts` `PRODUCT`, the app's light `:root` converted from oklch): | Token | Value | Use | |---|---|---| | `PRODUCT.bg` | `#fcfcfc` | page background | | `PRODUCT.surface` | `#ffffff` | card / panel | | `PRODUCT.border` | `#dbdbdb` | card border | | `PRODUCT.sidebarBorder` | `#e4e4e4` | sidebar divider | | `PRODUCT.active` | `#f2f2f2` | active sidebar row | | `PRODUCT.well` | `#ebebeb` | recessed field | | `PRODUCT.text` | `#0b0b0b` | primary text | | `PRODUCT.muted` | `#555555` | labels, secondary text | | `PRODUCT.primaryFg` | `#fcfcfc` | text on a near-black primary button | | `PRODUCT.accent` | `#059669` | the brand emerald | | `PRODUCT.radius` | `8px` | the app's radius | ### App dark theme — product previews only Source: `apps/frontend/app/globals.css` `.dark` in vrem-dev `origin/main`, checked 2026-09-17. Neutral hex values are rounded 8-bit sRGB conversions of the app's neutral oklch tokens. `lib/product-ui.ts` mirrors the theme as `PRODUCT_DARK`, `PRODUCT_BRAND_DARK` and `STATUS_DARK`. The page and marketing palette above stay unchanged. | Role | Dark value | App token | |---|---|---| | Ground | `#060606` | background, oklch(0.12 0 0) | | Card / popover | `#121212` | card, oklch(0.18 0 0) | | Sidebar | `#0B0B0B` | sidebar, oklch(0.15 0 0) | | Active / muted | `#222222` | sidebar-accent / muted, oklch(0.25 0 0) | | Well / sidebar divider | `#292929` | accent / sidebar-border, oklch(0.28 0 0) | | Container border | `#2E2E2E` | border, oklch(0.30 0 0) | | Primary ink | `#F8F8F8` | foreground, oklch(0.98 0 0) | | Muted ink | `#8F8F8F` | muted-foreground, oklch(0.65 0 0) | | Primary-button label | `#060606` | primary-foreground | | Emerald / gradient stop | `#10B981` / `#6EE7B7` | brand-green / brand-green-2 | | Gold / gradient stop | `#D4AF6A` / `#ECC88A` | brand-gold / brand-gold-2; rare app accent | Dark status hues, in the same order as the light table: Pending `#E0954A`, Assigned `#7D97E0`, In Progress `#A48CE6`, Uploaded `#2CC2C8`, Editing `#E05A60`, In Review `#D8A72A`, Delivered `#6FC47F`, In Revision `#EE5F92`, Cancelled `#94A3B8`. Never mix light and dark status sets in one window. The badge uses hue text over a 15% hue fill, with a 30% inset ring, not a white label on a solid hue. Measured against the `#121212` app card (8-bit sRGB): primary ink **17.64:1**, muted ink **5.79:1**, emerald **7.39:1**, gold **9.05:1**. Dark badge text against its composited 15% fill ranges from **4.38:1** (Editing, below 4.5:1 for small text) to **6.77:1** (Delivered). Preserve the app's exact hues in replicas and disclose this limitation; do not present these badges as universally AA-compliant. Other surfaces require their own measurement. The ported `AuroraGradient` already contains dark colourways. A `.dark` ancestor scoped inside the preview enables its literal `dark:` classes. Do not add `.dark` to `html`, `body` or the brand-page wrapper. A system dark preference does not change the marketing page or the preview's initial Light selection. Radii: buttons and chips `rounded-full` (999px); nav links `rounded-lg` (8px) and Features menu items `rounded-xl` (12px) (`components/landing/Navbar.tsx`); site cards `rounded-2xl` (16px); the nav pill `22px`; `--radius` 8px (`sm` 4, `md` 6, `lg` 8, `xl` 12 from `@theme inline`). The /brand kit: tile 16, swatch, table and font card 14, specimen and component panels 18, window 12 (the dashboard 14), console 10, KPI tile 10, quote card 22 (18 at ≤1024px), motion wash panel 14, For-AI panel 24 (20 on phones). Shadows are for windows, consoles and cards — never on type: `--vb-shadow-window`, `--vb-shadow-console`, `--vb-shadow-card`, `--vb-shadow-quote` above. The home hero's Safari frame uses `drop-shadow(0 22px 30px rgba(16, 32, 24, 0.20)) drop-shadow(0 3px 6px rgba(16, 32, 24, 0.06))` (`components/landing/hero-story/HeroScreen.tsx`). ## 3. Type The four faces, all Google Fonts through `next/font/google`: | Face | Weights | Where it loads | CSS variable | Role | |---|---|---|---|---| | IBM Plex Sans | 400 · 500 · 600 · 700 | `app/layout.tsx` (every route) | `--font-ibm-plex-sans` | every headline (`h1`–`h6` and `.font-heading`) | | Inter | variable (100–900 axis) | `app/layout.tsx` (every route) | `--font-inter` | body, ledes, eyebrows, buttons, nav (`body` and `.font-sans`) | | IBM Plex Serif | 400 + 700 · roman and italic | `app/layout.tsx` (every route) | `--font-ibm-plex-serif` | the accent word (`.accent-word`; `font-serif italic` in white on dark and emerald grounds); also the roman pull-quotes on /switch-from-aryeo and the /brand quote mark — never a whole headline | | IBM Plex Mono | 400 + 500 | `app/brand/fonts.ts` (/brand only) | `--font-ibm-plex-mono` | IDs, hex values, URLs, window and console labels — the product's mono | ```ts // app/layout.tsx const inter = Inter({ subsets: ["latin"], variable: "--font-inter", display: "swap", }); const ibmPlexSans = IBM_Plex_Sans({ subsets: ["latin"], variable: "--font-ibm-plex-sans", weight: ["400", "500", "600", "700"], display: "swap", }); const ibmPlexSerif = IBM_Plex_Serif({ subsets: ["latin"], variable: "--font-ibm-plex-serif", weight: ["400", "700"], style: ["normal", "italic"], display: "swap", }); // app/brand/fonts.ts — applied as plexMono.variable on the page wrapper export const plexMono = IBM_Plex_Mono({ subsets: ["latin"], weight: ["400", "500"], variable: "--font-ibm-plex-mono", display: "swap", }); ``` Stacks: `--vb-font-body` / `--vb-font-head` / `--vb-font-serif` / `--vb-font-mono` in section 2. The heading face is bound to `h1`–`h6` only (`app/globals.css`), so an element that should read as a heading but is not one — a card title in a `
`, a `
` — must carry `.font-heading` or it silently renders in Inter. Eyebrows are `

`, so they are Inter. Tailwind's `font-mono` resolves to the system monospace stack on every route, /brand included; IBM Plex Mono is reached only through `--vb-font-mono`. Do not add another monospace webfont. The type scale — each row's Role, Face and Spec as https://vremly.com/brand prints them (`components/brand/sections/Type.tsx`, each value read from the site's class strings); text in parentheses — sources and detail — is added here: | Role | Face / weight | Spec | |---|---|---| | Hero H1 | IBM Plex Sans Medium 500 | 32–64px · lh 1.25 · ls −1.5px (`components/landing/Hero.tsx`: `mx-auto max-w-2xl font-heading text-[2rem] font-medium leading-tight tracking-[-1.5px] text-[#1a1a1a] sm:text-[3.25rem] md:text-[4rem]`) | | Page H1 | IBM Plex Sans Bold 700 | 36–48px · lh 1.25 · ls −2px (`app/pricing/page.tsx`, `components/layout/FeaturePageLayout.tsx`) | | Section H2 | IBM Plex Sans Bold 700 | 32–44px · lh 1.1 · ls −1.5px · one accent word · two home outliers 28–48px at −2px (32 → 40px on `components/landing/HowItWorks.tsx` and most home heads; 44px at `lg` on `ProblemSection.tsx`; the outliers are `BeforeAfter.tsx` 32 → 48px and `AIVideo.tsx` 28 → 44px — build new sections at 32–44px / −1.5px) | | Card titles | IBM Plex Sans Bold 700 | 18px · ls −0.3px (`HowItWorks.tsx`: `text-lg font-bold text-[#1a1a1a] tracking-[-0.3px]`) | | Accent word | IBM Plex Serif Italic | inherits size · emerald gradient · 6s drift | | Lede | Inter 400 | 16–20px · lh 1.4–1.65 · `#525252` or `#737373`, by page (`#525252`: the home hero 16px / 1.6, /switch-from-aryeo 17 → 19px / 1.65; `#737373`: /pricing 18px / 1.625, feature pages 18 → 20px on Tailwind's `text-lg` / `text-xl` line-heights, 1.56 → 1.4) | | Body | Inter 400 | 15px · lh 1.625 · `#737373` | | Buttons | Inter SemiBold 600 · Medium 500 | 14px hero and menu pills · 13px nav pill | | Nav | Inter Medium 500 | 13px · `#525252` | | Eyebrow | Inter SemiBold 600 | 11px · uppercase · ls .15em · `#a3a3a3` | | Mono | IBM Plex Mono 400 | 9.5–12px · IDs, hex, URLs | The /brand kit's own sizes (`app/brand/brand.css`, section sheets): hero H1 `.vb-h1` 64px / 500 / lh 1.08 / −1.5px (40px on phones); hero lede `.vb-lede0` Inter 21px / 1.5 / `#737373`, max 620 (17px phones); section H2 `.vb-h2` 44px / 700 / lh 1.1 / −1.5px, max 720 (32px phones); lede `.vb-lede` 17px / 1.6 / `#525252`, max 480 (15px phones); kicker `.vb-k` = the Eyebrow (in `#737373` inside the type and component panels, `.vb-tcard` and `.vb-comp`); sub-head `.vb-sub-t` IBM Plex Sans 600 18px / −0.3px + caption `.vb-sub-c` 13.5px `#737373`; tile and swatch names Inter 600 13.5px; captions 12px `#737373`; specimens `.vb-t-h1` 46px / 500 and `.vb-t-h2` 32px / 700. Lockups - Hero: IBM Plex Sans Medium 500, ink, large, at most one accent word. It is 500, not 700, because it was dialled down from a synthesised 900. - Section head: grey Eyebrow → Bold 700 H2 with at most one accent word → a short lede. The only emerald in a section head is the accent word. The home Onboarding head, as `components/landing/Training.tsx` renders it (the wrapper is a `motion.div` that rises in; a centred head adds `mx-auto text-center` to the wrapper, as `HowItWorks.tsx` does): ```html

Onboarding

You're not switching alone.

Moving a crew off tools they already know is the hard part, and it is the part nobody quotes for. Three things happen, in this order.

``` Live eyebrows are split: `font-semibold` (600) in `Training.tsx`, `ReplacesStack.tsx`, `Pricing.tsx`, /pricing and /features; `font-medium` (500) in `HowItWorks.tsx`, `ProblemSection.tsx`, `WorkflowStepper.tsx`, `VremlyCapture.tsx` and `FAQs.tsx`. Build with 600. `components/ui/section.tsx` exports the same pattern as `SectionHeading`, `Eyebrow`, `Lede`, `Divider`, `Section` and `MediaCard`, but no page imports them: paste the class strings, not the components. - Ink → grey: the claim in ink, the explanation continuing in `#404040` in the same headline — "Start free. Pay only when you get paid." on https://vremly.com/pricing, the second sentence wrapped in `` (the pricing line itself is owner-asserted, llms-full.txt). No accent word with it. - Lede clause: a paragraph that opens with a semibold ink clause and continues in grey — the `Lede` pattern in `components/ui/section.tsx`, not yet on a live page: `

The lede. The rest.

`. Emphasis in body copy is semibold ink — never a colour, never the accent (the /brand Type card: "Emphasis is semibold ink."). - Headlines end with a full stop when they are sentences. Ellipses are typographic (…). The accent word — the one device. Verbatim from `app/globals.css`: ```css .accent-word { font-family: var(--font-ibm-plex-serif), ui-serif, Georgia, serif; font-style: italic; background: linear-gradient(135deg, #059669 0%, #34d399 50%, #059669 100%); background-size: 200% 200%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; animation: gradient-shift 6s ease-in-out infinite; display: inline; padding: 0.1em 0.05em; -webkit-box-decoration-break: clone; box-decoration-break: clone; } ``` Under `prefers-reduced-motion: reduce` its animation is `none` (`app/globals.css`): the gradient stays, frozen at its start position. - ONE word (a closing word with its full stop, like `chaos.`). Not a clause, not two words, not the second sentence. The home Pricing head accents three words (`work comes through.`, `components/landing/Pricing.tsx`) — a live exception, not a pattern. Two more live exceptions, both in the footer on every page (section 8): the H2's two italic words `one place.` (next bullet) and the bottom line `Built by people who've lived it.` Neither is a pattern. - On the word that carries the meaning, usually at the end of the line. Never on a function word ("your", "the", "and"). - Never on a name — a product, a company, a partner, Vremly. - Headlines and short signature lines only. Never running prose. - Never hand-roll it: `font-serif italic text-[#059669]` is a flat colour, not the gradient, and escapes the reduced-motion rule. Always use the class. - On a dark or emerald ground, drop the gradient and set ONE word in white IBM Plex Serif italic (``). The footer's own H2 italicises two words, `Run your entire operation in one place.` (`components/ui/motion-footer.tsx`) — a live exception, not a pattern. - `padding: 0.1em 0.05em` with `box-decoration-break: clone` is right for a word in a headline and opens a visible gap inside a word — never use the class on part of a word. - In email, never the gradient (see section 12, Email). ## 4. Layout rhythm Two rhythms are live. Use the marketing one for site pages and the /brand kit one for spec, kit and reference pages. Marketing site (the home sections in `components/landing/`, e.g. `Training.tsx`; `app/page.tsx`) - Container `max-w-7xl` (1280px); text pages `max-w-3xl` (768px); headings and ledes cap at `max-w-2xl` (672px). - Gutter `px-4 sm:px-6 lg:px-8` (16 / 24 / 32px); document pages `px-5 sm:px-8` (/switch-from-aryeo); feature and pricing pages `px-6`. - Section padding `py-16 sm:py-24` (64 / 96px); the Security band uses `py-20 sm:py-28` (`Enterprise.tsx`) and the product story `py-20 md:py-28` (`product-story/index.tsx`), 80 / 112px. Seams: `border-t border-[#e5e5e5]` full-bleed (/pricing, /switch-from-aryeo). The dashed `Divider` in `components/ui/section.tsx` (`border-0 border-t border-dashed border-[#e5e5e5]`) is not on any live page. - Grounds: `#FCFCFC` bands; one `#F5F5F5` band (`BeforeAfter.tsx`); three `#0C0C0E` dark bands kept non-adjacent (home sections 7, 12 and 15 — the order comment in `app/page.tsx`). - The shell: `
` → `Navbar` → sections, then `` as a SIBLING after the wrapper (the footer is pinned underneath; the opaque `z-10` wrapper slides off it). ```html
``` - Breakpoints are Tailwind v4's stock ones (no override in `app/globals.css`): `sm` 640 · `md` 768 · `lg` 1024 · `xl` 1280. Mobile first. The /brand kit (`app/brand/brand.css`) - Container `.vb-in` 1180px. Hero `.vb-hero` padding 144 / 32 / 88 (the 144 clears the floating nav pill). Bands `.vb-sec` 96 / 32 / 104; phones 64 / 20 / 72. - Alternate grounds: base `#FCFCFC` → white band `.vb-sec.is-white` (`#FFFFFF` with `#E5E5E5` top and bottom hairlines; two white bands in a row drop the second top line). /brand runs base, white, base, white, base, white, base, base (no line between 07 and 08), white. - Section head `.vb-head` (max 720): kicker `NN · Name` → H2 (margin-top 14) → lede (16). First content 36px below. - Grids: `.vb-grid3` / `.vb-grid4` gap 16; `.vb-sub` sub-head margin 44 / 16 with the grid under it at 0; swatches 4-up gap 12; components and specimen cards 2-up gap 16. Tablet (761–1024px): `.vb-grid4` and the font cards go 2-up; the swatches stay 4-up. Phone (≤760px): every grid one column, swatches and the dashboard's KPI tiles two. - Rules list `.vb-rules`: two columns (one on phones), 7px emerald square bullets (radius 2), 14px / 1.55 `#525252`, one rule per item. - One idea per section: a head and at most one visual group. Text that must sit on a gradient goes in a white card, or is white on the dark forms. ## 5. The emerald gradient — the only texture (page 04) Three greens — `#059669`, `#34D399`, `#6EE7B7` — with their tints and shades, drawn five ways, always clipped by the card or band they fill, plus a 2% grain over every page. No pattern, no photograph grade. | Form | Recipe | Where it lives | Type on it | |---|---|---|---| | Grainient | WebGL2 shader, `BRAND_GRAINIENT` (`components/ui/grainient.tsx`) | the CinematicFooter panel (`components/ui/motion-footer.tsx`), the Features menu card (`components/landing/Navbar.tsx`) | white; body `text-white/70` | | Hero wash | static CSS radial (`components/landing/Hero.tsx`) | the home hero card | ink on the pale centre; nothing on the outer stops | | Emerald card | `AnimatedGradientBackground`, dark ramp (`components/ui/animated-gradient-background.tsx`) | the closing CTA card on /pricing and on every page built on `FeaturePageLayout`, the /features AI card, /features/more, the /features/ai-video closing card, the home AI card (`AIVideo.tsx`), the plan cards (`components/ui/plan-cards.tsx`) | white; body `text-white/60` | | Emerald card, light | the same component, light ramp | the /features/ai-video hero | ink | | Fallback | CSS linear | wherever WebGL2 is unavailable (`grainient.tsx`) | white | | Grain | fractal-noise SVG at 2%, multiply | every page (`.noise` on ``, `app/layout.tsx`) | — | Ready-made files, each at `https://vremly.com/brand/texture/` (paths and sizes from `components/brand/assets.ts`) - `vremly-grainient-{wide|tall|square}.jpg` — a still of the live shader (JPG: its grain does not compress as PNG) - `vremly-hero-wash-{wide|tall|square}.png`, `vremly-card-dark-{…}.png`, `vremly-card-light-{…}.png`, `vremly-fallback-{…}.png` - sizes: wide 2400×1500 · tall 1200×2400 (phones) · square 1600×1600; opaque - `vremly-hero-wash.svg`, `vremly-card-dark.svg`, `vremly-card-light.svg` — box-relative radials (`preserveAspectRatio="none"`) that stretch exactly to any box - `vremly-fallback.svg` — exact at 16:10; stretched to another shape its bands tilt slightly off 45° - `vremly-grain.svg` — the page grain alone, a 256px transparent tile, for your own ground - The grainient has no vector form. The stills are pixel captures of the real CSS and WebGL in Chromium (`scripts/brand-assets.sh` documents the capture). The shader config, verbatim from `components/ui/grainient.tsx`: ```ts export const BRAND_GRAINIENT = { color1: "#059669", color2: "#34d399", color3: "#6ee7b7", timeSpeed: 0.08, warpStrength: 0.4, warpFrequency: 2.5, warpSpeed: 1.2, warpAmplitude: 80.0, grainAmount: 0.12, contrast: 1.4, saturation: 1.0, zoom: 1.0, } as const; ``` Spread it (``) and override nothing. Everything it does not set falls through to the component defaults, which include a magenta/violet palette (`#FF9FFC` / `#5227FF` / `#B497CF`) — a call site that forgets the spread does not draw Vremly's gradient at all. From a Server Component, spread it inside a `"use client"` wrapper (`components/brand/sections/gradient/LiveGrainient.tsx`): imported across the boundary, `BRAND_GRAINIENT` arrives as a client reference, not the object. - It animates only while its canvas is within 200px of the viewport (`IntersectionObserver` `rootMargin: '200px'`), the tab is visible, and the `active` prop is true (the footer passes whether it is uncovered). Stopping cancels the frame loop; elapsed time accumulates, so it never jumps on resume. - Under reduced motion it paints one still frame and never starts the loop. - Without WebGL2 it paints `linear-gradient(135deg, color1 0%, color2 50%, color3 100%)` — the Fallback form. - A WebGL canvas composites on its own layer, so a parent's `overflow: hidden` does not reliably clip it to rounded corners: repeat the radius on the canvas (`Navbar.tsx` passes `rounded-xl` to both). - One live grainient per view. The hero wash, verbatim from `components/landing/Hero.tsx`. The hero defines it as `--hero-wash`; `HeroStage.tsx` paints it on the sticky `100svh` screen, not the elongated scroll runway. Its size and position stay constant during the product sequence. The static/reduced-motion layout paints it on its normal-flow container: ```css background: radial-gradient(ellipse at 50% 20%, #fafafa 55%, #ecfdf5 70%, #d1fae5 82%, #a7f3d0 90%, #6ee7b7 96%, #34d399 100%); ``` The centre is `#FAFAFA`, not white and not the `#FCFCFC` ground; the colour only arrives in the last 45% of the ellipse. The emerald card. `AnimatedGradientBackground` rewrites one CSS radial per frame (no canvas), painting `radial-gradient(${width}% ${width + topOffset}% at 50% 20%, stops)` (`components/ui/animated-gradient-background.tsx`). Every live call site passes exactly these props: ```jsx // light variant — app/features/ai-video/page.tsx hero: // gradientColors={["#fafafa", "#ecfdf5", "#d1fae5", "#a7f3d0", "#6ee7b7", "#34d399", "#059669"]} // gradientStops={[30, 45, 55, 65, 75, 85, 100]} ``` At rest the width is `startingGap`, so the static paint is the TEXTURE_BACKGROUNDS entry below; `Breathing` swings the width ±6. It paints once and skips the loop under reduced motion, pauses off-screen (`rootMargin: "100px"`) and on `visibilitychange`, and never reassigns an unchanged string. Its component defaults are a rainbow; every call site must pass the props above. The four CSS forms as static backgrounds, verbatim from `components/brand/primitives.tsx`: ```ts export const TEXTURE_BACKGROUNDS: Record = { "hero-wash": "radial-gradient(ellipse at 50% 20%, #fafafa 55%, #ecfdf5 70%, #d1fae5 82%, #a7f3d0 90%, #6ee7b7 96%, #34d399 100%)", "card-dark": "radial-gradient(130% 150% at 50% 20%, #0a0a0a 20%, #042f2e 35%, #064e3b 45%, #059669 55%, #34d399 65%, #6ee7b7 80%, #059669 100%)", "card-light": "radial-gradient(130% 150% at 50% 20%, #fafafa 30%, #ecfdf5 45%, #d1fae5 55%, #a7f3d0 65%, #6ee7b7 75%, #34d399 85%, #059669 100%)", fallback: "linear-gradient(135deg, #059669 0%, #34d399 50%, #6ee7b7 100%)", }; ``` The grain, verbatim from `app/globals.css`: ```css .noise::before { content: ""; position: fixed; inset: 0; z-index: 9999; pointer-events: none; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); opacity: 0.02; mix-blend-mode: multiply; } ``` It suppresses banding on large flat fields; it is not a motif. Never raise it above 2%, never enlarge it, never reproduce it in print or social. Putting content on a form (the site's `relative z-10` pattern — `app/pricing/page.tsx`, `FeaturePageLayout.tsx`, `AIVideo.tsx`, `Navbar.tsx`): ```jsx
// the kit's version: + className="vb-on-tex" (components/brand/primitives.tsx) ``` Rules (the four printed on https://vremly.com/brand, `components/brand/sections/Gradient.tsx`) - Sizes: wide 2400×1500, tall 1200×2400 for phones, square 1600×1600. The three radial SVGs scale to any box; the fallback SVG is exact at 16:10. - In code: the parent is `position: relative; overflow: hidden`, the texture is absolute `inset: 0`, and content sits above it at `z-index: 10`. - One live grainient per view. It pauses off-screen and draws a single still frame under reduced motion. - Never place the emerald logo on any of these. Body type is white on the dark and grainy forms and ink on the pale ones. The page grain never goes above 2%. Also true - Anything dense on a gradient — a form, a table, a link list — sits in its own white card (the footer's link grid is a `rounded-2xl bg-white` card on the grainient). ## 6. Logo (page 01) The logo is the V mark plus the drawn letters REMLY — in the horizontal logo the mark IS the V of VREMLY. Four families, each in three colourways, each as a vector master and a transparent PNG. The SVGs are the Figma masters, untouched. SVG preferred wherever a tool accepts it. | File (stem) | Use | SVG viewBox | PNG | |---|---|---|---| | `vremly-logo-primary-horizontal-{emerald,white,black}` | the logo — leads everywhere there is width | 18388 × 3321 | 2400 × 434, transparent | | `vremly-logo-mark-{…}` | the V alone — small placements and tight spaces | 10987 × 10727 | 1024 × 1000, transparent | | `vremly-logo-primary-vertical-{…}` | stacked — square spaces and splash screens | 12098 × 10593 | 2400 × 2102, transparent | | `vremly-logo-text-{…}` | the wordmark — when the mark is already on screen | 18167 × 3321 | 2400 × 439, transparent | URLs: `https://vremly.com/brand/.svg` and `.png`, where `` is a row's stem with one colour — e.g. https://vremly.com/brand/vremly-logo-primary-horizontal-emerald.svg, https://vremly.com/brand/vremly-logo-primary-horizontal-emerald.png. PNGs are rendered from the SVGs with `rsvg-convert -w 2400` (logos) and `-w 1024` (marks), `-b none` (`scripts/brand-assets.sh`). Colourways and grounds | Colourway | Fill in the file | Place it on | Tile ground on /brand | |---|---|---|---| | Emerald | `#059669` | light grounds | light well `#FAFAFA` | | White | `white` | ink, `#0C0C0E` dark bands, the dark and grainy gradient forms | ink well `#1A1A1A` | | Black | `#0A0A0A` | light grounds, beside ink type | light well `#FAFAFA` | Rules (as printed, `components/brand/sections/Logo.tsx`) - Clear space: keep a quarter of the logo's height free on every side. (`RULE SET HERE`) - Use the SVG wherever a tool accepts it. The PNGs are transparent — 2400px wide for logos, 1024px for marks. - Never stretch, outline, rotate, add a shadow or recolour outside these three colourways. - Never place the white versions on light grounds, or the emerald versions on the emerald gradient. Also true - The letters are outlined paths in a geometric heavy sans — not IBM Plex Sans, not any installed font. Never retype "VREMLY" as a logo. - Avatars, favicons and app icons are the box logo (section 7), not the bare V: `app/icon.png`, `app/apple-icon.png`, `app/favicon.ico` and the nav avatar `/logos/vremly-icon.png` are all a white V on emerald. - The site nav shows that avatar at 28px, round (`h-7 w-7 rounded-full`), beside the word VREMLY set as text: `font-heading text-[15px] font-bold uppercase tracking-[-0.3px] text-[#1a1a1a]` (`components/landing/Navbar.tsx`). That is the nav's label, not a logo recipe to copy elsewhere. - In running prose the name is "Vremly": sentence case, one face, one colour, no styling. Never "VREMLY" or "vremly" in a sentence. - The /brand tiles render the horizontal logo and the wordmark 28px tall and the marks and stacked logo 76px tall. Not Vremly's logo — never use - `/logos/vrem-logo-optimized.svg`, `/logos/vremDarkVector.svg`, `/logos/vremLight.svg`: a "VR" monogram with a house roofline. It is not Vremly's mark. - `/logos/vrem-icon.svg`: a plain symmetric V placeholder. The real V is asymmetric, with a slit (section 7). - `/logos/vremly-lockup-{emerald|white|charcoal}.png`: legacy rasters with their grounds baked in. Use the files above. - `public/brand/mascot-concepts/`: unapproved concept sketches drawn on the placeholder V. Not brand, never publish. - Never sample brand colour from any raster; its greens are export values, not `#059669`. ## 7. The mark — the V's other forms (page 05) Vremly has no mascot, no character and no avatar figure. The slot is the V's other forms: the box logo and the notched Vremly Scan V. | File | Use | SVG | PNG | |---|---|---|---| | Box logo | app icon, avatar on any ground | https://vremly.com/brand/vremly-logo-mark-box.svg | https://vremly.com/brand/vremly-logo-mark-box.png (1024×1024, transparent corners) | | Scan · Emerald | Vremly Scan, on light grounds | https://vremly.com/brand/vremlyscan-logo-mark-emerald.svg | https://vremly.com/brand/vremlyscan-logo-mark-emerald.png (1024×1000, transparent) | | Scan · White | Vremly Scan, on ink | https://vremly.com/brand/vremlyscan-logo-mark-white.svg | https://vremly.com/brand/vremlyscan-logo-mark-white.png | | Scan · Black | Vremly Scan, on light | https://vremly.com/brand/vremlyscan-logo-mark-black.svg | https://vremly.com/brand/vremlyscan-logo-mark-black.png | Construction (read from the path data; Appendix B) - The V is two paths in a 10987 × 10727 box: a solid left stroke (a parallelogram), and one polygon that runs as a thin stripe beside it and turns up into the right arm. The slit between them is what makes the V Vremly's. - The stripe is 351.73 units wide against a 10727-unit height — 3.3% — so it falls under one pixel once the mark renders shorter than about 30px. The V still reads there; for favicons use the box logo. - The box logo: 1024 × 1024, `rx="200"`, `#059669` ground, the white mark's two paths scaled to 576px wide (56.25%, the shipped app icon's proportion) and mathematically centred. At 72px its CSS clip is `border-radius: 14px` (200/1024 × 72). - The horizontal logo's first two paths are the mark scaled to cap height. The wordmark's V is a plain letter. Never swap one for the other. - The Scan mark is the same V with nine ruler notches (five long, four short) cut into the outer edge of the left stroke; stripe and arm unchanged. It belongs to Vremly Scan, the capture app, alone — never use it for Vremly itself. - Place these files. Never rebuild the V in a font or a drawing tool, never round its corners, never add a stroke. ## 8. Components (page 06) Every component below is a real class string from the site, verbatim. Paste them; a reconstructed string is how a rebuild ends up almost right. The `.vb-*` kit CSS is in Appendix C. Buttons — ink or outline pills (hero size: Inter SemiBold 14px, 48px tall) ``` /* primary — components/landing/HeroCTA.tsx ("Get started free") */ inline-flex w-full items-center justify-center rounded-full bg-[#1a1a1a] px-8 py-3.5 sm:w-auto text-sm font-semibold text-white transition-colors hover:bg-[#333] /* secondary — HeroCTA.tsx ("Book a demo") */ inline-flex w-full items-center justify-center rounded-full border border-[#e5e5e5] bg-white px-8 py-3.5 sm:w-auto text-sm font-semibold text-[#1a1a1a] transition-colors hover:bg-[#f5f5f5] /* nav size — components/landing/Navbar.tsx ("Get started free") */ inline-flex items-center rounded-full bg-[#1a1a1a] px-5 py-2 text-[13px] font-medium text-white transition-colors hover:bg-[#333] whitespace-nowrap /* document page, fixed height — app/switch-from-aryeo/page.tsx */ inline-flex h-12 items-center rounded-full bg-[#1a1a1a] px-7 text-[14px] font-semibold text-white transition-colors hover:bg-[#333] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#1a1a1a] /* on a gradient: white primary + outline — components/ui/motion-footer.tsx */ inline-flex items-center rounded-full bg-white px-8 py-3.5 text-sm font-semibold text-[#1a1a1a] transition-colors hover:bg-white/90 inline-flex items-center rounded-full border border-white bg-white/10 px-8 py-3.5 text-sm font-semibold text-white backdrop-blur-sm transition-colors hover:bg-white/20 ``` The kit equivalents: `.vb-btn-dark` (ink, padding 13/28) and `.vb-btn-soft` (white, `#E5E5E5` border, padding 13/24), both 14px/600 pills; a scroll or download target may add a 12px `↓` in an `` at .7 opacity (on /brand only the hero's "See the brand" button does); `.vb-btns` row gap 12 (stacked and stretched on phones); `.vb-copy` quiet underlined 13px text button; `.vb-dl` download pill (12px/500, padding 6/11, an 11px `↓` in `#A3A3A3`, hover `#ECFDF5` + emerald .35 border). Focus ring on every link, button and `[tabindex]` inside `.vb-hero` and the `.vb-sec` bands (the shared nav and footer are outside that selector): `outline: 2px solid #059669; outline-offset: 2px`, inset to `outline-offset: -2px` on the phone step strip. Button card · phone menu — the one place a full-width pair stacks inside a card (`Navbar.tsx`): the menu panel `rounded-2xl border border-[#e5e5e5] bg-white`, its footer `border-t border-[#e5e5e5] p-4 flex flex-col gap-2`: ``` w-full rounded-full bg-[#1a1a1a] px-5 py-3 text-center text-[14px] font-medium text-white transition-colors hover:bg-[#333] ("Get started free") w-full rounded-full border border-[#e5e5e5] px-5 py-3 text-center text-[14px] font-medium text-[#525252] transition-colors hover:text-[#1a1a1a] ("Log in") ``` The kit specimen `.vb-bcard`: max 336px, centred, white, hairline, radius 16, padding 16, gap 8, `--vb-shadow-card`. Nav — `components/landing/Navbar.tsx` - Floating pill: `flex h-14 items-center justify-between rounded-[22px] border border-[#e5e5e5] bg-white/90 px-5 backdrop-blur-md` in a `max-w-7xl` container at `top-3 sm:top-4`. - Logo: `/logos/vremly-icon.png` `h-7 w-7 rounded-full` + the VREMLY label (section 6). - Links: `rounded-lg px-3 py-2 text-[13px] font-medium transition-colors text-[#525252] hover:text-[#1a1a1a]`. - The Features menu card carries the live grainient. Once the header scrolls away, a docked pill springs down (section 11). Eyebrow, heading, support line, seam — verbatim from a live home section (`components/landing/Training.tsx`) ``` EYEBROW text-[11px] font-semibold uppercase tracking-[0.15em] text-[#a3a3a3] (a

) H2 mt-4 font-heading text-[2rem] font-bold leading-[1.1] tracking-[-1.5px] text-[#1a1a1a] sm:text-[2.5rem] SUPPORT mt-4 text-[15px] leading-relaxed text-[#737373] SEAM border-t border-[#e5e5e5] ``` `components/ui/section.tsx` holds the same pattern as components no page imports, plus three strings that appear on no live page: the lede clause (`text-[15px] leading-relaxed text-[#737373]` with a `strong` in `font-semibold text-[#1a1a1a]`), the dashed divider (`border-0 border-t border-dashed border-[#e5e5e5]`) and the media tile (`aspect-[4/3] overflow-hidden rounded-2xl bg-[#f5f5f5]`). Chips and badges - Hero badge (`components/landing/Hero.tsx`): `inline-flex items-center gap-2.5 rounded-full bg-[#f0f0f0] px-4 py-2`, label `text-[13px] font-medium text-[#525252]`. - Chip `.vb-chip`: white, `#E5E5E5` border, radius 999, padding 7/14, 13px `#525252`. The /brand specimen row is the product's Production lanes: Photos · Video · Floor plans · Virtual tours · Brochures. - Add-on tag (`app/pricing/page.tsx`): `inline-block whitespace-nowrap rounded-full border border-[#e5e5e5] px-2 py-0.5 text-[11px] font-medium text-[#525252]`. - Dark pill — the home hero's scroll cue (`components/landing/hero-story/HeroStage.tsx`): `inline-flex items-center gap-2 rounded-full bg-[#1a1a1a]/85 px-4 py-2 text-[11px] font-medium uppercase tracking-[0.12em] text-white` + a 12px down-arrow. One line that must pop. - Status chip: section 9. - `components/ui/badge.tsx` and `components/ui/mockup.tsx` are never imported. Dead code; do not build from them. Cards - Card: `rounded-2xl border border-[#e5e5e5] bg-white p-6` on the `#FCFCFC` ground; a filled variant drops the border for `bg-[#f5f5f5]`; a card holding a product shot drops the padding: `overflow-hidden rounded-2xl border border-[#e5e5e5]`. - Window `.vb-win`: section 9.1. Section 06 shows one vertical JobCard shared with section 07 (`dash/Projects.tsx`): payment badge, technician avatar, order/client, flat status/media row, address, area and schedule. It is a decorative excerpt with illustrative account data. - Dark card `.vb-pc` (in `.vb-comp.is-dark`, which drops the panel padding so the card fills the cell): `#0C0C0E`, radius 16, padding 26/26/28; mono number `01` 11.5px `#6EE7B7`; title IBM Plex Sans 700 20px `#F5F5F7`; sub 14.5px / 1.55 `#A1A1AA`; label 11px uppercase .15em `#8B8B93`. Specimen: "Agent books" / "A slot is only offered if someone can actually serve that package." (its limits as copy are in section 13). - Emerald CTA card — how /pricing and every page built on `FeaturePageLayout` close (`components/layout/FeaturePageLayout.tsx`): `mt-16 rounded-2xl max-w-2xl relative overflow-hidden` (on /pricing: `relative overflow-hidden rounded-2xl`, `app/pricing/page.tsx`) + the emerald card gradient (section 5) + `relative z-10 p-6 sm:p-8 md:p-10` holding `text-xl font-bold text-white font-heading` title, `mt-2 text-white/60` line, and a white pill `mt-6 inline-flex items-center rounded-full bg-white px-6 py-3 text-sm font-semibold text-[#1a1a1a] transition-colors hover:bg-white/90` with an `ArrowRight h-4 w-4 ml-2`. - White card on the wash `.vb-quote` (in `.vb-comp.is-wash` over the hero wash): white, hairline, radius 22, padding 34/44/32, `--vb-shadow-quote` (at ≤1024px: radius 18, padding 26/22/24, the quote 18px, and the wash cell's padding 22/16); a 40px IBM Plex Serif `“` in `#059669`; IBM Plex Sans 500 21px / 1.35 ink; a 30px box logo (radius 7) + "Vremly" 13.5/600. It carries the company line "Vremly is built and run by a working real-estate media company." — owner-asserted, NOT a testimonial. Never put a customer quote, a name or a headshot in it. Footer — `CinematicFooter` (`components/ui/motion-footer.tsx`): pinned under the page on screens at least 640px wide and 700px tall; a full-bleed grainient panel; centred white CTA (H2 "Run your entire operation in one place." with the white serif italic accent, `text-white/70` line "Book, dispatch, deliver. One platform.", white "Get started free" + outline "Book a Demo"); then a white `rounded-2xl bg-white` card with the name "Vremly" (15px bold ink), the tagline "Real estate media, handled." in 13px `#A3A3A3`, and link columns (Brand → /brand among them), over a bottom bar with the line "Built by people who've lived it." (`text-[11px] sm:text-[13px] text-[#525252] text-center sm:text-left`, `.accent-word` on its last four words — a live exception, section 3). Every page ends with it. ## 9. Dashboards, consoles & UI graphics — the product-window kit (page 07) Every NEW product graphic is one kit: a light or dark app window with a three-dot bar and an `app.vremly.com` address, grey chrome, emerald for data and status hues for state — and under it, an ink console that shows Claude reading a Vremly account through the Vremly MCP connector — read only unless you allow writes. It is a visual kit: it depicts the connector, and states nothing about which plans include it. The live reference is https://vremly.com/brand#dash (`components/brand/sections/Dash.tsx`, `dash/Console.tsx`, `dash/scripts.ts`, `dash.css`). The CSS for every class below is in Appendix C; phone rules in Appendix D. ### 9.1 The window - Shell `.vb-win` — `#FFFFFF`, 1px `#E5E5E5`, radius 12 (14 for the dashboard), `--vb-shadow-window` `0 26px 60px -30px rgba(16, 32, 24, 0.35)`, `overflow: hidden`, text left. Max 480px as a specimen; the dashboard fills a 760px column (`.vb-dash`). - Bar `.vb-winbar` — padding 13/18, hairline bottom, three 9px `#F0F0F0` dots (`.vb-windot`), the address centred in IBM Plex Mono 11.5px `#737373` (`.vb-winurl`): `app.vremly.com/projects`, `app.vremly.com · dashboard`, `app.vremly.com/booking`. Never real browser chrome, never coloured traffic lights. - The former three-column table specimen has been replaced by the app-based JobCard. Do not rebuild a generic Property / Status / Technician table as a platform screenshot. - Caption `.vb-cap` — 12.5px `#525252` on a hairline, padding 12/18/14. - A replica is a picture, never a control: `.vb-inert` (no pointer events, no selection) + `aria-hidden`, like `lib/product-ui.ts` `INERT` / `inert(className)`. Spreading `{...INERT}` and then a `className` silently drops the pointer-events — use `inert()`. ### 9.2 Dashboard previews `components/brand/sections/dash/Preview.tsx` renders two static, explicitly labelled excerpts controlled by one Light / Dark button group. The buttons expose `aria-pressed` and remain outside the decorative replica. The default is Light; switching does not change page chrome or the console. - Overview excerpt: app header, left sidebar, timeframe, four metric cards (Total Jobs 51; Technician Utilization 78%, 2/9 available; Avg Delivery Time 19h; Revenue CA$22.5k) and two rate cards (94% on-time delivery; 97% client approval). Figures are illustrative, regular weight, with no deltas or sparklines. There is no chart or table on this dashboard. - Render the overview on a 1080×700 logical canvas using `Scaled`; below 761px use 480×1140, one column, no sidebar. The app sidebar is off-canvas on phones. The overview intentionally stops before Schedule. - Active Projects is a separate excerpt from further down the app page, after Schedule and Live Project Map. Three flat JobCards mirror the vertical app component: 5:2 assigned-status auroras, Unpaid badges, centered technician pills with avatar initials and checkmarks, prominent order/client names, flat status dots beside media icons, two-line addresses, area badges, shoot dates/times and optional notes. Desktop renders a 1080×520 canvas; phones stack the cards at native type sizes. Avatar initials use the app fallback and account data is illustrative. Do not imply they directly follow metrics in the app. - Product palette variables live on `.vb-preview-theme`, including the window bar. The local `.dark` class enables the aurora's dark colourways. No global brand tokens are swapped. - Both excerpts have a descriptive image role; their decorative contents are hidden from assistive technology and inert to pointers. Only the genuine theme controls enter keyboard navigation. Badge implementation (shared by both themes): ```ts export function tint(hex: string, alpha: number): string { const n = parseInt(hex.slice(1), 16); return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`; } export function statusChipStyle(hex: string) { return { backgroundColor: tint(hex, 0.15), color: hex, boxShadow: `inset 0 0 0 1px ${tint(hex, 0.3)}`, } as const; } ``` ### 9.3 The console (`.vb-term` — "vremly-mcp") - Shell — `#0C0C0E`, radius 10, `--vb-shadow-console` `0 26px 60px -30px rgba(16, 32, 24, 0.5)`, `overflow: hidden`, in a 760px column 18px under the window (`.vb-console`). - Bar `.vb-termbar` — padding 11/14, `rgba(255, 255, 255, 0.06)` bottom line, three 9px `rgba(255, 255, 255, 0.14)` dots, the title centred in mono 11.5px `#8B8B93`: `vremly-mcp · invoices` / `· schedule` / `· project` / `· delivered`. - Screen `.vb-term-screen` — a fixed 340px at EVERY width, `overflow: hidden`, padding 16/20 (14 on phones), IBM Plex Mono 12px / 1.7 (11px phones), `#D4D4D8`. When a new line makes the content taller, the screen scrolls to the bottom (smooth; instant on a fresh run or under reduced motion). The console never grows. - Lines: the prompt — `›` in `#34D399` + `#F5F5F7` text + a 7×13 `#34D399` caret blinking at 1s (`steps(1)`); a tool call — a 9px spinner ring (1.5px `rgba(255, 255, 255, 0.22)`, top `#34D399`, 0.8s linear) that becomes a 6px `#34D399` dot, then the tool name in `#F5F5F7` + ` · ` + its argument in `#A1A1AA`; the answer panel `.vb-term-out` — 1px `rgba(255, 255, 255, 0.08)` border, radius 8, padding 10/12, 11.5px (a table with a mono uppercase `#8B8B93` header and a total row, or label/value fields, or grouped lines); a note line in `#F5F5F7`; the done line `✓ Read only · nothing changed` in `#34D399`. New lines rise in (`vbDashIn`: opacity 0 + 4px → none, .35s; the answer panel .5s) — only on a live run. - Step strip `.vb-tabs` — under the console: a white pill track (1px `#E5E5E5`, radius 999, padding 3, gap 2), buttons 12px `#525252` padding 6/12 with a mono 10.5px `#A3A3A3` number `01`–`04`; the active tab `#ECFDF5` with `#047857` text and a `#047857` number; `aria-pressed`. Phones scroll it sideways and keep the active tab centred. - A screen reader gets a transcript of the finished run of the current tab (`sr-only`), not the moving text. - Console art only — never a headline, CTA, caption or marketing line: the prompts (section 9.4; tab 01's is "Which of my invoices are still unpaid?") and the done line "✓ Read only · nothing changed". - Where the connector facts come from: read only preselected, and writes refused on a read-only connection, are read from the product's own connect screen (Settings → Developers, "Connect Vremly to Claude"); the three tools and the saved prompts from its hosted MCP server. That is code in the product, not the public claim set (the product files for the prompts and tools are listed in the header of `components/brand/sections/dash/scripts.ts`). Draw the console from it; never write copy from it. ### 9.4 Timing (read from `components/brand/sections/dash/scripts.ts`) ```ts /** Per character. The site's typewriter speed: lib/use-loop.ts useTypewriter's default, which every caller uses. */ export const TYPE_MS = 45; /** The caret keeps blinking this long after the last character, then goes. */ export const CARET_MS = 300; /** Caret gone → first tool call. */ export const SETTLE_MS = 400; /** Pitch between tool calls. */ export const STEP_MS = 850; /** A call's spinner becomes an emerald dot this long after it appears. */ export const DOT_MS = 650; /** Last dot → answer panel. */ export const OUT_MS = 350; /** Answer panel → "✓ Read only · nothing changed". */ export const DONE_MS = 600; /** Done line → next tab (auto-cycle only). */ export const HOLD_MS = 3000; ``` - Per tab: type the prompt at 45ms a character (the site's typewriter speed — `useTypewriter`'s default in `lib/use-loop.ts`); the caret blinks 300ms more and goes; the first tool call lands 400ms later; the next every 850ms; each spinner becomes a dot 650ms after it appears; the answer panel lands 350ms after the last dot; the done line 600ms after that; a 3s hold; the next tab. - `typeEnd = chars × 45 + 300` · `stepAt[i] = typeEnd + 400 + i × 850` · `dotAt[i] = stepAt[i] + 650` · `outAt = last dot + 350` · `doneAt = outAt + 600` · `total = doneAt + 3000`. One `setTimeout` at a time, aimed at the next moment the frame changes — no per-frame loop, nothing during the hold. - Tab lengths: 01 8.71s (38 characters) · 02 9.025s (45) · 03 9.925s (65) · 04 9.07s (46). - Runs only while the console is on screen (`IntersectionObserver`, `rootMargin: "80px 0px"`) and the browser tab is visible; off screen it pauses and resumes where it stopped. The server renders tab 01 finished (no caret, no spinner), so there is no hydration mismatch and a reader without JavaScript sees the whole run. The first arrival from below restarts tab 01 from an empty prompt; if the page opens with the console already in view, that run starts at its done line instead of wiping what the reader is looking at. - A click on a tab pins it: auto-cycle stops for good and that tab replays from the prompt. - Reduced motion: the finished frame of the current tab, no cycle, no caret, no spinner, no rise. The four scripts (`SCRIPTS`, verbatim) — every call is a read, so every run ends "✓ Read only · nothing changed": | # | Tab | Prompt | Tool calls | Answer | |---|---|---|---|---| | 01 | Unpaid invoices | Which of my invoices are still unpaid? | `vremly_search_endpoints · "invoices"` → `vremly_describe_endpoint · GET /invoices` → `vremly_request · GET /invoices?status=SENT&status=OVERDUE` | Customer / Amount / Overdue: Marc Lefebvre CA$612.15 21 days · Priya Sandhu CA$389.00 9 days · Daniel Okafor CA$489.00 not yet due · Total CA$1,490.15 | | 02 | This week's shoots | Show the Vremly shoot schedule for this week. | `vremly_search_endpoints · "list projects"` → `vremly_describe_endpoint · GET /projects` → `vremly_request · GET /projects?scheduledAfter=2026-09-14&scheduledBefore=2026-09-20` | When / Address / Crew / Status: Tue 10:00 902 Crescent Rd NW Reyes Duke Editing · Thu 09:30 88 Bowness Rd NW Emily Tran In Progress · Fri 10:00 740 Aspen Stone Blvd SW Marcus Kaur Assigned · Sat 13:00 76 Silverado Blvd SW Unassigned Pending; note "Unassigned: 76 Silverado Blvd SW, Saturday 13:00." | | 03 | Project status | Give the current status of the Vremly project: 902 Crescent Rd NW | `vremly_request · GET /projects?search=902+Crescent+Rd+NW` → `vremly_request · GET /invoices/by-project/{id}` → `vremly_request · GET /projects/{id}/delivery` | Status Editing · Shoot Tue Sep 15, 10:00 · Reyes Duke · Media Uploaded · not delivered yet · Invoice CA$489.00 · sent, unpaid | | 04 | Delivered this week | Summarise Vremly projects delivered this week. | `vremly_search_endpoints · "list projects"` → `vremly_describe_endpoint · GET /projects` → `vremly_request · GET /projects?firstDeliveredAfter=2026-09-14` | Marc Lefebvre — 820 Copperfield Blvd SE · Sep 15 · Essential Listing · Priya Sandhu — 76 Sagewood Dr SW, Airdrie · Sep 16 · Premium Listing | (`{id}` is a uuid in the real script.) The prompts are the product's own: 01 is the "First prompt" on the in-app "Connect Vremly to Claude" screen; 02–04 are the hosted connector's saved prompts. ### 9.5 Rules for any new dashboard, console or UI graphic 1. Match the actual product screen and its theme. The dashboard has a sidebar, metrics, Schedule, Live Project Map and Active Projects; charts belong to Reports and tables to Projects. Use labelled excerpts when omitting intermediate sections. 2. In windows: grey chrome, emerald for data (bars, a legend square), status hues for state, and nothing else. Emerald never means "good" and never marks a delta. 3. Product excerpts use the app's Inter body and regular-weight metric figures. The outer window address uses IBM Plex Mono. Section 06 shares the section 07 JobCard implementation instead of drawing a generic table. Money uses the organisation's currency; compact dashboard totals may use the app's compact formatting. 4. Data is illustrative STATE, never an outcome: counts, amounts, statuses, dates. No deltas, no "% more", no "saved N hours", no forecast. 5. Windows never resize while animating; content scrolls inside a fixed screen. 6. Every animation has a finished static frame for reduced motion and for the server render. 7. The console is Claude using the Vremly MCP connector, drawn as the product's own connector screen describes it. Never "Vremly AI", never "ask your data", never a chatbot inside the product. It reads; it writes only if the connection allows it — read only is preselected. Never state which plan includes the connector. Only its three tools exist: `vremly_search_endpoints`, `vremly_describe_endpoint`, `vremly_request`. Never print an endpoint or operation count; never show it building a feature, pushing into the app or syncing to a BI tool; never name any AI client as supported, Claude included — the console only depicts it. 8. Use names and Calgary-area addresses the site's demos already use (Marcus Kaur, Reyes Duke, Emily Tran, Priya Sandhu, Daniel Okafor, Marc Lefebvre; 902 Crescent Rd NW, 88 Bowness Rd NW, 740 Aspen Stone Blvd SW, 76 Silverado Blvd SW …). Never a real customer. 9. Never draw: Marketplace, App Store or Google Play badges, SOC 2 or GDPR badges, "AI Studio", MLS, named testimonials, "+N more" chips. 10. Older windows on the home page differ — the hero's Safari SVG frame (`components/ui/safari.tsx`, given `url="yourcompany.vremly.com"` by `components/landing/hero-story/HeroScreen.tsx`) and the product story's `Window` with macOS traffic lights (`components/landing/product-story/shell.tsx`). They stay where they are; do not copy them for new graphics. ## 10. Icons Vremly has no drawn icon family. Icons are stock `lucide-react` strokes used as-is; brand glyphs of third parties come from `react-icons` where needed. Never present a Lucide icon as Vremly's own, never draw a custom family to fill a brief, never fill an icon. - Sizes in use: `h-3 w-3` (small icons on the home AI card, `AIVideo.tsx`), `h-3.5 w-3.5` (the breadcrumb chevron, `components/layout/Breadcrumbs.tsx`), `h-4 w-4` (in buttons, beside a label), `h-5 w-5` (list checks). - Colour: ink or a grey by default. The inclusion marks on /pricing: `Check` `text-[#059669]` = included, `Minus` `text-[#d4d4d4]` = not included. - The one decorative arrow is `ArrowRight h-4 w-4` after a CTA label on dark cards; a `↓` text glyph in an `` marks a download or a scroll target on /brand. - Live one-offs, not system — raw Tailwind emerald utilities, which Tailwind v4 defines in oklch, not as the hexes in section 2: `emerald-500` (`oklch(69.6% 0.17 162.48)`) in the feature pages' "Key highlights" checks (`components/layout/FeaturePageLayout.tsx`), the home lifecycle's progress bars and step dots (`WorkflowStepper.tsx`) and the Capture pulse dot (`VremlyCapture.tsx`); `text-emerald-600` checks on /features/ai-video; `text-emerald-400` icons and `text-emerald-300` labels on the home AI card (`AIVideo.tsx`). Also off-system reds: `red-400` on the home Problem section's "Your current stack" label and pain bullets (`ProblemSection.tsx`), and a `text-[#dc2626]` `X` on the /pricing Enterprise fee row. Do not reuse any of these colours. ## 11. Motion (page 08) Rules - Sections rise once, as they arrive (`whileInView`, `viewport={{ once: true }}`). Nothing re-animates on scroll back. - Product replicas loop through their steps only while they are on screen (`useLoop` in `lib/use-loop.ts`). - Product loops (replicas, the console) live inside windows and consoles. A window never grows while it animates. The loops outside a window are decorative: the accent-word (and `.accent-gold`) drift, the two role marquees, the grainient, the emerald card's breathing, the plan cards' 5200ms flip, and two one-offs — the gold `ShineBorder` lines on the Built by operators band (`motion-safe:animate-shine`) and the Capture pulse dot (`animate-pulse`, `VremlyCapture.tsx`). Add no new ones. - Tailwind's default transition is the site's: every `transition-colors` without a `duration-*` runs 150ms `cubic-bezier(0.4, 0, 0.2, 1)` (no override in `app/globals.css`). - Reduced motion never removes content and never changes colour; it removes movement (table below). The timings (the rows on https://vremly.com/brand with their values spelled out; `components/brand/sections/Motion.tsx`) | Scene | What happens | Timing | |---|---|---| | Section rise | Section heads rise 16px into place as they arrive, once; tiles follow in 0.4s (`BentoGrid.tsx` and the other section heads) | 0.6s | | Hero entrance | Copy is visible from the server; it rises 8px after the splash over 0.45s. The product frame rises 14px over 0.6s after a 0.54s delay. Reduced motion skips movement. | `Hero.tsx`, `HeroStage.tsx` | | Header | The nav pill fades in (`Navbar.tsx`) | 0.5s | | Dock | The docked pill springs down once the header scrolls away — `{ type: "spring", stiffness: 320, damping: 30, mass: 0.8 }` from y −72 | spring | | Features menu | Rises 8px and scales up from .96, `easeOut` | 0.2s | | Accent word | The gradient drifts across the word and back (`gradient-shift`, ease-in-out, infinite) | 6s | | Marquee | Role rows drift in opposite directions (`animate-marquee` / `-reverse`, linear, `translateX(-25%)`) | 30s | | Self-serve booking | Package grid, package picked, the day's times, a time picked, the total — holds 1.4 · 1.9 · 1.4 · 1.9 · 3s from its `useLoop` (`bento-demos/BookingDemo.tsx`) | 9.6s | | Easing | Section entrances ease on `cubic-bezier(0.25, 0.4, 0.25, 1)`; the hero's badge, buttons and frame, and the header, on `(0.25, 0.46, 0.45, 0.94)`. Under reduced motion, replicas pin their last step, the accent word and marquees freeze, and the grainient draws one frame. | — | Also exact - The home hero uses one restrained copy entrance, not a per-line text cascade. Its serif accent is “payment.” On desktop, the 560svh stage starts at zero progress before the order arrives; expansion ends at 18% of the scroll runway, then the existing assignment-to-delivery sequence plays. Product position follows the measured copy height with a 112px desktop gap, and the copy becomes inert when faded. On phones (below 640px), the hero stays in normal flow: a native-size JobCard below the copy has tappable Pending, Assigned, and Delivered stages. No pinned runway, simulated cursor, or scroll progress border on phones. - Splash (home only, and only at `(min-width: 640px)` — `NOT_PHONE`, `lib/use-media-query.ts`): a `#FCFCFC` panel holds 2200ms, then exits `y: "-100%"` over `{ duration: 0.85, ease: [0.76, 0, 0.24, 1] }` (`components/landing/SplashScreen.tsx`). The hero cascade starts with the exit. - Footer: its magnetic buttons follow the pointer on `{ stiffness: 260, damping: 18, mass: 0.6 }` through `useSpring` (`MagneticButton`, `components/ui/motion-footer.tsx`). - The plan cards (/pricing and the home page) auto-flip every 5200ms (`components/ui/plan-cards.tsx`); never under reduced motion. - `.clause-mark` sweeps in 0.5s `cubic-bezier(0.4, 0, 0.2, 1)` (/switch-from-aryeo only). - The typewriter: 45ms per character (`useTypewriter` default). - /brand's own: the console clock (section 9.4); a swatch reads "Copied" for 1400ms; the copy-file button settles after 1800ms; step-strip tabs cross-fade their colours over .25s; pills and buttons .15s. - The live replica on /brand: the home page's `BookingDemo` inside a `.vb-win` at `app.vremly.com/booking`, 482px wide (its 480px canvas at scale 1), its screen an `aspect-ratio: 480 / 256` box so the height depends on width only; in a `.vb-panel.is-wash` 720px wide, min-height 600 (440 on phones), over the hero wash. Reduced-motion contract (`prefers-reduced-motion: reduce`) | Behaviour | File | |---|---| | every looping replica pins its LAST step and never advances — so the last step must be the one worth reading | `lib/use-loop.ts` | | the typewriter shows its full text | `lib/use-loop.ts` | | the accent word and both marquees stop moving; the gradients stay, frozen | `app/globals.css` | | the grainient paints one frame and never starts its loop | `components/ui/grainient.tsx` | | the emerald card paints once; only the breathing loop is skipped | `components/ui/animated-gradient-background.tsx` | | `BlurFade` leaves the element visible, no animation | `components/ui/blur-fade.tsx` | | the hero and the product story lose their scroll pin and render as a static column (the hero also uses a static layout on viewports below 600px high) | `components/landing/hero-story/HeroStage.tsx`, `components/landing/product-story/index.tsx` | | anchor links jump instead of gliding (`scroll-behavior: smooth` only under `no-preference`) | `app/globals.css` | | the highlighter lands fully drawn | `app/globals.css` | | the /brand console shows the finished frame; caret, spinner and rise-ins are off | `components/brand/sections/dash.css`, `dash/Console.tsx` | The home hero explicitly respects reduced motion and displays the finished project frame without pinning. Short viewports below 600px high use the same static layout. ## 12. Page recipes (band order, top to bottom) Home — https://vremly.com (`app/page.tsx`; numbered as its own order comment numbers them — each section answers the question the one above raises) - Nav (floating pill). 1. Hero (`Hero.tsx`, in an `sm:p-4` inset): audience badge “For real estate media teams”; H1 “From first booking / to final payment.” (Medium 500; payment in emerald serif italic); sub “Run scheduling, dispatch, delivery, invoicing, and payroll in one place.”; “Get started free” + “Book a demo”. The existing no-card / 14-day trial line remains live copy only. Caption “One booking, through assignment and delivery” introduces the product preview. Marketing chrome remains light. 2. Roles — "For everyone in real estate media", with role marquees. 3. Problem — "Running a media company shouldn't take five tools and a group chat." 4. Replaces stack — "The tools you're stitching together, …" and what Vremly plugs into. 5. Before / after — the one `#F5F5F5` band: "One platform, …" 6. Lifecycle (`WorkflowStepper`) — "One job. One record. Start to finish." 7. Product story — `#0C0C0E` dark band: one real job through five real screens, scroll-pinned. 8. How it works — "Three steps. Zero chaos." — the same job from each role's side. 9. Platform bento — "Everything your operation needs": replica tiles (booking, dispatch, analytics, messaging, packages, team). 10. Capture (`VremlyCapture`) — "Own your floor plans and virtual tours." 11. AI (`AIVideo`) — on the emerald card gradient. 12. Security (`Enterprise`) — `#0C0C0E` dark band: "Switch without breaking your operation." 13. Onboarding (`Training`) — "You're not switching alone." 14. Pricing — "Pay nothing until work comes through." + the plan cards. 15. Built by operators — `#0C0C0E` dark band (its live H2 still names the operating company, which `lib/canonical/claims.ts` withdraws from public copy — never copy that line). 16. FAQs — "Frequently asked questions". - CinematicFooter. Pricing — https://vremly.com/pricing (`app/pricing/page.tsx`) header (`pt-24 pb-8 md:pt-32`, `max-w-5xl px-6`: breadcrumbs, eyebrow "Pricing", the ink → grey H1 (section 3), an 18px `#737373` lede) → the plan cards (`PlanCards`, the same component as the home page) → "What each plan includes" comparison table (emerald checks, grey dashes) → FAQ on a seam, "Questions people actually ask" (`dt` 15px semibold ink, `dd` 15px `#737373`) → the emerald CTA card "Still deciding?" with "Start free" + "Talk to us" → a 13px `#A3A3A3` support-email line → footer. Feature pages — https://vremly.com/features/* (`components/layout/FeaturePageLayout.tsx`) hero (`pt-24 pb-16 md:pt-32 md:pb-20`, `max-w-7xl px-6`: breadcrumbs, eyebrow = category, H1 40 → 48px Bold with the LAST word as the accent, an 18–20px `#737373` subheadline, and — when a feature is built but not open — an availability note in a `#F5F5F5` box above the fold) → body (`py-16 md:py-24`: the feature nav; an 18px `#525252` body paragraph; "Key highlights" list; the live product replica, inert; the emerald CTA card "Ready to see … in action?" + "Book a Demo") → footer. /features: hero → feature categories grid → the AI card on the emerald card gradient → more features. /features/ai-video: the light emerald card hero → how it works → a `#F5F5F5` roadmap band (not for sale) → the emerald CTA card. Switch from Aryeo — https://vremly.com/switch-from-aryeo (`app/switch-from-aryeo/page.tsx`, the document page) header (`px-5 pt-32 pb-16 sm:px-8 sm:pt-40 sm:pb-24`, `max-w-3xl`): H1 IBM Plex Sans 600 38 → 56px, lh 1.05, −0.03em, "Your photos. Your clients. / Your platform."; lede 17 → 19px / 1.65 `#525252`; the `h-12` ink pill + a source link; the exhibit — Aryeo's Terms §7.3 quoted with `.clause-mark` highlights → sections on seams (`border-t border-[#e5e5e5] py-16 sm:py-24`, eyebrow + H2 600 28 → 36px): The clause · The clause nobody quotes · Side by side · The conflict · Pricing · Migration · Who built it · The offer · Questions → footer. No accent word on this page; the highlighter is its one device, and only for words quoted from a linked source. Brand — https://vremly.com/brand (`app/brand/page.tsx`) Nav → hero (centred: kicker "Brand guidelines · v2 · September 2026", H1 "Everything you need to build like Vremly." with "Vremly" as the accent, lede, "See the brand ↓" + "Get the brand LLM file", "Prefer to paste? Copy the LLM file to clipboard") → 01 Logo (base) → 02 Colour (white) → 03 Type (base) → 04 The gradient (white) → 05 The mark (base) → 06 Components (white) → 07 Dashboards & consoles (base) → 08 Motion (base, no line between) → 09 For AI (white: the wash panel with "Download vremly-brand.md", "Open in the browser", "Copy to clipboard" and the file URL) → footer. Title "Brand - Vremly", canonical `/brand`. Other surfaces - Open Graph: `app/opengraph-image.png`, exactly 1200 × 630 (inherited by every route; a page that sets `openGraph` must name it again — metadata merges shallowly). - Social canvases (`RULE SET HERE` — the site has no social templates): safe margin 8% of the short edge. Square 1080 × 1080 (86px) · Portrait 1080 × 1350 (86px) · Story / Reel 1080 × 1920 (86px sides; keep type out of the top 250px and bottom 320px). Ground `#FCFCFC` or a gradient form; one idea, one accent word; place logo FILES on a matching ground, never cut out onto a photograph. - Email (`RULE SET HERE`; only the `lib/email.ts` stack quoted below is read from code): assume the fallback font is what ships. Sans `Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif`; accent `'IBM Plex Serif', Georgia, 'Times New Roman', serif` italic (`lib/email.ts` currently sends `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`). The accent word in email is a SOLID `color: #059669; font-style: italic` — never the gradient (Outlook on Windows does not support `background-clip: text`, so the word renders invisible). Weights 400 and 700 only. Carry contrast with `#1A1A1A` on `#FFFFFF`, not a near-white ground (clients invert near-white in dark mode). ## 13. Copy — voice and the approved lines Voice: plain, specific, unhurried. Short declarative sentences, concrete nouns, second person, present tense ("You book it once", not "users can book"). Name the reader's real problem before naming the product. Confidence comes from precision, not adjectives. Headlines are claims; the accent word is where they resolve. - British / Canadian spelling — "colour", "centre", "licence" (noun) — with one standing exception: "organization", with a z, because it is the name of an object in the product and the marketing, the privacy policy and the terms match the interface. Product and site labels quoted verbatim keep their own spelling ("Technician Utilization", "Help Center"). - No exclamation marks. Em dashes and commas instead. - Never stack three adjectives. Never write a headline that could belong to any other company. - Banned words: revolutionary, seamless, game-changing (or "game changing"), empower (`lib/canonical/claims.ts` `BANNED_WORDS`); also unlock, supercharge, effortless, 10x, and "AI-powered" as a virtue. - Forbidden shapes (enforced in `lib/canonical/claims.ts`): efficacy outcomes ("cut turnaround by 30%", "saved 5 hours"); zero-occurrence claims ("not one missed deadline"); customer results or counts ("200 companies"). Vremly is pre-launch: there are no customers to count, no results to report, no testimonials to quote. - Never bury the price: where pricing appears, the 3% must be as legible as the $0, and it is "3% per transaction" — never "per booking". $0 never appears alone. - Naming: "Vremly" in prose. The legal entity, "2817230 Alberta Ltd.", only where a legal entity is required. Companies that run on the platform are named as themselves, never as Vremly, and never in a way that implies Vremly did their work. Approved lines (verbatim, with where they live) - "Real estate media, handled." — the tagline (the footer, `components/ui/motion-footer.tsx`; the site title in `app/layout.tsx` sets it as "Vremly | Real estate media, handled", no full stop). - "One platform where agents book media, teams run operations, and providers find work. From order to delivery, everything stays in one place." — the site description (owner-asserted, llms-full.txt). - "Vremly is the operating system for real-estate media production." (owner-asserted, llms-full.txt) - "Vremly is built and run by a working real-estate media company." — owner-asserted, unnamed (llms-full.txt). - "From first booking to final payment." / "Run scheduling, dispatch, delivery, invoicing, and payroll in one place." — home hero. - "Three steps. Zero chaos." · "One job. One record. Start to finish." · "Everything your operation needs" · "Own your floor plans and virtual tours." · "Switch without breaking your operation." · "You're not switching alone." · "Pay nothing until work comes through." — home section heads. - "Run your entire operation in one place." / "Book, dispatch, deliver. One platform." — the footer CTA. - "Start free. Pay only when you get paid." — /pricing. "Free plan: $0/month, 3% per transaction." (owner-asserted, llms-full.txt) - "Your photos. Your clients. Your platform." — /switch-from-aryeo. - "Self-serve booking" / "Agents select services, pick a date, and go." — the home booking tile. - "A slot is only offered if someone can actually serve that package." — one of the Claims Fact Sheet's mechanisms cleared for organic social. It is not in llms-full.txt, so use it only as the section 8 dark-card specimen line and never widen it (never "prevents double-booking"). - Buttons: "Get started free", "Book a demo" (hero; "Book a Demo" on feature pages and the footer), "Log in", "Start free", "Talk to us". Never claim (for product capabilities the authority is https://vremly.com/llms-full.txt — if a capability is not there, treat it as not existing in copy. Two things in this file are not in it: the Vremly MCP connector the section 9 console depicts, as the product's own connector screen describes it; the dark-card specimen line above. Use each only where this file places it, and make no capability, plan or availability claim about the connector.) - AI video generated from listing photos; an AI assistant, chatbot or "ask your data" inside the product; anything predictive. - Social publishing or scheduling; a visual automation builder; MLS syndication; BI connectors or a bulk data-export API. - SSO (SAML, Okta, Azure AD); SOC 2 or ISO certification; App Store or Google Play listings for the Vremly app. - Which plan includes API keys, webhooks or the Vremly MCP connector — the site's own sources disagree (llms-full.txt puts API keys and webhooks on every plan; `lib/features.ts` calls API key management an Enterprise feature). Name no plan for any of them. - Anything about a marketplace: vetted or insured providers, escrow, held funds, payouts, disputes, guaranteed payments, marketplace numbers. - Double-booking prevention; automatic editor assignment or auto-start of editing; per-item media proofing. - Endpoint, file, test or model counts; the operating company's name, headcount, years trading or revenue; the names of the third-party engines behind Vremly's six AI capabilities (the section 9 console's Claude is the customer's own assistant and is named only inside that kit); any Enterprise price figure. - Customer names, logos, counts, quotes or results; invented incidents, dates or amounts presented as real. ## 14. Do / Don't Do: `#FCFCFC` as the page and `#FFFFFF` as a surface on it; one idea per section; IBM Plex Sans headlines with at most one accent word; Inter for everything you read; ink or outline pills; light or dark windows in the matching app palette for the product; status hues only in product art (window chips, the aurora under a home replica); the gradient clipped by the box it fills, with white type on the dark forms and ink on the pale ones; the logo files, placed; IBM Plex Mono for IDs, values and URLs; illustrative state in windows; a finished frame for reduced motion; the class strings in this file, pasted. Don't: add a second accent colour or a colour for one section; accent two words or a function word; accent a name outside the approved /brand hero; use `font-black` or any weight above 700; retype, redraw, recolour, outline, rotate, shadow or stretch the V or the logo; use the Scan mark for Vremly; use `/logos/vrem-icon.svg`, the VR monogram files or the mascot concepts; put the white logo on a light ground or the emerald logo on a gradient; sample colour from a raster; apply the product dark theme to marketing-page chrome; draw traffic lights, a browser chrome, a sparkline, a line chart or a delta in a product window; let a window grow while it animates; make the console "Vremly AI" or let it write on a read-only connection; raise the grain above 2%; invent a mascot, a pattern, an illustration style or an icon family; invent a customer, a quote, a metric or a count; use exclamation marks. ## Appendix A — the gradient files, complete (paste as-is) Verbatim from `public/brand/texture/`. The three radials are `objectBoundingBox` gradients carrying the CSS ellipse radii, so with `preserveAspectRatio="none"` they stretch exactly to any box. Use them as a background image or inline, clipped by the box they fill (section 5). The grainient has no vector form: use its stills or the live ``. `https://vremly.com/brand/texture/vremly-hero-wash.svg` ```svg ``` `https://vremly.com/brand/texture/vremly-card-dark.svg` ```svg ``` `https://vremly.com/brand/texture/vremly-card-light.svg` ```svg ``` `https://vremly.com/brand/texture/vremly-fallback.svg` ```svg ``` `https://vremly.com/brand/texture/vremly-grain.svg` ```svg ``` ## Appendix B — the V as SVG (emerald colourway; the white and black files differ only in `fill`: `white` and `#0A0A0A` — one exception, below) Place these files; the paths are here so an AI can recognise the real mark and never substitute a drawn one. Never edit a path. The one exception: the black stacked logo, `vremly-logo-primary-vertical-black.svg`, has three coordinates in its first path exported 0.01 unit apart — `H5284.71L2523.46 0.647458H4000.65`, where the emerald and white files have `H5284.7L2523.45 0.647458H4000.64` (in a 12098-unit-wide box). Place it as it is. The mark — `https://vremly.com/brand/vremly-logo-mark-emerald.svg` (left stroke, then the stripe-and-arm polygon): ```svg ``` The box logo — `https://vremly.com/brand/vremly-logo-mark-box.svg` (app icon, avatar): ```svg ``` The Vremly Scan mark — `https://vremly.com/brand/vremlyscan-logo-mark-emerald.svg` (one compound path: the V with nine notches on the left stroke): ```svg ``` The horizontal logo opens with the mark scaled to cap height — its first two paths, verbatim from `vremly-logo-primary-horizontal-emerald.svg` (viewBox `0 0 18388 3321`; the letters REMLY follow as five more paths): ```svg ``` Viewboxes: horizontal `0 0 18388 3321` · stacked `0 0 12098 10593` · wordmark `0 0 18167 3321` (six letter paths, a plain V) · mark and Scan `0 0 10987 10727` · box `0 0 1024 1024`. In HTML, give an `` the viewBox as its `width`/`height` attributes and fix the rendered height in CSS (`height: 28px; width: auto` for the horizontal logo and wordmark, `76px` for marks and the stacked logo, `72px` + `border-radius: 14px` for the box logo) — the box reserves its aspect ratio before the file loads (`components/brand/primitives.tsx` `LogoTile`). ## Appendix C — reference CSS, verbatim from the site (desktop, tablet and reduced motion; the phone block is Appendix D) Every rule of the /brand kit, in cascade order: `app/brand/brand.css` first, then each section sheet as the page imports it. Comments are removed and whitespace is collapsed. Most rules are on one line; the `.vb` token block and `.vb-dl` keep one declaration per line, and the at-rule blocks (`@media`, `@container`, `@keyframes`) keep their nested rules on their own lines. Selectors, properties and values are verbatim. The section sheets use `.vb .vb-*` (two classes) so they refine the shared one-class primitives. Tokens are the `.vb` block in section 2. ```css /* app/brand/brand.css */ .vb { --vb-ground: #fcfcfc; --vb-surface: #ffffff; --vb-raised: #fafafa; --vb-recessed: #f5f5f5; --vb-fill: #f0f0f0; --vb-wash: #ecfdf5; --vb-wash-tint: #d1fae5; --vb-ink: #1a1a1a; --vb-ink-hover: #333333; --vb-lede: #525252; --vb-body: #737373; --vb-label: #a3a3a3; --vb-quiet: #d4d4d4; --vb-hair: #e5e5e5; --vb-divider: #f0f0f0; --vb-accent: #059669; --vb-accent-deep: #047857; --vb-accent-mid: #34d399; --vb-accent-light: #6ee7b7; --vb-accent-edge: rgba(5, 150, 105, 0.14); --vb-accent-hover: rgba(5, 150, 105, 0.35); --vb-accent-hover-strong: rgba(5, 150, 105, 0.45); --vb-dark: #0c0c0e; --vb-dark-text: #f5f5f7; --vb-dark-body: #a1a1aa; --vb-dark-label: #8b8b93; --vb-shadow-window: 0 26px 60px -30px rgba(16, 32, 24, 0.35); --vb-shadow-console: 0 26px 60px -30px rgba(16, 32, 24, 0.5); --vb-shadow-card: 0 12px 34px -26px rgba(16, 32, 24, 0.35); --vb-shadow-quote: 0 6px 18px rgba(16, 32, 24, 0.06); --vb-font-body: var(--font-inter), ui-sans-serif, system-ui, sans-serif; --vb-font-head: var(--font-ibm-plex-sans), ui-sans-serif, system-ui, sans-serif; --vb-font-serif: var(--font-ibm-plex-serif), ui-serif, Georgia, serif; --vb-font-mono: var(--font-ibm-plex-mono), ui-monospace, SFMono-Regular, Menlo, monospace; font-family: var(--vb-font-body); color: var(--vb-ink); background-color: var(--vb-ground); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } .vb :where(.vb-hero, .vb-sec) :where(a, button, [tabindex]):focus-visible { outline: 2px solid var(--vb-accent); outline-offset: 2px } .vb-inert { pointer-events: none; user-select: none } .vb-in { max-width: 1180px; margin: 0 auto } .vb-hero { padding: 144px 32px 88px; text-align: center } .vb-sec { padding: 96px 32px 104px; scroll-margin-top: 80px } .vb-sec.is-white { background: var(--vb-surface); border-top: 1px solid var(--vb-hair); border-bottom: 1px solid var(--vb-hair) } .vb-sec.is-white + .vb-sec.is-white { border-top: 0 } .vb-k { font-family: var(--vb-font-body); font-size: 11px; font-weight: 600; line-height: 1.5; letter-spacing: 0.15em; text-transform: uppercase; color: var(--vb-label) } .vb-h1 { font-family: var(--vb-font-head); font-weight: 500; font-size: 64px; line-height: 1.08; letter-spacing: -1.5px; color: var(--vb-ink); margin: 18px 0 0; text-wrap: balance } .vb-lede0 { font-family: var(--vb-font-body); font-weight: 400; font-size: 21px; line-height: 1.5; color: var(--vb-body); max-width: 620px; margin: 16px auto 0; text-wrap: pretty } .vb-hero .vb-btns { margin-top: 32px } .vb-head { max-width: 720px } .vb-h2 { font-family: var(--vb-font-head); font-weight: 700; font-size: 44px; line-height: 1.1; letter-spacing: -1.5px; color: var(--vb-ink); margin: 14px 0 0; max-width: 720px; text-wrap: balance } .vb-lede { font-family: var(--vb-font-body); font-size: 17px; line-height: 1.6; color: var(--vb-lede); margin: 16px 0 0; max-width: 480px; text-wrap: pretty } .vb-sub { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin: 44px 0 16px } .vb-sub-t { font-family: var(--vb-font-head); font-size: 18px; font-weight: 600; line-height: 1.3; letter-spacing: -0.3px; color: var(--vb-ink); margin: 0 } .vb-sub-c { font-size: 13.5px; line-height: 1.4; color: var(--vb-body) } .vb-btns { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 12px } .vb-btns.is-left { justify-content: flex-start } .vb-btn-dark, .vb-btn-soft { display: inline-flex; align-items: center; justify-content: center; gap: 8px; border-radius: 999px; font-family: var(--vb-font-body); font-size: 14px; font-weight: 600; line-height: 20px; white-space: nowrap; text-decoration: none; cursor: pointer; transition: background-color 0.15s ease, border-color 0.15s ease } .vb-btn-dark { background: var(--vb-ink); color: #ffffff; border: 1px solid var(--vb-ink); padding: 13px 28px } .vb-btn-dark:hover { background: var(--vb-ink-hover); border-color: var(--vb-ink-hover) } .vb-btn-soft { background: var(--vb-surface); color: var(--vb-ink); border: 1px solid var(--vb-hair); padding: 13px 24px } .vb-btn-soft:hover { background: var(--vb-recessed) } .vb-btn-dark i, .vb-btn-soft i { font-style: normal; font-size: 12px; opacity: 0.7 } .vb-btn-dark svg, .vb-btn-soft svg { flex: none } .vb-copy { display: inline-block; background: none; border: 0; padding: 0; font-family: var(--vb-font-body); font-size: 13px; line-height: 1.5; color: var(--vb-body); text-decoration: underline; text-decoration-color: var(--vb-quiet); text-underline-offset: 4px; cursor: pointer; transition: color 0.15s ease, text-decoration-color 0.15s ease } .vb-copy:hover { color: var(--vb-ink); text-decoration-color: var(--vb-ink) } .vb-copy svg { display: inline-block; vertical-align: -3px; margin-right: 6px } .vb-hero .vb-copy { margin-top: 18px } .vb-dls { display: flex; flex-wrap: wrap; gap: 6px; flex: none } .vb-dl { display: inline-flex; align-items: center; gap: 5px; font-family: var(--vb-font-body); font-size: 12px; font-weight: 500; line-height: 1; color: var(--vb-ink); background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 999px; padding: 6px 11px; white-space: nowrap; text-decoration: none; transition: background-color 0.15s ease, border-color 0.15s ease; } .vb-dl i { font-style: normal; font-size: 11px; color: var(--vb-label) } .vb-dl:hover { background: var(--vb-wash); border-color: var(--vb-accent-hover) } .vb-grid3, .vb-grid4 { display: grid; gap: 16px; margin-top: 36px } .vb-grid3 { grid-template-columns: repeat(3, minmax(0, 1fr)) } .vb-grid4 { grid-template-columns: repeat(4, minmax(0, 1fr)) } .vb-sub + .vb-grid3, .vb-sub + .vb-grid4, .vb-sub + .vb-swatches { margin-top: 0 } .vb-rules { list-style: none; margin: 32px 0 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px 40px } .vb-rules li { position: relative; padding-left: 18px; font-size: 14px; line-height: 1.55; color: var(--vb-lede) } .vb-rules li::before { content: ""; position: absolute; left: 0; top: 8px; width: 7px; height: 7px; border-radius: 2px; background: var(--vb-accent) } .vb-rules code { font-family: var(--vb-font-mono); font-size: 12px; color: var(--vb-ink) } .vb-tile { container: vb-tile / inline-size; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 16px; overflow: hidden; min-width: 0 } .vb-tile-art { height: 168px; display: flex; align-items: center; justify-content: center; background: var(--vb-raised); border-bottom: 1px solid var(--vb-divider); font-size: 12px; line-height: 1.4; color: var(--vb-body) } .vb-tile-art.is-ink { background: var(--vb-ink); border-bottom-color: var(--vb-ink); color: var(--vb-dark-body) } .vb-tile-art.is-brand { background: var(--vb-accent); border-bottom-color: var(--vb-accent) } .vb-tile-art img { display: block; width: auto; height: 28px; max-width: calc(100% - 32px); object-fit: contain } .vb-tile-art img.is-box { border-radius: 14px } .vb-tile-cap { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px 14px 18px } .vb-tile-cap > div:first-child { min-width: 0 } .vb-tile-cap b { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3; color: var(--vb-ink) } .vb-tile-cap span { display: block; font-size: 12px; line-height: 1.4; color: var(--vb-body); margin-top: 2px } .vb-tile-cap code { display: block; margin-top: 6px; font-family: var(--vb-font-mono); font-size: 10.5px; line-height: 1.4; color: var(--vb-body) } .vb-tile-cap.is-col { flex-direction: column; align-items: flex-start; padding: 16px 18px 18px } @container vb-tile (max-width: 276px) { .vb-tile-cap { flex-direction: column; align-items: flex-start; gap: 10px } } .vb-cgroup { margin-top: 8px } .vb-cgroup .vb-sub { margin: 36px 0 14px } .vb-head + .vb-cgroup { margin-top: 36px } .vb-head + .vb-cgroup .vb-sub { margin-top: 0 } .vb-swatches { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px } .vb-sw { display: flex; flex-direction: column; width: 100%; min-width: 0; text-align: left; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 14px; padding: 0; overflow: hidden; cursor: pointer; font: inherit; color: inherit; transition: border-color 0.15s ease } .vb-sw:hover, .vb-sw.is-copied { border-color: var(--vb-accent-hover-strong) } .vb-sw-c { display: block; height: 76px; border-bottom: 1px solid var(--vb-divider) } .vb-sw-t { display: block; padding: 11px 14px 12px } .vb-sw-t b { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3; color: var(--vb-ink) } .vb-sw-t code { display: block; font-family: var(--vb-font-mono); font-size: 11.5px; line-height: 1.35; color: var(--vb-accent-deep); margin-top: 3px } .vb-sw-t small { display: block; font-size: 11.5px; line-height: 1.4; color: var(--vb-body); margin-top: 3px; overflow-wrap: anywhere } .vb-table { margin-top: 28px; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 14px; overflow: hidden } .vb-trow { display: grid; grid-template-columns: 1.1fr 1.3fr 2fr; gap: 16px; align-items: baseline; padding: 12px 18px; font-size: 13.5px; line-height: 1.5; color: var(--vb-lede) } .vb-trow + .vb-trow { border-top: 1px solid var(--vb-divider) } .vb-trow > :first-child { color: var(--vb-ink); font-weight: 500 } .vb-trow > :last-child { font-family: var(--vb-font-mono); font-size: 12px; color: var(--vb-lede) } .vb-table.vb-anat .vb-trow { grid-template-columns: 0.8fr 1.1fr 2.7fr } .vb-table.vb-anat .vb-trow > :nth-child(2) { font-family: var(--vb-font-mono); font-size: 12px; color: var(--vb-accent-deep) } .vb-table.vb-anat .vb-trow > :last-child { font-family: inherit; font-size: 13.5px; color: var(--vb-lede) } .vb-table.is-motion .vb-trow { grid-template-columns: 0.7fr 3fr 0.6fr } .vb-table.is-motion .vb-trow > :last-child { text-align: right; color: var(--vb-accent-deep) } .vb-trow.is-sum { background: var(--vb-raised) } .vb-trow.is-sum > :first-child { font-family: var(--vb-font-mono); font-size: 10.5px; font-weight: 400; letter-spacing: 0.1em; text-transform: uppercase; color: var(--vb-body); align-self: center } .vb-comps { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 36px } .vb-comp { position: relative; overflow: hidden; min-width: 0; background: var(--vb-raised); border: 1px solid var(--vb-hair); border-radius: 18px; padding: 26px 28px 30px } .vb-comp .vb-k { margin-bottom: 12px } .vb-mt { margin-top: 26px } .vb-comp.is-dark { background: var(--vb-dark); border-color: var(--vb-dark); padding: 0 } .vb-comp.is-wash { background: var(--vb-wash); border-color: var(--vb-accent-edge); display: flex; align-items: center; justify-content: center; padding: 32px } .vb-win { width: 100%; max-width: 480px; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 12px; box-shadow: var(--vb-shadow-window); overflow: hidden; text-align: left } .vb-winbar { position: relative; display: flex; align-items: center; gap: 7px; padding: 13px 18px; border-bottom: 1px solid var(--vb-hair) } .vb-windot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--vb-fill) } .vb-winurl { position: absolute; left: 50%; transform: translateX(-50%); font-family: var(--vb-font-mono); font-size: 11.5px; line-height: 1.3; color: var(--vb-body); white-space: nowrap } .vb-wrow { display: grid; grid-template-columns: minmax(0, 1fr) 120px 110px; align-items: center; padding: 11px 18px; font-size: 13px; line-height: 1.4; color: var(--vb-ink); text-align: left } .vb-wrow + .vb-wrow { border-top: 1px solid var(--vb-divider) } .vb-wrow > * { min-width: 0 } .vb-wrow.is-h { font-size: 10.5px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--vb-body) } .vb-cap { padding: 12px 18px 14px; border-top: 1px solid var(--vb-hair); font-size: 12.5px; line-height: 1.5; color: var(--vb-lede) } .vb-chip-status { display: inline-flex; align-items: center; width: fit-content; border-radius: 999px; padding: 2px 8px; font-family: var(--vb-font-body); font-size: 11px; font-weight: 500; line-height: 1.4; white-space: nowrap } .vb-chips { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; margin-top: 14px } .vb-chip { background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 999px; padding: 7px 14px; font-size: 13px; line-height: 1.3; color: var(--vb-lede); white-space: nowrap } .vb-tex { position: absolute; inset: 0; z-index: 0; pointer-events: none } .vb-on-tex { position: relative; z-index: 10 } .vb .is-checker { background-color: var(--vb-surface); background-image: linear-gradient(45deg, var(--vb-recessed) 25%, transparent 25%, transparent 75%, var(--vb-recessed) 75%), linear-gradient(45deg, var(--vb-recessed) 25%, transparent 25%, transparent 75%, var(--vb-recessed) 75%); background-size: 18px 18px; background-position: 0 0, 9px 9px } .vb-panel { position: relative; overflow: hidden; border-radius: 14px } .vb-panel.is-wash { background: var(--vb-wash); border: 1px solid var(--vb-accent-edge); padding: 56px 48px } .vb-ai { position: relative; overflow: hidden; background: var(--vb-wash); border: 1px solid var(--vb-accent-edge); border-radius: 24px; padding: 56px 48px 52px } .vb-ai-in { position: relative; z-index: 10; max-width: 720px } .vb-ai-in .vb-lede { max-width: 640px } .vb-ai .vb-btns { margin-top: 28px } .vb-url { display: block; margin-top: 18px; font-family: var(--vb-font-mono); font-size: 12px; line-height: 1.5; color: var(--vb-lede); overflow-wrap: anywhere } @media (min-width: 761px) and (max-width: 1024px) { .vb-grid4 { grid-template-columns: repeat(2, minmax(0, 1fr)) } } @media (prefers-reduced-motion: reduce) { .vb-btn-dark, .vb-btn-soft, .vb-copy, .vb-dl, .vb-sw { transition: none } } /* components/brand/sections/hero.css */ .vb .vb-hero .vb-copy { max-width: 100%; text-wrap: balance } /* components/brand/sections/colour.css */ .vb .vb-colour-note { max-width: 780px; margin: 14px 0 22px; color: var(--vb-lede); font-size: 13px; line-height: 1.7 } .vb .vb-colour-note a { color: var(--vb-accent-deep); text-decoration: underline; text-underline-offset: 3px } .vb .vb-status-pairs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px } .vb .vb-status-pair { border: 1px solid var(--vb-hair); border-radius: 14px; overflow: hidden; padding: 16px } .vb .vb-status-pair h4 { font-size: 14px; font-weight: 500 } .vb .vb-status-pair p { color: var(--vb-lede); font-size: 12px; margin: 5px 0 14px; min-height: 36px } .vb .vb-status-pair > div { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px } .vb .vb-status-pair .vb-sw { min-width: 0 } .vb .vb-status-pair .vb-sw-t { padding: 10px } .vb .vb-status-pair .vb-sw-t b { font-size: 11px } /* components/brand/sections/type.css */ .vb .vb-tgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 36px } .vb .vb-tcard { min-width: 0; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 18px; padding: 28px 30px 30px } .vb .vb-t-h1 { margin: 12px 0 0; font-family: var(--vb-font-head); font-weight: 500; font-size: 46px; line-height: 1.08; letter-spacing: -1.5px; color: var(--vb-ink); text-wrap: balance } .vb .vb-t-h2 { margin: 10px 0 0; font-family: var(--vb-font-head); font-weight: 700; font-size: 32px; line-height: 1.12; letter-spacing: -1px; color: var(--vb-ink); text-wrap: balance } .vb .vb-t-note { margin: 12px 0 0; font-size: 17px; line-height: 1.5; color: var(--vb-body); text-wrap: pretty } .vb .vb-t-lede { margin: 12px 0 0; font-size: 18px; line-height: 1.55; color: var(--vb-lede); text-wrap: balance } .vb .vb-t-body { margin: 14px 0 0; max-width: 470px; font-size: 15px; line-height: 1.625; color: var(--vb-body) } .vb .vb-t-body strong { font-weight: 600; color: var(--vb-ink) } .vb .vb-t-mk { margin-top: 22px } .vb .vb-tcard .vb-k { color: var(--vb-body) } .vb .vb-t-mono { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-top: 12px; font-family: var(--vb-font-mono); font-size: 11.5px; line-height: 1.4 } .vb .vb-t-label { font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--vb-label) } .vb .vb-t-id { color: var(--vb-accent-deep) } .vb .vb-t-url { color: var(--vb-lede); overflow-wrap: anywhere } .vb .vb-fonts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 16px } .vb .vb-font { min-width: 0; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 14px; padding: 16px 18px } .vb .vb-font b { display: block; font-size: 18px; font-weight: 500; line-height: 1.3; color: var(--vb-ink) } .vb .vb-font.is-sans b { font-family: var(--vb-font-head) } .vb .vb-font.is-inter b { font-family: var(--vb-font-body) } .vb .vb-font.is-serif b { font-family: var(--vb-font-serif); font-style: italic; font-weight: 400 } .vb .vb-font.is-mono b { font-family: var(--vb-font-mono) } .vb .vb-font > span { display: block; margin-top: 4px; font-size: 12.5px; line-height: 1.4; color: var(--vb-body) } .vb .vb-font code { display: block; margin-top: 10px; font-family: var(--vb-font-mono); font-size: 11px; line-height: 1.5; color: var(--vb-lede) } .vb .vb-font code > span { display: block; white-space: nowrap } @media (min-width: 761px) and (max-width: 1024px) { .vb .vb-fonts { grid-template-columns: repeat(2, minmax(0, 1fr)) } } /* components/brand/sections/gradient.css */ .vb .vb-zgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-top: 36px } .vb .vb-ztile { min-width: 0; background: var(--vb-raised); border: 1px solid var(--vb-hair); border-radius: 16px; overflow: hidden } .vb .vb-zt { position: relative; overflow: hidden; height: 200px; border-bottom: 1px solid var(--vb-divider); border-radius: 15px 15px 0 0 } .vb .vb-zt-gl, .vb .vb-zt-gl > div, .vb .vb-zt-gl canvas { border-radius: inherit } .vb .vb-zt.is-checker { isolation: isolate } .vb .vb-zt-grain { background-repeat: repeat; background-size: 256px 256px; mix-blend-mode: multiply } .vb .vb-ztile .vb-tile-cap.is-col { gap: 12px } /* components/brand/sections/components.css */ .vb .vb-comp .vb-k { color: var(--vb-body) } .vb .vb-crow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px } .vb .vb-bcard { display: flex; flex-direction: column; gap: 8px; max-width: 336px; margin: 30px auto 0; padding: 16px; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 16px; box-shadow: var(--vb-shadow-card) } .vb .vb-component-project { max-width: 360px; margin-inline: auto } .vb .vb-component-project .vb-win { background: var(--preview-bg) } .vb .vb-pillrow { display: flex; padding-top: 22px } .vb .vb-pc { height: 100%; background: var(--vb-dark); border-radius: 16px; padding: 26px 26px 28px } .vb .vb-pc-n { margin-top: 18px; font-family: var(--vb-font-mono); font-size: 11.5px; line-height: 1.3; letter-spacing: 0.1em; color: var(--vb-accent-light) } .vb .vb-pc-t { margin-top: 12px; font-family: var(--vb-font-head); font-size: 20px; font-weight: 700; line-height: 1.3; letter-spacing: -0.3px; color: var(--vb-dark-text); text-wrap: balance } .vb .vb-pc-s { margin-top: 8px; font-size: 14.5px; line-height: 1.55; color: var(--vb-dark-body) } .vb .vb-pc-l { margin-top: 18px; font-size: 11px; font-weight: 600; line-height: 1.5; letter-spacing: 0.15em; text-transform: uppercase; color: var(--vb-dark-label); text-wrap: balance } .vb .vb-quote { width: 100%; margin: 0; padding: 34px 44px 32px; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 22px; box-shadow: var(--vb-shadow-quote) } .vb .vb-quote-mark { display: block; margin-bottom: 8px; font-family: var(--vb-font-serif); font-size: 40px; font-weight: 400; line-height: 0.55; color: var(--vb-accent) } .vb .vb-quote-q { max-width: 620px; margin: 0; font-family: var(--vb-font-head); font-size: 21px; font-weight: 500; line-height: 1.35; color: var(--vb-ink); text-wrap: balance } .vb .vb-quote-who { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 16px } .vb .vb-quote-who img { flex: none; width: 30px; height: 30px; border-radius: 7px } .vb .vb-quote-who b { font-size: 13.5px; font-weight: 600; line-height: 1.3; color: var(--vb-ink) } @media (max-width: 1024px) { .vb .vb-comps .vb-wrow { grid-template-columns: minmax(0, 1fr) 92px 90px; padding: 10px 14px; font-size: 12px } .vb .vb-comp.is-wash { padding: 22px 16px } .vb .vb-quote { padding: 26px 22px 24px; border-radius: 18px } .vb .vb-quote-q { font-size: 18px } } /* components/brand/sections/dash.css */ .vb .vb-console { max-width: 760px; margin: 18px auto 0 } .vb .vb-term { background: var(--vb-dark); border-radius: 10px; box-shadow: var(--vb-shadow-console); overflow: hidden; text-align: left } .vb .vb-termbar { position: relative; display: flex; align-items: center; gap: 7px; padding: 11px 14px; border-bottom: 1px solid rgba(255, 255, 255, 0.06) } .vb .vb-termbar i { flex: none; width: 9px; height: 9px; border-radius: 50%; background: rgba(255, 255, 255, 0.14) } .vb .vb-termtitle { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); text-align: center; font-family: var(--vb-font-mono); font-size: 11.5px; line-height: 1.3; color: var(--vb-dark-label); white-space: nowrap; pointer-events: none } .vb .vb-term-screen { height: 340px; overflow: hidden; padding: 16px 20px; font-family: var(--vb-font-mono); font-size: 12px; line-height: 1.7; color: #d4d4d8 } .vb .vb-term-prompt { min-height: 1.7em; margin: 0 0 12px; color: var(--vb-dark-text); overflow-wrap: anywhere } .vb .vb-term-glyph { color: var(--vb-accent-mid); margin-right: 8px } .vb .vb-term-caret { display: inline-block; width: 7px; height: 13px; margin-left: 2px; vertical-align: -2px; background: var(--vb-accent-mid); animation: vbDashCaret 1s steps(1) infinite } .vb .vb-term-step { position: relative; margin: 0; padding-left: 18px; color: var(--vb-dark-body); overflow-wrap: anywhere } .vb .vb-term-step b { font-weight: 400; color: var(--vb-dark-text) } .vb .vb-term-mark { position: absolute; left: 0; top: 0; width: 10px; height: 1.7em; display: flex; align-items: center; justify-content: center } .vb .vb-term-spin { display: block; width: 9px; height: 9px; box-sizing: border-box; border-radius: 50%; border: 1.5px solid rgba(255, 255, 255, 0.22); border-top-color: var(--vb-accent-mid); animation: vbDashSpin 0.8s linear infinite } .vb .vb-term-dot { display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--vb-accent-mid) } .vb .vb-term-out { margin-top: 10px; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; padding: 10px 12px; font-size: 11.5px; line-height: 1.6 } .vb .vb-term-tr { display: grid; grid-template-columns: var(--vb-term-cols); gap: 10px; align-items: baseline; font-variant-numeric: tabular-nums } .vb .vb-term-tr > span { min-width: 0 } .vb .vb-term-tr.is-h { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--vb-dark-label); padding-bottom: 2px } .vb .vb-term-tr.is-total { margin-top: 4px; padding-top: 4px; border-top: 1px solid rgba(255, 255, 255, 0.08); color: var(--vb-dark-text) } .vb .vb-term-tr .is-r { text-align: right } .vb .vb-term-tr .is-dim { color: var(--vb-dark-label) } .vb .vb-term-field { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 10px } .vb .vb-term-field > span:first-child { color: var(--vb-dark-label) } .vb .vb-term-group + .vb-term-group { margin-top: 6px } .vb .vb-term-group b { display: block; font-weight: 400; color: var(--vb-dark-text) } .vb .vb-term-group span { display: block; padding-left: 12px; color: var(--vb-dark-body) } .vb .vb-term-note { margin: 8px 0 0; color: var(--vb-dark-text) } .vb .vb-term-done { margin: 10px 0 0; color: var(--vb-accent-mid) } .vb .vb-term-screen.is-live .vb-term-step, .vb .vb-term-screen.is-live .vb-term-note, .vb .vb-term-screen.is-live .vb-term-done { animation: vbDashIn 0.35s ease both } .vb .vb-term-screen.is-live .vb-term-out { animation: vbDashIn 0.5s ease both } .vb .vb-tabs { position: relative; display: flex; gap: 2px; width: fit-content; max-width: 100%; margin: 12px auto 0; padding: 3px; background: var(--vb-surface); border: 1px solid var(--vb-hair); border-radius: 999px } .vb .vb-tabs button { flex: none; display: inline-flex; align-items: baseline; padding: 6px 12px; border: 0; border-radius: 999px; background: transparent; font-family: var(--vb-font-body); font-size: 12px; line-height: 1.3; color: var(--vb-lede); white-space: nowrap; cursor: pointer; transition: background-color 0.25s ease, color 0.25s ease } .vb .vb-tabs button:hover { color: var(--vb-ink) } .vb .vb-tab-n { margin-right: 6px; font-family: var(--vb-font-mono); font-size: 10.5px; color: var(--vb-label) } .vb .vb-tabs button.is-on { background: var(--vb-wash); color: var(--vb-accent-deep) } .vb .vb-tabs button.is-on .vb-tab-n { color: var(--vb-accent-deep) } .vb .vb-table.vb-dash-timing { margin-top: 16px } @keyframes vbDashIn { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: none } } @keyframes vbDashSpin { to { transform: rotate(360deg) } } @keyframes vbDashCaret { 50% { opacity: 0 } } @media (prefers-reduced-motion: reduce) { .vb .vb-term-caret, .vb .vb-term-spin, .vb .vb-term-screen.is-live .vb-term-step, .vb .vb-term-screen.is-live .vb-term-out, .vb .vb-term-screen.is-live .vb-term-note, .vb .vb-term-screen.is-live .vb-term-done { animation: none } .vb .vb-tabs button { transition: none } } .vb .vb-dashboard-preview { max-width: 760px; margin: 36px auto 0 } .vb .vb-preview-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; color: var(--vb-lede) } .vb .vb-theme-toggle { display: flex; padding: 3px; border: 1px solid var(--vb-hair); border-radius: 999px; background: white } .vb .vb-theme-toggle button { min-height: 40px; padding: 8px 20px; border-radius: 999px; font-size: 13px; cursor: pointer; color: var(--vb-lede) } .vb .vb-theme-toggle button[aria-pressed="true"] { background: var(--vb-ink); color: white } .vb .vb-preview-caption { margin: 18px 0 10px; color: var(--vb-lede); font-size: 12px; line-height: 1.6 } .vb .vb-preview-theme .vb-win { max-width: none; border-radius: 14px; background: var(--preview-bg); border-color: var(--preview-border); color: var(--preview-text) } .vb .vb-preview-theme .vb-winbar { background: var(--preview-surface); border-color: var(--preview-border) } .vb .vb-preview-theme .vb-windot { background: var(--preview-active) } .vb .vb-preview-theme .vb-winurl { color: var(--preview-muted) } .vb .vb-app { height: 100%; background: var(--preview-bg); color: var(--preview-text); font-family: var(--font-inter), sans-serif; font-size: 14px; line-height: 1.4 } .vb .vb-app-header { height: 56px; padding: 0 20px; background: var(--preview-surface); display: flex; align-items: center; justify-content: space-between } .vb .vb-app-header > div { display: flex; align-items: center; gap: 18px } .vb .vb-app-org { font-size: 16px; font-weight: 500 } .vb .vb-app-avatar { display: grid; place-items: center; width: 32px; height: 32px; background: var(--preview-active); border-radius: 50%; font-size: 11px } .vb .vb-app-layout { display: flex; height: calc(100% - 56px) } .vb .vb-app-sidebar { width: 192px; flex: none; padding: 16px 8px; background: var(--preview-sidebar); font-size: 13px } .vb .vb-app-sidebar p { margin: 16px 8px 4px; color: var(--preview-muted); font-size: 11px } .vb .vb-app-sidebar > div > span, .vb .vb-app-search { display: flex; align-items: center; gap: 9px; padding: 7px 8px; border-radius: 6px } .vb .vb-app-sidebar .is-active { background: var(--preview-active) } .vb .vb-app-search { color: var(--preview-muted); margin-bottom: 10px } .vb .vb-app-search small { margin-left: auto } .vb .vb-app-main { flex: 1; min-width: 0; padding: 32px } .vb .vb-app-timeframe { display: flex; align-items: center; gap: 10px; margin-bottom: 24px } .vb .vb-app-timeframe > span { color: var(--preview-muted); font-size: 12px } .vb .vb-app-metrics, .vb .vb-app-rates { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px } .vb .vb-app-rates { margin-top: 16px } .vb .vb-app-metric { display: flex; flex-direction: column; gap: 8px; padding: 24px; border-radius: 6px; background: var(--preview-active) } .vb .vb-app-metric > div { display: flex; justify-content: space-between; align-items: start; gap: 10px; font-family: var(--vb-font-head); font-size: 14px } .vb .vb-app-metric svg { flex: none; color: var(--preview-muted) } .vb .vb-app-metric > b { font-size: 24px; font-weight: 400; font-variant-numeric: tabular-nums } .vb .vb-app-metric > span { font-size: 12px; color: var(--preview-muted) } .vb .vb-app-rates .vb-app-metric > b { font-size: 30px } .vb .vb-app-progress { display: block; height: 16px; border-radius: 999px; background: color-mix(in srgb, var(--preview-text) 20%, transparent); overflow: hidden } .vb .vb-app-progress i { display: block; height: 100%; background: var(--preview-text) } .vb .vb-app.is-phone .vb-app-metrics, .vb .vb-app.is-phone .vb-app-rates { grid-template-columns: 1fr } .vb .vb-app.is-phone .vb-app-main { padding: 24px } /* components/brand/sections/dash/projects.css */ .vb .vb-app-projects { padding: 16px; background: var(--preview-bg); color: var(--preview-text); font-family: var(--font-inter), sans-serif } .vb .vb-app-project-heading { display: flex; justify-content: space-between; align-items: center; margin-bottom: 40px; font-size: 18px; font-weight: 600; line-height: 28px } .vb .vb-app-project-heading small { font-size: 14px; font-weight: 500 } .vb .vb-app-job-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px } .vb .vb-app-job { min-width: 0; overflow: hidden; border-radius: 6px } .vb .vb-app-job-art { position: relative; display: flex; flex-direction: column; justify-content: space-between; aspect-ratio: 5 / 2; padding: 8px; border-radius: 6px; overflow: hidden; isolation: isolate } .vb .vb-app-job-payment, .vb .vb-app-job-tech { position: relative; z-index: 1; display: flex; align-items: center; width: fit-content; background: color-mix(in srgb, var(--preview-surface) 60%, transparent); backdrop-filter: blur(12px); border-radius: 999px } .vb .vb-app-job-payment { gap: 4px; padding: 2px 8px; color: #b91c1c; font-size: 11px; font-weight: 500; line-height: 16px } .vb .dark .vb-app-job-payment { color: #f87171 } .vb .vb-app-job-tech { align-self: center; max-width: 80%; gap: 12px; padding: 4px 12px 4px 4px; color: var(--preview-text) } .vb .vb-app-job-tech > span:nth-child(2) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; line-height: 1.4 } .vb .vb-app-job-tech > svg { flex: none; margin-left: -6px } .vb .vb-app-job-avatar { flex: none; display: grid; place-items: center; width: 32px; height: 32px; background: color-mix(in srgb, var(--preview-muted) 20%, transparent); border-radius: 50%; font-size: 12px } .vb .vb-app-job-content { display: flex; flex-direction: column; gap: 8px; padding: 16px } .vb .vb-app-job-identity { display: flex; flex-direction: column; min-width: 0; font-weight: 600 } .vb .vb-app-job-identity > span { font-size: 14px; line-height: 20px; font-variant-numeric: tabular-nums } .vb .vb-app-job-identity > b { font-size: 16px; line-height: 1.3; font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis } .vb .vb-app-job-status-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 24px } .vb .vb-app-job-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; line-height: 1.4 } .vb .vb-app-job-status i { width: 6px; height: 6px; border-radius: 50%; background: currentColor } .vb .vb-app-job-media { display: flex; flex: none } .vb .vb-app-job-media > span { display: grid; place-items: center; width: 20px; height: 20px; border: 1px solid var(--preview-bg); border-radius: 50%; background: var(--preview-active); color: var(--preview-muted) } .vb .vb-app-job-media > span + span { margin-left: -6px } .vb .vb-app-job-address { color: var(--preview-muted) } .vb .vb-app-job-address p { margin: 0; font-size: 13px; line-height: 1.4; overflow: hidden; white-space: nowrap; text-overflow: ellipsis } .vb .vb-app-job-address p + p { margin-top: 2px; font-size: 12px; opacity: 0.8 } .vb .vb-app-job-area { display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; padding: 2px 8px; border: 1px dashed var(--preview-border); border-radius: 999px; font-size: 10px; line-height: 14px; opacity: 0.7 } .vb .vb-app-job-schedule { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-bottom: 8px; color: var(--preview-muted); font-size: 12px; line-height: 16px } .vb .vb-app-job-schedule > span { display: inline-flex; align-items: center; gap: 4px } .vb .vb-app-job-notes { margin: 8px 0 0; padding-top: 8px; border-top: 1px solid var(--preview-border); color: var(--preview-muted); font-size: 13px; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden } .vb .vb-app-projects.is-phone .vb-app-job-grid { grid-template-columns: 1fr; gap: 24px } .vb .vb-app-projects.is-phone .vb-app-project-heading { margin-bottom: 24px } /* components/brand/sections/motion.css */ .vb .vb-motion { max-width: 720px; margin: 36px auto 0 } .vb .vb-mo-panel { min-height: 600px; display: flex; align-items: center; justify-content: center } .vb .vb-mo-win { max-width: 482px } .vb .vb-mo-screen { position: relative; aspect-ratio: 480 / 256; overflow: hidden; background: var(--vb-surface) } .vb .vb-mo-fill { position: absolute; inset: 0 } .vb .vb-mo-win .vb-cap b { font-weight: 600; color: var(--vb-ink) } .vb .vb-mo-table code { font-family: var(--vb-font-mono); font-size: 12px; color: var(--vb-ink) } ``` ## Appendix D — phone rules (@media (max-width: 760px)) for the kit above Verbatim from the same files, in the same order: every `@media (max-width: 760px)` block, wrapped in its own at-rule. Paste as-is. /brand collapses at 760px; its tablet steps — `@media (min-width: 761px) and (max-width: 1024px)`, and `@media (max-width: 1024px)` in `components.css` — are in Appendix C. ```css /* app/brand/brand.css */ @media (max-width: 760px) { .vb-hero { padding: 112px 20px 64px } .vb-h1 { font-size: 40px } .vb-h1 br { display: none } .vb-lede0 { font-size: 17px } .vb-sec { padding: 64px 20px 72px } .vb-h2 { font-size: 32px } .vb-lede { font-size: 15px } .vb-btns, .vb-btns.is-left { flex-direction: column; align-items: stretch } .vb-btns .vb-btn-dark, .vb-btns .vb-btn-soft { justify-content: center; text-align: center } .vb-grid3, .vb-grid4, .vb-comps { grid-template-columns: 1fr } .vb-rules { grid-template-columns: 1fr } .vb-swatches { grid-template-columns: repeat(2, minmax(0, 1fr)) } .vb-tile-art { height: 140px } .vb-tile-cap { flex-direction: column; align-items: flex-start; gap: 10px } .vb-trow, .vb-table.vb-anat .vb-trow, .vb-table.is-motion .vb-trow { grid-template-columns: 1fr; gap: 2px; padding: 12px 16px } .vb-table.is-motion .vb-trow > :last-child { text-align: left } .vb-comp { padding: 22px 20px 24px } .vb-winurl { font-size: 10px; max-width: 70%; overflow: hidden; text-overflow: ellipsis } .vb-chips { gap: 6px } .vb-chip { padding: 6px 12px; font-size: 12.5px } .vb-panel.is-wash { padding: 36px 16px } .vb-ai { padding: 32px 22px 34px; border-radius: 20px } } /* components/brand/sections/colour.css */ @media (max-width: 760px) { .vb .vb-status-pairs { grid-template-columns: 1fr } .vb .vb-status-pair p { min-height: 0 } } /* components/brand/sections/type.css */ @media (max-width: 760px) { .vb .vb-tgrid, .vb .vb-fonts { grid-template-columns: 1fr } .vb .vb-tcard { padding: 22px 20px 24px } .vb .vb-t-h1 { font-size: 36px } .vb .vb-t-h2 { font-size: 27px } } /* components/brand/sections/gradient.css */ @media (max-width: 760px) { .vb .vb-zgrid { grid-template-columns: 1fr } } /* components/brand/sections/components.css */ @media (max-width: 760px) { .vb .vb-bcard { max-width: none; margin-top: 22px } .vb .vb-pc { padding: 22px 20px 22px } } /* components/brand/sections/dash.css */ @media (max-width: 760px) { .vb .vb-term-screen { padding: 14px; font-size: 11px } .vb .vb-term-out { padding: 9px 10px; font-size: 10.5px } .vb .vb-term-tr { grid-template-columns: var(--vb-term-cols-phone); gap: 8px } .vb .vb-term-tr .is-wide { display: none } .vb .vb-term-field { grid-template-columns: 60px minmax(0, 1fr); gap: 8px } .vb .vb-termtitle { font-size: 10.5px } .vb .vb-tabs { overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain } .vb .vb-tabs::-webkit-scrollbar { display: none } .vb .vb-tabs button { padding: 6px 10px } .vb .vb-tabs button:focus-visible { outline-offset: -2px } } @media (max-width: 760px) { .vb .vb-dashboard-preview { max-width: 420px; margin-top: 28px } } /* components/brand/sections/motion.css */ @media (max-width: 760px) { .vb .vb-mo-panel { min-height: 440px } } ``` The marketing site is mobile first on Tailwind's stock breakpoints (`sm` 640 · `md` 768 · `lg` 1024): the bare utility is the phone value. The pairs that carry its layout: ``` px-4 sm:px-6 lg:px-8 /* section gutter 16 → 24 → 32px (components/landing/Training.tsx and the other home sections) */ py-16 sm:py-24 /* section rhythm 64 → 96px */ text-[2rem] sm:text-[2.5rem] /* section headline 32 → 40px */ grid-cols-1 sm:grid-cols-3 /* the bento card grid is one column on a phone (components/landing/BentoGrid.tsx) */ w-full sm:w-auto /* the hero CTAs fill the phone width (components/landing/HeroCTA.tsx) */ ``` Behaviours that change rather than resize: the splash never runs below 640px (`NOT_PHONE = "(min-width: 640px)"`, `lib/use-media-query.ts`); the footer only pins when the screen is at least 640px wide AND 700px tall (`[@media(min-width:640px)_and_(min-height:700px)]:fixed`, `components/ui/motion-footer.tsx`), because a landscape phone would push its CTA out of reach. The scroll pin does not change with width — "ONLY reduced motion loses the pin. Screen width does not — …" (`components/landing/hero-story/HeroStage.tsx`). The bar every page is held to: it works at 390px — no horizontal page scroll, and any wide thing (a table, a code block) scrolls inside its own container. Small repeated items may stay in two or more columns on a phone (the tool tiles in `ProblemSection.tsx`; the footer link grid, `components/ui/motion-footer.tsx`).