diff --git a/site/css/components.css b/site/css/components.css index e9b0120..0aed900 100644 --- a/site/css/components.css +++ b/site/css/components.css @@ -3940,6 +3940,9 @@ html[data-theme="halo"] aside { grid-template-rows: auto auto; min-width: 0; color: inherit; + /* When the card is rendered as , strip the default link underline so + the card surface reads as a card, not as linked text. */ + text-decoration: none; background: var(--color-paper); border: var(--rule-card, 1px) solid var(--color-rule); border-radius: var(--radius-card, 0); diff --git a/site/examples/bananastudio/.hallmark/log.json b/site/examples/bananastudio/.hallmark/log.json new file mode 100644 index 0000000..71cd62b --- /dev/null +++ b/site/examples/bananastudio/.hallmark/log.json @@ -0,0 +1,27 @@ +[ + { + "date": "2026-05-04", + "macrostructure": "Marquee Hero", + "theme": "Bloom", + "enrichment": "Tier-A animated aperture + breathing portraits + marquee strip", + "brief": "BananaStudio — redesign to dark mode, bold, smooth interactions, animated illustrations", + "knobs": { + "H7_marquee_hero": "title=display-italic-mix, alignment=left-bias, counterweight=animated-aperture", + "F2_gallery": "tiles=7, layout=feature+6-portraits, breathing-loop=on", + "F4_steps": "count=3, container=workbench-cards, divider=hairline-gradient" + }, + "theme_axes": "dark / italic-serif / saturated-amber-bloom" + }, + { + "date": "2026-05-04", + "macrostructure": "Bento Grid", + "theme": "Quiet", + "enrichment": "Tier-A pure-CSS portrait tiles", + "brief": "BananaStudio — SaaS landing page, AI-generated professional headshots", + "knobs": { + "F1_bento": "tiles=9, spans=irregular, accent=corner-only", + "H2_split_hero": "ratio=7/5, right=lede+CTA, divider=hairline-rule" + }, + "theme_axes": "light / geometric-sans / warm-amber" + } +] diff --git a/site/examples/bananastudio/index.html b/site/examples/bananastudio/index.html new file mode 100644 index 0000000..fae7032 --- /dev/null +++ b/site/examples/bananastudio/index.html @@ -0,0 +1,475 @@ + + + + + + BananaStudio — studio-grade headshots, in thirty minutes. + + + + + + + + + + + Skip to content + + + +
+ + +
+
+

Open beta · v1.0

+

+ Studio-grade
+ headshots,
+ in thirty minutes. +

+

+ Upload twelve selfies. Receive one hundred retouched portraits — your wardrobe, your lighting, your direction. No reschedules, no studio lights, no awkward small-talk with a photographer who'd rather be shooting weddings. +

+ +
+ 4K PNG output + 30-min turnaround + Commercial license +
+
+ + +
+
+ +
+ +
+
+ +
+ Selfie + Studio + +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+

Headshots delivered to founders, recruiters, sales teams, and a surprising number of family lawyers.

+

“100,000 portraits across 11,400 paying customers since beta. Average turnaround: 27 minutes. Re-shoot rate: 4.1%.”

+
+
+
+ + +
+
+
+
+

02 · The process

+

Three steps. Thirty minutes. No setup.

+
+

From the moment you upload to the moment your first edit lands in your inbox.

+
+ +
    +
  1. +

    01

    +

    Upload

    +

    Send twelve selfies.

    +

    Front, side, smile, neutral. Phone-quality is fine — the model is trained on phone cameras, not Hasselblads.

    +
  2. +
  3. +

    02

    +

    Direct

    +

    Pick a look, or write a brief.

    +

    Choose from twelve wardrobes and four lighting setups. Or write a sentence — “warm office, navy knit, side-light from the left” — and we'll match it.

    +
  4. +
  5. +

    03

    +

    Receive

    +

    One hundred edits, in thirty minutes.

    +

    4K PNG, retouched, with commercial license. Delivered to your inbox and your dashboard. Re-shoot any look free.

    +
  6. +
+
+
+ + +
+
+
+

03 · Pricing

+

Pay by output, not by seat.

+

Re-shoots are always free. Three plans, no annual lock-in, no surprise upcharges for "premium" presets.

+
+ +
+
+

Studio

+

$39one-off

+

For a single round of headshots, on demand.

+
    +
  • 100 retouched headshots
  • +
  • 4 wardrobes · 4 lighting presets
  • +
  • 30-minute turnaround
  • +
  • 4K PNG · 2048 social crops
  • +
  • Commercial license
  • +
+ Start a session +
+ +
+

Studio Plus

+

$79one-off

+

For founders and execs who reshoot every quarter.

+
    +
  • 300 retouched headshots
  • +
  • Unlimited wardrobes & lighting
  • +
  • Same-day turnaround, 12 h SLA
  • +
  • Custom briefs & reference images
  • +
  • Brand kit · 1 round of revisions
  • +
  • Commercial license
  • +
+ Start a session +
+ +
+

Editorial

+

$199per shoot

+

For press, book covers, and brand work that earns the front page.

+
    +
  • 500 retouched headshots
  • +
  • 30-minute creative direction call
  • +
  • Brand kit upload · LUT match
  • +
  • 3 rounds of revision
  • +
  • Print-ready 6K TIFF
  • +
  • Extended commercial license
  • +
+ Talk to direction +
+
+
+
+ + +
+
+
+

04 · FAQ

+

Questions, answered honestly.

+

Five we get most often. If yours isn't here, the answer is probably yes — write us.

+
+
+
+ How long does a session actually take? +
+

Thirty minutes from the moment your last selfie uploads. Studio Plus same-day SLA is 12 hours; we usually clear it in three.

+
+
+
+ What do my headshots actually look like? +
+

Studio-grade. Skin texture preserved, freckles intact, real light fall-off. No plastic finish, no over-smoothed faces, no AI fingers. The gallery above is built from real customer outputs — different ethnicity, age, and frame in every wardrobe sample.

+
+
+
+ Can I direct the shot? +
+

Yes. Pick wardrobe, lighting, backdrop, and framing in the dashboard. On Studio Plus and Editorial, drop a reference image — we mirror its colour temperature, depth of field, and posture inside three retries.

+
+
+
+ Can I use these for LinkedIn, work, or press? +
+

Yes. Every plan ships with a commercial license. Editorial includes the extended license you need for book jackets, magazine covers, or campaign use above 1 M impressions.

+
+
+
+ Do you keep my photos? +
+

Selfies and outputs are deleted after thirty days. You can wipe them in one click before that — your dashboard has a "delete everything" button that does exactly what it says.

+
+
+
+
+
+ + +
+
+
+

Your headshot, in thirty minutes.

+

Twelve selfies in. One hundred edits out. Re-shoot any look free, forever.

+
+ +
+
+ +
+ + + + + + diff --git a/site/examples/bananastudio/styles.css b/site/examples/bananastudio/styles.css new file mode 100644 index 0000000..d8a857b --- /dev/null +++ b/site/examples/bananastudio/styles.css @@ -0,0 +1,1584 @@ +/* Hallmark · genre: atmospheric · macrostructure: Marquee Hero + * H7 Marquee Hero knobs: title=display-italic-mix · alignment=left-bias · counterweight=animated-aperture + * theme: Bloom · accent: amber-bloom ~75° (radial bloom + ember-warm) + * enrichment: Tier-A pure-CSS aperture (animated) + breathing portrait silhouettes + scrolling marquee strip + * tone: cinematic + bold + precise · audience: founders/recruiters/sales/jobseekers · studied: no + */ + +@import url("./tokens.css"); + +/* ─── Reset · base ─────────────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; } +html { overflow-x: clip; scroll-behavior: smooth; } +body { overflow-x: clip; } +* { margin: 0; padding: 0; } + +html { + font-size: 16px; + line-height: 1.55; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +body { + font-family: var(--font-body); + background: var(--color-paper); + color: var(--color-ink); + font-feature-settings: "ss01", "cv11"; + font-weight: 400; + position: relative; + isolation: isolate; +} + +/* Page-wide ambient bloom — fixed, behind everything */ +body::before { + content: ""; + position: fixed; + inset: -20%; + z-index: -1; + pointer-events: none; + background: + radial-gradient(40% 30% at 80% 0%, var(--color-glow) 0%, transparent 60%), + radial-gradient(45% 35% at 10% 110%, oklch(60% 0.18 30 / 0.25) 0%, transparent 60%), + radial-gradient(60% 50% at 50% 50%, oklch(20% 0.025 285 / 0.6) 0%, transparent 70%); +} + +img, svg { display: block; max-width: 100%; } +a { color: inherit; text-decoration: none; } +button { font: inherit; background: none; border: 0; color: inherit; cursor: pointer; } + +::selection { background: var(--color-accent); color: oklch(15% 0.020 60); } + +/* ─── Page container ──────────────────────────────────────────────── */ +.shell { + width: 100%; + max-width: var(--page-max); + margin-inline: auto; + padding-inline: var(--page-gutter); +} +.section { padding-block: var(--space-3xl); } +.section--tight { padding-block: var(--space-2xl); } + +/* ─── Nav — morphs from full-width bar to floating pill on scroll ─── */ +.nav { + position: sticky; + top: 0; + z-index: var(--z-sticky); + + /* Default state: full-width bar, edge-to-edge, with backdrop blur */ + padding-block: 0; + background: oklch(10% 0.018 285 / 0.62); + border-block-end: var(--rule-hair) solid var(--color-rule); + -webkit-backdrop-filter: saturate(1.4) blur(14px); + backdrop-filter: saturate(1.4) blur(14px); + + transition: + padding-block var(--dur-mid) var(--ease-out), + background-color var(--dur-mid) var(--ease-out), + border-block-end-color var(--dur-mid) var(--ease-out), + -webkit-backdrop-filter var(--dur-mid) var(--ease-out), + backdrop-filter var(--dur-mid) var(--ease-out); +} + +/* Floating state: outer .nav goes transparent AND drops the blur — so + only the pill carries the glass effect, no wide blurred strip across + the viewport. Padding-block keeps total nav height at 60px (no jitter). + The visible top breathing room is delivered via transform on .nav__inner + (see below) — that way it doesn't push content below. */ +.nav.is-floating { + padding-block: var(--space-2xs); + background-color: transparent; + border-block-end-color: transparent; + -webkit-backdrop-filter: blur(0); + backdrop-filter: blur(0); +} + +.nav__inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-md); + padding-block: var(--space-sm); + min-height: 60px; + + /* Default: invisible — the bar visual lives on .nav above */ + background: oklch(10% 0.020 285 / 0); + border: 1px solid transparent; + border-radius: 0; + box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent; + -webkit-backdrop-filter: blur(0); + backdrop-filter: blur(0); + transform: translateY(0); + + transition: + max-width var(--dur-mid) var(--ease-out), + padding var(--dur-mid) var(--ease-out), + min-height var(--dur-mid) var(--ease-out), + border-radius var(--dur-mid) var(--ease-out), + border-color var(--dur-mid) var(--ease-out), + background-color var(--dur-mid) var(--ease-out), + box-shadow var(--dur-mid) var(--ease-out), + -webkit-backdrop-filter var(--dur-mid) var(--ease-out), + backdrop-filter var(--dur-mid) var(--ease-out), + transform var(--dur-mid) var(--ease-out); +} + +.nav.is-floating .nav__inner { + /* Pill — overrides .shell's max-width; .shell still provides margin-inline: auto */ + max-width: 58rem; + min-height: 52px; + padding-block: var(--space-2xs); + padding-inline: var(--space-md) var(--space-2xs); + + border-radius: var(--radius-pill); + border-color: oklch(48% 0.022 285 / 0.45); + background: oklch(8% 0.020 285 / 0.82); + -webkit-backdrop-filter: saturate(1.5) blur(18px); + backdrop-filter: saturate(1.5) blur(18px); + + box-shadow: + 0 18px 50px -18px oklch(0% 0 0 / 0.6), + 0 4px 14px -4px oklch(0% 0 0 / 0.35), + inset 0 0 0 1px oklch(98% 0.005 80 / 0.05), + 0 0 36px -8px var(--color-glow-soft); + + /* Visible breathing room from the viewport top — pure transform, + so it doesn't change layout / shift content below. The pill ends + up sitting ~16px from the top edge while the page underneath + scrolls cleanly past the rest of the (now-transparent) .nav. */ + transform: translateY(12px); +} +.wordmark { + font-family: var(--font-display); + font-size: var(--text-md); + font-weight: 600; + letter-spacing: -0.02em; + font-style: italic; + color: var(--color-ink); + display: inline-flex; + align-items: center; + gap: 0.6ch; + line-height: 1; +} +.wordmark::before { + content: ""; + display: inline-block; + width: 10px; height: 10px; + background: var(--color-accent); + border-radius: 50%; + box-shadow: 0 0 16px 2px var(--color-glow); + animation: pulse-soft var(--dur-breath) var(--ease-in-out) infinite; + transform: translateY(-1px); +} +@keyframes pulse-soft { + 0%, 100% { transform: translateY(-1px) scale(1); opacity: 1; } + 50% { transform: translateY(-1px) scale(0.85); opacity: 0.78; } +} +.nav__links { + display: flex; + align-items: center; + gap: var(--space-lg); + list-style: none; +} +.nav__links a:not(.btn) { + font-family: var(--font-body); + font-size: var(--text-sm); + color: var(--color-ink-2); + line-height: 1; + padding: var(--space-xs) var(--space-2xs); + position: relative; + transition: color var(--dur-short) var(--ease-out); +} +.nav__links > li:not(:last-child) > a::after { + content: ""; + position: absolute; + left: var(--space-2xs); + right: var(--space-2xs); + bottom: calc(var(--space-xs) - 2px); + height: 1px; + background: var(--color-accent); + transform: scaleX(0); + transform-origin: 0 50%; + transition: transform var(--dur-short) var(--ease-out); +} +.nav__links a:not(.btn):hover { color: var(--color-ink); } +.nav__links > li:not(:last-child) > a:hover::after { transform: scaleX(1); } +.nav__links a:not(.btn):focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 4px; + border-radius: 2px; +} + +/* ─── Buttons ─────────────────────────────────────────────────────── */ +.btn { + --btn-bg: var(--color-accent); + --btn-fg: oklch(14% 0.020 60); + display: inline-flex; + align-items: center; + gap: 0.6ch; + min-height: 48px; + padding: 0 var(--space-lg); + border-radius: var(--radius-pill); + background: var(--btn-bg); + color: var(--btn-fg); + font-family: var(--font-body); + font-size: var(--text-sm); + font-weight: 600; + letter-spacing: -0.005em; + line-height: 1; + position: relative; + isolation: isolate; + overflow: hidden; + box-shadow: 0 8px 30px -10px var(--color-glow); + transition: + transform var(--dur-micro) var(--ease-out), + box-shadow var(--dur-short) var(--ease-out), + background-color var(--dur-short) var(--ease-out); + white-space: nowrap; +} +.btn::before { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + background: linear-gradient(105deg, + transparent 0%, transparent 35%, + oklch(98% 0.04 80 / 0.55) 50%, + transparent 65%, transparent 100%); + transform: translateX(-100%); + transition: transform 700ms var(--ease-out); +} +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 14px 40px -8px var(--color-glow); + background: var(--color-accent-2); +} +.btn:hover::before { transform: translateX(100%); } +.btn:active { transform: translateY(0); transition-duration: 60ms; } +.btn:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; +} +.btn:disabled, .btn[aria-disabled="true"] { + opacity: 0.55; + cursor: not-allowed; + pointer-events: none; +} + +.btn--ghost { + --btn-bg: transparent; + --btn-fg: var(--color-ink); + border: var(--rule-hair) solid var(--color-rule-2); + box-shadow: none; +} +.btn--ghost:hover { + background: var(--color-paper-2); + border-color: var(--color-accent); + box-shadow: 0 0 0 1px var(--color-accent); +} +.btn--ghost::before { display: none; } +.btn--sm { + min-height: 40px; + padding: 0 var(--space-md); + font-size: var(--text-xs); + letter-spacing: 0.02em; +} +.btn .arrow { transition: transform var(--dur-short) var(--ease-out); } +.btn:hover .arrow { transform: translateX(3px); } + +/* ─── Hero — Marquee (huge title left, animated aperture right) ──── */ +.hero { + position: relative; + padding-block: clamp(var(--space-2xl), 8vw, var(--space-4xl)) var(--space-3xl); + display: grid; + grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); + gap: var(--space-2xl); + align-items: center; +} +.hero__copy { + display: grid; + gap: var(--space-lg); + position: relative; + z-index: 2; +} +.hero__eyebrow { + font-family: var(--font-outlier); + font-size: var(--text-xs); + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--color-accent); + display: inline-flex; + align-items: center; + gap: 0.6ch; + line-height: 1; +} +.hero__eyebrow::before { + content: ""; + width: 24px; + height: 1px; + background: currentColor; +} +.hero__title { + font-family: var(--font-display); + font-size: clamp(2.25rem, 4.5vw + 0.75rem, 5rem); + font-weight: 500; + letter-spacing: -0.04em; + line-height: 1.0; + text-wrap: balance; + color: var(--color-ink); + max-width: 14ch; +} +.hero__title em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); + background: linear-gradient(180deg, + var(--color-accent-2) 0%, + var(--color-accent) 60%, + var(--color-accent-deep) 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} +.hero__lede { + max-width: 46ch; + font-size: var(--text-md); + color: var(--color-ink-2); + text-wrap: pretty; + line-height: 1.55; +} +.hero__cta { + display: flex; + align-items: center; + gap: var(--space-sm); + margin-top: var(--space-xs); + flex-wrap: wrap; +} +.hero__meta { + font-family: var(--font-outlier); + font-size: var(--text-xs); + letter-spacing: 0.06em; + color: var(--color-muted); + display: flex; + align-items: center; + gap: var(--space-md); + flex-wrap: wrap; + padding-block-start: var(--space-md); + border-block-start: var(--rule-hair) solid var(--color-rule); + margin-top: var(--space-sm); +} +.hero__meta span { display: inline-flex; align-items: center; gap: 0.5ch; } +.hero__meta .dot { + width: 6px; height: 6px; + border-radius: 50%; + background: var(--color-accent); + display: inline-block; +} + +/* ─── Hero illustration — animated aperture + halo ────────────────── */ +.hero__aside { + position: relative; + aspect-ratio: 1; + max-width: 30rem; + justify-self: end; + width: 100%; + display: grid; + place-items: center; +} +.aperture { + position: relative; + width: 78%; + aspect-ratio: 1; + border-radius: 50%; + background: + radial-gradient(circle at 50% 50%, + oklch(20% 0.025 285) 0%, + oklch(12% 0.022 285) 60%, + oklch(10% 0.018 285) 100%); + box-shadow: + inset 0 0 0 1px var(--color-rule-2), + inset 0 0 80px 4px oklch(8% 0.018 285), + 0 0 0 1px var(--color-rule), + 0 30px 80px -20px oklch(0% 0 0 / 0.7); + overflow: hidden; + isolation: isolate; +} +.aperture__halo { + position: absolute; + inset: -22%; + border-radius: 50%; + background: conic-gradient( + from 0deg, + transparent 0deg, + var(--color-glow-soft) 30deg, + transparent 90deg, + var(--color-glow) 200deg, + transparent 260deg, + var(--color-glow-soft) 320deg, + transparent 360deg); + filter: blur(28px); + z-index: -1; + animation: spin var(--dur-aperture) linear infinite; +} +@keyframes spin { + to { transform: rotate(360deg); } +} +.aperture__pupil { + position: absolute; + inset: 28%; + border-radius: 50%; + background: + radial-gradient(circle at 35% 35%, oklch(98% 0.04 80 / 0.55) 0%, transparent 28%), + radial-gradient(circle at 50% 50%, + var(--color-accent) 0%, + var(--color-accent-deep) 50%, + oklch(35% 0.16 40) 90%); + box-shadow: + inset 0 0 30px 4px oklch(45% 0.18 40 / 0.7), + 0 0 60px 6px var(--color-glow); + animation: aperture-breath var(--dur-aperture) var(--ease-in-out) infinite; +} +@keyframes aperture-breath { + 0%, 100% { inset: 28%; } + 50% { inset: 36%; } +} +.aperture__blade { + position: absolute; + inset: 0; + transform-origin: 50% 50%; +} +.aperture__blade::before { + content: ""; + position: absolute; + inset: 8% 8% 50% 8%; + background: + linear-gradient(180deg, + oklch(28% 0.030 285) 0%, + oklch(18% 0.026 285) 60%, + oklch(13% 0.022 285) 100%); + clip-path: polygon(0 0, 100% 0, 60% 100%, 40% 100%); + border-radius: 50% 50% 0 0; + box-shadow: + inset 1px 1px 0 oklch(40% 0.030 285 / 0.6), + inset 0 -10px 20px oklch(8% 0.020 285 / 0.5); +} +.aperture__blade:nth-child(1) { transform: rotate(0deg); } +.aperture__blade:nth-child(2) { transform: rotate(60deg); } +.aperture__blade:nth-child(3) { transform: rotate(120deg); } +.aperture__blade:nth-child(4) { transform: rotate(180deg); } +.aperture__blade:nth-child(5) { transform: rotate(240deg); } +.aperture__blade:nth-child(6) { transform: rotate(300deg); } + +.aperture__ticks { + position: absolute; + inset: -4%; + border-radius: 50%; + border: 1px dashed oklch(38% 0.020 285 / 0.6); + animation: spin-slow calc(var(--dur-aperture) * 4) linear infinite reverse; +} +@keyframes spin-slow { + to { transform: rotate(360deg); } +} +.aperture__label { + position: absolute; + font-family: var(--font-outlier); + font-size: 11px; + letter-spacing: 0.18em; + color: var(--color-muted); + text-transform: uppercase; + z-index: 2; +} +.aperture__label--top { top: -8px; left: 50%; transform: translateX(-50%); } +.aperture__label--right { right: -8px; top: 50%; transform: translateY(-50%) rotate(90deg); transform-origin: 100% 50%; } +.aperture__label--btm { bottom: -8px; left: 50%; transform: translateX(-50%); } +.aperture__label--left { left: -8px; top: 50%; transform: translateY(-50%) rotate(-90deg); transform-origin: 0 50%; } + +/* ─── Hero before/after slider ────────────────────────────────────── */ +.hero__aside:has(.compare) { + aspect-ratio: auto; + max-width: 26rem; + display: grid; + gap: var(--space-md); + align-content: center; +} + +.compare { + position: relative; + width: 100%; + aspect-ratio: 4 / 5; + border-radius: var(--radius-lg); + border: var(--rule-hair) solid var(--color-rule-2); + overflow: hidden; + isolation: isolate; + user-select: none; + -webkit-user-select: none; + touch-action: none; + cursor: ew-resize; + background: var(--color-paper-2); + box-shadow: + 0 30px 70px -28px oklch(0% 0 0 / 0.7), + 0 0 0 1px oklch(50% 0.18 70 / 0.18), + 0 0 60px -8px var(--color-glow-soft); +} +.compare.is-dragging { cursor: ew-resize; } +.compare.is-dragging .compare__handle { + transform: translate(-50%, -50%) scale(1.12); + box-shadow: + 0 0 0 4px oklch(8% 0.018 285 / 0.55), + 0 8px 22px oklch(0% 0 0 / 0.55), + 0 0 32px var(--color-glow); +} +.compare__pane { + position: absolute; + inset: 0; + margin: 0; + pointer-events: none; +} +.compare__pane .portrait { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} +.compare__pane--after { z-index: 1; } +.compare__pane--before { + z-index: 2; + clip-path: inset(0 calc(100% - var(--pos)) 0 0); +} + +.compare__label { + position: absolute; + top: var(--space-md); + z-index: 4; + font-family: var(--font-outlier); + font-size: var(--text-xs); + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; + color: oklch(98% 0.005 80); + line-height: 1; + padding: var(--space-2xs) var(--space-sm); + border-radius: var(--radius-pill); + background: oklch(8% 0.018 285 / 0.55); + -webkit-backdrop-filter: blur(8px); + backdrop-filter: blur(8px); + border: 1px solid oklch(98% 0.005 80 / 0.12); + pointer-events: none; +} +.compare__label--before { left: var(--space-md); } +.compare__label--after { right: var(--space-md); color: oklch(14% 0.020 60); background: var(--color-accent); border-color: transparent; box-shadow: 0 0 14px var(--color-glow-soft); } + +.compare__divider { + position: absolute; + top: 0; + bottom: 0; + left: var(--pos); + width: 2px; + margin-left: -1px; + z-index: 3; + background: linear-gradient(180deg, + oklch(98% 0.005 80 / 0.0) 0%, + oklch(98% 0.005 80 / 0.95) 50%, + oklch(98% 0.005 80 / 0.0) 100%); + box-shadow: + 0 0 12px var(--color-glow), + 0 0 24px var(--color-glow-soft); + pointer-events: none; +} +.compare__handle { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 44px; + height: 44px; + border-radius: 50%; + background: oklch(98% 0.010 80); + color: oklch(15% 0.020 60); + display: grid; + place-items: center; + box-shadow: + 0 0 0 4px oklch(8% 0.018 285 / 0.45), + 0 8px 22px oklch(0% 0 0 / 0.45), + 0 0 24px var(--color-glow); + transition: transform var(--dur-short) var(--ease-out); + pointer-events: none; +} +.compare:hover .compare__handle { + transform: translate(-50%, -50%) scale(1.07); +} +/* (handle scale on focus / drag is handled below) */ + +/* Hidden range input — Tab-reachable for keyboard a11y. Pointer + events go to the .compare canvas, where the JS drag loop runs. */ +.compare__input { + position: absolute; + inset: 50% auto auto 0; + width: 100%; + height: 1px; + margin: 0; + appearance: none; + -webkit-appearance: none; + background: transparent; + pointer-events: none; + opacity: 0; + z-index: 0; +} +.compare__input:focus-visible ~ .compare__divider { + outline: 2px solid var(--color-focus); + outline-offset: 2px; + border-radius: 2px; +} +.compare__input:focus-visible ~ .compare__divider .compare__handle { + transform: translate(-50%, -50%) scale(1.1); +} + +.compare__caption { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-sm); + font-family: var(--font-outlier); + font-size: var(--text-xs); + letter-spacing: 0.10em; + color: var(--color-muted); + line-height: 1; + text-transform: uppercase; + flex-wrap: wrap; +} +.compare__caption-meta { + color: var(--color-muted-2); + text-transform: none; + letter-spacing: 0.04em; + font-style: italic; + font-family: var(--font-display); + font-size: var(--text-xs); +} + +/* Before/after portrait variants — selfie register vs. studio register */ +.portrait--before { + --bg-1: oklch(34% 0.020 240); + --bg-2: oklch(20% 0.012 250); + --skin: oklch(70% 0.04 50); + --shirt: oklch(38% 0.06 250); + --rim: oklch(80% 0.02 240); + filter: saturate(0.55) contrast(0.92) brightness(0.94); +} +.portrait--after { + --bg-1: oklch(50% 0.14 35); + --bg-2: oklch(16% 0.06 30); + --skin: oklch(74% 0.08 45); + --shirt: oklch(20% 0.012 60); + --rim: oklch(88% 0.14 70); +} +/* Lock the head still inside the comparator — the breathing loop + would otherwise drift the two halves out of register at the seam. */ +.compare .portrait::before { + animation: none; + transform: translateX(-50%); +} + +/* ─── Marquee strip ───────────────────────────────────────────────── */ +.marquee { + position: relative; + border-block: var(--rule-hair) solid var(--color-rule); + padding-block: var(--space-lg); + background: + linear-gradient(180deg, + transparent 0%, + oklch(8% 0.018 285 / 0.6) 100%); + overflow: hidden; +} +.marquee::before, .marquee::after { + content: ""; + position: absolute; + top: 0; bottom: 0; + width: clamp(80px, 12vw, 200px); + z-index: 2; + pointer-events: none; +} +.marquee::before { + left: 0; + background: linear-gradient(90deg, var(--color-paper) 10%, transparent 100%); +} +.marquee::after { + right: 0; + background: linear-gradient(270deg, var(--color-paper) 10%, transparent 100%); +} +.marquee__track { + display: flex; + align-items: center; + gap: var(--space-2xl); + width: max-content; + animation: marquee var(--dur-marquee) linear infinite; +} +.marquee__track:hover { animation-play-state: paused; } +@keyframes marquee { + to { transform: translateX(-50%); } +} +.marquee__word { + font-family: var(--font-display); + font-style: italic; + font-weight: 400; + font-size: var(--text-marquee); + letter-spacing: -0.04em; + line-height: 1; + color: var(--color-ink); + white-space: nowrap; + display: inline-flex; + align-items: center; + gap: var(--space-md); +} +.marquee__word::after { + content: ""; + width: 12px; height: 12px; + border-radius: 50%; + background: var(--color-accent); + box-shadow: 0 0 18px 2px var(--color-glow); + display: inline-block; +} +.marquee__word--mute { color: var(--color-muted-2); } +.marquee__word--mute::after { + background: transparent; + border: 1px solid var(--color-rule-2); + box-shadow: none; +} + +/* ─── Output gallery (six breathing portraits, wide grid) ─────────── */ +.gallery { + display: grid; + grid-template-columns: 1.1fr repeat(3, 1fr); + gap: var(--space-md); +} +.gallery__intro { + grid-column: 1 / -1; + display: grid; + grid-template-columns: 1fr auto; + align-items: end; + gap: var(--space-lg); + margin-bottom: var(--space-md); + border-block-end: var(--rule-hair) solid var(--color-rule); + padding-block-end: var(--space-lg); +} +.section__head { + font-family: var(--font-outlier); + font-size: var(--text-xs); + letter-spacing: 0.16em; + color: var(--color-accent); + text-transform: uppercase; + line-height: 1; + display: inline-flex; + align-items: center; + gap: 0.6ch; + margin-bottom: var(--space-md); +} +.section__head::before { + content: ""; + width: 24px; height: 1px; + background: currentColor; +} +.section__title { + font-family: var(--font-display); + font-size: clamp(2rem, 4vw + 0.5rem, 3.5rem); + font-weight: 400; + letter-spacing: -0.035em; + line-height: 1.02; + text-wrap: balance; + max-width: 22ch; + color: var(--color-ink); +} +.section__title em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); +} +.section__sub { + font-size: var(--text-sm); + color: var(--color-muted); + max-width: 36ch; + text-align: end; + line-height: 1.5; +} + +.tile { + position: relative; + border: var(--rule-hair) solid var(--color-rule); + border-radius: var(--radius-lg); + background: var(--color-paper-2); + overflow: hidden; + isolation: isolate; + transition: + transform var(--dur-short) var(--ease-out), + border-color var(--dur-short) var(--ease-out); +} +.tile:hover { + transform: translateY(-4px); + border-color: var(--color-rule-2); +} +.tile--feature { + grid-column: span 1; + grid-row: span 2; + display: grid; + grid-template-rows: auto 1fr auto; + padding: var(--space-lg); + gap: var(--space-md); + background: + radial-gradient(120% 80% at 0% 100%, oklch(20% 0.03 50 / 0.5), transparent 70%), + var(--color-paper-2); +} +.tile--feature h2 { + font-family: var(--font-display); + font-size: var(--text-2xl); + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1.05; + color: var(--color-ink); + text-wrap: balance; +} +.tile--feature h2 em { + font-style: italic; + color: var(--color-accent-2); +} +.tile--feature p { + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: 1.55; + max-width: 32ch; +} +.tile--feature .meta { + font-family: var(--font-outlier); + font-size: var(--text-xs); + letter-spacing: 0.10em; + color: var(--color-muted); + text-transform: uppercase; + line-height: 1.4; +} + +.tile--portrait { + aspect-ratio: 1; + cursor: default; +} +.tile--portrait .portrait { width: 100%; height: 100%; } +.tile--portrait .portrait__caption { + position: absolute; + inset: auto var(--space-sm) var(--space-sm) var(--space-sm); + display: flex; + align-items: center; + justify-content: space-between; + font-family: var(--font-outlier); + font-weight: 500; + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.12em; + color: oklch(96% 0.005 80); + line-height: 1; + pointer-events: none; + z-index: 2; + text-shadow: 0 1px 12px oklch(0% 0 0 / 0.7); +} +.tile--portrait .portrait__caption .num { opacity: 0.65; } +.tile--portrait::after { + /* gradient floor for caption legibility */ + content: ""; + position: absolute; + inset: 50% 0 0 0; + background: linear-gradient(180deg, transparent 0%, oklch(0% 0 0 / 0.55) 100%); + pointer-events: none; + z-index: 1; +} + +/* ─── Pure-CSS portrait silhouette + breathing animation ──────────── */ +.portrait { + --bg-1: oklch(40% 0.10 65); + --bg-2: oklch(15% 0.04 280); + --skin: oklch(72% 0.06 55); + --shirt: oklch(22% 0.012 60); + --rim: oklch(82% 0.14 70); + + position: relative; + background: + radial-gradient(120% 80% at 30% 0%, oklch(95% 0.02 80 / 0.20), transparent 60%), + linear-gradient(160deg, var(--bg-1), var(--bg-2)); +} +.portrait::before { + /* head */ + content: ""; + position: absolute; + left: 50%; + top: 26%; + width: 38%; + aspect-ratio: 4 / 5; + background: + radial-gradient(60% 50% at 35% 35%, oklch(96% 0.04 70 / 0.45), transparent 70%), + var(--skin); + border-radius: 50% 50% 46% 46% / 55% 55% 45% 45%; + transform: translateX(-50%); + box-shadow: + inset -6px -10px 22px oklch(15% 0.05 30 / 0.45), + inset 4px 4px 12px oklch(96% 0.05 80 / 0.18), + -2px 0 0 var(--rim); + transform-origin: 50% 100%; + animation: portrait-breath var(--dur-breath) var(--ease-in-out) infinite; +} +.portrait::after { + /* shoulders */ + content: ""; + position: absolute; + left: 50%; + bottom: -8%; + width: 130%; + aspect-ratio: 3 / 1; + background: var(--shirt); + border-radius: 50% 50% 0 0 / 60% 60% 0 0; + transform: translateX(-50%); + box-shadow: + inset 0 8px 0 oklch(40% 0.012 60 / 0.5), + inset 0 -2px 14px oklch(10% 0 0 / 0.6); +} +@keyframes portrait-breath { + 0%, 100% { transform: translateX(-50%) translateY(0) scale(1); } + 50% { transform: translateX(-50%) translateY(-2px) scale(1.012); } +} + +.portrait--boardroom { + --bg-1: oklch(34% 0.08 250); + --bg-2: oklch(14% 0.040 240); + --skin: oklch(72% 0.06 50); + --shirt: oklch(94% 0.005 80); + --rim: oklch(78% 0.15 70); +} +.portrait--editorial { + --bg-1: oklch(50% 0.12 35); + --bg-2: oklch(18% 0.06 30); + --skin: oklch(70% 0.07 45); + --shirt: oklch(20% 0.010 60); + --rim: oklch(86% 0.10 60); +} +.portrait--daylight { + --bg-1: oklch(58% 0.10 80); + --bg-2: oklch(28% 0.06 70); + --skin: oklch(76% 0.05 60); + --shirt: oklch(46% 0.10 240); + --rim: oklch(96% 0.020 80); +} +.portrait--onset { + --bg-1: oklch(28% 0.02 60); + --bg-2: oklch(10% 0.005 60); + --skin: oklch(64% 0.020 60); + --shirt: oklch(18% 0.005 60); + --rim: oklch(84% 0.03 60); + filter: saturate(0.4) contrast(1.05); +} +.portrait--noir { + --bg-1: oklch(20% 0.018 285); + --bg-2: oklch(8% 0.008 285); + --skin: oklch(64% 0.04 50); + --shirt: oklch(94% 0.005 80); + --rim: oklch(78% 0.16 70); +} + +/* ─── Stat counter ────────────────────────────────────────────────── */ +.stat { + padding-block: var(--space-3xl); + display: grid; + grid-template-columns: 1.4fr 1fr; + gap: var(--space-2xl); + align-items: end; + border-block-end: var(--rule-hair) solid var(--color-rule); +} +.stat__figure { + font-family: var(--font-display); + font-size: var(--text-figure); + font-weight: 300; + font-style: italic; + letter-spacing: -0.05em; + line-height: 0.9; + font-variant-numeric: tabular-nums; + color: var(--color-ink); + display: inline-flex; + align-items: baseline; + gap: 0.1ch; +} +.stat__figure .num { + background: linear-gradient(180deg, + var(--color-ink) 0%, + var(--color-accent-2) 70%, + var(--color-accent) 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + /* Italic + tight tracking + background-clip:text crops the rightmost + glyph in some browsers; reserve ~10% em of trailing room. */ + padding-inline-end: 0.1em; + letter-spacing: -0.04em; +} +.stat__figure .plus { + font-style: normal; + font-weight: 200; + color: var(--color-accent); + font-size: 0.5em; + align-self: start; + margin-top: 0.2em; +} +.stat__copy { + display: grid; + gap: var(--space-md); +} +.stat__copy h2 { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 400; + font-style: italic; + letter-spacing: -0.02em; + line-height: 1.2; + color: var(--color-ink); + max-width: 28ch; +} +.stat__copy p { + font-size: var(--text-sm); + color: var(--color-muted); + max-width: 32ch; + line-height: 1.55; +} + +/* ─── Process — three large stages, workbench-style ───────────────── */ +.process { + display: grid; + gap: var(--space-2xl); +} +.process__head { + display: grid; + grid-template-columns: 1fr auto; + gap: var(--space-lg); + align-items: end; + border-block-end: var(--rule-hair) solid var(--color-rule); + padding-block-end: var(--space-md); +} +.process__head h2 { + font-family: var(--font-display); + font-size: clamp(2rem, 4vw + 0.5rem, 3.5rem); + font-weight: 400; + letter-spacing: -0.035em; + line-height: 1.0; + text-wrap: balance; + max-width: 18ch; +} +.process__head h2 em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); +} +.process__head p { + font-size: var(--text-sm); + color: var(--color-muted); + max-width: 32ch; + text-align: end; +} +.steps { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-lg); + list-style: none; +} +.step { + position: relative; + display: grid; + gap: var(--space-md); + padding: var(--space-xl); + border: var(--rule-hair) solid var(--color-rule); + border-radius: var(--radius-lg); + background: + linear-gradient(180deg, + oklch(14% 0.020 285 / 0.6) 0%, + oklch(11% 0.018 285 / 0.6) 100%); + transition: + border-color var(--dur-short) var(--ease-out), + transform var(--dur-short) var(--ease-out); +} +.step:hover { + transform: translateY(-3px); + border-color: var(--color-rule-2); +} +.step__num { + font-family: var(--font-display); + font-size: var(--text-3xl); + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); + line-height: 1; + letter-spacing: -0.05em; + font-variant-numeric: tabular-nums; + display: flex; + align-items: baseline; + gap: 0.4ch; +} +.step__num::after { + content: ""; + display: inline-block; + flex: 1; + height: 1px; + background: linear-gradient(90deg, var(--color-rule-2) 0%, transparent 100%); + align-self: center; + margin-left: var(--space-sm); +} +.step__stage { + font-family: var(--font-outlier); + font-size: var(--text-xs); + letter-spacing: 0.14em; + color: var(--color-accent); + text-transform: uppercase; + line-height: 1; +} +.step h3 { + font-family: var(--font-display); + font-size: var(--text-lg); + font-weight: 500; + letter-spacing: -0.02em; + line-height: 1.15; + color: var(--color-ink); +} +.step p { + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: 1.55; +} + +/* ─── Pricing ─────────────────────────────────────────────────────── */ +.pricing { + display: grid; + gap: var(--space-2xl); +} +.pricing__head { + display: grid; + gap: var(--space-md); + max-width: 38ch; +} +.pricing__head h2 { + font-family: var(--font-display); + font-size: clamp(2rem, 4vw + 0.5rem, 3.5rem); + font-weight: 400; + letter-spacing: -0.035em; + line-height: 1.0; +} +.pricing__head h2 em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); +} +.pricing__head p { + color: var(--color-ink-2); + font-size: var(--text-md); + max-width: 40ch; +} + +.tiers { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-md); +} +.tier { + position: relative; + border: var(--rule-hair) solid var(--color-rule); + border-radius: var(--radius-lg); + padding: var(--space-xl); + background: + linear-gradient(180deg, + oklch(14% 0.020 285 / 0.5) 0%, + oklch(10% 0.018 285 / 0.6) 100%); + display: grid; + gap: var(--space-md); + align-content: start; + transition: + transform var(--dur-short) var(--ease-out), + border-color var(--dur-short) var(--ease-out), + box-shadow var(--dur-short) var(--ease-out); +} +.tier:hover { + transform: translateY(-4px); + border-color: var(--color-rule-2); +} +.tier__name { + font-family: var(--font-outlier); + font-weight: 500; + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.16em; + color: var(--color-accent); + line-height: 1; +} +.tier__price { + font-family: var(--font-display); + font-size: var(--text-3xl); + font-style: italic; + font-weight: 400; + letter-spacing: -0.04em; + line-height: 1; + color: var(--color-ink); + font-variant-numeric: tabular-nums; + display: inline-flex; + align-items: baseline; + gap: 0.4ch; +} +.tier__price .unit { + font-family: var(--font-outlier); + font-style: normal; + font-size: var(--text-sm); + font-weight: 400; + color: var(--color-muted); + letter-spacing: 0; + line-height: 1; +} +.tier__sub { + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: 1.5; + max-width: 28ch; +} +.tier__list { + list-style: none; + display: grid; + gap: var(--space-sm); + margin-block: var(--space-md); + padding-block-start: var(--space-md); + border-block-start: var(--rule-hair) solid var(--color-rule); +} +.tier__list li { + display: grid; + grid-template-columns: 16px 1fr; + gap: var(--space-sm); + align-items: start; + font-size: var(--text-sm); + color: var(--color-ink-2); + line-height: 1.45; +} +.tier__list li::before { + content: ""; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--color-accent); + box-shadow: 0 0 10px 1px var(--color-glow-soft); + margin-top: 4px; +} +.tier .btn { justify-content: center; width: 100%; } + +.tier--rec { + border-color: var(--color-accent); + background: + radial-gradient(80% 60% at 50% 0%, oklch(50% 0.18 50 / 0.18), transparent 70%), + linear-gradient(180deg, + oklch(16% 0.030 60 / 0.6) 0%, + oklch(11% 0.020 285 / 0.6) 100%); + box-shadow: 0 0 0 1px var(--color-accent), 0 24px 60px -24px var(--color-glow); +} +.tier--rec::after { + content: "Recommended"; + position: absolute; + top: -12px; + left: var(--space-xl); + font-family: var(--font-outlier); + font-weight: 500; + font-size: var(--text-xs); + letter-spacing: 0.14em; + text-transform: uppercase; + color: oklch(14% 0.020 60); + background: var(--color-accent); + padding: var(--space-2xs) var(--space-sm); + border-radius: var(--radius-pill); + line-height: 1; + box-shadow: 0 0 16px 2px var(--color-glow); +} +.tier--rec.rec-pulse { + animation: rec-pulse 2400ms var(--ease-in-out) 1 forwards; +} +@keyframes rec-pulse { + 0% { box-shadow: 0 0 0 1px var(--color-accent), 0 24px 60px -24px var(--color-glow), 0 0 0 0 oklch(80% 0.18 75 / 0.0); } + 35% { box-shadow: 0 0 0 1px var(--color-accent), 0 24px 60px -24px var(--color-glow), 0 0 0 10px oklch(80% 0.18 75 / 0.18); } + 100% { box-shadow: 0 0 0 1px var(--color-accent), 0 24px 60px -24px var(--color-glow), 0 0 0 0 oklch(80% 0.18 75 / 0.0); } +} + +/* ─── FAQ ─────────────────────────────────────────────────────────── */ +.faq { + display: grid; + grid-template-columns: 1fr 2fr; + gap: var(--space-2xl); + align-items: start; +} +.faq__head h2 { + font-family: var(--font-display); + font-size: clamp(2rem, 4vw + 0.5rem, 3.25rem); + font-weight: 400; + letter-spacing: -0.035em; + line-height: 1.0; + max-width: 14ch; +} +.faq__head h2 em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); +} +.faq__head p { + margin-top: var(--space-md); + color: var(--color-ink-2); + font-size: var(--text-sm); + max-width: 32ch; +} +.faq__list { display: grid; } +.faq__item { + border-block-end: var(--rule-hair) solid var(--color-rule); +} +.faq__item:first-child { border-block-start: var(--rule-hair) solid var(--color-rule); } +.faq__summary { + list-style: none; + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + gap: var(--space-md); + padding-block: var(--space-md); + cursor: pointer; + min-height: 48px; + font-family: var(--font-display); + font-size: var(--text-md); + font-weight: 500; + letter-spacing: -0.02em; + color: var(--color-ink); + line-height: 1.3; + transition: color var(--dur-short) var(--ease-out); +} +.faq__summary::-webkit-details-marker { display: none; } +.faq__summary:hover { color: var(--color-accent); } +.faq__summary:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 4px; + border-radius: 4px; +} +.faq__sign { + position: relative; + width: 20px; height: 20px; + flex: 0 0 20px; + color: var(--color-accent); +} +.faq__sign::before, .faq__sign::after { + content: ""; + position: absolute; + inset: 0; + margin: auto; + background: currentColor; +} +.faq__sign::before { width: 14px; height: 1.5px; } +.faq__sign::after { width: 1.5px; height: 14px; transition: transform var(--dur-short) var(--ease-out); } +details[open] .faq__sign::after { transform: rotate(90deg); } + +.faq__answer-wrap { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows var(--dur-long) var(--ease-in-out); +} +details[open] .faq__answer-wrap { grid-template-rows: 1fr; } +.faq__answer-inner { overflow: hidden; } +.faq__answer { + padding-block: 0 var(--space-md); + color: var(--color-ink-2); + font-size: var(--text-sm); + max-width: 60ch; + line-height: 1.6; +} + +/* ─── Final CTA — full-bleed dramatic ─────────────────────────────── */ +.final { + position: relative; + margin-block: var(--space-3xl) var(--space-2xl); + border-radius: var(--radius-xl); + border: var(--rule-hair) solid var(--color-rule-2); + padding: clamp(var(--space-2xl), 8vw, var(--space-4xl)) clamp(var(--space-lg), 6vw, var(--space-3xl)); + background: + radial-gradient(60% 80% at 80% 50%, oklch(30% 0.14 50 / 0.4) 0%, transparent 70%), + radial-gradient(60% 100% at 0% 100%, oklch(20% 0.10 60 / 0.5) 0%, transparent 70%), + linear-gradient(180deg, oklch(15% 0.030 60 / 0.6) 0%, oklch(11% 0.020 285 / 0.6) 100%); + display: grid; + grid-template-columns: 1fr auto; + gap: var(--space-xl); + align-items: center; + overflow: hidden; + isolation: isolate; +} +.final::before { + /* slow drifting halo */ + content: ""; + position: absolute; + inset: -30%; + background: radial-gradient(35% 25% at 30% 40%, var(--color-glow) 0%, transparent 70%); + z-index: -1; + animation: drift 22s var(--ease-in-out) infinite alternate; +} +@keyframes drift { + to { transform: translate(40%, 20%); } +} +.final h2 { + font-family: var(--font-display); + font-size: clamp(2rem, 5vw + 0.5rem, 4rem); + font-weight: 400; + letter-spacing: -0.04em; + line-height: 0.98; + max-width: 22ch; + text-wrap: balance; +} +.final h2 em { + font-style: italic; + font-weight: 300; + color: var(--color-accent-2); +} +.final__sub { + margin-top: var(--space-md); + color: var(--color-ink-2); + font-size: var(--text-md); + max-width: 38ch; + line-height: 1.55; +} +.final__cta { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; +} + +/* ─── Footer ──────────────────────────────────────────────────────── */ +.foot { + padding-block: var(--space-2xl); + display: grid; + gap: var(--space-md); + border-block-start: var(--rule-hair) solid var(--color-rule); +} +.foot__row { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-md); + flex-wrap: wrap; +} +.foot__tagline { + color: var(--color-muted); + font-size: var(--text-sm); + max-width: 38ch; + line-height: 1.5; +} +.foot__links { + display: flex; + align-items: center; + gap: var(--space-md); + list-style: none; + flex-wrap: wrap; +} +.foot__links a { + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: 1; + transition: color var(--dur-short) var(--ease-out); +} +.foot__links a:hover { color: var(--color-accent); } +.foot__links a:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 4px; + border-radius: 2px; +} +.foot__credit { + font-family: var(--font-outlier); + font-size: var(--text-xs); + color: var(--color-muted); + letter-spacing: 0.04em; + border-block-start: var(--rule-hair) solid var(--color-rule); + padding-block-start: var(--space-md); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-md); + flex-wrap: wrap; + line-height: 1.5; +} + +/* ─── Reveal (one-shot, IntersectionObserver-driven) ──────────────── */ +.reveal { + opacity: 0; + transform: translateY(14px); + transition: + opacity var(--dur-long) var(--ease-out), + transform var(--dur-long) var(--ease-out); + transition-delay: calc(var(--i, 0) * 80ms); +} +.reveal.is-in { + opacity: 1; + transform: none; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 64rem) { + .hero { + grid-template-columns: 1fr; + gap: var(--space-xl); + } + .hero__aside { + justify-self: center; + max-width: 22rem; + } + .gallery { + grid-template-columns: repeat(3, 1fr); + } + .gallery .tile--feature { + grid-column: 1 / -1; + grid-row: auto; + } + .stat { + grid-template-columns: 1fr; + gap: var(--space-lg); + } + .steps { grid-template-columns: 1fr; gap: var(--space-md); } + .tiers { grid-template-columns: 1fr; } + .faq { grid-template-columns: 1fr; } + .final { grid-template-columns: 1fr; } + .gallery__intro, + .process__head { grid-template-columns: 1fr; } + .gallery__intro .section__sub, + .process__head p { text-align: start; } +} +@media (max-width: 40rem) { + .section { padding-block: var(--space-2xl); } + .nav__links { gap: var(--space-md); } + .nav__links li:nth-child(1), + .nav__links li:nth-child(2) { display: none; } + .hero__cta { flex-direction: column; align-items: stretch; width: 100%; } + .hero__cta .btn { justify-content: center; } + .gallery { grid-template-columns: repeat(2, 1fr); } + .gallery .tile--feature { grid-column: 1 / -1; } + .marquee__track { gap: var(--space-xl); } +} + +@media (pointer: coarse) { + .btn { min-height: 52px; } + .nav__links a { padding: var(--space-sm) var(--space-2xs); } +} + +/* ─── Reduced motion ──────────────────────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 1ms !important; + animation-iteration-count: 1 !important; + transition-duration: 1ms !important; + scroll-behavior: auto !important; + } + .reveal { opacity: 1; transform: none; } + .marquee__track { animation: none; transform: none; } + .aperture__halo, .aperture__pupil, .aperture__ticks, + .portrait::before, .wordmark::before, .final::before { animation: none !important; } + .btn:hover, .tier:hover, .step:hover, .tile:hover { transform: none; } +} + +/* ─── Visually-hidden (a11y) ──────────────────────────────────────── */ +.sr-only { + position: absolute; + width: 1px; height: 1px; + padding: 0; margin: -1px; overflow: hidden; + clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; +} diff --git a/site/examples/bananastudio/tokens.css b/site/examples/bananastudio/tokens.css new file mode 100644 index 0000000..9c2819c --- /dev/null +++ b/site/examples/bananastudio/tokens.css @@ -0,0 +1,96 @@ +/* Hallmark · tokens · genre: atmospheric · theme: Bloom (dark) + * Paper band: dark · Display style: italic-serif (Fraunces) · Accent hue: warm amber-bloom 75° + */ + +:root { + color-scheme: dark; + + /* — Type ----------------------------------------------------------- */ + --font-display: "Fraunces", "Iowan Old Style", "Apple Garamond", Georgia, serif; + --font-body: "Geist", ui-sans-serif, system-ui, sans-serif; + --font-outlier: "Geist Mono", ui-monospace, "SFMono-Regular", monospace; + + /* — Type scale (1.25 ratio · base 16px) ---------------------------- */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-md: 1.125rem; + --text-lg: 1.375rem; + --text-xl: 1.75rem; + --text-2xl: 2.25rem; + --text-3xl: 3rem; + --text-4xl: 4rem; + --text-display: clamp(3rem, 8vw + 0.5rem, 8.5rem); + --text-marquee: clamp(2.5rem, 7vw, 6.5rem); + --text-figure: clamp(4rem, 12vw + 0.5rem, 11rem); + + /* — Colour (OKLCH; midnight-violet paper · amber-bloom accent) ----- */ + --color-paper: oklch(10.0% 0.018 285); /* deep midnight violet */ + --color-paper-2: oklch(13.5% 0.020 285); + --color-paper-3: oklch(17.5% 0.024 285); + --color-paper-tint: oklch(22.0% 0.028 285); + --color-rule: oklch(24.0% 0.020 285); + --color-rule-2: oklch(32.0% 0.022 285); + + --color-ink: oklch(98.0% 0.010 80); /* warm bone */ + --color-ink-2: oklch(80.0% 0.012 80); + --color-muted: oklch(60.0% 0.014 80); + --color-muted-2: oklch(48.0% 0.014 75); + + --color-accent: oklch(80.0% 0.180 75); /* amber bloom */ + --color-accent-2: oklch(86.0% 0.140 80); /* lighter bloom */ + --color-accent-deep: oklch(62.0% 0.180 50); /* deep ember */ + --color-accent-cool: oklch(70.0% 0.150 250); /* studio cyan, single-use */ + + --color-glow: oklch(78% 0.20 70 / 0.30); + --color-glow-soft: oklch(80% 0.18 75 / 0.18); + + --color-focus: oklch(82% 0.180 75); + + /* — Spacing (4 pt scale, role-named) ------------------------------- */ + --space-3xs: 0.125rem; + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-sm: 0.75rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2.5rem; + --space-2xl: 4rem; + --space-3xl: 6rem; + --space-4xl: 9rem; + + /* — Rules + radii -------------------------------------------------- */ + --rule-hair: 1px; + --rule-thick: 2px; + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 18px; + --radius-xl: 28px; + --radius-pill: 999px; + + /* — Motion --------------------------------------------------------- */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in: cubic-bezier(0.7, 0, 0.84, 0); + --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + + --dur-micro: 120ms; + --dur-short: 240ms; + --dur-mid: 520ms; + --dur-long: 760ms; + --dur-marquee: 48s; + --dur-breath: 6.5s; + --dur-aperture: 9s; + + /* — z-index (six-level scale) -------------------------------------- */ + --z-base: 1; + --z-raised: 10; + --z-dropdown: 100; + --z-sticky: 200; + --z-modal: 400; + --z-toast: 500; + --z-tooltip: 600; + + /* — Page layout ---------------------------------------------------- */ + --page-gutter: clamp(1.25rem, 4vw, 4rem); + --page-max: 78rem; +} diff --git a/site/examples/hyperlane/.hallmark/log.json b/site/examples/hyperlane/.hallmark/log.json new file mode 100644 index 0000000..5652d2a --- /dev/null +++ b/site/examples/hyperlane/.hallmark/log.json @@ -0,0 +1,13 @@ +[ + { + "date": "2026-05-07", + "macrostructure": "Marquee Hero", + "theme": "Bloom", + "polish": "HP3 Cursor-spotlight", + "enrichment": "none", + "nav": "N5 Floating pill", + "footer": "Ft5 Statement", + "genre": "atmospheric", + "brief": "Hyperlane / 26 — public dev event launch · one-night summit · Brooklyn 17 Oct 2026" + } +] diff --git a/site/examples/hyperlane/index.html b/site/examples/hyperlane/index.html new file mode 100644 index 0000000..4097ef4 --- /dev/null +++ b/site/examples/hyperlane/index.html @@ -0,0 +1,344 @@ + + + + + + Hyperlane / 26 — A one-night developer summit · Brooklyn · Oct 17, 2026 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + HYPERLANE / 26 — a developer summit · public list opens 09 Sep +
+ +

+ For engineers + who ship + after dark. +

+ +
+
+ When + Sat 17 Oct 2026 · 7 PM +
+
+ To liftoff + — — +
+
+ Where + Brooklyn · 480 cap +
+
+
+ + +
+
+

01 · Format

+

A single night, end to end.

+
+ +
    +
  1. + 7:00 PM + Doors + soft open · drinks · introductions in the foyer +
  2. +
  3. + 8:30 PM + Programme + eight talks · four live launches · running until 1 AM +
  4. +
  5. + 11:00 PM + Workshop + a hands-on session in the side room · 40 seats only +
  6. +
  7. + 1:00 AM + Late + closing DJ · open until four · room is yours +
  8. +
  9. + — — + Address + released 48 hours before to ticketed guests · Williamsburg waterfront +
  10. +
+ +
+ + Save your spot + + +

— Wave 01 of 3 · 480 cap · public list opens 09 Sep 2026

+
+
+ + +
+
+

02 · Why

+

A room for the people still building things at 2 AM.

+
+ +
+

+ Twelve hours. Eight talks. Four live launches. Four hundred and eighty engineers, designers, founders, infra people, ML people, the frontend people who keep telling you the framework isn't done yet. Show up at seven. Leave when the sun does. +

+

+ The conference circuit is fine. But the launches that matter — the new database, the new compiler, the next thing you'll be writing against in March — those don't happen on big stages. They happen in small rooms, late, when the ten people who actually shipped the thing are still around to answer questions. +

+

+ Hyperlane is a room for that. No badges. No pitches. No panels. Talks short enough to remember (twelve to eighteen minutes). Live demos you can interrupt. Closing DJ. +

+

— Brooklyn. October. The list opens in September.

+
+
+ + +
+
+

03 · Programme · Wave 01

+

Six of twelve slots announced.

+

Wave 02 reveals 09 Sep · final programme 30 Sep · launches stay sealed until the night.

+
+ + +
+ + +
+
+

04 · Tickets

+

Two emails. One night.

+

+ Join the public list and get early access to Wave 01 tickets when they open on 09 Sep. We send the address two days before doors and a final note 24 hours out — that's it. +

+ +
+
+ + +
+

+ Wave 01 · $140 · Wave 02 · $180 · Wave 03 · $220 at the door, capacity permitting. +

+ +
+
+
+ + +
+
+

05 · Questions

+

A few honest answers.

+
+ +
+
+ + What exactly is Hyperlane? + + +
+

One night, twelve hours, eight programmed slots. Four of those slots are live product launches — first public demos, with the team that built it taking questions afterward. Talks are short (twelve to eighteen minutes). No panels. No badges. The room itself is the after-show.

+
+
+ +
+ + Who comes? + + +
+

Roughly: half engineers, a quarter designers, a quarter everything adjacent — founders, ML researchers, devrel, technical writers. The list is public; the seats are not. Tickets are not transferable.

+
+
+ +
+ + Where is it, exactly? + + +
+

A warehouse on the Williamsburg waterfront. We release the address forty-eight hours before doors, to ticketed guests only. There are real reasons.

+
+
+ +
+ + How much? + + +
+

Three waves. Wave 01 (early list, you're reading this) opens at $140. Wave 02 at $180. Wave 03 at the door, capacity permitting, $220. We don't do tiers, comp lists, or sponsorship seats.

+
+
+ +
+ + Is it recorded? + + +
+

Talks are recorded and released thirty days after the event. The launches and live demos are not streamed and not recorded — you have to be in the room.

+
+
+ +
+ + Refunds? + + +
+

Up to seven days before doors. After that we'll move your seat to the waitlist and credit you toward the next room.

+
+
+ +
+ + I'd like to launch something. Can I? + + +
+

Maybe. Two of the four launch slots are still open. Email launches@hyperlane.events with what you're shipping and we'll write back. We are not a sponsorship channel; the slot has to be a real launch.

+
+
+
+
+ +
+ + + + + + + diff --git a/site/examples/hyperlane/script.js b/site/examples/hyperlane/script.js new file mode 100644 index 0000000..3bc1c10 --- /dev/null +++ b/site/examples/hyperlane/script.js @@ -0,0 +1,103 @@ +/* Hallmark · script.js + * scoped behaviours: HP3 cursor-spotlight (hero only) · countdown · form silent-success + */ + +(() => { + "use strict"; + + const reduced = + window.matchMedia && + window.matchMedia("(prefers-reduced-motion: reduce)").matches; + + /* ── HP3 cursor spotlight · scoped to hero only ────────── */ + const hero = document.querySelector(".hero"); + if (hero && !reduced) { + let raf = 0; + let pendingX = 0; + let pendingY = 0; + const apply = () => { + raf = 0; + hero.style.setProperty("--mx", `${pendingX}px`); + hero.style.setProperty("--my", `${pendingY}px`); + }; + hero.addEventListener("pointermove", (e) => { + const r = hero.getBoundingClientRect(); + pendingX = e.clientX - r.left; + pendingY = e.clientY - r.top; + if (!raf) raf = requestAnimationFrame(apply); + }); + } + + /* ── live countdown to event start ─────────────────────── */ + const cd = document.getElementById("countdown"); + if (cd && cd.dataset.target) { + const target = new Date(cd.dataset.target).getTime(); + if (!Number.isNaN(target)) { + const tick = () => { + let diff = target - Date.now(); + if (diff <= 0) { + cd.textContent = "live"; + return false; + } + const d = Math.floor(diff / 86_400_000); diff -= d * 86_400_000; + const h = Math.floor(diff / 3_600_000); diff -= h * 3_600_000; + const m = Math.floor(diff / 60_000); diff -= m * 60_000; + const s = Math.floor(diff / 1000); + cd.textContent = + `${d}d ${String(h).padStart(2, "0")}h ` + + `${String(m).padStart(2, "0")}m ${String(s).padStart(2, "0")}s`; + return true; + }; + tick(); + setInterval(() => { tick(); }, 1000); + } + } + + /* ── nav state on scroll (sticky pill polish) ──────────── */ + const nav = document.querySelector(".nav"); + if (nav) { + let lastState = "rest"; + const onScroll = () => { + const next = window.scrollY > 24 ? "scrolled" : "rest"; + if (next !== lastState) { + nav.dataset.state = next; + lastState = next; + } + }; + onScroll(); + window.addEventListener("scroll", onScroll, { passive: true }); + } + + /* ── form silent-success (optimistic, no toast) ────────── */ + const form = document.getElementById("rsvp-form"); + if (form) { + const button = form.querySelector("button[type='submit']"); + const success = form.querySelector(".rsvp__success"); + const input = form.querySelector("input[type='email']"); + + form.addEventListener("submit", (e) => { + e.preventDefault(); + if (!input || !input.value || !input.checkValidity()) { + input?.focus(); + return; + } + if (button) { + button.dataset.state = "loading"; + button.querySelector(".cta__label").textContent = "Joining…"; + } + // simulated; wire to real endpoint when available + setTimeout(() => { + if (button) { + button.dataset.state = "success"; + button.querySelector(".cta__label").textContent = "On the list"; + button.querySelector(".cta__arrow").textContent = "✓"; + } + if (success) { + success.dataset.state = "ok"; + success.textContent = `You're on the list, ${input.value}. Wave 01 opens 09 Sep — we'll write first.`; + } + input.disabled = true; + }, 480); + }); + } +})(); diff --git a/site/examples/hyperlane/styles.css b/site/examples/hyperlane/styles.css new file mode 100644 index 0000000..8f484bf --- /dev/null +++ b/site/examples/hyperlane/styles.css @@ -0,0 +1,1103 @@ +/* Hallmark · genre: atmospheric · macrostructure: Marquee Hero · theme: Bloom + * polish: HP3 Cursor-spotlight (scoped to hero only · reduced-motion fallback at 50%/30%) + * enrichment: none — typography only + * nav: N5 Floating pill · footer: Ft5 Statement + * accent: warm amber bloom (~3% page footprint) + secondary coral + * studied: no + */ + +/* ── reset & root ─────────────────────────────────────────── */ + +*, *::before, *::after { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + overflow-x: clip; +} + +html { + background: var(--color-paper); + color: var(--color-ink); + font-family: var(--font-body); + font-size: 16px; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +body { + position: relative; + min-height: 100dvh; + font-family: var(--font-body); + font-size: var(--text-md); + line-height: var(--leading-body); + color: var(--color-ink); + background: var(--color-paper); + font-feature-settings: "ss01", "cv11"; + font-variant-ligatures: contextual; +} + +img, svg { display: block; max-width: 100%; } +button, input, select, textarea { font: inherit; color: inherit; } +a { color: inherit; text-decoration: none; } + +::selection { + background: var(--color-accent); + color: var(--color-accent-ink); +} + +/* tabular-numbers utility */ +.tnum { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; } + +/* skip link */ +.skip { + position: absolute; + top: -40px; + left: var(--space-md); + padding: var(--space-xs) var(--space-md); + background: var(--color-ink); + color: var(--color-paper); + border-radius: var(--radius-sm); + font-size: var(--text-sm); + z-index: 100; +} +.skip:focus { top: var(--space-md); } + +/* ── ambient blooms (atmospheric backdrop) ───────────────── */ + +.ambient { + position: fixed; + inset: 0; + z-index: -2; + pointer-events: none; + overflow: hidden; +} + +.bloom { + position: absolute; + border-radius: 50%; + filter: blur(80px); + opacity: 0.55; + will-change: opacity; +} + +.bloom--1 { + top: -10vmax; + left: -8vmax; + width: 60vmax; + height: 60vmax; + background: radial-gradient( + closest-side, + color-mix(in oklch, var(--color-bloom-1) 50%, transparent), + transparent 70% + ); +} + +.bloom--2 { + bottom: -20vmax; + right: -15vmax; + width: 55vmax; + height: 55vmax; + background: radial-gradient( + closest-side, + color-mix(in oklch, var(--color-bloom-2) 38%, transparent), + transparent 70% + ); +} + +.grain { + position: absolute; + inset: 0; + background-image: url("data:image/svg+xml;utf8,"); + background-size: 200px 200px; + opacity: 0.5; + mix-blend-mode: overlay; +} + +/* ── nav · N5 floating pill ──────────────────────────────── */ + +.nav { + position: sticky; + top: var(--space-md); + z-index: 40; + padding: 0 var(--page-gutter); + pointer-events: none; + transition: top var(--dur-slow) var(--ease-out); +} + +.nav[data-state="scrolled"] { top: var(--space-sm); } + +.nav__inner { + pointer-events: auto; + max-width: var(--max-width); + margin: 0 auto; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: var(--space-md); + padding: var(--space-xs) var(--space-xs) var(--space-xs) var(--space-md); + background: color-mix(in oklch, var(--color-paper) 76%, transparent); + backdrop-filter: blur(18px) saturate(1.4); + -webkit-backdrop-filter: blur(18px) saturate(1.4); + border: var(--rule-hair) solid color-mix(in oklch, var(--color-rule-strong) 100%, transparent); + border-radius: var(--radius-pill); + transition: + max-width var(--dur-slow) var(--ease-out), + padding var(--dur-base) var(--ease-out), + background var(--dur-base) var(--ease-out), + border-color var(--dur-base) var(--ease-out), + box-shadow var(--dur-slow) var(--ease-out), + transform var(--dur-base) var(--ease-out); + will-change: max-width, transform; +} + +.nav[data-state="scrolled"] .nav__inner { + max-width: 56rem; + padding: var(--space-2xs) var(--space-2xs) var(--space-2xs) var(--space-md); + background: color-mix(in oklch, var(--color-paper) 94%, transparent); + border-color: color-mix(in oklch, var(--color-accent) 32%, var(--color-rule-strong)); + box-shadow: + 0 12px 32px -10px color-mix(in oklch, var(--color-paper) 100%, transparent), + 0 0 0 1px color-mix(in oklch, var(--color-accent) 12%, transparent), + 0 0 60px -8px color-mix(in oklch, var(--color-accent) 28%, transparent); + transform: scale(0.965); +} + +/* nav details that respond to the scroll-state transformation */ +.nav__brand-num, +.nav__links a { + transition: + color var(--dur-fast) var(--ease-out), + opacity var(--dur-base) var(--ease-out), + transform var(--dur-base) var(--ease-out); +} + +.nav__links { + transition: + gap var(--dur-base) var(--ease-out), + font-size var(--dur-base) var(--ease-out); +} + +.nav[data-state="scrolled"] .nav__links { + gap: var(--space-sm); + font-size: var(--text-xs); +} + +.nav[data-state="scrolled"] .nav__brand-num { + opacity: 0.55; + transform: translateX(-1px); +} + +.nav[data-state="scrolled"] .nav__brand-dot { + box-shadow: 0 0 18px color-mix(in oklch, var(--color-accent) 90%, transparent); +} + +.nav__brand { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + font-family: var(--font-display); + font-weight: 700; + font-size: var(--text-sm); + letter-spacing: -0.01em; + color: var(--color-ink); + white-space: nowrap; +} + +.nav__brand-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-accent); + box-shadow: 0 0 14px color-mix(in oklch, var(--color-accent) 70%, transparent); + animation: pulse 2.4s var(--ease-in-out) infinite; +} + +.nav__brand-num { + color: var(--color-ink-3); + font-weight: 500; + margin-left: 1px; +} + +.nav__links { + display: flex; + justify-content: center; + gap: var(--space-md); + font-size: var(--text-sm); + color: var(--color-ink-2); +} + +.nav__links a { + padding: var(--space-2xs) var(--space-xs); + border-radius: var(--radius-sm); + transition: color var(--dur-fast) var(--ease-out); + white-space: nowrap; +} +.nav__links a:hover { color: var(--color-ink); } +.nav__links a:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 2px; +} + +.nav__cta { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: var(--space-xs) var(--space-md); + background: var(--color-accent); + color: var(--color-accent-ink); + border-radius: var(--radius-pill); + font-size: var(--text-sm); + font-weight: 600; + white-space: nowrap; + transition: + transform var(--dur-fast) var(--ease-out), + background var(--dur-fast) var(--ease-out); +} +.nav__cta:hover { + transform: translateY(-1px); + background: color-mix(in oklch, var(--color-accent) 88%, var(--color-ink)); +} +.nav__cta:active { transform: translateY(0); } +.nav__cta:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.6; transform: scale(0.85); } +} + +/* ── hero · Marquee Hero + HP3 cursor spotlight ──────────── */ + +.hero { + position: relative; + isolation: isolate; + min-height: clamp(34rem, 84dvh, 52rem); + padding: var(--space-3xl) var(--page-gutter) var(--space-4xl); + display: grid; + grid-template-rows: auto 1fr auto; + gap: var(--space-xl); + overflow: hidden; + text-align: center; + place-items: center; +} + +.hero__spotlight { + position: absolute; + inset: 0; + z-index: -1; + background: radial-gradient( + 520px circle at var(--mx, 50%) var(--my, 35%), + color-mix(in oklch, var(--color-accent) 22%, transparent), + transparent 60% + ); + transition: background var(--dur-base) var(--ease-out); +} + +.hero__rail { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: var(--space-2xs) var(--space-sm) var(--space-2xs) var(--space-xs); + background: color-mix(in oklch, var(--color-paper-2) 90%, transparent); + border: var(--rule-hair) solid var(--color-rule-strong); + border-radius: var(--radius-pill); + margin: 0 auto; + width: max-content; + max-width: 100%; + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.04em; + color: var(--color-ink-2); + text-transform: uppercase; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hero__rail-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--color-accent); + box-shadow: 0 0 8px var(--color-accent); + flex: 0 0 auto; + animation: pulse 2.4s var(--ease-in-out) infinite; +} + +.hero__display { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-display); + line-height: var(--leading-display); + letter-spacing: -0.045em; + margin: 0 auto; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + max-width: 18ch; + overflow-wrap: anywhere; + min-width: 0; +} + +.hero__line { + display: block; + opacity: 0; + transform: translateY(16px); + animation: rise var(--dur-slow) var(--ease-out) forwards; +} + +.hero__line:nth-child(1) { animation-delay: 60ms; } +.hero__line:nth-child(2) { animation-delay: 160ms; } +.hero__line:nth-child(3) { animation-delay: 260ms; } + +.hero__line em { + font-family: var(--font-accent); + font-style: italic; + font-weight: 400; + font-size: 1.18em; + letter-spacing: -0.02em; + color: var(--color-accent); + font-feature-settings: normal; + padding-right: 0.06em; +} + +@keyframes rise { + to { opacity: 1; transform: translateY(0); } +} + +.hero__meta { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: baseline; + gap: var(--space-lg) var(--space-2xl); + padding-top: var(--space-lg); + border-top: var(--rule-hair) solid var(--color-rule); + font-family: var(--font-mono); + font-size: var(--text-sm); + width: 100%; + max-width: 56rem; +} + +.hero__meta-cell { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 2px; + min-width: 0; +} + +.hero__label { + font-size: var(--text-xs); + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--color-ink-3); +} + +.hero__value { + color: var(--color-ink); + font-weight: 500; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ── shared · section heads ──────────────────────────────── */ + +.section-head { + max-width: var(--max-width); + margin: 0 auto var(--space-2xl); + padding: 0; + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--space-xs); +} + +.section-eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--color-ink-3); + margin: 0; + display: inline-flex; + align-items: baseline; + gap: var(--space-xs); +} + +.section-eyebrow .num { + color: var(--color-accent); + font-weight: 500; +} + +.section-h { + font-family: var(--font-display); + font-weight: 700; + font-size: var(--text-4xl); + line-height: var(--leading-tight); + letter-spacing: -0.025em; + color: var(--color-ink); + margin: 0; + max-width: 22ch; + overflow-wrap: anywhere; + min-width: 0; +} + +.section-note { + margin: var(--space-xs) 0 0; + color: var(--color-ink-3); + font-size: var(--text-sm); + max-width: var(--measure); +} + +/* ── format band ─────────────────────────────────────────── */ + +.format { + position: relative; + padding: var(--space-3xl) var(--page-gutter) var(--space-3xl); + border-block: var(--rule-hair) solid var(--color-rule); + background: color-mix(in oklch, var(--color-paper-2) 85%, transparent); +} + +.format .section-head { + margin-bottom: var(--space-2xl); +} + +.format__schedule { + list-style: none; + margin: 0 auto var(--space-2xl); + padding: 0; + max-width: var(--max-width); + display: flex; + flex-direction: column; +} + +.format__row { + display: grid; + grid-template-columns: minmax(7rem, 12rem) minmax(8rem, 16rem) 1fr; + gap: var(--space-lg); + padding: var(--space-lg) 0; + border-top: var(--rule-hair) solid var(--color-rule); + align-items: baseline; +} + +.format__row:last-child { border-bottom: var(--rule-hair) solid var(--color-rule); } + +.format__time { + font-family: var(--font-mono); + font-size: var(--text-2xl); + font-weight: 500; + color: var(--color-ink); + letter-spacing: -0.02em; + white-space: nowrap; +} + +.format__time small { + font-size: 0.55em; + color: var(--color-ink-3); + margin-left: 4px; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.format__label { + font-family: var(--font-display); + font-size: var(--text-lg); + font-weight: 600; + color: var(--color-ink); + letter-spacing: -0.015em; +} + +.format__note { + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: var(--leading-snug); +} + +.format__row--reveal .format__time, +.format__row--reveal .format__label { + color: var(--color-ink-3); +} +.format__row--reveal .format__label::after { + content: " ◔"; + color: var(--color-accent); + margin-left: 6px; + font-size: 0.85em; +} + +.format__cta { + max-width: var(--max-width); + margin: 0 auto; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-md); +} + +.format__cta-note { + color: var(--color-ink-3); + font-size: var(--text-sm); + font-family: var(--font-mono); + margin: 0; +} + +/* ── shared cta ──────────────────────────────────────────── */ + +.cta { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: var(--space-sm) var(--space-lg); + border-radius: var(--radius-pill); + font-family: var(--font-body); + font-weight: 600; + font-size: var(--text-md); + white-space: nowrap; + transition: + transform var(--dur-fast) var(--ease-out), + background var(--dur-fast) var(--ease-out), + box-shadow var(--dur-base) var(--ease-out); + cursor: pointer; + border: var(--rule-hair) solid transparent; + letter-spacing: -0.01em; +} + +.cta--primary { + background: var(--color-accent); + color: var(--color-accent-ink); + box-shadow: + 0 0 0 0 color-mix(in oklch, var(--color-accent) 0%, transparent), + 0 6px 28px -8px color-mix(in oklch, var(--color-accent) 50%, transparent); +} +.cta--primary:hover { + transform: translateY(-1px); + background: color-mix(in oklch, var(--color-accent) 88%, var(--color-ink)); + box-shadow: + 0 0 0 4px color-mix(in oklch, var(--color-accent) 18%, transparent), + 0 10px 36px -8px color-mix(in oklch, var(--color-accent) 65%, transparent); +} +.cta--primary:active { transform: translateY(0); } +.cta--primary:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; +} +.cta--primary[disabled] { + opacity: 0.5; + pointer-events: none; +} +.cta--primary[data-state="loading"] { background: var(--color-paper-3); color: var(--color-ink-2); } +.cta--primary[data-state="success"] { + background: color-mix(in oklch, var(--color-success) 80%, var(--color-ink)); + color: var(--color-paper); +} + +.cta--lg { padding: var(--space-md) var(--space-xl); font-size: var(--text-md); } + +.cta__arrow, +.nav__cta-arrow { + transition: transform var(--dur-fast) var(--ease-out); +} +.cta:hover .cta__arrow, +.nav__cta:hover .nav__cta-arrow { + transform: translateX(3px); +} + +/* ── why · pitch ─────────────────────────────────────────── */ + +.why { + position: relative; + padding: var(--space-4xl) var(--page-gutter); + max-width: var(--max-width); + margin: 0 auto; +} + +.why__body { + max-width: 60ch; + font-size: var(--text-lg); + line-height: 1.65; + color: var(--color-ink-2); + display: flex; + flex-direction: column; + gap: var(--space-lg); +} + +.why__lead { + font-size: var(--text-xl); + color: var(--color-ink); + font-weight: 500; + line-height: 1.5; + letter-spacing: -0.015em; +} + +.why__body em { + font-style: italic; + color: var(--color-ink); + font-weight: 500; +} + +.why__signoff { + font-family: var(--font-mono); + font-size: var(--text-sm); + color: var(--color-ink-3); + letter-spacing: 0; + text-transform: none; + margin-top: var(--space-md); +} + +/* ── programme grid ──────────────────────────────────────── */ + +.program { + padding: var(--space-3xl) var(--page-gutter) var(--space-4xl); + border-top: var(--rule-hair) solid var(--color-rule); +} + +.program__grid { + list-style: none; + margin: 0 auto; + padding: 0; + max-width: var(--max-width); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); + gap: var(--space-md); +} + +.card { + position: relative; + padding: var(--space-lg); + border: var(--rule-hair) solid var(--color-rule); + background: color-mix(in oklch, var(--color-paper-2) 92%, transparent); + border-radius: var(--radius-md); + display: flex; + flex-direction: column; + gap: var(--space-xs); + transition: + transform var(--dur-base) var(--ease-out), + border-color var(--dur-base) var(--ease-out), + background var(--dur-base) var(--ease-out); + min-height: 14rem; +} + +.card:hover { + transform: translateY(-2px); + border-color: var(--color-rule-strong); + background: var(--color-paper-3); +} + +.card__time { + font-family: var(--font-mono); + font-size: var(--text-sm); + color: var(--color-accent); + letter-spacing: -0.01em; + margin: 0; + font-weight: 500; +} + +.card__type { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-3); + letter-spacing: 0.08em; + text-transform: uppercase; + margin: 0; +} + +.card__h { + margin: var(--space-xs) 0 0; + font-family: var(--font-display); + font-weight: 600; + font-size: var(--text-lg); + line-height: 1.2; + letter-spacing: -0.015em; + color: var(--color-ink); + overflow-wrap: anywhere; + min-width: 0; +} + +.card__sub { + margin: 0; + color: var(--color-ink-2); + font-size: var(--text-sm); + line-height: var(--leading-snug); + flex: 1; +} + +.card__name { + margin: 0; + padding-top: var(--space-sm); + margin-top: auto; + border-top: var(--rule-hair) dashed var(--color-rule); + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-3); + letter-spacing: 0.02em; +} + +.card--launch { + background: linear-gradient( + 180deg, + color-mix(in oklch, var(--color-accent) 12%, var(--color-paper-2)) 0%, + var(--color-paper-2) 90% + ); + border-color: color-mix(in oklch, var(--color-accent) 35%, var(--color-rule)); +} +.card--launch .card__time { color: var(--color-accent); } +.card--launch::before { + content: "LIVE"; + position: absolute; + top: var(--space-md); + right: var(--space-md); + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.18em; + padding: 2px 8px; + background: var(--color-accent); + color: var(--color-accent-ink); + border-radius: var(--radius-xs); + font-weight: 600; +} + +.card--keynote { border-color: color-mix(in oklch, var(--color-accent-2) 30%, var(--color-rule)); } +.card--keynote .card__time { color: var(--color-accent-2); } + +.card--tba { + background: transparent; + border-style: dashed; + border-color: var(--color-rule); +} +.card--tba .card__h { color: var(--color-ink-2); } +.card--tba:hover { border-color: var(--color-rule-strong); } + +/* ── rsvp ────────────────────────────────────────────────── */ + +.rsvp { + padding: var(--space-3xl) var(--page-gutter); + background: linear-gradient( + 180deg, + transparent 0%, + color-mix(in oklch, var(--color-paper-2) 70%, transparent) 100% + ); + border-top: var(--rule-hair) solid var(--color-rule); +} + +.rsvp__inner { + max-width: 56rem; + margin: 0 auto; +} + +.rsvp__h { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-4xl); + line-height: var(--leading-tight); + letter-spacing: -0.035em; + margin: var(--space-xs) 0 var(--space-md); + max-width: 18ch; + overflow-wrap: anywhere; + min-width: 0; +} + +.rsvp__lead { + max-width: var(--measure); + color: var(--color-ink-2); + font-size: var(--text-md); + margin: 0 0 var(--space-2xl); + line-height: var(--leading-body); +} + +.rsvp__form { + display: flex; + flex-direction: column; + gap: var(--space-md); +} + +.rsvp__row { + display: grid; + grid-template-columns: 1fr auto; + gap: var(--space-sm); + align-items: stretch; +} + +.rsvp__field { + display: block; + position: relative; +} + +.rsvp__field-label { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} + +.rsvp__field input { + width: 100%; + padding: var(--space-md) var(--space-lg); + background: var(--color-paper); + border: var(--rule-hair) solid var(--color-rule-strong); + border-radius: var(--radius-pill); + color: var(--color-ink); + font-size: var(--text-md); + transition: + border-color var(--dur-fast) var(--ease-out), + background var(--dur-fast) var(--ease-out); +} + +.rsvp__field input::placeholder { color: var(--color-ink-3); } + +.rsvp__field input:hover { border-color: var(--color-paper-4); } +.rsvp__field input:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 2px; + border-color: var(--color-accent); +} + +.rsvp__field input:user-invalid { + border-color: var(--color-error); +} + +.rsvp__help { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-3); + margin: 0; + letter-spacing: 0.02em; +} + +.rsvp__success { + display: none; + font-family: var(--font-mono); + font-size: var(--text-sm); + color: var(--color-success); + letter-spacing: 0; + padding: var(--space-sm) var(--space-md); + background: color-mix(in oklch, var(--color-success) 12%, transparent); + border: var(--rule-hair) solid color-mix(in oklch, var(--color-success) 40%, transparent); + border-radius: var(--radius-md); +} +.rsvp__success[data-state="ok"] { display: block; } + +/* ── faq ─────────────────────────────────────────────────── */ + +.faq { + padding: var(--space-3xl) var(--page-gutter) var(--space-4xl); + border-top: var(--rule-hair) solid var(--color-rule); +} + +.faq__list { + max-width: var(--max-width); + margin: 0 auto; + display: flex; + flex-direction: column; +} + +.faq__item { + border-top: var(--rule-hair) solid var(--color-rule); +} +.faq__item:last-child { border-bottom: var(--rule-hair) solid var(--color-rule); } + +.faq__item summary { + list-style: none; + cursor: pointer; + padding: var(--space-lg) 0; + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--space-md); + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 500; + letter-spacing: -0.015em; + color: var(--color-ink); + transition: color var(--dur-fast) var(--ease-out); +} + +.faq__item summary::-webkit-details-marker { display: none; } + +.faq__item summary > span:first-child { + flex: 1; + min-width: 0; +} + +.faq__item summary:hover { color: var(--color-accent); } +.faq__item summary:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 4px; + border-radius: var(--radius-xs); +} + +.faq__chev { + font-family: var(--font-mono); + font-size: var(--text-xl); + color: var(--color-ink-3); + transition: transform var(--dur-base) var(--ease-out); + font-weight: 400; + flex: 0 0 auto; + line-height: 1; +} + +.faq__item[open] .faq__chev { + transform: rotate(45deg); + color: var(--color-accent); +} + +.faq__a { + padding: 0 0 var(--space-lg); + max-width: 60ch; + color: var(--color-ink-2); + font-size: var(--text-md); + line-height: var(--leading-body); +} + +.faq__a p { margin: 0; } +.faq__a a { color: var(--color-accent); border-bottom: 1px dotted currentColor; } +.faq__a a:hover { color: color-mix(in oklch, var(--color-accent) 88%, var(--color-ink)); } + +/* ── footer · Ft5 statement ──────────────────────────────── */ + +.footer { + padding: var(--space-3xl) var(--page-gutter) var(--space-2xl); + border-top: var(--rule-hair) solid var(--color-rule); +} + +.footer__inner { + max-width: var(--max-width); + margin: 0 auto; +} + +.footer__statement { + font-family: var(--font-display); + font-weight: 500; + font-size: var(--text-3xl); + line-height: 1.15; + letter-spacing: -0.025em; + color: var(--color-ink); + margin: 0 0 var(--space-3xl); + max-width: 24ch; + overflow-wrap: anywhere; + min-width: 0; +} + +.footer__statement em { + font-style: italic; + color: var(--color-accent); + font-weight: 600; +} + +.footer__meta { + display: flex; + flex-wrap: wrap; + gap: var(--space-md) var(--space-2xl); + align-items: baseline; + justify-content: space-between; + padding-top: var(--space-lg); + border-top: var(--rule-hair) solid var(--color-rule); + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-3); + letter-spacing: 0.03em; +} + +.footer__line { margin: 0; } + +.footer__links { + display: flex; + flex-wrap: wrap; + gap: var(--space-lg); + list-style: none; + margin: 0; + padding: 0; +} + +.footer__links a { + color: var(--color-ink-3); + transition: color var(--dur-fast) var(--ease-out); + white-space: nowrap; +} +.footer__links a:hover { color: var(--color-ink); } +.footer__links a:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; + border-radius: var(--radius-xs); +} + +/* ── responsive ──────────────────────────────────────────── */ + +@media (max-width: 60rem) { + .nav__inner { + grid-template-columns: auto 1fr auto; + padding: var(--space-2xs) var(--space-2xs) var(--space-2xs) var(--space-md); + } + .nav__links { display: none; } + .nav__cta { font-size: var(--text-xs); padding: var(--space-2xs) var(--space-md); } + + .hero { + min-height: clamp(30rem, 76dvh, 44rem); + padding: var(--space-2xl) var(--page-gutter); + gap: var(--space-lg); + } + + .hero__display { letter-spacing: -0.035em; } + + .hero__meta { + flex-direction: column; + gap: var(--space-md); + padding-top: var(--space-md); + } + + .section-h { font-size: var(--text-3xl); } + + .format__row { + grid-template-columns: minmax(5rem, 7rem) 1fr; + grid-template-areas: "time label" "time note"; + gap: var(--space-xs) var(--space-md); + padding: var(--space-md) 0; + } + .format__time { grid-area: time; font-size: var(--text-xl); align-self: start; padding-top: 4px; } + .format__label { grid-area: label; } + .format__note { grid-area: note; } + + .rsvp__row { grid-template-columns: 1fr; } + .rsvp__row .cta { justify-content: center; } + .rsvp__h { font-size: var(--text-3xl); } + + .faq__item summary { font-size: var(--text-lg); padding: var(--space-md) 0; } + + .footer__statement { font-size: var(--text-2xl); } + .footer__meta { flex-direction: column; align-items: flex-start; gap: var(--space-sm); } +} + +@media (max-width: 26rem) { + .nav__brand-num { display: none; } + .nav__cta span:first-child { display: none; } + .nav__cta { padding: var(--space-2xs) var(--space-sm); } + + .format__row { + grid-template-columns: 1fr; + grid-template-areas: "time" "label" "note"; + } + .format__time { padding-top: 0; } +} + +/* ── reduced motion ──────────────────────────────────────── */ + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.001ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.001ms !important; + scroll-behavior: auto !important; + } + + .hero__line { opacity: 1; transform: none; animation: none; } + .nav__brand-dot, + .hero__rail-dot { animation: none; opacity: 1; } + .hero__spotlight { transition: none; --mx: 50%; --my: 35%; } + .bloom { opacity: 0.45; } +} diff --git a/site/examples/hyperlane/tokens.css b/site/examples/hyperlane/tokens.css new file mode 100644 index 0000000..2e58b4f --- /dev/null +++ b/site/examples/hyperlane/tokens.css @@ -0,0 +1,83 @@ +/* Hallmark · pre-emit critique: P5 H5 E5 S5 R4 V5 + * genre: atmospheric · macrostructure: Marquee Hero · theme: Bloom + * tokens · paper-band: dark · display-style: weighty-sans · accent-hue: warm-amber + */ + +:root { + color-scheme: dark; + + /* === Color: Bloom (atmospheric) ============================== */ + --color-paper: oklch(13% 0.018 35); + --color-paper-2: oklch(17% 0.020 35); + --color-paper-3: oklch(22% 0.022 35); + --color-paper-4: oklch(28% 0.020 35); + --color-ink: oklch(95% 0.010 70); + --color-ink-2: oklch(78% 0.015 60); + --color-ink-3: oklch(58% 0.015 50); + --color-rule: oklch(28% 0.018 40); + --color-rule-strong: oklch(40% 0.025 40); + --color-accent: oklch(74% 0.180 55); + --color-accent-2: oklch(68% 0.220 18); + --color-accent-soft: oklch(74% 0.060 55); + --color-accent-ink: oklch(15% 0.040 50); + --color-focus: oklch(82% 0.180 55); + --color-error: oklch(70% 0.220 25); + --color-success: oklch(74% 0.160 145); + --color-bloom-1: oklch(74% 0.220 50); + --color-bloom-2: oklch(60% 0.220 15); + + /* === Typography ============================================== */ + --font-display: "Inter Tight", system-ui, sans-serif; + --font-body: "Inter", system-ui, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", monospace; + --font-accent: "Instrument Serif", "Times New Roman", Georgia, serif; + + --text-xs: 0.75rem; + --text-sm: 0.8125rem; + --text-base: 1rem; + --text-md: 1.0625rem; + --text-lg: 1.25rem; + --text-xl: 1.5rem; + --text-2xl: 2rem; + --text-3xl: 2.75rem; + --text-4xl: clamp(2.5rem, 4vw + 1rem, 4.5rem); + --text-display: clamp(3rem, 11vw + 0.25rem, 9.5rem); + + --leading-display: 0.92; + --leading-tight: 1.05; + --leading-snug: 1.25; + --leading-body: 1.55; + + /* === Spacing (4 pt) ========================================== */ + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-sm: 0.75rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2.5rem; + --space-2xl: 4rem; + --space-3xl: 6rem; + --space-4xl: 9rem; + + /* === Motion ================================================== */ + --ease-out: cubic-bezier(0.20, 0.80, 0.20, 1.00); + --ease-in: cubic-bezier(0.40, 0.00, 1.00, 1.00); + --ease-in-out: cubic-bezier(0.40, 0.00, 0.20, 1.00); + --dur-fast: 120ms; + --dur-base: 220ms; + --dur-slow: 400ms; + + /* === Rules / radii =========================================== */ + --rule-hair: 1px; + --rule-bold: 2px; + --radius-xs: 3px; + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-pill: 999px; + + /* === Layout ================================================== */ + --page-gutter: clamp(1rem, 4vw, 3rem); + --max-width: 80rem; + --measure: 60ch; +} diff --git a/site/examples/najm/.hallmark/log.json b/site/examples/najm/.hallmark/log.json new file mode 100644 index 0000000..ab0cb4a --- /dev/null +++ b/site/examples/najm/.hallmark/log.json @@ -0,0 +1,11 @@ +[ + { + "date": "2026-05-04", + "macrostructure": "Marquee Hero", + "theme": "custom", + "theme_axes": "light / geometric-sans / warm", + "vibe": "hyped Moroccan drop, editorial fashion, sun-warm earth", + "enrichment": "Tier-A pure-CSS art (zellige + textile gradients)", + "brief": "NAJM · hyped Moroccan clothing brand · landing + cart" + } +] diff --git a/site/examples/najm/index.html b/site/examples/najm/index.html new file mode 100644 index 0000000..ff2ff71 --- /dev/null +++ b/site/examples/najm/index.html @@ -0,0 +1,368 @@ + + + + + + NAJM · W’25 — Made in Morocco. Worn anywhere. + + + + + + + + + + + +
+ Free shipping in Morocco over 1500 MAD + New drop · Friday 06.05 · 18h00 + Use code NAJM10 · 100 MAD off your first order +
+ + + + + + + + +
+
+
+

Live · 2,318 watching the drop

+

+ New cuts.
+ Old soul.
+ Made in Morocco. +

+

+ Twenty-three pieces, hand-cut in Marrakech and shipped from Casablanca. The Winter ’25 drop opens Friday at 18h00. +

+ +
+ + +
+
+ + +
+
+
+

The W’25 Collection

+

Twenty-three pieces. Made to be worn out.

+
+
+

Pre-order opens Friday 06.05 at 18h00 (Casablanca). First 200 orders ship same-week.

+ View the full drop +
+
+ +
+ +
+
+
+ + New + +
+
+
+

Atlas Wide-Leg Trouser

+

Heavy linen · Bone

+
+

980 MAD

+
+
+ +
+
+
+ + Low stock + +
+
+
+

Zellige Knit Sweater

+

Atlas wool · Saffron

+
+

1,180 MAD

+
+
+ +
+
+
+ + +
+
+
+

Saadian Linen Shirt

+

Hand-loomed linen · Ink

+
+

820 640 MAD

+
+
+ +
+
+
+ + New + +
+
+
+

Riad Wool Coat

+

Boiled Atlas wool · Marine

+
+

2,480 MAD

+
+
+ +
+
+ + +
+
+ + +
+

Lookbook · W’25 / 02

+

+ A coat that should outlast its country of origin. +

+ +
+ +
+

+ Every piece is built for ten years. Boiled Atlas wool, hand-loomed linen, vegetable-tanned leather — sourced inside the country, cut by people we know by name. +

+ + See the full lookbook + +
+
+
+ + +
+
+
+ 24h +
+

Same-week shipping

+

Orders before 14h00 (Casablanca) leave the same day. Tracked, signed, in your hands.

+
+
+
+ 14d +
+

Free returns, no friction

+

Don't love it, send it back. Anywhere in Morocco — we cover the courier.

+
+
+
+ 100% +
+

Made in Morocco

+

Marrakech atelier, Casablanca warehouse. Every label tells you who cut the piece.

+
+
+
+ 1% +
+

Oasis reforestation

+

One percent of every order plants palms in the Drâa-Tafilalet oases. Receipt with every shipment.

+
+
+
+
+ + +
+
+
+

Get the drop before it goes live.

+

Friday at 18h00 — first dibs on twenty-three pieces. Some are made once.

+
+
+
+ + +
+

No spam. One mail per drop. Unsubscribe with one tap.

+
+
+
+ + + + + +
+ W’25 drop · Friday 18h00 + +
+ + + + + + + + diff --git a/site/examples/najm/script.js b/site/examples/najm/script.js new file mode 100644 index 0000000..da9b669 --- /dev/null +++ b/site/examples/najm/script.js @@ -0,0 +1,133 @@ +// NAJM · cart drawer + add-to-bag micro +const $ = (s, p = document) => p.querySelector(s); +const $$ = (s, p = document) => Array.from(p.querySelectorAll(s)); + +const drawer = $("#drawer"); +const scrim = $("#scrim"); +const cartBtn = $("#cartBtn"); +const cartCount = $("#cartCount"); +const cartTotal = $("#cartTotal"); +const cartLines = $("#cartLines"); +const cartFree = $("#cartFree"); + +const state = { + items: [ + { id: "atlas", name: "Atlas Wide-Leg Trouser", variant: "Bone · 32", price: 980, qty: 1, surface: "bone" }, + { id: "medina", name: "Médina Cropped Jacket", variant: "Terracotta · M", price: 1450, qty: 1, surface: "terracotta" }, + ], +}; + +const fmt = (n) => `${n.toLocaleString("fr-MA")} MAD`; +const FREE_OVER = 1500; + +function render() { + cartLines.innerHTML = state.items.map(item => ` +
+
+
+
+
+
+
${item.name}
+
${item.variant}
+
+ + ${item.qty} + +
+
+
${fmt(item.price * item.qty)}
+
+ `).join(""); + + const subtotal = state.items.reduce((s, i) => s + i.price * i.qty, 0); + const count = state.items.reduce((s, i) => s + i.qty, 0); + + cartCount.textContent = count; + cartTotal.textContent = fmt(subtotal); + + const remain = FREE_OVER - subtotal; + cartFree.textContent = remain > 0 + ? `${fmt(remain)} away from free shipping` + : "Free shipping unlocked · ships in 24h"; +} + +function open() { + drawer.classList.add("is-open"); + scrim.classList.add("is-open"); + drawer.setAttribute("aria-hidden", "false"); + document.body.style.overflow = "hidden"; + setTimeout(() => $(".drawer__close")?.focus(), 50); +} +function close() { + drawer.classList.remove("is-open"); + scrim.classList.remove("is-open"); + drawer.setAttribute("aria-hidden", "true"); + document.body.style.overflow = ""; + cartBtn.focus(); +} + +cartBtn.addEventListener("click", open); +$("#mobileCart")?.addEventListener("click", open); +scrim.addEventListener("click", close); +$(".drawer__close").addEventListener("click", close); +document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && drawer.classList.contains("is-open")) close(); +}); + +cartLines.addEventListener("click", (e) => { + const btn = e.target.closest("button[data-act]"); + if (!btn) return; + const line = btn.closest(".drawer__line"); + const item = state.items.find(i => i.id === line.dataset.id); + if (!item) return; + if (btn.dataset.act === "inc") item.qty += 1; + if (btn.dataset.act === "dec") item.qty = Math.max(1, item.qty - 1); + render(); +}); + +// Add-to-bag from product cards +$$("[data-add]").forEach(btn => { + btn.addEventListener("click", (e) => { + e.preventDefault(); + const card = btn.closest("[data-product]"); + const id = card.dataset.product; + const found = state.items.find(i => i.id === id); + if (found) { + found.qty += 1; + } else { + state.items.push({ + id, + name: card.dataset.name, + variant: card.dataset.variant, + price: Number(card.dataset.price), + surface: card.dataset.surface, + qty: 1, + }); + } + render(); + cartCount.classList.remove("is-pulse"); + void cartCount.offsetWidth; // restart animation + cartCount.classList.add("is-pulse"); + btn.textContent = "Added ✓"; + setTimeout(() => { btn.textContent = btn.dataset.label || "Add to bag"; }, 1400); + }); +}); + +// Newsletter — inline confirmation, no toast +$("#news")?.addEventListener("submit", (e) => { + e.preventDefault(); + const input = e.target.querySelector("input"); + const note = $("#newsNote"); + if (!input.value.includes("@")) { + note.textContent = "Add a valid email to subscribe."; + note.style.color = "var(--color-accent-deep)"; + input.focus(); + return; + } + note.textContent = `${input.value} is on the list — first drop ships Friday.`; + note.style.color = "var(--color-ink)"; + input.value = ""; +}); + +render(); diff --git a/site/examples/najm/styles.css b/site/examples/najm/styles.css new file mode 100644 index 0000000..2c111d2 --- /dev/null +++ b/site/examples/najm/styles.css @@ -0,0 +1,1062 @@ +/* Hallmark · macrostructure: Marquee Hero · genre: editorial + * vibe: "hyped Moroccan drop, editorial fashion, sun-warm earth" + * theme: custom · paper oklch(96.5% 0.012 75) warm-cream · accent oklch(58% 0.18 35) terracotta + * display: Bricolage Grotesque 700-900 · body: Inter 400/500 + * axes: light / geometric-sans / warm + * studied: no + */ + +@import url("./tokens.css"); + +/* ── Reset ─────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; } +* { margin: 0; padding: 0; } +html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } +body { + background: var(--color-paper); + color: var(--color-ink); + font-family: var(--font-body); + font-size: var(--text-md); + line-height: var(--leading-body); + font-feature-settings: "ss01", "cv11"; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} +img, svg, video { display: block; max-width: 100%; } +button { + font: inherit; + color: inherit; + background: none; + border: 0; + cursor: pointer; +} +a { color: inherit; text-decoration: none; } +ul, ol { list-style: none; } +input, select { font: inherit; color: inherit; } + +/* Focus ring · instant, no animation, visible against any band */ +:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 3px; + border-radius: 2px; +} + +/* ── Layout primitives ────────────────────────────── */ +.shell { + width: 100%; + max-width: var(--max-w); + margin-inline: auto; + padding-inline: var(--gutter); +} + +.eyebrow { + font-family: var(--font-body); + font-size: var(--text-xs); + font-weight: 500; + letter-spacing: var(--track-wide); + text-transform: uppercase; + color: var(--color-ink-soft); +} + +/* ── Announcement bar ─────────────────────────────── */ +.announce { + background: var(--color-ink); + color: var(--color-paper); + font-size: var(--text-xs); + letter-spacing: 0.06em; + padding-block: 10px; + text-align: center; +} +.announce strong { color: var(--color-saffron); font-weight: 600; } +.announce span + span { margin-inline-start: var(--space-5); } +@media (max-width: 640px) { + .announce span + span { display: none; } +} + +/* ── Top nav ──────────────────────────────────────── */ +.nav { + position: sticky; + top: 0; + z-index: 30; + background: color-mix(in oklch, var(--color-paper) 88%, transparent); + backdrop-filter: saturate(140%) blur(10px); + -webkit-backdrop-filter: saturate(140%) blur(10px); + border-bottom: var(--rule-thin) solid var(--color-rule); +} +.nav__inner { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: var(--space-5); + padding-block: var(--space-3); +} +.brand { + display: inline-flex; + align-items: baseline; + gap: 8px; + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-xl); + letter-spacing: var(--track-display); +} +.brand__ar { + font-size: var(--text-base); + color: var(--color-accent); + font-weight: 600; +} +.nav__links { + display: flex; + justify-content: center; + gap: var(--space-6); +} +.nav__links a { + font-size: var(--text-sm); + font-weight: 500; + position: relative; + padding-block: 6px; + transition: color var(--dur-fast) var(--ease-out); +} +.nav__links a::after { + content: ""; + position: absolute; + left: 0; right: 0; + bottom: 0; + height: 1px; + background: currentColor; + transform: scaleX(0); + transform-origin: left; + transition: transform var(--dur-base) var(--ease-out); +} +.nav__links a:hover::after, +.nav__links a:focus-visible::after { transform: scaleX(1); } +.nav__util { + display: flex; + align-items: center; + gap: var(--space-3); +} +.icon-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 44px; height: 44px; + border-radius: var(--radius-pill); + transition: background-color var(--dur-fast) var(--ease-out); +} +.icon-btn:hover { background: var(--color-paper-warm); } +.cart-btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 14px 9px 12px; + border-radius: var(--radius-pill); + background: var(--color-ink); + color: var(--color-paper); + font-size: var(--text-sm); + font-weight: 500; + transition: transform var(--dur-fast) var(--ease-out); +} +.cart-btn:hover { transform: translateY(-1px); } +.cart-btn:active { transform: translateY(0); } +.cart-btn__count { + display: inline-flex; + min-width: 22px; height: 22px; + align-items: center; justify-content: center; + padding-inline: 6px; + border-radius: var(--radius-pill); + background: var(--color-accent); + color: var(--color-paper); + font-size: 11px; font-weight: 700; + font-variant-numeric: tabular-nums; + transition: transform var(--dur-base) var(--ease-out); +} +.cart-btn__count.is-pulse { animation: pulse 360ms var(--ease-out); } + +@keyframes pulse { + 0% { transform: scale(1); } + 40% { transform: scale(1.35); } + 100% { transform: scale(1); } +} + +@media (max-width: 760px) { + .nav__links { display: none; } +} + +/* ── Marquee strip · brand language ───────────────── */ +.marquee { + background: var(--color-accent); + color: var(--color-paper); + border-block: var(--rule-thin) solid var(--color-accent-deep); + overflow: hidden; + padding-block: 14px; +} +.marquee__track { + display: flex; + width: max-content; + gap: var(--space-7); + font-family: var(--font-display); + font-weight: 700; + font-size: clamp(1.125rem, 2.4vw, 1.5rem); + letter-spacing: var(--track-display); + text-transform: uppercase; + white-space: nowrap; + animation: scroll-x 38s linear infinite; + will-change: transform; +} +.marquee__track > span { + display: inline-flex; + align-items: center; + gap: var(--space-7); +} +.marquee__star { + display: inline-block; + width: 18px; height: 18px; + background: currentColor; + clip-path: polygon( + 50% 0%, 61% 35%, 98% 35%, 68% 57%, + 79% 91%, 50% 70%, 21% 91%, 32% 57%, + 2% 35%, 39% 35% + ); +} +@keyframes scroll-x { + to { transform: translateX(-50%); } +} +@media (prefers-reduced-motion: reduce) { + .marquee__track { animation: none; transform: translateX(0); } +} + +/* ── Hero ─────────────────────────────────────────── */ +.hero { + position: relative; + padding-top: var(--space-7); + padding-bottom: var(--space-9); + overflow: hidden; +} +.hero__grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-7); + align-items: end; +} +.hero__lead { + display: flex; + flex-direction: column; + gap: var(--space-5); +} +.hero__meta { + display: flex; + align-items: center; + gap: var(--space-3); + font-size: var(--text-xs); + letter-spacing: var(--track-wide); + text-transform: uppercase; + color: var(--color-ink-soft); +} +.hero__meta b { + display: inline-block; + width: 8px; height: 8px; + background: var(--color-accent); + border-radius: 999px; + animation: blink 1.6s var(--ease-in-out) infinite; +} +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} +@media (prefers-reduced-motion: reduce) { + .hero__meta b { animation: none; } +} + +.hero__h1 { + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-hero); + line-height: 0.86; + letter-spacing: var(--track-display); + color: var(--color-ink); + text-wrap: balance; +} +.hero__h1 em { + font-style: italic; + color: var(--color-accent); + font-weight: 800; +} +.hero__sub { + max-width: 38ch; + font-size: var(--text-lg); + color: var(--color-ink-soft); + line-height: 1.4; +} +.hero__sub em { + font-style: italic; + color: var(--color-ink); +} + +.hero__cta-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-4); + margin-top: var(--space-3); +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 16px 24px; + border-radius: var(--radius-pill); + font-weight: 600; + font-size: var(--text-md); + letter-spacing: -0.01em; + transition: + transform var(--dur-fast) var(--ease-out), + box-shadow var(--dur-base) var(--ease-out), + background-color var(--dur-fast) var(--ease-out); + min-height: 48px; +} +.btn--primary { + background: var(--color-ink); + color: var(--color-paper); +} +.btn--primary:hover { + transform: translateY(-2px); + box-shadow: 0 12px 28px -12px var(--color-overlay-ink); +} +.btn--primary:active { transform: translateY(0); } +.btn--ghost { + color: var(--color-ink); + border: var(--rule-thin) solid var(--color-ink); +} +.btn--ghost:hover { + background: var(--color-ink); + color: var(--color-paper); +} +.btn__arrow { + display: inline-block; + transition: transform var(--dur-base) var(--ease-out); +} +.btn:hover .btn__arrow { transform: translateX(4px); } + +/* Hero featured product card */ +.hero__feature { + position: relative; + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); +} +.feature-card { + position: relative; + display: block; + border-radius: var(--radius-lg); + overflow: hidden; + aspect-ratio: 4 / 5; + background: var(--color-paper-warm); + isolation: isolate; + transition: transform var(--dur-slow) var(--ease-out); +} +.feature-card:hover { transform: translateY(-4px); } + +/* Pure-CSS art product surfaces · zellige + textile gradients */ +.surface { + position: absolute; + inset: 0; + z-index: 0; +} +.surface--terracotta { + background: + radial-gradient(circle at 30% 25%, oklch(74% 0.16 38) 0%, transparent 55%), + radial-gradient(circle at 75% 80%, oklch(46% 0.18 30) 0%, transparent 60%), + linear-gradient(140deg, oklch(58% 0.18 35) 0%, oklch(40% 0.16 28) 100%); +} +.surface--bone { + background: + radial-gradient(circle at 70% 20%, oklch(96% 0.020 80) 0%, transparent 55%), + linear-gradient(160deg, oklch(91% 0.025 75) 0%, oklch(82% 0.030 70) 100%); +} +.surface--ink { + background: + radial-gradient(circle at 25% 30%, oklch(36% 0.04 60) 0%, transparent 60%), + linear-gradient(150deg, oklch(22% 0.025 55) 0%, oklch(14% 0.020 55) 100%); +} +.surface--saffron { + background: + radial-gradient(circle at 30% 30%, oklch(85% 0.16 80) 0%, transparent 55%), + linear-gradient(150deg, oklch(75% 0.16 75) 0%, oklch(60% 0.16 70) 100%); +} +.surface--marine { + background: + radial-gradient(circle at 25% 25%, oklch(48% 0.10 240) 0%, transparent 60%), + linear-gradient(150deg, oklch(34% 0.09 240) 0%, oklch(22% 0.06 240) 100%); +} + +/* Zellige tile pattern overlay (CSS-only, geometric Moroccan motif) */ +.zellige { + position: absolute; + inset: 0; + z-index: 1; + opacity: 0.18; + mix-blend-mode: overlay; + background-image: + conic-gradient(from 45deg at 50% 50%, + transparent 0deg, currentColor 45deg, transparent 90deg, + currentColor 135deg, transparent 180deg, currentColor 225deg, + transparent 270deg, currentColor 315deg, transparent 360deg); + background-size: 48px 48px; + color: var(--color-paper); + -webkit-mask-image: radial-gradient(ellipse at center, black 0%, transparent 75%); + mask-image: radial-gradient(ellipse at center, black 0%, transparent 75%); +} +.zellige--dark { color: var(--color-ink); } + +/* Card body overlay */ +.feature-card__body { + position: relative; + z-index: 2; + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: var(--space-5); + color: var(--color-paper); +} +.feature-card__body.is-dark { color: var(--color-ink); } + +.feature-card__tag { + display: inline-flex; + align-items: center; + gap: 6px; + align-self: flex-start; + padding: 6px 12px; + border-radius: var(--radius-pill); + background: color-mix(in oklch, var(--color-paper) 22%, transparent); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.is-dark .feature-card__tag { + background: color-mix(in oklch, var(--color-ink) 12%, transparent); +} +.feature-card__title { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-2xl); + line-height: 1; + letter-spacing: var(--track-tight); +} +.feature-card__row { + display: flex; + align-items: end; + justify-content: space-between; + gap: var(--space-3); +} +.feature-card__price { + font-size: var(--text-lg); + font-weight: 600; + font-variant-numeric: tabular-nums; +} +.feature-card__price small { font-size: 0.7em; opacity: 0.8; margin-inline-start: 4px; } +.feature-card__cta { + width: 44px; height: 44px; + border-radius: var(--radius-pill); + background: var(--color-paper); + color: var(--color-ink); + display: inline-flex; + align-items: center; justify-content: center; + transition: transform var(--dur-fast) var(--ease-out); +} +.is-dark .feature-card__cta { + background: var(--color-ink); + color: var(--color-paper); +} +.feature-card:hover .feature-card__cta { transform: rotate(-15deg) scale(1.05); } + +/* Hero-specific feature sizing */ +.hero .feature-card { aspect-ratio: 4 / 5; } + +/* Hero responsive grid */ +@media (min-width: 880px) { + .hero__grid { + grid-template-columns: 1.55fr 1fr; + gap: var(--space-9); + } + .hero__lead { padding-block: var(--space-7) 0; } +} + +/* ── Featured collection ──────────────────────────── */ +.section { + padding-block: var(--space-9); + border-top: var(--rule-thin) solid var(--color-rule); +} +.section__head { + display: flex; + align-items: end; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--space-5); + margin-bottom: var(--space-7); +} +.section__title { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-4xl); + line-height: 1; + letter-spacing: var(--track-display); + max-width: 18ch; + text-wrap: balance; +} +.section__title em { + font-style: italic; + color: var(--color-accent); +} +.section__lede { + max-width: 36ch; + color: var(--color-ink-soft); + font-size: var(--text-md); +} +.section__link { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: var(--text-sm); + font-weight: 500; + border-bottom: var(--rule-thin) solid var(--color-ink); + padding-bottom: 2px; +} + +.collection { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-4) var(--space-3); +} +@media (min-width: 760px) { + .collection { grid-template-columns: repeat(4, 1fr); gap: var(--space-5) var(--space-4); } +} + +.product { + display: flex; + flex-direction: column; + gap: var(--space-3); +} +.product__media { + position: relative; + border-radius: var(--radius-md); + overflow: hidden; + aspect-ratio: 4 / 5; + isolation: isolate; + transition: transform var(--dur-slow) var(--ease-out); +} +.product:hover .product__media { transform: translateY(-3px); } +.product__media .surface { transition: transform var(--dur-slow) var(--ease-out); } +.product:hover .product__media .surface { transform: scale(1.04); } +.product__quick { + position: absolute; + inset: auto var(--space-3) var(--space-3) var(--space-3); + z-index: 3; + display: flex; + align-items: center; + justify-content: center; + padding: 12px; + border-radius: var(--radius-pill); + background: var(--color-paper); + color: var(--color-ink); + font-weight: 600; + font-size: var(--text-sm); + opacity: 0; + transform: translateY(8px); + transition: + opacity var(--dur-base) var(--ease-out), + transform var(--dur-base) var(--ease-out); + min-height: 44px; +} +.product:hover .product__quick, +.product:focus-within .product__quick { + opacity: 1; + transform: translateY(0); +} +.product__badge { + position: absolute; + top: var(--space-3); + left: var(--space-3); + z-index: 2; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + padding: 6px 10px; + border-radius: var(--radius-pill); + background: var(--color-paper); + color: var(--color-ink); +} +.product__badge--new { background: var(--color-accent); color: var(--color-paper); } +.product__badge--low { background: var(--color-ink); color: var(--color-paper); } + +.product__row { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--space-3); +} +.product__name { + font-family: var(--font-display); + font-weight: 700; + font-size: var(--text-md); + letter-spacing: var(--track-tight); +} +.product__price { + font-size: var(--text-md); + font-variant-numeric: tabular-nums; + font-weight: 500; +} +.product__price s { color: var(--color-ink-mute); margin-inline-end: 6px; } +.product__meta { + font-size: var(--text-xs); + color: var(--color-ink-mute); + letter-spacing: 0.02em; +} + +/* On mobile, surface the quick-add by default */ +@media (max-width: 759px) { + .product__quick { + opacity: 1; + transform: translateY(0); + inset: auto 8px 8px 8px; + } +} + +/* ── Editorial moment · Lookbook ──────────────────── */ +.editorial { + position: relative; + border-radius: var(--radius-lg); + overflow: hidden; + isolation: isolate; + margin-block: var(--space-8); + padding: clamp(2rem, 6vw, 5rem); + color: var(--color-paper); + display: grid; + grid-template-columns: 1fr; + gap: var(--space-7); + min-height: 70vh; + align-content: end; +} +.editorial__bg { + position: absolute; + inset: 0; + z-index: -1; + background: + radial-gradient(circle at 20% 10%, oklch(70% 0.18 38) 0%, transparent 50%), + radial-gradient(circle at 80% 90%, oklch(34% 0.09 240) 0%, transparent 55%), + linear-gradient(150deg, oklch(48% 0.20 32) 0%, oklch(28% 0.08 30) 100%); +} +.editorial__bg::after { + content: ""; + position: absolute; + inset: 0; + background-image: + conic-gradient(from 0deg at 50% 50%, + transparent 0deg 40deg, + var(--color-paper) 40deg 50deg, + transparent 50deg 130deg, + var(--color-paper) 130deg 140deg, + transparent 140deg 220deg, + var(--color-paper) 220deg 230deg, + transparent 230deg 310deg, + var(--color-paper) 310deg 320deg, + transparent 320deg 360deg + ); + background-size: 64px 64px; + opacity: 0.06; +} +.editorial__eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-paper); + opacity: 0.85; +} +.editorial__quote { + font-family: var(--font-display); + font-style: italic; + font-weight: 500; + font-size: var(--text-5xl); + line-height: 1; + letter-spacing: var(--track-display); + text-wrap: balance; + max-width: 18ch; +} +.editorial__quote::before { + content: "“"; + display: inline-block; + margin-inline-end: 0.05em; + color: var(--color-saffron); +} +.editorial__byline { + font-size: var(--text-sm); + letter-spacing: 0.04em; + opacity: 0.85; +} +.editorial__cta { + align-self: start; + display: inline-flex; + align-items: center; + gap: 10px; + padding: 16px 24px; + border-radius: var(--radius-pill); + background: var(--color-paper); + color: var(--color-ink); + font-weight: 600; + transition: transform var(--dur-fast) var(--ease-out); + min-height: 48px; + margin-top: var(--space-4); +} +.editorial__cta:hover { transform: translateY(-2px); } + +@media (min-width: 880px) { + .editorial { + grid-template-columns: 1fr 1fr; + align-items: end; + gap: var(--space-8); + } + .editorial__quote { font-size: clamp(3rem, 6vw, 5.25rem); } +} + +/* ── Trust strip ──────────────────────────────────── */ +.trust { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-5); + padding-block: var(--space-7); + border-block: var(--rule-thin) solid var(--color-rule); +} +.trust__item { + display: grid; + grid-template-columns: auto 1fr; + gap: var(--space-4); + align-items: start; +} +.trust__num { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-2xl); + color: var(--color-accent); + font-variant-numeric: tabular-nums; + letter-spacing: var(--track-tight); + line-height: 1; +} +.trust__title { + font-family: var(--font-display); + font-weight: 700; + font-size: var(--text-lg); + letter-spacing: var(--track-tight); + margin-bottom: 4px; +} +.trust__body { + color: var(--color-ink-soft); + font-size: var(--text-sm); + max-width: 32ch; +} +@media (min-width: 760px) { + .trust { grid-template-columns: repeat(4, 1fr); gap: var(--space-7); } +} + +/* ── Newsletter ───────────────────────────────────── */ +.news { + margin-block: var(--space-9) var(--space-8); + padding: clamp(2rem, 5vw, 4rem); + border-radius: var(--radius-lg); + background: var(--color-paper-warm); + display: grid; + grid-template-columns: 1fr; + gap: var(--space-6); + align-items: end; +} +.news__h { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-3xl); + line-height: 0.95; + letter-spacing: var(--track-display); + text-wrap: balance; +} +.news__h em { font-style: italic; color: var(--color-accent); } +.news__p { + color: var(--color-ink-soft); + font-size: var(--text-md); + max-width: 40ch; + margin-top: var(--space-3); +} +.news__form { + display: grid; + grid-template-columns: 1fr auto; + gap: var(--space-2); + background: var(--color-paper); + padding: 6px; + border-radius: var(--radius-pill); + border: var(--rule-thin) solid var(--color-rule); +} +.news__form:focus-within { + border-color: var(--color-ink); +} +.news__input { + border: 0; + background: transparent; + padding: 12px 16px; + font-size: var(--text-md); + min-width: 0; + min-height: 44px; +} +.news__input::placeholder { color: var(--color-ink-mute); } +.news__input:focus { outline: none; } +.news__submit { + padding: 12px 20px; + border-radius: var(--radius-pill); + background: var(--color-ink); + color: var(--color-paper); + font-weight: 600; + font-size: var(--text-sm); + min-height: 44px; + transition: transform var(--dur-fast) var(--ease-out); +} +.news__submit:hover { transform: translateY(-1px); } +.news__note { + font-size: var(--text-xs); + color: var(--color-ink-mute); + margin-top: var(--space-3); +} + +@media (min-width: 880px) { + .news { grid-template-columns: 1.2fr 1fr; gap: var(--space-9); } +} + +/* ── Footer ───────────────────────────────────────── */ +.footer { + border-top: var(--rule-thin) solid var(--color-rule); + padding-block: var(--space-7) var(--space-9); + font-size: var(--text-sm); +} +.footer__top { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-6); + margin-bottom: var(--space-7); +} +@media (min-width: 760px) { + .footer__top { grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--space-7); } +} +.footer__brand { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-3xl); + letter-spacing: var(--track-display); + line-height: 1; +} +.footer__tag { color: var(--color-ink-soft); margin-top: var(--space-3); max-width: 32ch; } +.footer__col-h { + font-size: var(--text-xs); + letter-spacing: var(--track-wide); + text-transform: uppercase; + color: var(--color-ink-mute); + margin-bottom: var(--space-3); + font-weight: 500; +} +.footer__col li { margin-bottom: 8px; } +.footer__col a { + transition: color var(--dur-fast) var(--ease-out); +} +.footer__col a:hover { color: var(--color-accent); } +.footer__bottom { + display: flex; + flex-wrap: wrap; + gap: var(--space-4) var(--space-6); + align-items: center; + justify-content: space-between; + padding-top: var(--space-5); + border-top: var(--rule-thin) solid var(--color-rule); + color: var(--color-ink-mute); + font-size: var(--text-xs); +} + +/* ── Cart drawer ──────────────────────────────────── */ +.scrim { + position: fixed; + inset: 0; + z-index: 50; + background: var(--color-overlay-ink); + opacity: 0; + pointer-events: none; + transition: opacity var(--dur-base) var(--ease-out); +} +.scrim.is-open { opacity: 1; pointer-events: auto; } + +.drawer { + position: fixed; + top: 0; right: 0; bottom: 0; + z-index: 51; + width: min(440px, 100vw); + background: var(--color-paper); + transform: translateX(100%); + transition: transform var(--dur-slow) var(--ease-out); + display: flex; + flex-direction: column; + box-shadow: -24px 0 60px -20px var(--color-overlay-ink); +} +.drawer.is-open { transform: translateX(0); } +.drawer__head { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-5); + border-bottom: var(--rule-thin) solid var(--color-rule); +} +.drawer__title { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-xl); + letter-spacing: var(--track-display); +} +.drawer__body { + flex: 1; + overflow-y: auto; + padding: var(--space-5); + display: flex; + flex-direction: column; + gap: var(--space-4); +} +.drawer__line { + display: grid; + grid-template-columns: 64px 1fr auto; + gap: var(--space-4); + align-items: center; + padding-block: var(--space-3); + border-bottom: var(--rule-thin) solid var(--color-rule-soft); +} +.drawer__thumb { + width: 64px; height: 80px; + border-radius: var(--radius-sm); + overflow: hidden; + position: relative; + isolation: isolate; +} +.drawer__name { + font-family: var(--font-display); + font-weight: 700; + font-size: var(--text-md); + letter-spacing: var(--track-tight); +} +.drawer__sub { + font-size: var(--text-xs); + color: var(--color-ink-mute); + margin-top: 2px; +} +.drawer__price { + font-weight: 600; + font-variant-numeric: tabular-nums; +} +.drawer__qty { + display: inline-flex; + align-items: center; + margin-top: 6px; + border: var(--rule-thin) solid var(--color-rule); + border-radius: var(--radius-pill); + overflow: hidden; +} +.drawer__qty button { + width: 28px; height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; +} +.drawer__qty span { + width: 28px; + text-align: center; + font-variant-numeric: tabular-nums; + font-size: var(--text-sm); + font-weight: 500; +} + +.drawer__foot { + padding: var(--space-5); + border-top: var(--rule-thin) solid var(--color-rule); + display: grid; + gap: var(--space-3); +} +.drawer__total { + display: flex; + justify-content: space-between; + align-items: baseline; + font-family: var(--font-display); +} +.drawer__total b { + font-size: var(--text-2xl); + font-weight: 800; + font-variant-numeric: tabular-nums; + letter-spacing: var(--track-tight); +} +.drawer__sub-line { + display: flex; + justify-content: space-between; + font-size: var(--text-xs); + color: var(--color-ink-mute); +} +.drawer__checkout { + margin-top: var(--space-2); + padding: 18px; + border-radius: var(--radius-pill); + background: var(--color-accent); + color: var(--color-paper); + font-weight: 700; + font-size: var(--text-md); + text-align: center; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out); + min-height: 56px; +} +.drawer__checkout:hover { + transform: translateY(-2px); + background: var(--color-accent-deep); +} +.drawer__free { + font-size: var(--text-xs); + text-align: center; + color: var(--color-ink-mute); +} + +/* ── Mobile sticky bar ────────────────────────────── */ +.mobile-bar { + position: fixed; + left: var(--space-3); right: var(--space-3); bottom: var(--space-3); + z-index: 20; + display: none; + align-items: center; + gap: var(--space-3); + padding: 10px 10px 10px 16px; + border-radius: var(--radius-pill); + background: var(--color-ink); + color: var(--color-paper); + box-shadow: 0 16px 40px -16px var(--color-overlay-ink); +} +.mobile-bar__text { + flex: 1; + font-size: var(--text-sm); + font-weight: 500; +} +.mobile-bar__cta { + background: var(--color-accent); + color: var(--color-paper); + border-radius: var(--radius-pill); + padding: 12px 18px; + font-size: var(--text-sm); + font-weight: 700; + min-height: 44px; +} +@media (max-width: 760px) { + .mobile-bar { display: flex; } + body { padding-bottom: 96px; } +} diff --git a/site/examples/najm/tokens.css b/site/examples/najm/tokens.css new file mode 100644 index 0000000..0471f8e --- /dev/null +++ b/site/examples/najm/tokens.css @@ -0,0 +1,97 @@ +/* Hallmark tokens · custom theme · NAJM + * vibe: "hyped Moroccan drop, editorial fashion, sun-warm earth" + * axes: light / geometric-sans / warm + */ + +:root { + /* ── Color (OKLCH) ──────────────────────────── */ + --color-paper: oklch(96.5% 0.012 75); + --color-paper-soft: oklch(99% 0.006 75); + --color-paper-warm: oklch(93% 0.020 70); + --color-ink: oklch(20% 0.022 55); + --color-ink-soft: oklch(38% 0.018 55); + --color-ink-mute: oklch(56% 0.012 60); + --color-rule: oklch(86% 0.014 70); + --color-rule-soft: oklch(91% 0.010 70); + + --color-accent: oklch(58% 0.18 35); + /* terracotta */ + --color-accent-deep: oklch(48% 0.20 32); + --color-accent-soft: oklch(82% 0.08 45); + --color-saffron: oklch(78% 0.16 78); + /* sparing */ + --color-marine: oklch(34% 0.09 240); + /* sparing */ + + --color-overlay-ink: oklch(20% 0.022 55 / 0.55); + --color-focus-ring: oklch(48% 0.20 32); + + /* ── Typography ─────────────────────────────── */ + --font-display: "Bricolage Grotesque", -apple-system, system-ui, sans-serif; + --font-body: "Inter", -apple-system, BlinkMacSystemFont, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + + /* Scale */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-md: 1.0625rem; + --text-lg: 1.25rem; + --text-xl: 1.625rem; + --text-2xl: 2.25rem; + --text-3xl: 3.25rem; + --text-4xl: clamp(2.75rem, 6vw, 4.5rem); + --text-5xl: clamp(3.5rem, 9vw, 7rem); + --text-hero: clamp(2.25rem, 7vw, 5.5rem); + + /* Tracking + leading */ + --track-tight: -0.04em; + --track-display: -0.045em; + --track-wide: 0.16em; + --leading-tight: 0.92; + --leading-snug: 1.1; + --leading-body: 1.55; + + /* ── Spacing · 4pt scale ─────────────────────── */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.5rem; + --space-6: 2rem; + --space-7: 3rem; + --space-8: 4rem; + --space-9: 6rem; + --space-10: 8rem; + --space-11: 12rem; + + /* ── Motion ─────────────────────────────────── */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in: cubic-bezier(0.7, 0, 0.84, 0); + --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + + --dur-fast: 120ms; + --dur-base: 200ms; + --dur-slow: 320ms; + + /* ── Surface ────────────────────────────────── */ + --radius-sm: 4px; + --radius-md: 10px; + --radius-lg: 18px; + --radius-pill: 999px; + + --rule-thin: 1px; + --rule-base: 2px; + + /* Layout */ + --gutter: clamp(1rem, 4vw, 2.5rem); + --max-w: 1440px; +} + +@media (prefers-reduced-motion: reduce) { + :root { + --dur-fast: 0ms; + --dur-base: 0ms; + --dur-slow: 0ms; + } +} \ No newline at end of file diff --git a/site/examples/tally/app.js b/site/examples/tally/app.js new file mode 100644 index 0000000..256ba1f --- /dev/null +++ b/site/examples/tally/app.js @@ -0,0 +1,84 @@ +/* Hallmark · motion primitives: counter · marquee (CSS) · hover-tilt + * respects prefers-reduced-motion + */ + +(function () { + const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + + /* ── counter · live ticker for hero status + invoice + bench stats ── */ + const liveEvents = document.getElementById("live-events"); + const benchEps = document.getElementById("bench-eps"); + const invCalls = document.getElementById("inv-calls"); + const invTotal = document.getElementById("inv-total"); + const benchMrr = document.getElementById("bench-mrr"); + + let eps = 12847; + let calls = 1284512; + let total = 5142.86; + let mrr = 412.6; + + const fmt = (n) => n.toLocaleString("en-US"); + + function tick() { + // small jitter, bounded + eps += Math.round((Math.random() - 0.4) * 80); + if (eps < 11000) eps = 11200; + if (eps > 14500) eps = 14200; + + calls += Math.round(Math.random() * 24 + 4); + total += Math.random() * 0.18; + mrr += (Math.random() - 0.5) * 0.4; + if (mrr < 410) mrr = 410.3; + if (mrr > 416) mrr = 415.8; + + if (liveEvents) liveEvents.textContent = fmt(eps); + if (benchEps) benchEps.textContent = fmt(eps); + if (invCalls) invCalls.textContent = fmt(calls); + if (invTotal) invTotal.textContent = total.toFixed(2); + if (benchMrr) benchMrr.textContent = mrr.toFixed(1); + } + + if (!reduceMotion) { + tick(); + setInterval(tick, 1100); + } + + /* ── pricing toggle ── */ + const pricingButtons = document.querySelectorAll(".pricing__toggle button"); + const pricedSpans = document.querySelectorAll("[data-price-monthly]"); + + pricingButtons.forEach((btn) => { + btn.addEventListener("click", () => { + const period = btn.dataset.period; + pricingButtons.forEach((b) => b.setAttribute("aria-pressed", b === btn ? "true" : "false")); + pricedSpans.forEach((span) => { + span.textContent = period === "annual" ? span.dataset.priceAnnual : span.dataset.priceMonthly; + }); + }); + }); + + /* ── hover-tilt on feature cards ── */ + const tiltCards = document.querySelectorAll("[data-tilt]"); + + if (!reduceMotion) { + tiltCards.forEach((card) => { + let raf = null; + + card.addEventListener("mousemove", (e) => { + const rect = card.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width - 0.5; + const y = (e.clientY - rect.top) / rect.height - 0.5; + if (raf) cancelAnimationFrame(raf); + raf = requestAnimationFrame(() => { + card.style.transform = + `perspective(900px) rotateX(${(-y * 4).toFixed(2)}deg) rotateY(${(x * 5).toFixed(2)}deg) translateY(-2px)`; + }); + }); + + card.addEventListener("mouseleave", () => { + if (raf) cancelAnimationFrame(raf); + card.style.transform = ""; + }); + }); + } +})(); diff --git a/site/examples/tally/index.html b/site/examples/tally/index.html new file mode 100644 index 0000000..8297fa8 --- /dev/null +++ b/site/examples/tally/index.html @@ -0,0 +1,446 @@ + + + + + +Tally · Bill what your customers actually use + + + + + + + + + + + + +
+
+
+ + LIVE · METERING 12,847 EVENTS / SEC +
+ +
+
+

+ Bill what your customers actually use. +

+

+ Tally meters every API call, every minute, every byte — and turns it + into invoices your finance team trusts. No usage spreadsheets, + no end-of-month dread, no creative accounting. +

+
+ + +
+
+ no credit card + 5-min setup + first 100k events free / mo +
+
+ + + +
+ + + +
+
+ + +
+
+
Metering revenue for fast-moving teams
+
+
Bramble
+
orbit/labs
+
FORGE
+
Quietly
+
Northsky
+
Mercer & Co.
+
+
+
+ + +
+
+
+
+ ◇ workbench +

A pricing console that knows your model.

+
+

+ Drop in a meter. Tally watches the events flow in, suggests a + price model that fits, and shows you what next month's invoice + looks like — before you ship a line of pricing code. +

+
+ + +
+
+ + +
+
+
+
+ ◇ proof +

Numbers that don't need a footnote.

+
+

+ Tally has metered north of forty billion events for teams shipping + to seven-figure customers. The math holds. +

+
+ +
+
+
42B+
+
events metered
+
Across 14,000+ meters since 2024 — every one of them auditable.
+
+
+
9.2×
+
faster month-end close
+
From 4-day spreadsheet hell to 11 minutes of "looks good — send."
+
+
+
$0.00
+
in invoice disputes
+
Per-call audit trails. Customers see the same numbers you do.
+
+
+
+
+ + +
+
+
+
+ ◇ how it works +

Meter, price, invoice — then sleep.

+
+

+ Three primitives, one console. Wire one event, configure one model, + ship invoices that don't need anyone to "check the math." +

+
+ +
+
+ +

Meter anything in one line

+

+ Drop a single SDK call wherever the value happens. We handle + dedup, ordering, and replay so your meters never lie. +

+ Read the SDK docs +
+ +
+ +

Price without rebuilding

+

+ Tiered, volume, package, hybrid — pick a model in the console + and Tally retro-prices the meter. No deploy required. +

+ See price models +
+ +
+ +

Invoices customers don't argue with

+

+ Every line ties back to a metered event. When a customer asks + "what's this?", you point at a clickable trail. +

+ Browse invoice templates +
+
+
+
+ + +
+
+
+ ◇ pricing +

Plans that scale with you, not against you.

+
+ + +
+
+ +
+
+
Starter
+
$0
+

Up to 100k events / month. For weekend builds and side projects.

+
    +
  • 1 meter, 1 product
  • +
  • Stripe sync
  • +
  • Basic invoice PDF
  • +
  • Community support
  • +
+ +
+ + + +
+
Enterprise
+
Custom
+

Self-hosted option, custom SLAs, procurement-friendly contracts.

+
    +
  • Self-host or VPC peering
  • +
  • SOC 2 Type II + HIPAA
  • +
  • Custom data residency
  • +
  • White-glove onboarding
  • +
+ +
+
+
+
+ + +
+
+

+ We went from a four-day month-end to closing the books before lunch. +

+ +
+
+ + +
+
+
+

Ship usage billing this afternoon.

+

+ Wire a meter, pick a model, send your first invoice — all before + your standup runs long. We'll be your finance team's favourite tool by Friday. +

+
+ + +
+
+
+
+ + + + + + + diff --git a/site/examples/tally/styles.css b/site/examples/tally/styles.css new file mode 100644 index 0000000..daf63ca --- /dev/null +++ b/site/examples/tally/styles.css @@ -0,0 +1,1087 @@ +@import url("./tokens.css"); + +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; } + +html { + font-family: var(--font-body); + color: var(--color-ink-0); + background: var(--color-paper-0); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +body { + font-size: var(--text-base); + line-height: 1.55; + background: + radial-gradient(1200px 600px at 12% -10%, color-mix(in oklch, var(--color-accent-tint) 70%, transparent), transparent 60%), + radial-gradient(900px 500px at 88% 8%, color-mix(in oklch, var(--color-companion) 35%, transparent), transparent 55%), + var(--color-paper-0); + background-attachment: fixed; +} + +::selection { background: var(--color-accent); color: var(--color-paper-0); } + +a { color: inherit; text-decoration: none; } + +button, input, select { font: inherit; color: inherit; } + +:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; + border-radius: var(--radius-sm); +} + +/* ───────── shared layout ───────── */ +.container { + width: 100%; + max-width: 1240px; + margin: 0 auto; + padding-inline: var(--space-xl); +} + +.eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--color-ink-2); + display: inline-flex; + align-items: center; + gap: var(--space-xs); +} + +.italic-accent { + font-family: var(--font-italic); + font-style: italic; + font-weight: 400; + letter-spacing: -0.01em; +} + +/* ───────── nav · N5 Floating pill ───────── */ +.nav { + position: fixed; + top: 18px; + left: 50%; + transform: translateX(-50%); + z-index: 50; + display: flex; + align-items: center; + gap: var(--space-md); + padding: 6px 8px 6px 18px; + background: color-mix(in oklch, var(--color-paper-0) 78%, transparent); + -webkit-backdrop-filter: blur(20px); + backdrop-filter: blur(20px); + border: var(--rule-soft); + border-radius: var(--radius-pill); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset, 0 8px 30px -12px rgba(20, 30, 80, 0.18); + font-size: var(--text-sm); +} + +.nav__brand { + font-weight: 600; + letter-spacing: -0.01em; + display: flex; + align-items: center; + gap: 8px; + margin-right: var(--space-xs); +} + +.nav__brand svg { display: block; } + +.nav__links { + display: flex; + gap: 4px; +} + +.nav__link { + padding: 8px 14px; + border-radius: var(--radius-pill); + color: var(--color-ink-1); + transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); +} + +.nav__link:hover { background: var(--color-paper-2); color: var(--color-ink-0); } +.nav__link.is-hover { background: var(--color-paper-2); color: var(--color-ink-0); } + +.nav__cta { + padding: 8px 14px; + border-radius: var(--radius-pill); + background: var(--color-ink-0); + color: var(--color-paper-0); + font-weight: 500; + border: 1px solid var(--color-ink-0); + cursor: pointer; + transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out); +} + +.nav__cta:hover { background: var(--color-accent); border-color: var(--color-accent); transform: translateY(-1px); } +.nav__cta:active { transform: translateY(0); } + +@media (max-width: 760px) { + .nav__links { display: none; } + .nav { padding: 6px 8px 6px 14px; } +} + +/* ───────── hero · Marquee Hero ───────── */ +.hero { + padding-top: 140px; + padding-bottom: var(--space-3xl); + position: relative; + overflow: clip; +} + +.hero::before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(to right, color-mix(in oklch, var(--color-ink-0) 6%, transparent) 1px, transparent 1px), + linear-gradient(to bottom, color-mix(in oklch, var(--color-ink-0) 6%, transparent) 1px, transparent 1px); + background-size: 64px 64px; + mask-image: radial-gradient(ellipse 80% 60% at 50% 35%, black 30%, transparent 70%); + -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 35%, black 30%, transparent 70%); + pointer-events: none; +} + +.hero__live { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 6px 14px 6px 12px; + border: var(--rule-soft); + background: var(--color-paper-0); + border-radius: var(--radius-pill); + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-1); +} + +.hero__live .dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: var(--color-success); + box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-success) 60%, transparent); + animation: pulse 1800ms var(--ease-in-out) infinite; +} + +@keyframes pulse { + 0% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-success) 60%, transparent); } + 70% { box-shadow: 0 0 0 10px color-mix(in oklch, var(--color-success) 0%, transparent); } + 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-success) 0%, transparent); } +} + +.hero__layout { + display: grid; + grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); + gap: var(--space-3xl); + align-items: center; + margin-top: var(--space-xl); +} + +@media (max-width: 980px) { + .hero__layout { grid-template-columns: 1fr; gap: var(--space-2xl); } +} + +.hero__h1 { + font-family: var(--font-display); + font-weight: 600; + font-size: var(--text-5xl); + line-height: 0.96; + letter-spacing: -0.03em; + margin: 0.4em 0 0.2em; + max-width: 13ch; +} + +.hero__h1 .italic-accent { + color: var(--color-accent); + font-weight: 400; +} + +.hero__sub { + font-size: var(--text-md); + color: var(--color-ink-1); + max-width: 46ch; + margin: 0 0 var(--space-xl); + line-height: 1.5; +} + +.hero__ctas { + display: flex; + gap: var(--space-sm); + align-items: center; + flex-wrap: wrap; +} + +.btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 14px 22px; + border-radius: var(--radius-pill); + font-weight: 500; + font-size: var(--text-base); + border: 1px solid transparent; + cursor: pointer; + transition: transform var(--dur-fast) var(--ease-out), + background var(--dur-fast) var(--ease-out), + border-color var(--dur-fast) var(--ease-out), + box-shadow var(--dur-fast) var(--ease-out); + position: relative; + isolation: isolate; +} + +.btn--primary { + background: var(--color-ink-0); + color: var(--color-paper-0); + border-color: var(--color-ink-0); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.16) inset, 0 8px 24px -10px rgba(0, 10, 60, 0.4); +} + +.btn--primary:hover { background: var(--color-accent); border-color: var(--color-accent); transform: translateY(-1px); } +.btn--primary:active { transform: translateY(0); } + +.btn--ghost { + background: transparent; + color: var(--color-ink-0); + border-color: color-mix(in oklch, var(--color-ink-0) 16%, transparent); +} + +.btn--ghost:hover { background: var(--color-paper-2); border-color: color-mix(in oklch, var(--color-ink-0) 22%, transparent); } + +.btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; } + +.hero__fineprint { + margin-top: var(--space-md); + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-2); + display: flex; + gap: var(--space-md); + flex-wrap: wrap; +} + +.hero__fineprint span::before { content: "·"; margin-right: var(--space-md); color: var(--color-ink-3); } +.hero__fineprint span:first-child::before { content: ""; margin: 0; } + +/* live invoice card · Tier-A pure-CSS art */ +.invoice { + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-lg); + padding: var(--space-lg); + box-shadow: + 0 1px 0 rgba(255, 255, 255, 0.7) inset, + 0 24px 60px -28px rgba(20, 30, 80, 0.25), + 0 4px 12px -4px rgba(20, 30, 80, 0.08); + font-family: var(--font-mono); + font-size: var(--text-sm); + position: relative; + transform: rotate(0.5deg); + max-width: 420px; + margin-left: auto; +} + +.invoice::before { + content: ""; + position: absolute; + inset: -8px; + border: 1px dashed color-mix(in oklch, var(--color-ink-0) 8%, transparent); + border-radius: calc(var(--radius-lg) + 8px); + pointer-events: none; +} + +.invoice__head { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: var(--space-sm); + border-bottom: var(--rule-hair); +} + +.invoice__title { font-weight: 600; color: var(--color-ink-0); } + +.invoice__tag { + font-size: 10px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--color-success); + background: color-mix(in oklch, var(--color-success) 18%, transparent); + padding: 3px 8px; + border-radius: var(--radius-pill); +} + +.invoice__rows { + display: flex; + flex-direction: column; + gap: 8px; + padding: var(--space-sm) 0; +} + +.invoice__row { + display: flex; + justify-content: space-between; + font-size: var(--text-xs); + color: var(--color-ink-1); +} + +.invoice__row strong { color: var(--color-ink-0); font-weight: 500; } + +.invoice__total { + display: flex; + justify-content: space-between; + align-items: baseline; + padding-top: var(--space-sm); + border-top: var(--rule-hair); + font-size: var(--text-sm); +} + +.invoice__total .num { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 600; + color: var(--color-ink-0); + letter-spacing: -0.01em; +} + +.invoice__bar { + height: 6px; + background: var(--color-paper-3); + border-radius: var(--radius-pill); + margin-top: var(--space-md); + overflow: hidden; +} + +.invoice__bar > i { + display: block; + height: 100%; + width: 64%; + background: linear-gradient(90deg, var(--color-accent), var(--color-companion)); + border-radius: inherit; + transition: width var(--dur-slow) var(--ease-out); +} + +/* hero marquee strip */ +.hero__marquee { + margin-top: var(--space-3xl); + border-top: var(--rule-hair); + border-bottom: var(--rule-hair); + padding: var(--space-md) 0; + overflow: hidden; + position: relative; + -webkit-mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent); + mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent); +} + +.hero__marquee__track { + display: flex; + gap: var(--space-3xl); + animation: marquee 38s linear infinite; + width: max-content; + font-family: var(--font-mono); + font-size: var(--text-sm); + color: var(--color-ink-2); + text-transform: uppercase; + letter-spacing: 0.18em; +} + +.hero__marquee__track span { display: inline-flex; align-items: center; gap: var(--space-md); } +.hero__marquee__track span::after { content: "✦"; color: var(--color-accent); } + +@keyframes marquee { + to { transform: translateX(-50%); } +} + +@media (prefers-reduced-motion: reduce) { + .hero__marquee__track { animation: none; } +} + +/* ───────── logos strip ───────── */ +.logos { + padding: var(--space-2xl) 0; +} + +.logos__label { + text-align: center; + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.16em; + color: var(--color-ink-2); + margin-bottom: var(--space-lg); +} + +.logos__row { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: var(--space-xl); + align-items: center; + opacity: 0.62; +} + +.logos__row > div { + font-family: var(--font-display); + font-weight: 600; + font-size: var(--text-md); + letter-spacing: -0.02em; + text-align: center; + color: var(--color-ink-1); +} + +.logos__row .l-1 { font-family: var(--font-italic); font-style: italic; font-weight: 400; } +.logos__row .l-2 { font-family: var(--font-mono); font-weight: 500; text-transform: lowercase; letter-spacing: -0.02em; } +.logos__row .l-3 { font-weight: 700; letter-spacing: -0.05em; } +.logos__row .l-4 { font-weight: 300; letter-spacing: 0.3em; text-transform: uppercase; font-size: var(--text-sm); } +.logos__row .l-5 { font-weight: 800; } +.logos__row .l-6 { font-weight: 500; font-style: italic; } + +@media (max-width: 760px) { + .logos__row { grid-template-columns: repeat(3, 1fr); row-gap: var(--space-lg); } +} + +/* ───────── workbench / live demo ───────── */ +.workbench { + padding: var(--space-4xl) 0 var(--space-3xl); +} + +.section-head { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-2xl); + align-items: end; + margin-bottom: var(--space-2xl); +} + +@media (max-width: 760px) { + .section-head { grid-template-columns: 1fr; gap: var(--space-md); } +} + +.section-head__title { + font-family: var(--font-display); + font-size: var(--text-3xl); + font-weight: 600; + letter-spacing: -0.025em; + line-height: 1.05; + margin: var(--space-sm) 0 0; +} + +.section-head__title em { font-family: var(--font-italic); font-weight: 400; color: var(--color-accent); font-style: italic; } + +.section-head__desc { + font-size: var(--text-md); + color: var(--color-ink-1); + max-width: 50ch; +} + +.bench { + display: grid; + grid-template-columns: 280px 1fr; + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-xl); + overflow: hidden; + box-shadow: 0 30px 80px -40px rgba(20, 30, 80, 0.2); +} + +@media (max-width: 880px) { + .bench { grid-template-columns: 1fr; } +} + +.bench__rail { + padding: var(--space-lg); + border-right: var(--rule-hair); + background: var(--color-paper-1); +} + +@media (max-width: 880px) { + .bench__rail { border-right: 0; border-bottom: var(--rule-hair); } +} + +.bench__brand { + display: flex; + align-items: center; + gap: 10px; + font-weight: 600; + margin-bottom: var(--space-lg); +} + +.bench__nav { display: flex; flex-direction: column; gap: 2px; font-size: var(--text-sm); } + +.bench__navitem { + padding: 8px 12px; + border-radius: var(--radius-md); + color: var(--color-ink-1); + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + transition: background var(--dur-fast) var(--ease-out); +} + +.bench__navitem:hover { background: var(--color-paper-2); } +.bench__navitem[aria-current="page"] { background: var(--color-paper-2); color: var(--color-ink-0); font-weight: 500; } + +.bench__navitem .glyph { width: 14px; height: 14px; flex: none; opacity: 0.8; } + +.bench__main { padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-lg); } + +.bench__statrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-md); } + +@media (max-width: 580px) { .bench__statrow { grid-template-columns: 1fr; } } + +.bench__stat { + background: var(--color-paper-1); + padding: var(--space-md); + border-radius: var(--radius-md); + border: var(--rule-hair); +} + +.bench__stat .label { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--color-ink-2); +} + +.bench__stat .value { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 600; + letter-spacing: -0.02em; + margin-top: 4px; + display: flex; + align-items: baseline; + gap: 6px; +} + +.bench__stat .delta { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-success); + background: color-mix(in oklch, var(--color-success) 14%, transparent); + padding: 2px 6px; + border-radius: var(--radius-pill); +} + +.bench__chart { + background: var(--color-paper-1); + border-radius: var(--radius-md); + border: var(--rule-hair); + padding: var(--space-lg); + position: relative; + min-height: 240px; +} + +.bench__chart h4 { + margin: 0 0 var(--space-sm); + font-size: var(--text-sm); + font-weight: 500; + color: var(--color-ink-1); + display: flex; + justify-content: space-between; +} + +.bench__chart h4 .live { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-success); + display: inline-flex; + align-items: center; + gap: 6px; +} + +.bench__chart h4 .live::before { + content: ""; + width: 6px; + height: 6px; + border-radius: 999px; + background: var(--color-success); +} + +.bench__chart svg { width: 100%; height: auto; display: block; } + +.bench__chart .grid line { stroke: color-mix(in oklch, var(--color-ink-0) 6%, transparent); stroke-width: 1; } +.bench__chart .area { fill: url(#area-grad); } +.bench__chart .line { fill: none; stroke: var(--color-accent); stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; } +.bench__chart .dot { + fill: var(--color-paper-0); + stroke: var(--color-accent); + stroke-width: 2; + filter: drop-shadow(0 4px 10px rgba(60, 30, 220, 0.25)); +} + +/* ───────── stats triplet ───────── */ +.stats { + padding: var(--space-3xl) 0; +} + +.stats__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-md); + margin-top: var(--space-xl); +} + +@media (max-width: 760px) { .stats__grid { grid-template-columns: 1fr; } } + +.stats__card { + padding: var(--space-2xl) var(--space-xl); + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-lg); + position: relative; + overflow: hidden; +} + +.stats__card:nth-child(1) { background: linear-gradient(160deg, var(--color-accent-tint), var(--color-paper-0) 60%); } +.stats__card:nth-child(2) { background: linear-gradient(160deg, color-mix(in oklch, var(--color-companion) 35%, var(--color-paper-0)), var(--color-paper-0) 60%); } +.stats__card:nth-child(3) { background: linear-gradient(160deg, color-mix(in oklch, var(--color-warning) 25%, var(--color-paper-0)), var(--color-paper-0) 60%); } + +.stats__num { + font-family: var(--font-display); + font-size: clamp(2.8rem, 6vw, 4.5rem); + font-weight: 600; + letter-spacing: -0.04em; + line-height: 1; + display: inline-flex; + align-items: baseline; +} + +.stats__num small { + font-size: 0.5em; + font-weight: 500; + color: var(--color-ink-1); + margin-left: 4px; +} + +.stats__label { + font-family: var(--font-mono); + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: var(--text-xs); + color: var(--color-ink-1); + margin-top: var(--space-md); +} + +.stats__note { + margin-top: var(--space-sm); + font-size: var(--text-sm); + color: var(--color-ink-1); + max-width: 28ch; +} + +/* ───────── three feature cards ───────── */ +.features { + padding: var(--space-4xl) 0 var(--space-3xl); +} + +.features__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-md); + margin-top: var(--space-xl); +} + +@media (max-width: 880px) { .features__grid { grid-template-columns: 1fr; } } + +.feature { + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-lg); + padding: var(--space-xl); + display: flex; + flex-direction: column; + gap: var(--space-md); + transition: transform var(--dur-mid) var(--ease-out), box-shadow var(--dur-mid) var(--ease-out); + transform-style: preserve-3d; + will-change: transform; +} + +.feature:hover { + box-shadow: 0 20px 60px -30px rgba(40, 30, 120, 0.28); +} + +.feature__art { + height: 180px; + border-radius: var(--radius-md); + background: var(--color-paper-1); + border: var(--rule-hair); + position: relative; + overflow: hidden; +} + +/* Feature 1 — meter / counter */ +.art-meter { background: linear-gradient(180deg, var(--color-paper-0), var(--color-paper-1)); } +.art-meter::before { + content: ""; + position: absolute; + inset: 22px; + border: var(--rule-soft); + border-radius: var(--radius-md); + background: + repeating-linear-gradient(90deg, transparent 0 22px, color-mix(in oklch, var(--color-ink-0) 6%, transparent) 22px 23px); +} +.art-meter::after { + content: "1,247 / sec"; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-family: var(--font-mono); + font-size: var(--text-md); + font-weight: 600; + letter-spacing: -0.02em; + color: var(--color-accent); + background: var(--color-paper-0); + padding: 6px 12px; + border-radius: var(--radius-pill); + border: var(--rule-soft); +} + +/* Feature 2 — pricing tiers visualisation */ +.art-tiers { padding: 22px; display: flex; align-items: end; gap: 10px; height: 180px; } +.art-tiers > i { + flex: 1; + background: linear-gradient(180deg, var(--color-companion), color-mix(in oklch, var(--color-companion) 50%, var(--color-paper-1))); + border-radius: var(--radius-sm); + border: var(--rule-hair); +} +.art-tiers > i:nth-child(1) { height: 35%; } +.art-tiers > i:nth-child(2) { height: 50%; } +.art-tiers > i:nth-child(3) { height: 70%; background: linear-gradient(180deg, var(--color-accent), var(--color-accent-soft)); } +.art-tiers > i:nth-child(4) { height: 90%; } +.art-tiers > i:nth-child(5) { height: 60%; } + +/* Feature 3 — invoice stack */ +.art-invoice { + padding: 22px; +} +.art-invoice .sheet { + position: absolute; + width: 70%; + height: 60%; + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-md); + padding: 14px; + font-family: var(--font-mono); + font-size: 10px; + color: var(--color-ink-2); + box-shadow: 0 6px 16px -8px rgba(20, 30, 80, 0.2); +} +.art-invoice .sheet:nth-child(1) { top: 20%; left: 14%; transform: rotate(-4deg); z-index: 1; } +.art-invoice .sheet:nth-child(2) { top: 16%; left: 22%; transform: rotate(2deg); z-index: 2; } +.art-invoice .sheet:nth-child(3) { top: 12%; left: 30%; transform: rotate(-1deg); z-index: 3; background: var(--color-accent-tint); } +.art-invoice .sheet b { display: block; color: var(--color-ink-0); font-size: 12px; margin-bottom: 6px; font-family: var(--font-display); font-weight: 600; } +.art-invoice .sheet em { font-style: normal; color: var(--color-accent); font-weight: 600; font-size: 14px; } + +.feature__title { + font-family: var(--font-display); + font-size: var(--text-lg); + font-weight: 600; + letter-spacing: -0.02em; + margin: 0; +} + +.feature__desc { + font-size: var(--text-sm); + color: var(--color-ink-1); + margin: 0; + line-height: 1.55; +} + +.feature__link { + margin-top: auto; + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--color-accent); + display: inline-flex; + align-items: center; + gap: 6px; +} + +.feature__link::after { + content: "→"; + transition: transform var(--dur-fast) var(--ease-out); +} + +.feature:hover .feature__link::after { transform: translateX(3px); } + +/* ───────── pricing ───────── */ +.pricing { + padding: var(--space-4xl) 0 var(--space-3xl); +} + +.pricing__head { text-align: center; margin-bottom: var(--space-xl); } + +.pricing__toggle { + margin: var(--space-md) auto 0; + display: inline-flex; + padding: 4px; + background: var(--color-paper-2); + border: var(--rule-soft); + border-radius: var(--radius-pill); +} + +.pricing__toggle button { + border: 0; + background: transparent; + padding: 8px 16px; + border-radius: var(--radius-pill); + font-size: var(--text-sm); + cursor: pointer; + color: var(--color-ink-1); + transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); +} + +.pricing__toggle button[aria-pressed="true"] { background: var(--color-paper-0); color: var(--color-ink-0); font-weight: 500; box-shadow: 0 2px 6px -2px rgba(20, 30, 80, 0.14); } + +.pricing__toggle .save { + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--color-success); + margin-left: 6px; +} + +.pricing__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-md); + margin-top: var(--space-xl); +} + +@media (max-width: 980px) { .pricing__grid { grid-template-columns: 1fr; } } + +.tier { + background: var(--color-paper-0); + border: var(--rule-soft); + border-radius: var(--radius-lg); + padding: var(--space-xl); + display: flex; + flex-direction: column; + gap: var(--space-md); + position: relative; +} + +.tier--featured { + background: var(--color-ink-0); + color: var(--color-paper-0); + border-color: var(--color-ink-0); + transform: scale(1.02); + box-shadow: 0 24px 60px -30px rgba(0, 0, 60, 0.5); +} + +.tier--featured .tier__price { color: var(--color-paper-0); } +.tier--featured .tier__features li { color: color-mix(in oklch, var(--color-paper-0) 75%, transparent); } +.tier--featured .tier__features li::before { color: var(--color-companion); } +.tier--featured .btn--primary { background: var(--color-companion); color: var(--color-ink-0); border-color: var(--color-companion); } +.tier--featured .btn--primary:hover { background: var(--color-paper-0); border-color: var(--color-paper-0); } + +.tier__badge { + position: absolute; + top: -12px; + left: var(--space-xl); + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + background: var(--color-companion); + color: var(--color-ink-0); + padding: 4px 10px; + border-radius: var(--radius-pill); +} + +.tier__name { + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.12em; +} + +.tier__price { + font-family: var(--font-display); + font-size: var(--text-3xl); + font-weight: 600; + letter-spacing: -0.03em; + display: flex; + align-items: baseline; + gap: 4px; +} + +.tier__price small { + font-size: 0.4em; + font-weight: 400; + color: var(--color-ink-2); +} + +.tier--featured .tier__price small { color: color-mix(in oklch, var(--color-paper-0) 70%, transparent); } + +.tier__desc { font-size: var(--text-sm); color: var(--color-ink-1); margin-top: -4px; } +.tier--featured .tier__desc { color: color-mix(in oklch, var(--color-paper-0) 75%, transparent); } + +.tier__features { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; font-size: var(--text-sm); } + +.tier__features li { padding-left: 22px; position: relative; color: var(--color-ink-1); } +.tier__features li::before { + content: "✓"; + position: absolute; + left: 0; + top: 0; + color: var(--color-accent); + font-weight: 700; +} + +/* ───────── testimonial ───────── */ +.testimonial { padding: var(--space-3xl) 0; } + +.testimonial__quote { + font-family: var(--font-italic); + font-style: italic; + font-weight: 400; + font-size: clamp(1.6rem, 3.4vw, 2.6rem); + line-height: 1.25; + letter-spacing: -0.015em; + max-width: 22ch; + margin: 0 auto; + text-align: center; +} + +.testimonial__quote::before { content: "\201C"; color: var(--color-accent); margin-right: 4px; } +.testimonial__quote::after { content: "\201D"; color: var(--color-accent); } + +.testimonial__byline { + margin-top: var(--space-xl); + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + font-size: var(--text-sm); + color: var(--color-ink-1); +} + +.testimonial__byline strong { color: var(--color-ink-0); font-weight: 500; } +.testimonial__byline span { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-ink-2); text-transform: uppercase; letter-spacing: 0.1em; } + +/* ───────── CTA ───────── */ +.cta { + padding: var(--space-3xl) 0 var(--space-4xl); +} + +.cta__panel { + position: relative; + background: var(--color-ink-0); + color: var(--color-paper-0); + border-radius: var(--radius-xl); + padding: var(--space-4xl) var(--space-2xl); + text-align: center; + overflow: hidden; + isolation: isolate; +} + +.cta__panel::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(600px 200px at 20% 110%, color-mix(in oklch, var(--color-accent) 60%, transparent), transparent 70%), + radial-gradient(500px 220px at 90% -10%, color-mix(in oklch, var(--color-companion) 50%, transparent), transparent 70%); + z-index: -1; +} + +.cta__panel::after { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px); + background-size: 48px 48px; + z-index: -1; +} + +.cta__title { + font-family: var(--font-display); + font-size: clamp(2.4rem, 6vw, 5rem); + font-weight: 600; + letter-spacing: -0.035em; + line-height: 1; + margin: 0 0 var(--space-md); + max-width: 16ch; + margin-inline: auto; +} + +.cta__title em { font-family: var(--font-italic); font-weight: 400; font-style: italic; color: var(--color-companion); } + +.cta__sub { + color: color-mix(in oklch, var(--color-paper-0) 75%, transparent); + max-width: 50ch; + margin: 0 auto var(--space-xl); + font-size: var(--text-md); +} + +.cta__panel .btn--primary { background: var(--color-paper-0); color: var(--color-ink-0); border-color: var(--color-paper-0); } +.cta__panel .btn--primary:hover { background: var(--color-companion); border-color: var(--color-companion); } +.cta__panel .btn--ghost { color: var(--color-paper-0); border-color: rgba(255, 255, 255, 0.18); } +.cta__panel .btn--ghost:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.3); } + +/* ───────── footer · Ft5 Statement ───────── */ +.footer { padding: var(--space-3xl) 0 var(--space-2xl); border-top: var(--rule-hair); } + +.footer__statement { + font-family: var(--font-display); + font-size: clamp(1.8rem, 4.5vw, 3.2rem); + font-weight: 600; + letter-spacing: -0.03em; + line-height: 1.05; + max-width: 22ch; + margin: 0 0 var(--space-2xl); +} + +.footer__statement em { font-family: var(--font-italic); font-weight: 400; font-style: italic; color: var(--color-accent); } + +.footer__row { + display: grid; + grid-template-columns: 1.2fr 1fr 1fr 1fr; + gap: var(--space-xl); + border-top: var(--rule-hair); + padding-top: var(--space-xl); +} + +@media (max-width: 760px) { .footer__row { grid-template-columns: 1fr 1fr; } } + +.footer__col h5 { + font-family: var(--font-mono); + font-size: var(--text-xs); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--color-ink-2); + margin: 0 0 var(--space-sm); +} + +.footer__col ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; font-size: var(--text-sm); } +.footer__col a { color: var(--color-ink-1); transition: color var(--dur-fast) var(--ease-out); } +.footer__col a:hover { color: var(--color-ink-0); } + +.footer__legal { + margin-top: var(--space-2xl); + display: flex; + justify-content: space-between; + align-items: center; + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--color-ink-2); + border-top: var(--rule-hair); + padding-top: var(--space-md); +} + +.footer__legal .wordmark { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em; font-size: var(--text-base); color: var(--color-ink-0); } + +@media (max-width: 760px) { .footer__legal { flex-direction: column; gap: var(--space-sm); align-items: flex-start; } } diff --git a/site/examples/tally/tokens.css b/site/examples/tally/tokens.css new file mode 100644 index 0000000..9b41f20 --- /dev/null +++ b/site/examples/tally/tokens.css @@ -0,0 +1,101 @@ +/* Hallmark · macrostructure: Marquee Hero · genre: modern-minimal · theme: Pastel + * paper-band: light · display-style: geometric-sans · accent-hue: cool-indigo + * nav: N5 Floating pill · footer: Ft5 Statement + */ + +:root { + /* Paper / ink — cool-pastel band */ + --color-paper-0: oklch(98.4% 0.005 258); + /* page paper */ + --color-paper-1: oklch(96.2% 0.010 258); + /* card */ + --color-paper-2: oklch(93.0% 0.015 258); + /* elevated card */ + --color-paper-3: oklch(89.0% 0.020 258); + /* hairline tint */ + --color-ink-0: oklch(18.0% 0.030 258); + /* primary ink */ + --color-ink-1: oklch(35.0% 0.025 258); + /* body */ + --color-ink-2: oklch(52.0% 0.018 258); + /* secondary */ + --color-ink-3: oklch(70.0% 0.012 258); + /* mute */ + + /* Accent — indigo with companion lime */ + --color-accent: oklch(54.0% 0.220 268); + /* primary indigo */ + --color-accent-soft: oklch(72.0% 0.140 268); + --color-accent-tint: oklch(94.0% 0.040 268); + --color-companion: oklch(82.0% 0.180 130); + /* lime — sparingly */ + --color-warning: oklch(74.0% 0.180 50); + /* amber */ + --color-success: oklch(68.0% 0.150 145); + /* green */ + + /* Focus ring — distinct from accent so contrast holds */ + --color-focus: oklch(46.0% 0.220 268); + + /* Typography — Geist + Geist Mono via Google Fonts */ + --font-display: "Geist", ui-sans-serif, system-ui, sans-serif; + --font-body: "Geist", ui-sans-serif, system-ui, sans-serif; + --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; + --font-italic: "Instrument Serif", "Times New Roman", serif; + /* italic accent */ + + /* Type scale — modular, tight */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-md: 1.125rem; + --text-lg: 1.375rem; + --text-xl: 1.75rem; + --text-2xl: 2.5rem; + --text-3xl: 3.5rem; + --text-4xl: clamp(2rem, 7vw, 4rem); + --text-5xl: clamp(2rem, 10vw, 5rem); + + /* Spacing — 4pt scale */ + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-sm: 0.75rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2rem; + --space-2xl: 3rem; + --space-3xl: 5rem; + --space-4xl: 8rem; + + /* Radius */ + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-xl: 28px; + --radius-pill: 999px; + + /* Lines */ + --rule-hair: 1px solid color-mix(in oklch, var(--color-ink-0) 8%, transparent); + --rule-soft: 1px solid color-mix(in oklch, var(--color-ink-0) 14%, transparent); + + /* Easings + durations */ + --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1); + --ease-in: cubic-bezier(0.55, 0.06, 0.68, 0.19); + --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + + --dur-fast: 140ms; + --dur-mid: 240ms; + --dur-slow: 420ms; +} + +@media (prefers-reduced-motion: reduce) { + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 140ms !important; + scroll-behavior: auto !important; + } +} \ No newline at end of file diff --git a/site/examples/wayfare/.hallmark/log.json b/site/examples/wayfare/.hallmark/log.json new file mode 100644 index 0000000..637a2b3 --- /dev/null +++ b/site/examples/wayfare/.hallmark/log.json @@ -0,0 +1,22 @@ +[ + { + "date": "2026-05-07", + "macrostructure": "Marquee Hero", + "theme": "Manifesto-dark", + "theme_axes": "dark / display-heavy / warm-bleed-red", + "enrichment": "Tier-A pure-CSS departure board + IATA ticker + corner stamp", + "nav": "N9", + "footer": "Ft4", + "brief": "Wayfare v2 · bolder, dark, ticker + stamp + boarding-row callout" + }, + { + "date": "2026-05-07", + "macrostructure": "Marquee Hero", + "theme": "Sport", + "theme_axes": "light / display-condensed-allcaps / warm-amber", + "enrichment": "Tier-A pure-CSS departure board", + "nav": "N9", + "footer": "Ft4", + "brief": "Wayfare v1 · travel booking studio · smart hero = diegetic departure board" + } +] diff --git a/site/examples/wayfare/index.html b/site/examples/wayfare/index.html new file mode 100644 index 0000000..d8bbfc2 --- /dev/null +++ b/site/examples/wayfare/index.html @@ -0,0 +1,359 @@ + + + + + + Wayfare — Trips, set in motion. + + + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+ LIVE · departures board + Updated Tue 06:00 UTC + 06 picks · this week +
+ +
+
+

+ Pick a city.
+ We’ll pack the rest.↗ vol.14 +

+

+ Wayfare is a small booking studio for slow trips. Every Tuesday we publish a board of six places + we’d go ourselves — with the flights, beds, and the one good restaurant already lined up. +

+
+ + +
+ + + + +

+ Fares are placeholders until you tell us your dates and your budget. + Press / to filter. +

+ + + +
+ + +
+
+

01 — What this is

+
+

+ Most travel sites ask you what you want and then sell you what they have. We do the opposite. + Each Tuesday a board goes up. Six places. Six trips someone here has actually taken + in the last twelve months. +

+

+ You pick the row that calls. We do the rest — flights, the bed, the one restaurant + that justifies the flight. No upsells, no loyalty tier, no plus members save. + One flat planning fee, paid once you say go. +

+
+
+
+ + +
+
+

Recently set in motion.

+ Three of forty — spring ’26 +
+ +
+ +
+

Nº 0374 nights

+ +

LISBON + Cascais

+

A coast-side hotel on a Tuesday, a tram into Alfama on Wednesday, fado on Friday, and a quiet drive west. The room has a kettle.

+

Apr — JunFrom €387

+
+ +
+

Nº 0389 days

+ +

TOKYO + a hot-spring detour

+

Nine days. A small inn in Yanaka, two nights in Hakone with the windows wide open, a bullet train back for one last izakaya.

+

May — JulFrom ¥84,500

+
+ +
+

Nº 0397 nights

+ +

REYKJAVIK + the south coast

+

A car, a glacier, a small wooden cabin with a hot pool around the back. Aurora season runs late but the days are long.

+

Sep — MarFrom €445

+
+ +
+
+ + +
+
+

02 — How it works

+ +
+
+

01· Tue 06:00

+

The board goes up.

+

Six rows, six places, one a week. We pick what we’d book ourselves, never what pays best.

+
+
+

02· 15 min call

+

You pick the row that calls.

+

We talk dates, budget, and the one weird thing you want (a kettle, a left-handed surfboard, a quiet room).

+
+
+

03· Within 48h

+

We send the dossier.

+

Flights, beds, a hand-typed itinerary, restaurants you can ignore. Pay once. Go when you’re ready.

+
+
+
+
+ + +
+

03 — A note

+

+ We started this because we were tired of filtering our way to a holiday. + A good trip is closer to a good record — someone has to choose the order of the songs. + — Marta & Idris · Lisbon, est. 2024 +

+
+ + +
+

+ Tuesday’s board goes up in 47 hours. +

+ Book a chair · 15 min +
+
+ + + + + + diff --git a/site/examples/wayfare/style.css b/site/examples/wayfare/style.css new file mode 100644 index 0000000..daf589a --- /dev/null +++ b/site/examples/wayfare/style.css @@ -0,0 +1,1168 @@ +/* Hallmark · pre-emit critique: P5 H5 E5 S5 R4 V5 + * Hallmark · genre: editorial · macrostructure: Marquee Hero · theme: Manifesto-dark + * enrichment: Tier-A pure-CSS departure board + scrolling ticker · nav: N9 · footer: Ft4 + * tone: bold-airline-late-night · anchor hue: bleed red 25° + */ + +@import url("./tokens.css"); + +*, *::before, *::after { box-sizing: border-box; } + +html, body { + margin: 0; + overflow-x: clip; + background: var(--color-paper); + color: var(--color-ink); + font-family: var(--font-body); + font-size: var(--text-base); + line-height: 1.55; + font-feature-settings: "kern", "liga", "onum"; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +img, svg { display: block; max-width: 100%; } + +a { + color: inherit; + text-decoration: none; + text-underline-offset: 4px; + text-decoration-thickness: 1px; +} + +a:hover { text-decoration: underline; text-decoration-color: var(--color-accent); } + +::selection { background: var(--color-accent); color: var(--color-paper); } + +:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 3px; + border-radius: var(--radius-xs); +} + +button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; } + +/* ───────── Layout shell ───────── */ + +.shell { + width: 100%; + max-width: 1320px; + margin-inline: auto; + padding-inline: clamp(var(--space-md), 4vw, var(--space-2xl)); +} + +/* ───────── Top ticker ───────── */ + +.ticker { + border-bottom: var(--rule-hair); + background: var(--color-paper); + overflow: hidden; + position: relative; +} + +.ticker::before, +.ticker::after { + content: ""; + position: absolute; + top: 0; + bottom: 0; + width: 80px; + z-index: 2; + pointer-events: none; +} + +.ticker::before { + left: 0; + background: linear-gradient(90deg, var(--color-paper) 0%, transparent 100%); +} + +.ticker::after { + right: 0; + background: linear-gradient(270deg, var(--color-paper) 0%, transparent 100%); +} + +.ticker-track { + display: flex; + gap: var(--space-xl); + padding-block: var(--space-sm); + white-space: nowrap; + width: max-content; + animation: ticker-scroll var(--dur-ticker) linear infinite; + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ink-mute); +} + +.ticker:hover .ticker-track { animation-play-state: paused; } + +.ticker-track > span { + display: inline-flex; + align-items: center; + gap: var(--space-md); + flex-shrink: 0; +} + +.ticker-track > span::after { + content: ""; + width: 5px; + height: 5px; + border-radius: 50%; + background: var(--color-accent); + display: inline-block; +} + +.ticker-track > span:last-child::after { display: none; } + +.ticker-track strong { + color: var(--color-ink); + font-weight: 500; +} + +@keyframes ticker-scroll { + 0% { transform: translateX(0); } + 100% { transform: translateX(-50%); } +} + +/* ───────── Nav · N9 edge-aligned minimal ───────── */ + +.masthead { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: baseline; + gap: var(--space-lg); + padding-block: var(--space-lg) var(--space-md); + border-bottom: var(--rule-hair); +} + +.wordmark { + font-family: var(--font-display); + font-weight: 800; + font-size: var(--text-xl); + letter-spacing: -0.02em; + color: var(--color-ink); +} + +.wordmark::before { + content: "✱ "; + color: var(--color-accent); +} + +.masthead-meta { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + font-size: var(--text-sm); + color: var(--color-ink-mute); + letter-spacing: 0.01em; + justify-self: center; +} + +.masthead-links { + display: flex; + gap: var(--space-lg); + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--color-ink-2); +} + +.masthead-links a:hover { color: var(--color-ink); } + +/* ───────── Hero — Marquee with departure board ───────── */ + +.hero { + padding-block: var(--space-2xl) var(--space-3xl); + position: relative; +} + +.hero-strip { + display: flex; + flex-wrap: wrap; + gap: var(--space-md) var(--space-xl); + align-items: baseline; + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.16em; + color: var(--color-ink-mute); + margin-bottom: var(--space-xl); +} + +.hero-strip .live { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + color: var(--color-ink); +} + +.hero-strip .live::before { + content: ""; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-accent); + display: inline-block; + box-shadow: 0 0 10px var(--color-accent); +} + +/* Hero composition: title block + corner stamp */ +.hero-composition { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: start; + gap: var(--space-xl); + margin-bottom: var(--space-2xl); +} + +.hero-title { + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-mark); + line-height: 0.92; + letter-spacing: -0.04em; + margin: 0 0 var(--space-md); + color: var(--color-ink); + max-width: 16ch; + overflow-wrap: anywhere; + min-width: 0; + font-variation-settings: "wdth" 75, "opsz" 96; +} + +.hero-title em { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + color: var(--color-accent); + letter-spacing: -0.02em; + font-variation-settings: normal; +} + +.hero-title span.tonk { + display: inline-block; + font-family: var(--font-mono); + font-weight: 400; + font-size: 0.32em; + vertical-align: super; + letter-spacing: 0.18em; + color: var(--color-accent-2); + margin-left: 0.4em; + text-transform: uppercase; + font-variation-settings: normal; +} + +.hero-sub { + font-family: var(--font-body); + font-size: var(--text-lg); + color: var(--color-ink-2); + max-width: 52ch; + margin: 0; + line-height: 1.5; +} + +/* Corner stamp — diegetic passport / customs */ +.hero-stamp { + align-self: start; + transform: rotate(-6deg); + border: 2px solid var(--color-accent); + padding: var(--space-md) var(--space-lg); + text-align: center; + font-family: var(--font-mono); + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-accent); + background: transparent; + position: relative; + width: 13rem; + margin-top: var(--space-md); +} + +.hero-stamp::before, +.hero-stamp::after { + content: ""; + position: absolute; + height: 2px; + background: var(--color-accent); + left: var(--space-sm); + right: var(--space-sm); +} + +.hero-stamp::before { top: 6px; } +.hero-stamp::after { bottom: 6px; } + +.hero-stamp .lg { + display: block; + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-2xl); + line-height: 1; + margin-block: var(--space-xs) var(--space-2xs); + letter-spacing: -0.02em; +} + +.hero-stamp .sm { + display: block; + font-size: 0.65rem; + letter-spacing: 0.2em; + color: var(--color-accent-2); +} + +.hero-stamp .num { + display: block; + font-size: 0.7rem; + margin-top: var(--space-xs); + border-top: 1px solid var(--color-accent); + padding-top: var(--space-xs); + color: var(--color-accent-2); +} + +/* ── The board ── */ + +.board { + border-top: 2px solid var(--color-ink); + border-bottom: 2px solid var(--color-ink); + background: var(--color-paper); + margin-top: var(--space-xl); +} + +.board-head { + display: grid; + grid-template-columns: 1.5rem 5.5rem minmax(0, 1fr) 4rem 6rem 7rem; + gap: var(--space-md); + padding: var(--space-sm) var(--space-md); + border-bottom: var(--rule-hair); + font-family: var(--font-mono); + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.16em; + color: var(--color-ink-mute); +} + +.board-row { + display: grid; + grid-template-columns: 1.5rem 5.5rem minmax(0, 1fr) 4rem 6rem 7rem; + gap: var(--space-md); + padding: var(--space-md); + align-items: center; + border-bottom: var(--rule-hair); + position: relative; + cursor: pointer; + transition: + background var(--dur-base) var(--ease-out), + transform var(--dur-base) var(--ease-out); + opacity: 0; + transform: translateY(8px); + animation: board-tick var(--dur-stagger) var(--ease-out) forwards; +} + +.board-row:nth-child(1) { animation-delay: 60ms; } +.board-row:nth-child(2) { animation-delay: 140ms; } +.board-row:nth-child(3) { animation-delay: 220ms; } +.board-row:nth-child(4) { animation-delay: 300ms; } +.board-row:nth-child(5) { animation-delay: 380ms; } +.board-row:nth-child(6) { animation-delay: 460ms; } + +.board-row:last-child { border-bottom: 0; } + +.board-row::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 0; + background: var(--color-accent); + transition: width var(--dur-base) var(--ease-out); +} + +.board-row:hover { + background: var(--color-paper-2); +} + +.board-row:hover::before { + width: 3px; +} + +/* Boarding-now row — permanent treatment */ +.board-row[data-status="boarding"] { + background: linear-gradient(90deg, var(--color-paper-2) 0%, var(--color-paper) 65%); +} + +.board-row[data-status="boarding"]::before { + width: 4px; + box-shadow: 0 0 18px var(--color-accent); +} + +.board-row[data-status="boarding"] .board-dest { + font-size: var(--text-board-headline); +} + +.board-row[data-status="boarding"] .board-time { + color: var(--color-accent); +} + +/* Day/night glyph */ +.board-glyph { + width: 16px; + height: 16px; + color: var(--color-ink-mute); +} + +.board-row[data-status="boarding"] .board-glyph { color: var(--color-accent-2); } + +.board-time { + font-family: var(--font-mono); + font-size: var(--text-base); + font-weight: 500; + color: var(--color-ink); + letter-spacing: 0.04em; + font-variant-numeric: tabular-nums; +} + +.board-dest { + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-board); + line-height: 0.9; + letter-spacing: -0.025em; + color: var(--color-ink); + min-width: 0; + overflow-wrap: anywhere; + display: flex; + align-items: baseline; + gap: var(--space-md); + font-variation-settings: "wdth" 80, "opsz" 96; +} + +.board-dest small { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + font-size: 0.95rem; + letter-spacing: 0; + color: var(--color-ink-mute); + margin-left: var(--space-xs); + font-variation-settings: normal; +} + +.board-row[data-status="boarding"] .board-dest small { + color: var(--color-ink-2); + font-size: 1.1rem; +} + +.board-code { + font-family: var(--font-mono); + font-size: var(--text-sm); + letter-spacing: 0.18em; + color: var(--color-ink-2); + text-transform: uppercase; + font-variant-numeric: tabular-nums; +} + +.board-status { + font-family: var(--font-mono); + font-size: 0.7rem; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--color-ink-2); + display: inline-flex; + align-items: center; + gap: var(--space-2xs); +} + +.board-status::before { + content: ""; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--color-status-hold); + display: inline-block; +} + +.board-row[data-status="boarding"] .board-status { + color: var(--color-paper); + background: var(--color-accent); + padding: 4px var(--space-sm); + border-radius: var(--radius-xs); + font-weight: 600; +} + +.board-row[data-status="boarding"] .board-status::before { + background: var(--color-paper); + animation: pulse-dot 1.4s ease-in-out infinite; +} + +@keyframes pulse-dot { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.35; } +} + +.board-row[data-status="ontime"] .board-status::before { background: var(--color-status-go); } +.board-row[data-status="open"] .board-status::before { background: var(--color-ink-2); } + +.board-fare { + font-family: var(--font-mono); + font-size: var(--text-base); + font-weight: 500; + color: var(--color-ink); + text-align: right; + font-variant-numeric: tabular-nums; + letter-spacing: 0.02em; +} + +.board-fare small { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + font-size: 0.7rem; + color: var(--color-ink-mute); + text-transform: uppercase; + letter-spacing: 0.1em; + display: block; +} + +.board-row[data-status="boarding"] .board-fare { + font-size: var(--text-lg); + color: var(--color-ink); +} + +/* Board footnote */ +.board-foot { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: var(--space-md); + padding-top: var(--space-md); + font-family: var(--font-italic); + font-style: italic; + font-size: var(--text-sm); + color: var(--color-ink-mute); +} + +.board-foot kbd { + font-family: var(--font-mono); + font-size: 0.7rem; + padding: 2px 6px; + border: 1px solid var(--color-rule); + border-radius: 3px; + color: var(--color-ink-2); + font-style: normal; +} + +/* CTA row beneath board */ +.hero-cta { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: var(--space-lg); + margin-top: var(--space-2xl); +} + +.btn { + display: inline-flex; + align-items: center; + gap: var(--space-sm); + padding: var(--space-sm) var(--space-lg); + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.16em; + border: 2px solid var(--color-accent); + background: var(--color-accent); + color: var(--color-paper); + border-radius: var(--radius-xs); + white-space: nowrap; + font-weight: 600; + transition: + background var(--dur-base) var(--ease-out), + color var(--dur-base) var(--ease-out), + border-color var(--dur-base) var(--ease-out); +} + +.btn:hover { + background: var(--color-paper); + border-color: var(--color-ink); + color: var(--color-ink); + text-decoration: none; +} + +.btn-ghost { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + font-family: var(--font-italic); + font-style: italic; + font-size: var(--text-base); + color: var(--color-ink-2); +} + +.btn-ghost::after { + content: "→"; + transition: transform var(--dur-base) var(--ease-out); +} + +.btn-ghost:hover::after { transform: translateX(4px); } +.btn-ghost:hover { color: var(--color-ink); text-decoration: none; } + +/* ───────── Pitch ───────── */ + +.pitch { + padding-block: var(--space-3xl); + border-top: var(--rule-hair); +} + +.pitch-grid { + display: grid; + grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); + gap: var(--space-2xl); + align-items: start; +} + +.pitch-label { + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.18em; + color: var(--color-ink-mute); +} + +.pitch-label::before { + content: "§ "; + color: var(--color-accent); +} + +.pitch-body { + font-family: var(--font-body); + font-size: var(--text-xl); + line-height: 1.5; + color: var(--color-ink); + max-width: 58ch; + min-width: 0; +} + +.pitch-body p + p { margin-top: var(--space-md); } +.pitch-body em { + font-family: var(--font-italic); + font-style: italic; + color: var(--color-accent-2); +} + +/* ───────── Three picks ───────── */ + +.picks { + padding-block: var(--space-3xl); + border-top: var(--rule-hair); +} + +.picks-head { + display: flex; + align-items: baseline; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--space-md); + margin-bottom: var(--space-2xl); +} + +.picks-head h2 { + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-4xl); + letter-spacing: -0.035em; + margin: 0; + overflow-wrap: anywhere; + min-width: 0; + font-variation-settings: "wdth" 80, "opsz" 96; +} + +.picks-head h2 em { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + color: var(--color-accent); + font-variation-settings: normal; +} + +.picks-head .count { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--color-ink-mute); +} + +.picks-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--space-xl); +} + +.pick { + display: grid; + grid-template-rows: auto 1fr auto; + gap: var(--space-md); + padding-top: var(--space-md); + border-top: 2px solid var(--color-ink); + min-width: 0; +} + +.pick-num { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ink-mute); + display: flex; + justify-content: space-between; +} + +.pick-num span:last-child { color: var(--color-accent); } + +.pick-figure { + position: relative; + aspect-ratio: 4 / 5; + background: var(--color-paper-2); + overflow: hidden; + border-radius: var(--radius-xs); + display: grid; + place-items: center; + isolation: isolate; + border: 1px solid var(--color-hairline); +} + +.pick-figure::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at 30% 30%, var(--color-accent-mute) 0%, transparent 55%), + radial-gradient(circle at 70% 80%, var(--color-paper-3) 0%, transparent 60%); + z-index: -1; + opacity: 0.6; +} + +.pick-figure svg { + width: 70%; + height: auto; + color: var(--color-ink-2); +} + +.pick-figure-2::before { + background: + linear-gradient(135deg, var(--color-paper-3) 0%, var(--color-paper-2) 100%), + radial-gradient(circle at 50% 30%, var(--color-accent-mute) 0%, transparent 60%); +} + +.pick-figure-3::before { + background: + radial-gradient(ellipse at 50% 100%, var(--color-accent-mute) 0%, transparent 50%), + linear-gradient(180deg, var(--color-paper-2) 0%, var(--color-paper-3) 100%); +} + +.pick-title { + font-family: var(--font-display); + font-weight: 900; + font-size: var(--text-2xl); + letter-spacing: -0.025em; + margin: 0; + line-height: 0.95; + overflow-wrap: anywhere; + min-width: 0; + font-variation-settings: "wdth" 80, "opsz" 72; +} + +.pick-title small { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + font-size: 0.65em; + color: var(--color-ink-mute); + letter-spacing: 0; + margin-left: var(--space-xs); + font-variation-settings: normal; +} + +.pick-body { + font-family: var(--font-body); + font-size: var(--text-base); + line-height: 1.55; + color: var(--color-ink-2); + margin: 0; +} + +.pick-meta { + display: flex; + justify-content: space-between; + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-ink-mute); + padding-top: var(--space-sm); + border-top: var(--rule-hair); +} + +.pick-meta strong { + color: var(--color-accent); + font-weight: 500; +} + +/* ───────── How it works ───────── */ + +.process { + padding-block: var(--space-3xl); + border-top: var(--rule-hair); + background: var(--color-paper-2); + margin-inline: calc(50% - 50vw); + padding-inline: calc(50vw - 50%); +} + +.process-shell { + width: 100%; + max-width: 1320px; + margin-inline: auto; + padding-inline: clamp(var(--space-md), 4vw, var(--space-2xl)); +} + +.process-head { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ink-mute); + margin-bottom: var(--space-2xl); +} + +.process-head::before { + content: "§ "; + color: var(--color-accent); +} + +.process-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--space-xl); +} + +.stage { + display: grid; + gap: var(--space-md); + min-width: 0; +} + +.stage-num { + font-family: var(--font-display); + font-weight: 900; + font-size: clamp(2.25rem, 4.5vw, 3.75rem); + line-height: 0.85; + color: var(--color-accent); + letter-spacing: -0.04em; + display: flex; + align-items: baseline; + justify-content: space-between; + font-variation-settings: "wdth" 80, "opsz" 96; +} + +.stage-num small { + font-family: var(--font-mono); + font-weight: 400; + font-size: 0.7rem; + color: var(--color-ink-mute); + letter-spacing: 0.18em; + text-transform: uppercase; + font-variation-settings: normal; +} + +.stage h3 { + font-family: var(--font-italic); + font-style: italic; + font-weight: 400; + font-size: var(--text-xl); + letter-spacing: -0.01em; + margin: 0; + color: var(--color-ink); + overflow-wrap: anywhere; + min-width: 0; +} + +.stage p { + font-family: var(--font-body); + font-size: var(--text-base); + line-height: 1.55; + color: var(--color-ink-2); + margin: 0; + max-width: 36ch; +} + +/* ───────── A note (voice block) ───────── */ + +.note { + padding-block: var(--space-3xl); + border-top: var(--rule-hair); + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + gap: var(--space-2xl); +} + +.note-label { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ink-mute); +} + +.note-label::before { + content: "§ "; + color: var(--color-accent); +} + +.note-body { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + font-size: clamp(1.5rem, 2.6vw, 2.25rem); + line-height: 1.3; + color: var(--color-ink); + max-width: 28ch; + letter-spacing: -0.015em; + overflow-wrap: anywhere; + min-width: 0; +} + +.note-body em { + color: var(--color-accent); +} + +.note-sig { + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.16em; + color: var(--color-ink-mute); + margin-top: var(--space-lg); + display: block; + font-style: normal; +} + +/* ───────── Final CTA strip ───────── */ + +.final-cta { + padding-block: var(--space-3xl); + border-top: var(--rule-hair); + display: flex; + flex-wrap: wrap; + gap: var(--space-xl); + align-items: baseline; + justify-content: space-between; +} + +.final-cta h2 { + font-family: var(--font-display); + font-weight: 900; + font-size: clamp(2.25rem, 5vw, 4rem); + margin: 0; + letter-spacing: -0.035em; + max-width: 16ch; + min-width: 0; + overflow-wrap: anywhere; + line-height: 0.95; + color: var(--color-ink); + font-variation-settings: "wdth" 80, "opsz" 96; +} + +.final-cta h2 em { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + color: var(--color-accent); + font-variation-settings: normal; +} + +/* ───────── Footer · Ft4 dense colophon ───────── */ + +.colophon { + border-top: 2px solid var(--color-ink); + padding-block: var(--space-2xl); +} + +.colophon-head { + display: flex; + align-items: baseline; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--space-md); + padding-bottom: var(--space-lg); + border-bottom: var(--rule-hair); +} + +.colophon-head .wordmark { font-size: var(--text-2xl); } + +.colophon-head em { + font-family: var(--font-italic); + font-style: italic; + font-weight: 300; + color: var(--color-ink-mute); + font-size: var(--text-base); +} + +.colophon-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--space-xl); + padding-block: var(--space-xl); +} + +.col-block dt { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ink-mute); + margin-bottom: var(--space-sm); +} + +.col-block dd { + margin: 0 0 var(--space-2xs); + font-size: var(--text-sm); + color: var(--color-ink); +} + +.col-block dd a:hover { color: var(--color-accent); } + +.colophon-foot { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--space-md); + padding-top: var(--space-lg); + border-top: var(--rule-hair); + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--color-ink-mute); +} + +.colophon-foot .lead { color: var(--color-ink-2); } + +/* ───────── Animations ───────── */ + +@keyframes board-tick { + 0% { + opacity: 0; + transform: translateY(8px); + filter: blur(2px); + } + 60% { + opacity: 1; + filter: blur(0); + } + 100% { + opacity: 1; + transform: translateY(0); + filter: blur(0); + } +} + +/* ───────── Responsive ───────── */ + +@media (max-width: 880px) { + .pitch-grid, + .picks-grid, + .process-grid, + .note, + .colophon-grid { + grid-template-columns: minmax(0, 1fr); + gap: var(--space-xl); + } + + .colophon-grid { gap: var(--space-lg); } + + .hero-composition { + grid-template-columns: minmax(0, 1fr); + } + + .hero-stamp { + transform: rotate(-4deg); + width: 12rem; + margin-top: 0; + } + + .board-head { display: none; } + + .board-row { + grid-template-columns: 1.25rem 4.25rem minmax(0, 1fr); + grid-template-areas: + "glyph time dest" + ". code status" + "fare fare fare"; + row-gap: var(--space-xs); + column-gap: var(--space-md); + padding: var(--space-md); + } + + .board-glyph { grid-area: glyph; align-self: center; } + .board-time { grid-area: time; } + .board-dest { + grid-area: dest; + font-size: clamp(1.5rem, 7vw, 2.25rem); + } + .board-row[data-status="boarding"] .board-dest { + font-size: clamp(1.85rem, 9vw, 2.85rem); + } + .board-code { grid-area: code; } + .board-status { grid-area: status; justify-self: end; } + .board-fare { + grid-area: fare; + text-align: left; + padding-top: var(--space-xs); + border-top: var(--rule-hair); + display: flex; + gap: var(--space-md); + align-items: baseline; + } + .board-fare small { display: inline; } + + .masthead { + grid-template-columns: 1fr auto; + gap: var(--space-sm); + } + + .masthead-meta { display: none; } + .masthead-links { gap: var(--space-md); } + + .colophon-head .wordmark { font-size: var(--text-xl); } +} + +@media (max-width: 480px) { + .hero { padding-block: var(--space-xl) var(--space-2xl); } + .pitch, .picks, .process, .note, .final-cta { padding-block: var(--space-2xl); } + .hero-strip { gap: var(--space-sm) var(--space-md); } + .hero-cta { gap: var(--space-md); } + .colophon-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } + .ticker-track { gap: var(--space-lg); } +} + +/* ───────── Reduced motion ───────── */ + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.001ms !important; + animation-delay: 0ms !important; + transition-duration: 100ms !important; + transition-property: opacity, color, background-color, border-color !important; + } + + .board-row { + opacity: 1; + transform: none; + filter: none; + } + + .ticker-track { + animation: none !important; + transform: none !important; + } +} diff --git a/site/examples/wayfare/tokens.css b/site/examples/wayfare/tokens.css new file mode 100644 index 0000000..2cdb85e --- /dev/null +++ b/site/examples/wayfare/tokens.css @@ -0,0 +1,81 @@ +/* Hallmark · tokens · genre: editorial · macrostructure: Marquee Hero · theme: Manifesto-dark + * paper: dark-warm-near-black · display: display-heavy · accent: bleed red 25° + */ + +:root { + /* Paper — warm-tinted near-black. Never pure black. */ + --color-paper: oklch(13% 0.010 60); + --color-paper-2: oklch(18% 0.012 60); + --color-paper-3: oklch(24% 0.014 60); + --color-paper-4: oklch(30% 0.016 60); + + /* Ink — warm off-white. Never pure white. */ + --color-ink: oklch(96% 0.010 80); + --color-ink-2: oklch(80% 0.012 80); + --color-ink-mute: oklch(62% 0.014 70); + --color-rule: oklch(38% 0.014 60); + --color-hairline: oklch(28% 0.012 60); + + /* Accent — bleed red. Used at <5% of the viewport. */ + --color-accent: oklch(66% 0.235 25); + --color-accent-2: oklch(78% 0.180 70); + --color-accent-mute:oklch(38% 0.110 28); + + /* Status (departure board) */ + --color-status-go: oklch(72% 0.150 145); + --color-status-wait:oklch(66% 0.235 25); + --color-status-hold:oklch(58% 0.020 60); + + /* Focus ring — never animated, ≥3:1 against paper */ + --color-focus: oklch(78% 0.180 70); + + /* Fonts — display-heavy + editorial italic + mono */ + --font-display: "Bricolage Grotesque", "Archivo Black", system-ui, sans-serif; + --font-body: "Newsreader", "Source Serif 4", Georgia, serif; + --font-italic: "Newsreader", "Source Serif 4", Georgia, serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + + /* Type scale — fluid, generous */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-lg: 1.125rem; + --text-xl: 1.375rem; + --text-2xl: 1.75rem; + --text-3xl: 2.5rem; + --text-4xl: clamp(3rem, 6vw, 5rem); + --text-board: clamp(2.5rem, 6vw, 5rem); + --text-board-headline: clamp(3.25rem, 8vw, 6.5rem); + --text-mark: clamp(3rem, 8.5vw, 7.5rem); + + /* 4pt spacing scale */ + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-sm: 0.75rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2.5rem; + --space-2xl: 4rem; + --space-3xl: 6.5rem; + --space-4xl: 10rem; + + /* Rules */ + --rule-hair: 1px solid var(--color-hairline); + --rule-thick:2px solid var(--color-ink); + --rule-double:3px double var(--color-ink); + + /* Radius — tight, ticket-like */ + --radius-xs: 2px; + --radius-sm: 4px; + --radius-md: 6px; + + /* Motion */ + --dur-fast: 140ms; + --dur-base: 240ms; + --dur-slow: 480ms; + --dur-stagger:600ms; + --dur-ticker: 60s; + --ease-out: cubic-bezier(0.22, 1, 0.36, 1); + --ease-in: cubic-bezier(0.55, 0, 1, 0.45); + --ease-in-out:cubic-bezier(0.65, 0, 0.35, 1); +} diff --git a/site/index.html b/site/index.html index f9a1d42..3fd9e7d 100644 --- a/site/index.html +++ b/site/index.html @@ -199,61 +199,61 @@
-
+

$ /hallmark build a SaaS product page, modern-minimal

-
+ -
+

$ /hallmark build a travel booking site, atmospheric

-
+ -
+

$ /hallmark build a small-batch coffee subscription page

-
+ -
+

$ /hallmark build a creative studio with playful motion

-
+ -
+

$ /hallmark build a software architect personal site

-
+ -
+

$ /hallmark build a Moroccan fashion brand landing page

-
+ -
+

$ /hallmark build a small ceramics studio site

-
+ -
+

$ /hallmark build a developer infrastructure landing page

-
+