Section label
Section heading and its content
This document defines the visual system for the rebuilt Voltage Vet site and its internal admin. It carries forward the existing brand’s typefaces and palette, and adopts a new layout language adapted from the Colorlib “Hamlet” template. Section 9 contains the token block to paste into the codebase.
Voltage Vet is a one-person personal electric vehicle repair shop in Brightwood, Washington DC. Blake has completed 250+ repairs across 60+ brands of scooters, e-bikes and unicycles, working out of a home workshop rather than a storefront. The design intent follows from that: utilitarian, confident, workshop-honest. No showroom gloss, no stock-photo optimism. The palette stays near-monochrome — black ink on white paper, gray for structure — with a deep brick red used sparingly enough that it always means something: an action, a link, a section marker. The layout language is editorial and generous, borrowed from Hamlet: full-bleed photography with dark overlays, content panels that offset and overlap those images, small uppercase labels above large headings, and a lot of vertical air between sections. The result should read like a tradesperson’s portfolio, not a marketplace listing.
Sources merged here: the live Squarespace theme (typefaces and the four brand colors, all retained) and the Hamlet template at preview.colorlib.com/#hamlet (structure only — none of its type or color). Page references throughout point at mockups.html, screens 1–7.
Four colors carry over unchanged from the existing site. Everything else is derived from them by fixed rules: the accent shades move only in lightness at hue 0, and the tint is a hand-tuned warm neutral in the same hue family.
| Name / token | Hex | HSL | Role and usage rule | |
|---|---|---|---|---|
| --color-ink | #211F1F | 0, 3.1%, 12.5% | All body and heading text on light surfaces. Background of dark sections (stats row, footer, hero overlay base). This is the site’s black — never use #000. | |
| --color-paper | #FFFFFF | 0, 0%, 100% | Default page background and card fill. Text color on ink and on accent. | |
| --color-panel | #F2F2F2 | 0, 0%, 94.9% | Neutral panel fill: alternating section backgrounds, table row hover, admin canvas. The quiet alternative to the red tint. | |
| --color-line | #DFDFDF | 0, 0%, 87.5% | Borders, hairline rules, table row dividers, input borders, disabled button fill. Too dark to be a large surface fill — use --color-panel for that. | |
| --color-accent | #591A1A | 0, 54.8%, 22.5% | The single brand accent. Primary buttons, inline links, active nav, the uppercase section labels, focus rings, the “new” status chip. Reserve it: if more than roughly 5% of a screen is red, cut something. | |
| --color-accent-hover | #6E2020 | 0, 54.9%, 27.8% | Hover state for accent-filled surfaces and accent text. Derived: accent lightness +5.3, hue and saturation held. | |
| --color-accent-active | #470F0F | 0, 65.1%, 16.9% | Pressed / active state. Also the underline color for a link that is the current page. | |
| --color-tint | #F4E7E7 | 0, 37.1%, 93.1% | The warm panel. This is our replacement for Hamlet’s #ffdfdf, pulled toward the brand’s brick hue and lightened so black text sits on it comfortably. Used for the offset detail card, testimonial blocks, and callout panels. One per screen, at most two. | |
| --color-tint-line | #EBD9D9 | 0, 31.0%, 88.6% | Rules and dividers drawn inside a tint panel, where #DFDFDF reads cold. | |
| --color-ink-muted | #5C5858 | 0, 2.2%, 35.3% | Secondary text on light surfaces: captions, hints, table headers, timestamps. Passes AA at all sizes (7.02:1 on white). | |
| --color-accent-on-dark | #D9A6A6 | 0, 40.2%, 75.1% | Required substitute for the accent on ink backgrounds. Uppercase labels, stat captions, and footer headers inside dark sections use this, not #591A1A. | |
| --color-on-dark-muted | #B5AEAE | 0, 4.5%, 69.6% | Secondary text on ink: footer links at rest, hero subheads, fine print. Warm gray, not blue-gray. |
Two additional hues exist only for form and status feedback. The error red is deliberately brighter and slightly warmer than the brand accent so an inline error never reads as a section label. Never signal state by color alone — errors carry an icon and text, success carries text.
| Token | Hex | HSL | Role | |
|---|---|---|---|---|
| --color-error | #A4231C | 3, 70.8%, 37.6% | Invalid field border, error text and icon. 7.41:1 on paper. | |
| --color-error-surface | #FDECEA | 6, 82.6%, 95.5% | Fill for form-level error banners. Error text on it is 5.72:1. | |
| --color-success | #14654A | 160, 66.9%, 23.7% | Submission confirmations, “ready for pickup” status. 7.02:1 on paper. | |
| --color-success-surface | #E8F3EE | 153, 31.4%, 93.1% | Fill for confirmation banners and the ready chip. 6.18:1. |
Ratios computed against WCAG 2.1. Normal text needs 4.5:1, large text (24px, or 18.66px bold) needs 3:1, and UI borders / focus indicators need 3:1.
| Foreground | Background | Ratio | Verdict |
|---|---|---|---|
| #211F1F ink | #FFFFFF paper | 16.40:1 | AAA — default body pair |
| #591A1A accent | #FFFFFF paper | 13.23:1 | AAA — links and labels on paper |
| #591A1A accent | #F4E7E7 tint | 10.98:1 | AAA — labels inside offset cards |
| #591A1A accent | #DFDFDF line | 9.93:1 | AAA |
| #5C5858 ink muted | #FFFFFF paper | 7.02:1 | AAA — smallest usable gray on paper |
| #FFFFFF paper | #591A1A accent | 13.23:1 | AAA — primary button label |
| #FFFFFF paper | #211F1F ink | 16.40:1 | AAA — dark sections |
| #D9A6A6 accent-on-dark | #211F1F ink | 7.77:1 | AAA — labels in dark sections |
| #B5AEAE muted-on-dark | #211F1F ink | 7.52:1 | AAA — footer links at rest |
| #591A1A accent | #211F1F ink | 1.24:1 | Forbidden — invisible. Use #D9A6A6. |
| #6E2020 accent hover | #211F1F ink | 1.48:1 | Forbidden — same failure. |
| #DFDFDF line | #FFFFFF paper | 1.33:1 | Decoration only. Adequate as a border, never as text. |
| #F4E7E7 tint | #FFFFFF paper | 1.20:1 | Decoration only. A tint panel on paper needs its own edge (shadow or 1px #EBD9D9) to be visible. |
Put accent red on ink, or ink text on accent red. Both fail.
Introduce a fifth hue. If something needs to stand out and red is taken, use weight, size or space.
Assume any text over a photograph sits on the hero overlay (rgba(33,31,31,.55) or heavier) and is white.
Two families, carried over from the existing site, with a hard division of labor.
| Family | Weights | Fallback stack | Scope |
|---|---|---|---|
| Archivo Black | 400 only | 'Arial Black', 'Arial Bold', Gadget, sans-serif | Display only: page titles, section headings, card headings, stat numbers, price figures. Never body copy, never a paragraph, never more than about twelve words. |
| Space Grotesk | 300–700 variable | ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif | Everything else: body, lead paragraphs, uppercase labels, buttons, form fields and labels, tables, captions, navigation, the entire admin. |
Archivo Black ships one weight and no true italic. Never apply font-weight:bold or font-style:italic to it — the browser will synthesize both and the result smears. Arial Black is wider than Archivo Black by roughly 4%, so headings may gain a line during the font swap; keep display=swap and reserve headline space with min-height on the hero.
Sentence case throughout. Never all-caps a display heading; the face is already loud and Archivo Black in caps at large sizes loses word shape. Tight leading, negative tracking, and never let a display line exceed about 28 characters before it wraps.
Display / 68px
Same-week repairs
H1 / 52px
What things cost
H2 / 40px
The repairs I do every week
H3 / 24px
Tires and flats
H4 / 18px
Solid tire swap
Stat / 56px
250+
| Role | Desktop | Mobile | Line height | Tracking | Where |
|---|---|---|---|---|---|
| Display (hero) | 68px | 38px | 1.02 | -0.02em | Home hero only |
| H1 (page title) | 52px | 34px | 1.05 | -0.02em | Services, Brands, Request |
| H2 (section) | 40px | 28px | 1.08 | -0.015em | Every section heading |
| H3 (card / sub) | 24px | 20px | 1.15 | -0.01em | Service cards, brand blocks |
| H4 (minor) | 18px | 17px | 1.25 | 0 | Detail-card columns, footer brand line |
| Stat number | 56px | 40px | 1.00 | -0.02em | Counter row |
| Price figure | 36px / 26px | 28px / 22px | 1.00 | -0.02em | Pricing table, service cards |
Services & pricing
Label / 13px · 500 · 0.12em · uppercase · accent
Most repairs come back to you inside a week. Bring me the scooter, tell me what it’s doing, and you’ll get a quote before anything gets opened up.
Lead / 20px · 400 · 1.55
A flat on a solid tire is a different job from a flat on an air tire, and the difference matters more than most shops explain. Air tires ride better and puncture more. Solid tires never puncture and beat up your wrists on brick. I quote both when the choice is real, with the trade-off written out, because you are the one who has to ride it home over Georgia Avenue.
Body / 17px · 400 · 1.65 · max 66ch
Drop-off is by appointment at a residential address in Brightwood, shared after you accept a quote.
Small / 14px · 400 · 1.5 · ink muted
Photograph: rear hub teardown, Apollo City Pro, March 2026.
Caption / 13px · 400 · 1.45 · ink muted
| Role | Size | Weight | Line height | Tracking | Notes |
|---|---|---|---|---|---|
| Lead paragraph | 20px | 400 | 1.55 | 0 | One per section maximum, directly under an H2. |
| Body | 17px (16px mobile) | 400 | 1.65 | 0 | Measure capped at 66ch (~640px). |
| Small | 14px | 400 | 1.50 | 0 | Secondary detail, ink muted. |
| Label | 13px | 500 | 1.20 | 0.12em | Uppercase, accent red. Always immediately above an H1/H2, 12px gap. |
| Caption / hint | 13px | 400 | 1.45 | 0 | Ink muted. Photo credits, field hints. |
| Button | 15px (13.5px small) | 500 | 1.00 | 0.02em | Sentence case, never uppercase. |
| Field label | 13px | 600 | 1.30 | 0.01em | Sentence case, above the input. |
| Input text | 16px | 400 | 1.40 | 0 | 16px minimum — anything smaller triggers zoom on iOS. |
| Table header | 11.5px | 500 | 1.20 | 0.10em | Uppercase, ink muted. |
| Table cell | 15px | 400 | 1.45 | 0 | font-variant-numeric: tabular-nums on every numeric column. |
| Chip | 12px | 500 | 1.00 | 0.02em | Sentence case. |
Pair every H1 and H2 with a label above it. That pairing is the signature of the layout.
Set a paragraph in Archivo Black, or a heading in Space Grotesk on a public page. The split is the identity.
Seven patterns, adapted from Hamlet. They compose the entire public site. The mockettes below are rendered at reduced type scale to fit this page; production sizes are in section 3.
Full-bleed photograph, ink overlay at 55%, content anchored to the bottom-left of a 1140px container. Label, then display headline in white, then one sentence, then two actions. Height is 78vh on desktop with a 560px floor, 62vh on mobile.
Brightwood, Washington DC
Same-week scooter repair
Two hundred and fifty repairs, sixty-plus brands, one bench. Tell me what it’s doing and you’ll have a quote before anything comes apart.
Used on: Home (screen 1). A shorter variant — 320px tall, H1 instead of display, no photograph, panel background — heads Services & Pricing (2), Brands (3) and Request a Repair (4).
Hamlet’s signature move. A tint panel pulled up over the bottom edge of the hero image, inset from the container edges, floating on a very long soft shadow. It holds the three facts a visitor needs immediately. This appears exactly once per site, on the home page.
Turnaround
Most jobs back within a week. Rush work when parts are on the shelf.
Quote first
Nothing gets opened up before you’ve seen a price and said yes.
Drop-off
By appointment in Brightwood. Address shared once the quote is accepted.
Used on: Home (screen 1), directly under the hero. Specification: background --color-tint, radius 5px, shadow 73px 73px 78px -70px rgba(33,31,31,.24), overlap -64px desktop / -32px mobile, horizontal inset 44px from the container, padding 40px 48px at production scale. On mobile the three columns stack and the shadow is reduced to --shadow-soft.
The workhorse. Label, H2, and a 5/7 split: heading and a short stance on the left, the substance on the right. Vertical padding 96px desktop / 56px mobile. Alternate between paper and panel backgrounds down the page so sections separate without rules.
How it works
Four steps, no showroom
Send a description and a photo. I’ll ask a couple of questions to work out whether it’s something I can actually fix — a fair number of requests aren’t. If it is, you get a written quote, and only then the drop-off address.
Tire jobs get two quotes: solid and air, with the trade-off spelled out, so the choice is yours rather than mine.
Used on: Home (screen 1) for “Four steps, no showroom” and “Parts sourced, quirks memorized”; Services & Pricing (2) for the solid-vs-air explainer; Brands (3) for the intro.
Three across on desktop, two at tablet, one on mobile, 24px gaps. Photograph on top at 4:3, then H3, then two lines of copy, then the price range pinned to the bottom of the card above a hairline. Cards are equal height; the price rule aligns across the row.
Tires & flats
Tube, solid or air swap on most scooter and EUC sizes. Quoted both ways.
Throttles & displays
Dead screens, ghost throttles, connector corrosion. Bench-tested before return.
Brake service
Pads, rotors, cable and hydraulic bleeds. Drum brakes on request.
Used on: Home (screen 1) “The repairs I do every week”; Services & Pricing (2) as the full catalogue; Brands (3) for the four featured brand blocks, where the price range is replaced by a text link.
Full-bleed ink band, four figures, hairline dividers at 14% white. Numbers in Archivo Black, captions in the accent-on-dark red. Count-up animation is optional and must respect prefers-reduced-motion; the final value is always in the markup so it renders without JavaScript.
Used on: Home (screen 1), between the services grid and the testimonials. Optionally repeated at the foot of Brands (3).
Tint panel, an oversized quotation mark in accent at 50% opacity, the quote in Space Grotesk at 19–22px (not Archivo Black — a customer’s words are not a headline), attribution and the machine serviced separated by a tint rule. Two or three across, or a single centered block at 720px.
Told me straight that the controller was gone and it wasn’t worth replacing on that model. Charged me nothing for the diagnosis. That’s the whole reason I came back with the next one.
Two quotes, air and solid, with an actual explanation of which one made sense for my commute. Nobody else bothered to ask where I ride.
Used on: Home (screen 1), “What people say afterward”. A single block also sits above the form on Request a Repair (4).
Ink background, four columns: brand statement, two link columns, and contact. Column headers use the label style in accent-on-dark. Links rest at muted-on-dark and go pure white on hover. A hairline at 14% white separates the fine-print row.
Personal electric vehicle repair in Brightwood, Washington DC. One bench, one person, no showroom.
Get in touch
Used on: every public page (screens 1–4). The admin (screens 5–7) has no footer.
Three levels only. Radius 5px, 1.5px border on every variant so the outline and filled buttons share a bounding box. Padding 15px/26px at default size, 10px/16px small. Transitions at 180ms. The states below are pinned open so they can be read without hovering; live hover and keyboard focus also work.
| Variant | Rest | Hover | Active | Focus | Disabled |
|---|---|---|---|---|---|
| Primary | Request a repair | Request a repair | Request a repair | Request a repair | Request a repair |
| Secondary | See pricing | See pricing | See pricing | See pricing | See pricing |
| Quiet link | Read the explainer | Read the explainer | Read the explainer | Read the explainer | Read the explainer |
On ink, primary inverts to a white fill with ink text; the accent red is unusable there (1.24:1). Focus ring switches to --focus-ring-dark.
| State | Primary | Secondary | Quiet |
|---|---|---|---|
| Rest | bg #591A1A, text white | 1.5px #211F1F, text ink, transparent | text #591A1A, 1px underline at 5px offset |
| Hover | bg #6E2020 | bg #211F1F, text white | text #6E2020, underline 2px |
| Active | bg #470F0F | bg #470F0F, border same | text #470F0F |
| Focus visible | box-shadow: 0 0 0 3px rgba(89,26,26,.30) and no outline suppression on keyboard nav. Never remove focus styling. | ||
| Disabled | bg #DFDFDF, text #8B8585, cursor:not-allowed, aria-disabled rather than the disabled attribute where the reason needs explaining. | ||
Put two primary buttons in the same view. There is one action per screen that matters.
The repair-request form is the site’s only real conversion surface, so it gets the most careful treatment. Single column, labels above inputs, hints below labels, generous 18px gaps between fields, no placeholder-as-label. Inputs are 16px to prevent iOS zoom on focus.
Used on: Request a Repair (screen 4), and the admin quote and payment forms (screens 6–7) at admin density.
| State | Border | Ring / fill | Notes |
|---|---|---|---|
| Rest | 1px #DFDFDF | white fill | Radius 5px, padding 12px 14px. |
| Hover | 1px #BFB9B9 | — | Signals interactivity without shouting. |
| Focus | 1px #591A1A | 0 0 0 3px rgba(89,26,26,.18) | Ring is 3:1 against white and against the field fill. |
| Error | 1px #A4231C | 0 0 0 3px rgba(164,35,28,.14) | Label turns #A4231C; message carries an icon and is linked by aria-describedby. |
| Disabled | 1px #DFDFDF | #F2F2F2 fill | Text #8B8585. |
Pill shape, 12px, sentence case. The job pipeline reads left to right; only “New” gets a solid accent fill, because only new requests demand action. Every chip is text-first — the color is reinforcement, not the signal.
Used on: Request Queue (screen 5) and Job Detail (screen 6).
No zebra striping and no vertical rules. A 1.5px ink rule under the header, 1px line rules between rows, panel fill on row hover. Numeric columns are right-aligned and tabular.
| Service | Notes | Typical range |
|---|---|---|
| Solid tire swap, front | Includes tube removal and rim clean | $45 – $75 |
| Air tire swap, rear | Hub motor removal on most models | $70 – $110 |
| Brake service | Pads and cable; hydraulic bleed +$25 | $40 – $85 |
| Controller replacement | Part cost quoted separately | $90 – $160 |
| Diagnosis only | Waived if you go ahead with the repair | $0 – $35 |
Used on: Services & Pricing (screen 2); the same table at admin density carries the queue (5) and weekly books (7).
Prices are ranges, never single figures, and the range is the honest part of the brand. Archivo Black for the figure with tabular numerals, an en dash with hair spaces around it, and a small uppercase qualifier underneath so nobody mistakes a range for a quote.
Used on: Services & Pricing (screen 2), service cards on Home (1). The qualifier line is mandatory; a bare number reads as a fixed price and invites disputes.
Logos sit grayscale at 50% opacity so the strip reads as texture rather than a set of competing brands, and come to full color on hover or focus. Equal-height cells divided by hairlines, five across desktop, two on mobile.
Used on: Home (screen 1) under the services grid; Brands (screen 3) above the “60+ other brands serviced” list. Manufacturer marks are third-party trademarks — reproduce them unmodified apart from the grayscale filter, and keep the affiliate disclosure adjacent where a link is monetized.
Photography is the only source of color and texture in an otherwise monochrome system, which makes it load-bearing. Everything should be shot on the actual bench: parts laid out, hands in the work, a scooter mid-teardown, the tidy corner of the workshop. Grease is fine. A person is better than an object.
Plain. Cards and galleries. Radius 5px, no shadow, no border.
On the bench
Rear hub teardown
Overlaid. Any time text sits on the image: rgba(33,31,31,.55).
Light subject. Deepen the overlay to .65 before adding text, or move the text out of the image.
Shoot real jobs. Blake’s own workshop, his own hands, actual customer machines with permission.
Use 5px radius on every image, everywhere, with no exceptions. It is the one shape rule in the system.
Apply the ink overlay whenever text or a button sits over a photograph, and verify white text clears 4.5:1 against the lightest region of the image, not the average.
Crop to 4:3 for cards, 3:2 for editorial blocks, and 16:9 or wider for the hero. Serve AVIF/WebP with a JPEG fallback and always set explicit dimensions.
Use stock photography of models on scooters, glossy studio product shots, lens flare, or anything with a color-graded teal-and-orange look.
Drop-shadow a photograph. The only long shadow in the system belongs to the offset detail card.
Desaturate photographs. Grayscale is reserved for the brand logo strip so that treatment stays meaningful.
Put a photograph behind body copy. Overlays are for headlines and short lines only.
Base unit is 8px. Every margin, padding and gap is a multiple of it, with 4px available for optical adjustment inside components. Nothing else.
| Measure | Desktop | Mobile | Notes |
|---|---|---|---|
| Max content width | 1140px | 100% | Matches Hamlet’s Bootstrap container. Full-bleed backgrounds extend past it; content never does. |
| Gutter (page edge) | 24px | 20px | Minimum breathing room at the viewport edge. |
| Section padding, vertical | 96px | 56px | The primary rhythm. Two adjacent sections give 192px of separation, which is intentional. |
| Feature section padding | 128px | 72px | Hero-adjacent and closing call-to-action sections only. |
| Grid gap (main columns) | 32px | 24px | Hamlet’s 30px, rounded onto the 8px grid. |
| Card gap | 24px | 16px | Service grids, brand blocks, testimonials. |
| Card padding | 24px | 20px | Offset detail card is the exception at 40px / 48px. |
| Label → heading | 12px | 10px | Fixed. The label should feel attached to the heading. |
| Heading → body | 20px | 16px | |
| Body paragraph gap | 16px | 14px | |
| Body measure | max 66ch | — | About 640px at 17px. Hard cap; wide text is the fastest way to look unedited. |
| Corner radius | 5px | 5px | Universal, from Hamlet. Chips and avatars use a full pill instead. |
Section label
Section heading and its content
Next section on panel
Alternating background separates it
Bands shown at half scale. Sections separate by background change and space, never by a horizontal rule across the full width.
The internal admin — request queue, job detail, weekly books — runs on the same tokens with three deliberate departures. It is a tool Blake uses weekly on a laptop, not a page anyone is being persuaded by.
Request queue
27 open · 4 new since yesterday · oldest untouched request is 2 days old
| Customer | Machine | Status | Age | Quote |
|---|---|---|---|---|
| Lena Fischer | EMOVE Cruiser | In repair | 3d | $140 |
| Marcus Terrell | Ninebot Max G30 | New | 2h | — |
| Priya Raman | Apollo City Pro | Quoted | 1d | $95 |
| Dee Okafor | KingSong S18 | Ready for pickup | 6d | $210 |
Used on: Request Queue (screen 5), Job Detail (6), Weekly books (7).
Drop this into the global stylesheet. Every value in this document resolves to one of these tokens. The @import should become next/font/google with display:'swap' in the Next.js build; the fallback stacks stay as written so the site remains readable if the font request fails.
@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Space+Grotesk:[email protected]&display=swap');
:root {
/* ---- Color: carried over from the Squarespace theme ---- */
--color-ink: #211F1F; /* hsl(0 3.1% 12.5%) text, dark sections */
--color-paper: #FFFFFF; /* page background, card fill */
--color-line: #DFDFDF; /* hsl(0 0% 87.5%) borders, rules */
--color-accent: #591A1A; /* hsl(0 54.8% 22.5%) the one accent */
/* ---- Color: derived ---- */
--color-ink-muted: #5C5858; /* secondary text on light 7.02:1 */
--color-panel: #F2F2F2; /* neutral surface */
--color-tint: #F4E7E7; /* warm panel (offset card, testimonials) */
--color-tint-line: #EBD9D9; /* rules inside a tint panel */
--color-accent-hover: #6E2020;
--color-accent-active: #470F0F;
--color-accent-on-dark: #D9A6A6; /* accent substitute on ink 7.77:1 */
--color-on-dark: #FFFFFF;
--color-on-dark-muted: #B5AEAE; /* secondary text on ink 7.52:1 */
/* ---- Color: form semantics ---- */
--color-success: #14654A;
--color-success-surface: #E8F3EE;
--color-error: #A4231C;
--color-error-surface: #FDECEA;
/* ---- Overlays ---- */
--overlay-hero: rgba(33,31,31,.55); /* text over photography */
--overlay-image: rgba(33,31,31,.28); /* mood only, no text on top */
/* ---- Type ---- */
--font-display: 'Archivo Black', 'Arial Black', 'Arial Bold', Gadget, sans-serif;
--font-sans: 'Space Grotesk', ui-sans-serif, system-ui, -apple-system,
'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
--fs-display: clamp(2.375rem, 1.05rem + 4.2vw, 4.25rem); /* 38 -> 68 */
--fs-h1: clamp(2.125rem, 1.20rem + 2.9vw, 3.25rem); /* 34 -> 52 */
--fs-h2: clamp(1.75rem, 1.22rem + 1.7vw, 2.5rem); /* 28 -> 40 */
--fs-h3: clamp(1.25rem, 1.11rem + 0.4vw, 1.5rem); /* 20 -> 24 */
--fs-h4: 1.125rem; /* 18 */
--fs-stat: clamp(2.5rem, 1.79rem + 2.2vw, 3.5rem); /* 40 -> 56 */
--fs-lead: 1.25rem; /* 20 */
--fs-body: 1.0625rem; /* 17 */
--fs-small: 0.875rem; /* 14 */
--fs-label: 0.8125rem; /* 13 */
--fs-input: 1rem; /* 16, iOS */
--lh-display: 1.02; --lh-heading: 1.08; --lh-tight: 1.25;
--lh-body: 1.65; --lh-snug: 1.45;
--ls-display: -0.02em; --ls-label: 0.12em;
--weight-label: 500; --weight-field: 600;
/* ---- Space: 8px base ---- */
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
--sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px; --sp-10: 128px;
--container: 1140px;
--gutter: 24px;
--section-y: 96px;
--section-y-lg:128px;
--grid-gap: 32px;
--card-gap: 24px;
--card-pad: 24px;
/* ---- Surface ---- */
--radius: 5px;
--radius-pill: 999px;
--shadow-offset: 73px 73px 78px -70px rgba(33,31,31,.24); /* offset card only */
--shadow-soft: 0 2px 4px rgba(33,31,31,.05),
0 12px 32px -12px rgba(33,31,31,.16);
--focus-ring: 0 0 0 3px rgba(89,26,26,.30);
--focus-ring-soft: 0 0 0 3px rgba(89,26,26,.18); /* inputs */
--focus-ring-dark: 0 0 0 3px rgba(255,255,255,.55); /* on ink */
--dur: 180ms;
--ease: cubic-bezier(.2,.7,.3,1);
}
@media (max-width: 768px) {
:root {
--gutter: 20px; --section-y: 56px; --section-y-lg: 72px;
--grid-gap: 24px; --card-gap: 16px; --card-pad: 20px;
--fs-body: 1rem;
}
}
@media (prefers-reduced-motion: reduce) {
:root { --dur: 0ms; }
}
/* Admin: same tokens, denser. Apply to the admin layout root. */
[data-density="admin"] {
--section-y: 24px; --card-pad: 16px; --card-gap: 16px; --grid-gap: 24px;
--fs-body: 0.9375rem; /* 15 */
--fs-small: 0.8125rem; /* 13 */
--radius: 4px;
--row-height: 40px;
--control-height: 34px;
}
Deliberately absent: a dark mode, a second accent, an elevation scale beyond the two shadows, and any token for a color not listed in section 2. Adding one is a design decision, not an implementation detail.