mirror of
https://github.com/Nutlope/hallmark.git
synced 2026-08-14 12:35:33 +02:00
Marketing · make Worked-examples cards clickable, open in new tab
Each .ex-card div is now an <a target="_blank" rel="noopener"> wrapping the same content. The hover-play video, prompt line, and .ex-card styles all work unchanged (the class already had color: inherit + focus-visible). Added one CSS line — text-decoration: none on .ex-card — so the <a> doesn't underline the card. Linked destinations: - examples/tally/ — SaaS, modern-minimal (from /Users/youssef/test-hallmark-9) - examples/wayfare/ — travel booking, atmospheric (from test-hallmark-12) - _tests/09-slow-pour/ — small-batch coffee subscription (already in site) - examples/bananastudio/ — creative studio with playful motion (from test-hallmark-6) - _tests/06-anya-portfolio/ — software architect personal site (already in site) - examples/najm/ — Moroccan fashion (from test-hallmark-7) - _tests/11-soroe-ceramics/ — small ceramics studio (already in site) - examples/hyperlane/ — developer infrastructure (from test-hallmark-15) site/examples/ adds ~308 KB total across the 5 copied projects. The other 3 already live under site/_tests/ so no copying needed for them.
This commit is contained in:
@@ -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 <a>, 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);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,475 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>BananaStudio — studio-grade headshots, in thirty minutes.</title>
|
||||
<meta name="description" content="Upload twelve selfies. Receive one hundred retouched headshots in thirty minutes — your wardrobe, your lighting, your direction." />
|
||||
<meta name="theme-color" content="#0a0712" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..600;1,9..144,300..600&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a class="sr-only" href="#main">Skip to content</a>
|
||||
|
||||
<header class="nav">
|
||||
<div class="shell nav__inner">
|
||||
<a class="wordmark" href="#" aria-label="BananaStudio — home">BananaStudio</a>
|
||||
<ul class="nav__links">
|
||||
<li><a href="#examples">Work</a></li>
|
||||
<li><a href="#pricing">Pricing</a></li>
|
||||
<li><a href="#faq">FAQ</a></li>
|
||||
<li><a class="btn btn--ghost btn--sm" href="#cta">Start a session<span class="arrow" aria-hidden="true">→</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
|
||||
<!-- ─── Hero — Marquee, huge title left, animated aperture right ─── -->
|
||||
<section class="shell hero" aria-labelledby="hero-title">
|
||||
<div class="hero__copy reveal" style="--i:0">
|
||||
<p class="hero__eyebrow">Open beta · v1.0</p>
|
||||
<h1 id="hero-title" class="hero__title">
|
||||
Studio-grade<br/>
|
||||
headshots,<br/>
|
||||
<em>in thirty minutes.</em>
|
||||
</h1>
|
||||
<p class="hero__lede">
|
||||
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.
|
||||
</p>
|
||||
<div class="hero__cta">
|
||||
<a class="btn" href="#cta">Start a session<span class="arrow" aria-hidden="true">→</span></a>
|
||||
<a class="btn btn--ghost" href="#examples">See examples</a>
|
||||
</div>
|
||||
<div class="hero__meta">
|
||||
<span><span class="dot" aria-hidden="true"></span>4K PNG output</span>
|
||||
<span><span class="dot" aria-hidden="true"></span>30-min turnaround</span>
|
||||
<span><span class="dot" aria-hidden="true"></span>Commercial license</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive before/after slider -->
|
||||
<div class="hero__aside reveal" style="--i:1">
|
||||
<div class="compare" style="--pos: 50%;">
|
||||
<input class="compare__input" type="range" min="0" max="100" value="50" step="0.1"
|
||||
aria-label="Drag to compare a phone selfie with the studio result. Use arrow keys to adjust." />
|
||||
<figure class="compare__pane compare__pane--after" aria-label="Studio output">
|
||||
<div class="portrait portrait--after" aria-hidden="true"></div>
|
||||
</figure>
|
||||
<figure class="compare__pane compare__pane--before" aria-label="Phone selfie">
|
||||
<div class="portrait portrait--before" aria-hidden="true"></div>
|
||||
</figure>
|
||||
<span class="compare__label compare__label--before">Selfie</span>
|
||||
<span class="compare__label compare__label--after">Studio</span>
|
||||
<div class="compare__divider" aria-hidden="true">
|
||||
<span class="compare__handle">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
|
||||
<path d="M9 5 L4 12 L9 19 M15 5 L20 12 L15 19"
|
||||
fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="compare__caption" aria-hidden="true">
|
||||
<span>Drag to compare</span>
|
||||
<span class="compare__caption-meta">phone-quality input → 4K retouched output</span>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Marquee strip ────────────────────────────────────────────── -->
|
||||
<section class="marquee" aria-label="Wardrobe samples">
|
||||
<div class="marquee__track" aria-hidden="true">
|
||||
<span class="marquee__word">Boardroom</span>
|
||||
<span class="marquee__word marquee__word--mute">Editorial</span>
|
||||
<span class="marquee__word">Daylight</span>
|
||||
<span class="marquee__word marquee__word--mute">On-set</span>
|
||||
<span class="marquee__word">Noir</span>
|
||||
<span class="marquee__word marquee__word--mute">Press</span>
|
||||
<span class="marquee__word">Boardroom</span>
|
||||
<span class="marquee__word marquee__word--mute">Editorial</span>
|
||||
<span class="marquee__word">Daylight</span>
|
||||
<span class="marquee__word marquee__word--mute">On-set</span>
|
||||
<span class="marquee__word">Noir</span>
|
||||
<span class="marquee__word marquee__word--mute">Press</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Output gallery (six breathing portraits, wide grid) ─────── -->
|
||||
<section class="shell section" id="examples" aria-labelledby="ex-title">
|
||||
<div class="gallery">
|
||||
<header class="gallery__intro">
|
||||
<div>
|
||||
<p class="section__head">01 · The output</p>
|
||||
<h2 id="ex-title" class="section__title">Looks like a photographer made it. <em>The kind your CMO's husband doesn't.</em></h2>
|
||||
</div>
|
||||
<p class="section__sub">Trained on twelve thousand portrait sessions. Real skin texture, real fall-off, no plastic finish, no extra fingers. 4K PNG and 2048-px social crops, ready for LinkedIn or the back of a book.</p>
|
||||
</header>
|
||||
|
||||
<article class="tile tile--feature reveal" style="--i:0">
|
||||
<p class="meta">06 · Direction</p>
|
||||
<div>
|
||||
<h2>Pick wardrobe, lighting, backdrop, framing. <em>Or write a brief and we'll match it.</em></h2>
|
||||
<p style="margin-top: var(--space-md);">Twelve presets ship in every plan. Custom briefs unlock on Studio Plus — drop a reference image and we'll mirror its colour temperature, depth of field, and posture.</p>
|
||||
</div>
|
||||
<p class="meta">⌗ 4K PNG · 2048 social · retouched</p>
|
||||
</article>
|
||||
|
||||
<article class="tile tile--portrait reveal" style="--i:1" aria-label="Wardrobe sample: boardroom">
|
||||
<div class="portrait portrait--boardroom"></div>
|
||||
<div class="portrait__caption"><span>Boardroom</span><span class="num">02</span></div>
|
||||
</article>
|
||||
<article class="tile tile--portrait reveal" style="--i:2" aria-label="Wardrobe sample: editorial">
|
||||
<div class="portrait portrait--editorial"></div>
|
||||
<div class="portrait__caption"><span>Editorial</span><span class="num">03</span></div>
|
||||
</article>
|
||||
<article class="tile tile--portrait reveal" style="--i:3" aria-label="Wardrobe sample: daylight">
|
||||
<div class="portrait portrait--daylight"></div>
|
||||
<div class="portrait__caption"><span>Daylight</span><span class="num">04</span></div>
|
||||
</article>
|
||||
|
||||
<article class="tile tile--portrait reveal" style="--i:4" aria-label="Wardrobe sample: on-set">
|
||||
<div class="portrait portrait--onset"></div>
|
||||
<div class="portrait__caption"><span>On-set</span><span class="num">05</span></div>
|
||||
</article>
|
||||
<article class="tile tile--portrait reveal" style="--i:5" aria-label="Wardrobe sample: noir">
|
||||
<div class="portrait portrait--noir"></div>
|
||||
<div class="portrait__caption"><span>Noir</span><span class="num">06</span></div>
|
||||
</article>
|
||||
<article class="tile tile--portrait reveal" style="--i:6" aria-label="Wardrobe sample: editorial alt">
|
||||
<div class="portrait portrait--editorial" style="filter: hue-rotate(28deg) saturate(1.1);"></div>
|
||||
<div class="portrait__caption"><span>Press</span><span class="num">07</span></div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Stat counter ─────────────────────────────────────────────── -->
|
||||
<section class="shell" aria-labelledby="stat-title">
|
||||
<div class="stat">
|
||||
<p class="stat__figure" id="stat-figure" aria-hidden="true">
|
||||
<span class="num" data-counter="100000">100,000</span><span class="plus">+</span>
|
||||
</p>
|
||||
<div class="stat__copy">
|
||||
<h2 id="stat-title">Headshots delivered to founders, recruiters, sales teams, and a surprising number of family lawyers.</h2>
|
||||
<p>“100,000 portraits across 11,400 paying customers since beta. Average turnaround: 27 minutes. Re-shoot rate: 4.1%.”</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Process — three steps, workbench-style ─────────────────── -->
|
||||
<section class="shell section" id="howto" aria-labelledby="howto-title">
|
||||
<div class="process">
|
||||
<header class="process__head">
|
||||
<div>
|
||||
<p class="section__head">02 · The process</p>
|
||||
<h2 id="howto-title">Three steps. Thirty minutes. <em>No setup.</em></h2>
|
||||
</div>
|
||||
<p>From the moment you upload to the moment your first edit lands in your inbox.</p>
|
||||
</header>
|
||||
|
||||
<ol class="steps">
|
||||
<li class="step reveal" style="--i:0">
|
||||
<p class="step__num">01</p>
|
||||
<p class="step__stage">Upload</p>
|
||||
<h3>Send twelve selfies.</h3>
|
||||
<p>Front, side, smile, neutral. Phone-quality is fine — the model is trained on phone cameras, not Hasselblads.</p>
|
||||
</li>
|
||||
<li class="step reveal" style="--i:1">
|
||||
<p class="step__num">02</p>
|
||||
<p class="step__stage">Direct</p>
|
||||
<h3>Pick a look, or write a brief.</h3>
|
||||
<p>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.</p>
|
||||
</li>
|
||||
<li class="step reveal" style="--i:2">
|
||||
<p class="step__num">03</p>
|
||||
<p class="step__stage">Receive</p>
|
||||
<h3>One hundred edits, in thirty minutes.</h3>
|
||||
<p>4K PNG, retouched, with commercial license. Delivered to your inbox and your dashboard. Re-shoot any look free.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Pricing ──────────────────────────────────────────────────── -->
|
||||
<section class="shell section" id="pricing" aria-labelledby="pricing-title">
|
||||
<div class="pricing">
|
||||
<header class="pricing__head">
|
||||
<p class="section__head">03 · Pricing</p>
|
||||
<h2 id="pricing-title">Pay by output, <em>not by seat.</em></h2>
|
||||
<p>Re-shoots are always free. Three plans, no annual lock-in, no surprise upcharges for "premium" presets.</p>
|
||||
</header>
|
||||
|
||||
<div class="tiers">
|
||||
<article class="tier reveal" style="--i:0" aria-labelledby="t1">
|
||||
<p class="tier__name" id="t1">Studio</p>
|
||||
<p class="tier__price">$39<span class="unit">one-off</span></p>
|
||||
<p class="tier__sub">For a single round of headshots, on demand.</p>
|
||||
<ul class="tier__list">
|
||||
<li>100 retouched headshots</li>
|
||||
<li>4 wardrobes · 4 lighting presets</li>
|
||||
<li>30-minute turnaround</li>
|
||||
<li>4K PNG · 2048 social crops</li>
|
||||
<li>Commercial license</li>
|
||||
</ul>
|
||||
<a class="btn btn--ghost" href="#cta">Start a session<span class="arrow" aria-hidden="true">→</span></a>
|
||||
</article>
|
||||
|
||||
<article class="tier tier--rec reveal" style="--i:1" aria-labelledby="t2">
|
||||
<p class="tier__name" id="t2">Studio Plus</p>
|
||||
<p class="tier__price">$79<span class="unit">one-off</span></p>
|
||||
<p class="tier__sub">For founders and execs who reshoot every quarter.</p>
|
||||
<ul class="tier__list">
|
||||
<li>300 retouched headshots</li>
|
||||
<li>Unlimited wardrobes & lighting</li>
|
||||
<li>Same-day turnaround, 12 h SLA</li>
|
||||
<li>Custom briefs & reference images</li>
|
||||
<li>Brand kit · 1 round of revisions</li>
|
||||
<li>Commercial license</li>
|
||||
</ul>
|
||||
<a class="btn" href="#cta">Start a session<span class="arrow" aria-hidden="true">→</span></a>
|
||||
</article>
|
||||
|
||||
<article class="tier reveal" style="--i:2" aria-labelledby="t3">
|
||||
<p class="tier__name" id="t3">Editorial</p>
|
||||
<p class="tier__price">$199<span class="unit">per shoot</span></p>
|
||||
<p class="tier__sub">For press, book covers, and brand work that earns the front page.</p>
|
||||
<ul class="tier__list">
|
||||
<li>500 retouched headshots</li>
|
||||
<li>30-minute creative direction call</li>
|
||||
<li>Brand kit upload · LUT match</li>
|
||||
<li>3 rounds of revision</li>
|
||||
<li>Print-ready 6K TIFF</li>
|
||||
<li>Extended commercial license</li>
|
||||
</ul>
|
||||
<a class="btn btn--ghost" href="#cta">Talk to direction<span class="arrow" aria-hidden="true">→</span></a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FAQ ──────────────────────────────────────────────────────── -->
|
||||
<section class="shell section" id="faq" aria-labelledby="faq-title">
|
||||
<div class="faq">
|
||||
<header class="faq__head">
|
||||
<p class="section__head">04 · FAQ</p>
|
||||
<h2 id="faq-title">Questions, <em>answered honestly.</em></h2>
|
||||
<p>Five we get most often. If yours isn't here, the answer is probably yes — write us.</p>
|
||||
</header>
|
||||
<div class="faq__list">
|
||||
<details class="faq__item">
|
||||
<summary class="faq__summary"><span>How long does a session actually take?</span><span class="faq__sign" aria-hidden="true"></span></summary>
|
||||
<div class="faq__answer-wrap"><div class="faq__answer-inner">
|
||||
<p class="faq__answer">Thirty minutes from the moment your last selfie uploads. Studio Plus same-day SLA is 12 hours; we usually clear it in three.</p>
|
||||
</div></div>
|
||||
</details>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__summary"><span>What do my headshots actually look like?</span><span class="faq__sign" aria-hidden="true"></span></summary>
|
||||
<div class="faq__answer-wrap"><div class="faq__answer-inner">
|
||||
<p class="faq__answer">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.</p>
|
||||
</div></div>
|
||||
</details>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__summary"><span>Can I direct the shot?</span><span class="faq__sign" aria-hidden="true"></span></summary>
|
||||
<div class="faq__answer-wrap"><div class="faq__answer-inner">
|
||||
<p class="faq__answer">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.</p>
|
||||
</div></div>
|
||||
</details>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__summary"><span>Can I use these for LinkedIn, work, or press?</span><span class="faq__sign" aria-hidden="true"></span></summary>
|
||||
<div class="faq__answer-wrap"><div class="faq__answer-inner">
|
||||
<p class="faq__answer">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.</p>
|
||||
</div></div>
|
||||
</details>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__summary"><span>Do you keep my photos?</span><span class="faq__sign" aria-hidden="true"></span></summary>
|
||||
<div class="faq__answer-wrap"><div class="faq__answer-inner">
|
||||
<p class="faq__answer">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.</p>
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Final CTA — full-bleed dramatic ─────────────────────────── -->
|
||||
<section class="shell" id="cta">
|
||||
<div class="final">
|
||||
<div>
|
||||
<h2>Your headshot, <em>in thirty minutes.</em></h2>
|
||||
<p class="final__sub">Twelve selfies in. One hundred edits out. Re-shoot any look free, forever.</p>
|
||||
</div>
|
||||
<div class="final__cta">
|
||||
<a class="btn" href="#cta-form">Start a session<span class="arrow" aria-hidden="true">→</span></a>
|
||||
<a class="btn btn--ghost" href="#pricing">See plans</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="shell foot">
|
||||
<div class="foot__row">
|
||||
<a class="wordmark" href="#" aria-label="BananaStudio — home">BananaStudio</a>
|
||||
<p class="foot__tagline">Studio-grade headshots, in thirty minutes. Built in Lisbon and Toronto, since 2025.</p>
|
||||
</div>
|
||||
<ul class="foot__links" aria-label="Footer links">
|
||||
<li><a href="#pricing">Pricing</a></li>
|
||||
<li><a href="#faq">FAQ</a></li>
|
||||
<li><a href="#">Privacy</a></li>
|
||||
<li><a href="#">Terms</a></li>
|
||||
<li><a href="#">Imprint</a></li>
|
||||
<li><a href="mailto:hello@bananastudio.com">hello@bananastudio.com</a></li>
|
||||
</ul>
|
||||
<div class="foot__credit">
|
||||
<span>© 2026 BananaStudio · All portraits used on this page are AI-generated.</span>
|
||||
<span>v1.0 · made by hand, finished by machine.</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Nav float morph — toggles .is-floating once user scrolls past the masthead.
|
||||
(() => {
|
||||
const nav = document.querySelector(".nav");
|
||||
if (!nav) return;
|
||||
const THRESHOLD = 80;
|
||||
let floating = false;
|
||||
const update = () => {
|
||||
const next = window.scrollY > THRESHOLD;
|
||||
if (next !== floating) {
|
||||
floating = next;
|
||||
nav.classList.toggle("is-floating", floating);
|
||||
}
|
||||
};
|
||||
// rAF-throttled to avoid layout thrash on rapid scroll
|
||||
let ticking = false;
|
||||
const onScroll = () => {
|
||||
if (ticking) return;
|
||||
ticking = true;
|
||||
requestAnimationFrame(() => { update(); ticking = false; });
|
||||
};
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
update();
|
||||
})();
|
||||
|
||||
// Stagger reveal — one-shot, IntersectionObserver only.
|
||||
(() => {
|
||||
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const targets = document.querySelectorAll(".reveal");
|
||||
if (reduce || !("IntersectionObserver" in window)) {
|
||||
targets.forEach(el => el.classList.add("is-in"));
|
||||
return;
|
||||
}
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
if (e.isIntersecting) {
|
||||
e.target.classList.add("is-in");
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
}
|
||||
}, { rootMargin: "0px 0px -8% 0px", threshold: 0.05 });
|
||||
targets.forEach(el => io.observe(el));
|
||||
})();
|
||||
|
||||
// Recommended-tier pulse — fires once when the pricing section enters.
|
||||
(() => {
|
||||
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const rec = document.querySelector(".tier--rec");
|
||||
if (!rec || reduce || !("IntersectionObserver" in window)) return;
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
if (e.isIntersecting) {
|
||||
rec.classList.add("rec-pulse");
|
||||
io.disconnect();
|
||||
}
|
||||
}
|
||||
}, { threshold: 0.4 });
|
||||
io.observe(rec);
|
||||
})();
|
||||
|
||||
// Before/after slider — pointer-event drag with capture; range input
|
||||
// is hidden but kept in sync for keyboard a11y (Tab + arrows).
|
||||
(() => {
|
||||
const compare = document.querySelector(".compare");
|
||||
if (!compare) return;
|
||||
const input = compare.querySelector(".compare__input");
|
||||
|
||||
const set = (v) => {
|
||||
const clamped = Math.max(0, Math.min(100, v));
|
||||
compare.style.setProperty("--pos", clamped + "%");
|
||||
input.value = clamped;
|
||||
};
|
||||
|
||||
const posFromEvent = (e) => {
|
||||
const r = compare.getBoundingClientRect();
|
||||
return ((e.clientX - r.left) / r.width) * 100;
|
||||
};
|
||||
|
||||
let dragging = false;
|
||||
|
||||
compare.addEventListener("pointerdown", (e) => {
|
||||
dragging = true;
|
||||
compare.setPointerCapture(e.pointerId);
|
||||
compare.classList.add("is-dragging");
|
||||
set(posFromEvent(e));
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
compare.addEventListener("pointermove", (e) => {
|
||||
if (!dragging) return;
|
||||
set(posFromEvent(e));
|
||||
});
|
||||
|
||||
const endDrag = (e) => {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
compare.classList.remove("is-dragging");
|
||||
try { compare.releasePointerCapture(e.pointerId); } catch {}
|
||||
};
|
||||
compare.addEventListener("pointerup", endDrag);
|
||||
compare.addEventListener("pointercancel", endDrag);
|
||||
|
||||
// Keyboard a11y — input is hidden but Tab-reachable.
|
||||
input.addEventListener("input", (e) =>
|
||||
compare.style.setProperty("--pos", e.target.value + "%")
|
||||
);
|
||||
|
||||
set(parseFloat(input.value));
|
||||
})();
|
||||
|
||||
// Animated counter — one-shot, eases into the final value.
|
||||
(() => {
|
||||
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const el = document.querySelector("[data-counter]");
|
||||
if (!el) return;
|
||||
const target = parseInt(el.dataset.counter, 10);
|
||||
if (reduce || !("IntersectionObserver" in window)) {
|
||||
el.textContent = target.toLocaleString("en-US");
|
||||
return;
|
||||
}
|
||||
const fmt = new Intl.NumberFormat("en-US");
|
||||
const easeOut = (t) => 1 - Math.pow(1 - t, 3);
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
if (!e.isIntersecting) continue;
|
||||
io.disconnect();
|
||||
const start = performance.now();
|
||||
const dur = 2000;
|
||||
const tick = (now) => {
|
||||
const t = Math.min(1, (now - start) / dur);
|
||||
const v = Math.round(target * easeOut(t));
|
||||
el.textContent = fmt.format(v);
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
}, { threshold: 0.4 });
|
||||
io.observe(el);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,344 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Hyperlane / 26 — A one-night developer summit · Brooklyn · Oct 17, 2026</title>
|
||||
<meta name="description" content="Twelve hours. Eight talks. Four live product launches. Brooklyn, October 17, 2026. Public list opens September 9." />
|
||||
<meta name="theme-color" content="#1a120c" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;700;800;900&family=Inter:wght@400;500;600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="tokens.css" />
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ambient blooms (atmospheric backdrop) -->
|
||||
<div class="ambient" aria-hidden="true">
|
||||
<div class="bloom bloom--1"></div>
|
||||
<div class="bloom bloom--2"></div>
|
||||
<div class="grain"></div>
|
||||
</div>
|
||||
|
||||
<!-- skip link for keyboard users -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<!-- ─── nav · N5 floating pill ────────────────────────────── -->
|
||||
<header class="nav" data-state="rest">
|
||||
<div class="nav__inner">
|
||||
<a class="nav__brand" href="#top" aria-label="Hyperlane 2026 · home">
|
||||
<span class="nav__brand-dot" aria-hidden="true"></span>
|
||||
<span class="nav__brand-text">Hyperlane<span class="nav__brand-num">/26</span></span>
|
||||
</a>
|
||||
<nav class="nav__links" aria-label="Primary">
|
||||
<a href="#format">Format</a>
|
||||
<a href="#why">Why</a>
|
||||
<a href="#program">Programme</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
</nav>
|
||||
<a class="nav__cta" href="#rsvp">
|
||||
<span>Join the list</span>
|
||||
<span class="nav__cta-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
|
||||
<!-- ─── hero · Marquee Hero + HP3 cursor-spotlight ──────── -->
|
||||
<section class="hero" id="top" aria-labelledby="hero-h">
|
||||
<div class="hero__spotlight" aria-hidden="true"></div>
|
||||
|
||||
<div class="hero__rail">
|
||||
<span class="hero__rail-dot" aria-hidden="true"></span>
|
||||
<span>HYPERLANE / 26 — a developer summit · public list opens 09 Sep</span>
|
||||
</div>
|
||||
|
||||
<h1 class="hero__display" id="hero-h">
|
||||
<span class="hero__line">For engineers</span>
|
||||
<span class="hero__line">who ship</span>
|
||||
<span class="hero__line"><em>after dark.</em></span>
|
||||
</h1>
|
||||
|
||||
<div class="hero__meta">
|
||||
<div class="hero__meta-cell">
|
||||
<span class="hero__label">When</span>
|
||||
<span class="hero__value">Sat 17 Oct 2026 · 7 PM</span>
|
||||
</div>
|
||||
<div class="hero__meta-cell hero__meta-cell--mid">
|
||||
<span class="hero__label">To liftoff</span>
|
||||
<span class="hero__value tnum" id="countdown" data-target="2026-10-17T19:00:00-04:00">— —</span>
|
||||
</div>
|
||||
<div class="hero__meta-cell hero__meta-cell--end">
|
||||
<span class="hero__label">Where</span>
|
||||
<span class="hero__value">Brooklyn · 480 cap</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── format band · spec sheet ────────────────────────── -->
|
||||
<section class="format" id="format" aria-labelledby="format-h">
|
||||
<header class="section-head">
|
||||
<p class="section-eyebrow"><span class="num tnum">01</span> · Format</p>
|
||||
<h2 class="section-h" id="format-h">A single night, end to end.</h2>
|
||||
</header>
|
||||
|
||||
<ol class="format__schedule" aria-label="Schedule">
|
||||
<li class="format__row">
|
||||
<span class="format__time tnum">7:00 <small>PM</small></span>
|
||||
<span class="format__label">Doors</span>
|
||||
<span class="format__note">soft open · drinks · introductions in the foyer</span>
|
||||
</li>
|
||||
<li class="format__row">
|
||||
<span class="format__time tnum">8:30 <small>PM</small></span>
|
||||
<span class="format__label">Programme</span>
|
||||
<span class="format__note">eight talks · four live launches · running until 1 AM</span>
|
||||
</li>
|
||||
<li class="format__row">
|
||||
<span class="format__time tnum">11:00 <small>PM</small></span>
|
||||
<span class="format__label">Workshop</span>
|
||||
<span class="format__note">a hands-on session in the side room · 40 seats only</span>
|
||||
</li>
|
||||
<li class="format__row">
|
||||
<span class="format__time tnum">1:00 <small>AM</small></span>
|
||||
<span class="format__label">Late</span>
|
||||
<span class="format__note">closing DJ · open until four · room is yours</span>
|
||||
</li>
|
||||
<li class="format__row format__row--reveal">
|
||||
<span class="format__time tnum">— —</span>
|
||||
<span class="format__label">Address</span>
|
||||
<span class="format__note">released 48 hours before to ticketed guests · Williamsburg waterfront</span>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="format__cta">
|
||||
<a class="cta cta--primary cta--lg" href="#rsvp">
|
||||
<span>Save your spot</span>
|
||||
<span aria-hidden="true">→</span>
|
||||
</a>
|
||||
<p class="format__cta-note tnum">— Wave 01 of 3 · 480 cap · public list opens 09 Sep 2026</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── why · pitch ────────────────────────────────────── -->
|
||||
<section class="why" id="why" aria-labelledby="why-h">
|
||||
<header class="section-head">
|
||||
<p class="section-eyebrow"><span class="num tnum">02</span> · Why</p>
|
||||
<h2 class="section-h" id="why-h">A room for the people still building things at 2 AM.</h2>
|
||||
</header>
|
||||
|
||||
<div class="why__body">
|
||||
<p class="why__lead">
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Hyperlane is a room for that. <em>No badges. No pitches. No panels.</em> Talks short enough to remember (twelve to eighteen minutes). Live demos you can interrupt. Closing DJ.
|
||||
</p>
|
||||
<p class="why__signoff">— Brooklyn. October. The list opens in September.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── programme grid · Wave 01 ───────────────────────── -->
|
||||
<section class="program" id="program" aria-labelledby="program-h">
|
||||
<header class="section-head">
|
||||
<p class="section-eyebrow"><span class="num tnum">03</span> · Programme · Wave 01</p>
|
||||
<h2 class="section-h" id="program-h">Six of twelve slots announced.</h2>
|
||||
<p class="section-note">Wave 02 reveals 09 Sep · final programme 30 Sep · launches stay sealed until the night.</p>
|
||||
</header>
|
||||
|
||||
<ul class="program__grid">
|
||||
<li class="card card--keynote">
|
||||
<p class="card__time tnum">8:30 PM</p>
|
||||
<p class="card__type">Opening keynote</p>
|
||||
<h3 class="card__h">A short talk on shipping under constraint.</h3>
|
||||
<p class="card__sub">Twelve minutes. From an engineer who's released compilers to production for the last decade.</p>
|
||||
<p class="card__name">— announced soon</p>
|
||||
</li>
|
||||
|
||||
<li class="card card--launch">
|
||||
<p class="card__time tnum">9:00 PM</p>
|
||||
<p class="card__type">Live launch · 01</p>
|
||||
<h3 class="card__h">A new way to ship developer tools.</h3>
|
||||
<p class="card__sub">First public demo. Press embargo lifts at the door.</p>
|
||||
<p class="card__name">— sealed until the night</p>
|
||||
</li>
|
||||
|
||||
<li class="card card--talk">
|
||||
<p class="card__time tnum">9:45 PM</p>
|
||||
<p class="card__type">Talk</p>
|
||||
<h3 class="card__h">Live coding · forty-five minutes · no slides.</h3>
|
||||
<p class="card__sub">A working session, in front of the room, on a real production codebase.</p>
|
||||
<p class="card__name">— announced soon</p>
|
||||
</li>
|
||||
|
||||
<li class="card card--tba">
|
||||
<p class="card__time tnum">10:30 PM</p>
|
||||
<p class="card__type">Wave 02</p>
|
||||
<h3 class="card__h">Reveal · 09 Sep 2026.</h3>
|
||||
<p class="card__sub">A speaker we're glad to have. The third name we're announcing first.</p>
|
||||
<p class="card__name">TBA</p>
|
||||
</li>
|
||||
|
||||
<li class="card card--launch">
|
||||
<p class="card__time tnum">11:30 PM</p>
|
||||
<p class="card__type">Live launch · 02</p>
|
||||
<h3 class="card__h">Open-source. Day one. The repo opens during the demo.</h3>
|
||||
<p class="card__sub">Bring a laptop if you'd like to follow along.</p>
|
||||
<p class="card__name">— sealed until the night</p>
|
||||
</li>
|
||||
|
||||
<li class="card card--closing">
|
||||
<p class="card__time tnum">12:30 AM</p>
|
||||
<p class="card__type">Closing</p>
|
||||
<h3 class="card__h">Eight engineers · one sentence each.</h3>
|
||||
<p class="card__sub">Then dancing. The DJ takes over at one.</p>
|
||||
<p class="card__name">— announced soon</p>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- ─── rsvp · join the list ───────────────────────────── -->
|
||||
<section class="rsvp" id="rsvp" aria-labelledby="rsvp-h">
|
||||
<div class="rsvp__inner">
|
||||
<p class="section-eyebrow"><span class="num tnum">04</span> · Tickets</p>
|
||||
<h2 class="rsvp__h" id="rsvp-h">Two emails. One night.</h2>
|
||||
<p class="rsvp__lead">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<form class="rsvp__form" id="rsvp-form" novalidate>
|
||||
<div class="rsvp__row">
|
||||
<label class="rsvp__field">
|
||||
<span class="rsvp__field-label">Email</span>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="rsvp-email"
|
||||
autocomplete="email"
|
||||
placeholder="you@studio.work"
|
||||
required
|
||||
aria-describedby="rsvp-help"
|
||||
/>
|
||||
</label>
|
||||
<button class="cta cta--primary cta--lg" type="submit" data-state="default">
|
||||
<span class="cta__label">Get on the list</span>
|
||||
<span class="cta__arrow" aria-hidden="true">→</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="rsvp__help" id="rsvp-help">
|
||||
<span class="tnum">Wave 01 · $140</span> · <span class="tnum">Wave 02 · $180</span> · <span class="tnum">Wave 03 · $220</span> at the door, capacity permitting.
|
||||
</p>
|
||||
<output class="rsvp__success" for="rsvp-email" aria-live="polite"></output>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FAQ · conversational ───────────────────────────── -->
|
||||
<section class="faq" id="faq" aria-labelledby="faq-h">
|
||||
<header class="section-head">
|
||||
<p class="section-eyebrow"><span class="num tnum">05</span> · Questions</p>
|
||||
<h2 class="section-h" id="faq-h">A few honest answers.</h2>
|
||||
</header>
|
||||
|
||||
<div class="faq__list">
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>What exactly is Hyperlane?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>Who comes?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>Where is it, exactly?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>A warehouse on the Williamsburg waterfront. We release the address forty-eight hours before doors, to ticketed guests only. There are real reasons.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>How much?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>Three waves. Wave 01 (early list, you're reading this) opens at <span class="tnum">$140</span>. Wave 02 at <span class="tnum">$180</span>. Wave 03 at the door, capacity permitting, <span class="tnum">$220</span>. We don't do tiers, comp lists, or sponsorship seats.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>Is it recorded?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>Refunds?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>Up to seven days before doors. After that we'll move your seat to the waitlist and credit you toward the next room.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq__item">
|
||||
<summary>
|
||||
<span>I'd like to launch something. Can I?</span>
|
||||
<span class="faq__chev" aria-hidden="true">+</span>
|
||||
</summary>
|
||||
<div class="faq__a">
|
||||
<p>Maybe. Two of the four launch slots are still open. Email <a href="mailto:launches@hyperlane.events">launches@hyperlane.events</a> with what you're shipping and we'll write back. We are not a sponsorship channel; the slot has to be a real launch.</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- ─── footer · Ft5 statement ──────────────────────────── -->
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<p class="footer__statement">
|
||||
<em>Built late. Shipped live.</em> One room a year — for the people who'd be coding tonight anyway.
|
||||
</p>
|
||||
<div class="footer__meta">
|
||||
<p class="footer__line tnum">Hyperlane / 26 · 17 Oct 2026 · Brooklyn · 7 PM</p>
|
||||
<ul class="footer__links">
|
||||
<li><a href="#main">Top</a></li>
|
||||
<li><a href="#program">Programme</a></li>
|
||||
<li><a href="#rsvp">List</a></li>
|
||||
<li><a href="mailto:hello@hyperlane.events">hello@hyperlane.events</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,368 @@
|
||||
<!doctype html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>NAJM · W’25 — Made in Morocco. Worn anywhere.</title>
|
||||
<meta name="description" content="NAJM is a Moroccan clothing house. Hand-cut in Marrakech, shipped from Casablanca. New drop every six weeks." />
|
||||
<meta name="theme-color" content="#1a1411" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,700;12..96,800;12..96,900&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Announcement -->
|
||||
<div class="announce" role="status" aria-label="Site promotions">
|
||||
<span><strong>★</strong> Free shipping in Morocco over 1500 MAD</span>
|
||||
<span>New drop · Friday 06.05 · 18h00</span>
|
||||
<span>Use code <strong>NAJM10</strong> · 100 MAD off your first order</span>
|
||||
</div>
|
||||
|
||||
<!-- Nav -->
|
||||
<header class="nav">
|
||||
<div class="shell nav__inner">
|
||||
<a href="#" class="brand" aria-label="NAJM home">
|
||||
NAJM<span class="brand__ar" aria-hidden="true">نجم</span>
|
||||
</a>
|
||||
<nav class="nav__links" aria-label="Primary">
|
||||
<a href="#shop">Shop</a>
|
||||
<a href="#lookbook">Lookbook</a>
|
||||
<a href="#story">Story</a>
|
||||
<a href="#stockists">Stockists</a>
|
||||
</nav>
|
||||
<div class="nav__util">
|
||||
<button class="icon-btn" aria-label="Search">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
</button>
|
||||
<button id="cartBtn" class="cart-btn" aria-label="Open bag" aria-controls="drawer">
|
||||
Bag
|
||||
<span id="cartCount" class="cart-btn__count" aria-live="polite">2</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Marquee -->
|
||||
<div class="marquee" aria-hidden="true">
|
||||
<div class="marquee__track">
|
||||
<span>
|
||||
<span>Hand-cut in Marrakech</span><span class="marquee__star"></span>
|
||||
<span>New drop W’25</span><span class="marquee__star"></span>
|
||||
<span>Free returns across Morocco</span><span class="marquee__star"></span>
|
||||
<span>Shipped from Casablanca</span><span class="marquee__star"></span>
|
||||
<span>1% to oasis reforestation</span><span class="marquee__star"></span>
|
||||
</span>
|
||||
<span>
|
||||
<span>Hand-cut in Marrakech</span><span class="marquee__star"></span>
|
||||
<span>New drop W’25</span><span class="marquee__star"></span>
|
||||
<span>Free returns across Morocco</span><span class="marquee__star"></span>
|
||||
<span>Shipped from Casablanca</span><span class="marquee__star"></span>
|
||||
<span>1% to oasis reforestation</span><span class="marquee__star"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero -->
|
||||
<section class="hero shell" id="shop">
|
||||
<div class="hero__grid">
|
||||
<div class="hero__lead">
|
||||
<p class="hero__meta"><b></b> Live · 2,318 watching the drop</p>
|
||||
<h1 class="hero__h1">
|
||||
New cuts.<br/>
|
||||
Old <em>soul</em>.<br/>
|
||||
Made in Morocco.
|
||||
</h1>
|
||||
<p class="hero__sub">
|
||||
Twenty-three pieces, hand-cut in Marrakech and shipped from Casablanca. The Winter <em>’25</em> drop opens Friday at 18h00.
|
||||
</p>
|
||||
<div class="hero__cta-row">
|
||||
<a class="btn btn--primary" href="#shop">
|
||||
Shop the drop <span class="btn__arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
<a class="btn btn--ghost" href="#lookbook">Lookbook</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero__feature">
|
||||
<a class="feature-card" href="#"
|
||||
data-product="medina"
|
||||
data-name="Médina Cropped Jacket"
|
||||
data-variant="Terracotta · M"
|
||||
data-price="1450"
|
||||
data-surface="terracotta">
|
||||
<div class="surface surface--terracotta"></div>
|
||||
<div class="zellige" aria-hidden="true"></div>
|
||||
<div class="feature-card__body">
|
||||
<span class="feature-card__tag">★ Drop · 01 / 23</span>
|
||||
<div>
|
||||
<div class="feature-card__title">Médina<br/>Cropped Jacket</div>
|
||||
<div class="feature-card__row" style="margin-top:.5rem">
|
||||
<div class="feature-card__price">1,450 <small>MAD</small></div>
|
||||
<button class="feature-card__cta" aria-label="Add Médina Cropped Jacket to bag" data-add data-label="↗">↗</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Collection -->
|
||||
<section class="section shell">
|
||||
<div class="section__head">
|
||||
<div>
|
||||
<p class="eyebrow">The W’25 Collection</p>
|
||||
<h2 class="section__title">Twenty-three pieces. <em>Made to be worn out.</em></h2>
|
||||
</div>
|
||||
<div>
|
||||
<p class="section__lede">Pre-order opens Friday 06.05 at 18h00 (Casablanca). First 200 orders ship same-week.</p>
|
||||
<a href="#shop" class="section__link" style="margin-top:.75rem; display:inline-flex">View the full drop <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collection" role="list">
|
||||
|
||||
<article class="product" role="listitem"
|
||||
data-product="atlas"
|
||||
data-name="Atlas Wide-Leg Trouser"
|
||||
data-variant="Bone · 32"
|
||||
data-price="980"
|
||||
data-surface="bone">
|
||||
<div class="product__media">
|
||||
<div class="surface surface--bone"></div>
|
||||
<div class="zellige zellige--dark" aria-hidden="true"></div>
|
||||
<span class="product__badge product__badge--new">New</span>
|
||||
<button class="product__quick" data-add data-label="Add to bag">Add to bag</button>
|
||||
</div>
|
||||
<div class="product__row">
|
||||
<div>
|
||||
<h3 class="product__name">Atlas Wide-Leg Trouser</h3>
|
||||
<p class="product__meta">Heavy linen · Bone</p>
|
||||
</div>
|
||||
<p class="product__price">980 MAD</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="product" role="listitem"
|
||||
data-product="zellige"
|
||||
data-name="Zellige Knit Sweater"
|
||||
data-variant="Saffron · M"
|
||||
data-price="1180"
|
||||
data-surface="saffron">
|
||||
<div class="product__media">
|
||||
<div class="surface surface--saffron"></div>
|
||||
<div class="zellige zellige--dark" aria-hidden="true"></div>
|
||||
<span class="product__badge product__badge--low">Low stock</span>
|
||||
<button class="product__quick" data-add data-label="Add to bag">Add to bag</button>
|
||||
</div>
|
||||
<div class="product__row">
|
||||
<div>
|
||||
<h3 class="product__name">Zellige Knit Sweater</h3>
|
||||
<p class="product__meta">Atlas wool · Saffron</p>
|
||||
</div>
|
||||
<p class="product__price">1,180 MAD</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="product" role="listitem"
|
||||
data-product="saadian"
|
||||
data-name="Saadian Linen Shirt"
|
||||
data-variant="Ink · L"
|
||||
data-price="640"
|
||||
data-surface="ink">
|
||||
<div class="product__media">
|
||||
<div class="surface surface--ink"></div>
|
||||
<div class="zellige" aria-hidden="true"></div>
|
||||
<button class="product__quick" data-add data-label="Add to bag">Add to bag</button>
|
||||
</div>
|
||||
<div class="product__row">
|
||||
<div>
|
||||
<h3 class="product__name">Saadian Linen Shirt</h3>
|
||||
<p class="product__meta">Hand-loomed linen · Ink</p>
|
||||
</div>
|
||||
<p class="product__price"><s>820</s> 640 MAD</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="product" role="listitem"
|
||||
data-product="riad"
|
||||
data-name="Riad Wool Coat"
|
||||
data-variant="Marine · M"
|
||||
data-price="2480"
|
||||
data-surface="marine">
|
||||
<div class="product__media">
|
||||
<div class="surface surface--marine"></div>
|
||||
<div class="zellige" aria-hidden="true"></div>
|
||||
<span class="product__badge product__badge--new">New</span>
|
||||
<button class="product__quick" data-add data-label="Add to bag">Add to bag</button>
|
||||
</div>
|
||||
<div class="product__row">
|
||||
<div>
|
||||
<h3 class="product__name">Riad Wool Coat</h3>
|
||||
<p class="product__meta">Boiled Atlas wool · Marine</p>
|
||||
</div>
|
||||
<p class="product__price">2,480 MAD</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Editorial moment -->
|
||||
<section class="shell" id="lookbook">
|
||||
<div class="editorial">
|
||||
<div class="editorial__bg" aria-hidden="true"></div>
|
||||
|
||||
<div>
|
||||
<p class="editorial__eyebrow">Lookbook · W’25 / 02</p>
|
||||
<p class="editorial__quote">
|
||||
A coat that should outlast its country of origin.
|
||||
</p>
|
||||
<p class="editorial__byline">— Karim Bennani · founder, NAJM</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p style="max-width: 36ch; opacity: .9; line-height: 1.5; font-size: var(--text-md);">
|
||||
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.
|
||||
</p>
|
||||
<a href="#" class="editorial__cta">
|
||||
See the full lookbook <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Trust strip -->
|
||||
<section class="shell" id="story">
|
||||
<div class="trust">
|
||||
<div class="trust__item">
|
||||
<span class="trust__num">24h</span>
|
||||
<div>
|
||||
<h3 class="trust__title">Same-week shipping</h3>
|
||||
<p class="trust__body">Orders before 14h00 (Casablanca) leave the same day. Tracked, signed, in your hands.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trust__item">
|
||||
<span class="trust__num">14d</span>
|
||||
<div>
|
||||
<h3 class="trust__title">Free returns, no friction</h3>
|
||||
<p class="trust__body">Don't love it, send it back. Anywhere in Morocco — we cover the courier.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trust__item">
|
||||
<span class="trust__num">100%</span>
|
||||
<div>
|
||||
<h3 class="trust__title">Made in Morocco</h3>
|
||||
<p class="trust__body">Marrakech atelier, Casablanca warehouse. Every label tells you who cut the piece.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trust__item">
|
||||
<span class="trust__num">1%</span>
|
||||
<div>
|
||||
<h3 class="trust__title">Oasis reforestation</h3>
|
||||
<p class="trust__body">One percent of every order plants palms in the Drâa-Tafilalet oases. Receipt with every shipment.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Newsletter -->
|
||||
<section class="shell">
|
||||
<div class="news">
|
||||
<div>
|
||||
<h2 class="news__h">Get the drop <em>before</em> it goes live.</h2>
|
||||
<p class="news__p">Friday at 18h00 — first dibs on twenty-three pieces. Some are made once.</p>
|
||||
</div>
|
||||
<form id="news" class="news__form-wrap" novalidate>
|
||||
<div class="news__form">
|
||||
<input class="news__input" type="email" required placeholder="you@email.com" aria-label="Email" />
|
||||
<button class="news__submit" type="submit">Notify me</button>
|
||||
</div>
|
||||
<p id="newsNote" class="news__note">No spam. One mail per drop. Unsubscribe with one tap.</p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
<div class="shell">
|
||||
<div class="footer__top">
|
||||
<div>
|
||||
<div class="footer__brand">NAJM<span style="color:var(--color-accent); margin-inline-start:8px; font-size:.6em" aria-hidden="true">نجم</span></div>
|
||||
<p class="footer__tag">A clothing house from Morocco. Hand-cut in Marrakech, shipped from Casablanca, worn from Tangier to Tokyo.</p>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4 class="footer__col-h">Shop</h4>
|
||||
<ul>
|
||||
<li><a href="#">Winter ’25</a></li>
|
||||
<li><a href="#">Outerwear</a></li>
|
||||
<li><a href="#">Knitwear</a></li>
|
||||
<li><a href="#">Linen</a></li>
|
||||
<li><a href="#">Archive</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4 class="footer__col-h">Help</h4>
|
||||
<ul>
|
||||
<li><a href="#">Sizing</a></li>
|
||||
<li><a href="#">Shipping</a></li>
|
||||
<li><a href="#">Returns</a></li>
|
||||
<li><a href="#">Care</a></li>
|
||||
<li><a href="#">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4 class="footer__col-h">House</h4>
|
||||
<ul>
|
||||
<li><a href="#">Story</a></li>
|
||||
<li><a href="#">Atelier</a></li>
|
||||
<li><a href="#">Stockists</a></li>
|
||||
<li><a href="#">Press</a></li>
|
||||
<li><a href="#">Newsletter</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__bottom">
|
||||
<span>© 2026 NAJM · Casablanca</span>
|
||||
<span>EN · FR · العربية</span>
|
||||
<span>MAD · EUR · USD</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Mobile sticky bar -->
|
||||
<div class="mobile-bar" role="region" aria-label="Quick shop">
|
||||
<span class="mobile-bar__text">W’25 drop · Friday 18h00</span>
|
||||
<button id="mobileCart" class="mobile-bar__cta">Shop now →</button>
|
||||
</div>
|
||||
|
||||
<!-- Cart drawer -->
|
||||
<div id="scrim" class="scrim" aria-hidden="true"></div>
|
||||
<aside id="drawer" class="drawer" role="dialog" aria-modal="true" aria-labelledby="drawerTitle" aria-hidden="true">
|
||||
<div class="drawer__head">
|
||||
<h3 id="drawerTitle" class="drawer__title">Your bag</h3>
|
||||
<button class="icon-btn drawer__close" aria-label="Close bag">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="cartLines" class="drawer__body"></div>
|
||||
<div class="drawer__foot">
|
||||
<p id="cartFree" class="drawer__free">Calculating shipping…</p>
|
||||
<div class="drawer__total">
|
||||
<span>Total</span>
|
||||
<b id="cartTotal">—</b>
|
||||
</div>
|
||||
<a href="#checkout" class="drawer__checkout">
|
||||
Checkout <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
<p class="drawer__free">Apple Pay · CMI · Cash on delivery (Morocco only)</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script src="./script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 => `
|
||||
<div class="drawer__line" data-id="${item.id}">
|
||||
<div class="drawer__thumb">
|
||||
<div class="surface surface--${item.surface}"></div>
|
||||
<div class="zellige${item.surface === "bone" ? " zellige--dark" : ""}"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="drawer__name">${item.name}</div>
|
||||
<div class="drawer__sub">${item.variant}</div>
|
||||
<div class="drawer__qty" role="group" aria-label="Quantity for ${item.name}">
|
||||
<button data-act="dec" aria-label="Decrease">–</button>
|
||||
<span aria-live="polite">${item.qty}</span>
|
||||
<button data-act="inc" aria-label="Increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer__price">${fmt(item.price * item.qty)}</div>
|
||||
</div>
|
||||
`).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();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 = "";
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,446 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tally · Bill what your customers actually use</title>
|
||||
<meta name="description" content="Tally meters every API call, every minute, every byte — and turns it into invoices your finance team trusts." />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500;600&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ───────── nav · N5 Floating pill ───────── -->
|
||||
<nav class="nav" aria-label="Primary">
|
||||
<a href="#" class="nav__brand" aria-label="Tally home">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<rect x="3" y="3" width="6" height="18" rx="1.5" fill="currentColor"/>
|
||||
<rect x="11" y="9" width="6" height="12" rx="1.5" fill="currentColor" opacity="0.55"/>
|
||||
<rect x="19" y="14" width="3" height="7" rx="1" fill="currentColor" opacity="0.3"/>
|
||||
</svg>
|
||||
Tally
|
||||
</a>
|
||||
<div class="nav__links">
|
||||
<a class="nav__link" href="#workbench">Product</a>
|
||||
<a class="nav__link" href="#pricing">Pricing</a>
|
||||
<a class="nav__link" href="#">Docs</a>
|
||||
<a class="nav__link" href="#">Changelog</a>
|
||||
</div>
|
||||
<a class="nav__link" href="#">Sign in</a>
|
||||
<button class="nav__cta" type="button">Start free</button>
|
||||
</nav>
|
||||
|
||||
<!-- ───────── hero · Marquee Hero ───────── -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<div class="hero__live" role="status" aria-live="polite">
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
<span>LIVE · METERING <strong id="live-events">12,847</strong> EVENTS / SEC</span>
|
||||
</div>
|
||||
|
||||
<div class="hero__layout">
|
||||
<div>
|
||||
<h1 class="hero__h1">
|
||||
Bill what your customers <span class="italic-accent">actually</span> use.
|
||||
</h1>
|
||||
<p class="hero__sub">
|
||||
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.
|
||||
</p>
|
||||
<div class="hero__ctas">
|
||||
<button class="btn btn--primary" type="button">Start free <span aria-hidden="true">→</span></button>
|
||||
<button class="btn btn--ghost" type="button">Book a demo</button>
|
||||
</div>
|
||||
<div class="hero__fineprint">
|
||||
<span>no credit card</span>
|
||||
<span>5-min setup</span>
|
||||
<span>first 100k events free / mo</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- live invoice card (Tier-A pure-CSS art) -->
|
||||
<aside class="invoice" aria-label="Live invoice preview">
|
||||
<div class="invoice__head">
|
||||
<div>
|
||||
<div class="invoice__title">INV-04212</div>
|
||||
<div style="font-size: var(--text-xs); color: var(--color-ink-2); margin-top: 2px;">Acme Robotics · May 2026</div>
|
||||
</div>
|
||||
<span class="invoice__tag">draft · live</span>
|
||||
</div>
|
||||
<div class="invoice__rows">
|
||||
<div class="invoice__row"><span>API calls</span><strong id="inv-calls">1,284,512</strong></div>
|
||||
<div class="invoice__row"><span>Compute (vCPU·hr)</span><strong>3,847.2</strong></div>
|
||||
<div class="invoice__row"><span>Storage (GB)</span><strong>912.8</strong></div>
|
||||
<div class="invoice__row" style="color: var(--color-success);"><span>Volume discount</span><strong>−$214.20</strong></div>
|
||||
</div>
|
||||
<div class="invoice__total">
|
||||
<span>Total this month</span>
|
||||
<span class="num">$<span id="inv-total">5,142.86</span></span>
|
||||
</div>
|
||||
<div class="invoice__bar" aria-hidden="true"><i></i></div>
|
||||
<div style="font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-ink-2); margin-top: 8px;">
|
||||
64 % of $8,000 budget · 12 days remaining
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- marquee strip -->
|
||||
<div class="hero__marquee" aria-hidden="true">
|
||||
<div class="hero__marquee__track">
|
||||
<span>METER · EVERY · CALL</span>
|
||||
<span>PRICE · WITHOUT · GUESSING</span>
|
||||
<span>INVOICE · ON · AUTOPILOT</span>
|
||||
<span>SYNC · TO · STRIPE</span>
|
||||
<span>AUDIT · WITHOUT · PANIC</span>
|
||||
<span>METER · EVERY · CALL</span>
|
||||
<span>PRICE · WITHOUT · GUESSING</span>
|
||||
<span>INVOICE · ON · AUTOPILOT</span>
|
||||
<span>SYNC · TO · STRIPE</span>
|
||||
<span>AUDIT · WITHOUT · PANIC</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── logos ───────── -->
|
||||
<section class="logos">
|
||||
<div class="container">
|
||||
<div class="logos__label">Metering revenue for fast-moving teams</div>
|
||||
<div class="logos__row">
|
||||
<div class="l-1">Bramble</div>
|
||||
<div class="l-2">orbit/labs</div>
|
||||
<div class="l-3">FORGE</div>
|
||||
<div class="l-4">Quietly</div>
|
||||
<div class="l-5">Northsky</div>
|
||||
<div class="l-6">Mercer & Co.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── workbench ───────── -->
|
||||
<section class="workbench" id="workbench">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">◇ workbench</span>
|
||||
<h2 class="section-head__title">A pricing console that <em>knows</em> your model.</h2>
|
||||
</div>
|
||||
<p class="section-head__desc">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bench" role="img" aria-label="Tally console preview">
|
||||
<div class="bench__rail">
|
||||
<div class="bench__brand">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<rect x="3" y="3" width="6" height="18" rx="1.5"/>
|
||||
<rect x="11" y="9" width="6" height="12" rx="1.5" opacity="0.55"/>
|
||||
<rect x="19" y="14" width="3" height="7" rx="1" opacity="0.3"/>
|
||||
</svg>
|
||||
Tally
|
||||
</div>
|
||||
<div class="bench__nav">
|
||||
<div class="bench__navitem" aria-current="page">
|
||||
<svg class="glyph" viewBox="0 0 16 16" fill="currentColor"><circle cx="8" cy="8" r="3"/><circle cx="8" cy="8" r="6.5" fill="none" stroke="currentColor"/></svg>
|
||||
Live meters
|
||||
</div>
|
||||
<div class="bench__navitem">
|
||||
<svg class="glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="2" width="12" height="12" rx="1.5"/><path d="M2 6h12M6 2v12"/></svg>
|
||||
Price models
|
||||
</div>
|
||||
<div class="bench__navitem">
|
||||
<svg class="glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="2" width="10" height="12" rx="1.5"/><path d="M5 5h6M5 8h6M5 11h4"/></svg>
|
||||
Invoices
|
||||
</div>
|
||||
<div class="bench__navitem">
|
||||
<svg class="glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="8" cy="8" r="6"/><path d="M8 4v4l3 2"/></svg>
|
||||
Forecasts
|
||||
</div>
|
||||
<div class="bench__navitem">
|
||||
<svg class="glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M3 13l4-4 3 3 4-6"/><circle cx="13" cy="6" r="0.8" fill="currentColor"/></svg>
|
||||
Audit log
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bench__main">
|
||||
<div class="bench__statrow">
|
||||
<div class="bench__stat">
|
||||
<div class="label">Events / sec</div>
|
||||
<div class="value"><span id="bench-eps">12,847</span><span class="delta">+8.4%</span></div>
|
||||
</div>
|
||||
<div class="bench__stat">
|
||||
<div class="label">MRR (live)</div>
|
||||
<div class="value">$<span id="bench-mrr">412.6</span><small>K</small><span class="delta">+12.1%</span></div>
|
||||
</div>
|
||||
<div class="bench__stat">
|
||||
<div class="label">Active meters</div>
|
||||
<div class="value">237<span class="delta">+6</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bench__chart">
|
||||
<h4>Revenue · last 30 days <span class="live">live</span></h4>
|
||||
<svg viewBox="0 0 600 200" preserveAspectRatio="none" aria-hidden="true">
|
||||
<defs>
|
||||
<linearGradient id="area-grad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="oklch(54% 0.220 268)" stop-opacity="0.34"/>
|
||||
<stop offset="100%" stop-color="oklch(54% 0.220 268)" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g class="grid">
|
||||
<line x1="0" y1="40" x2="600" y2="40"/>
|
||||
<line x1="0" y1="80" x2="600" y2="80"/>
|
||||
<line x1="0" y1="120" x2="600" y2="120"/>
|
||||
<line x1="0" y1="160" x2="600" y2="160"/>
|
||||
</g>
|
||||
<path class="area"
|
||||
d="M0,160 L40,150 L80,140 L120,148 L160,128 L200,134 L240,110 L280,118 L320,96 L360,82 L400,90 L440,68 L480,76 L520,52 L560,44 L600,30 L600,200 L0,200 Z"/>
|
||||
<path class="line"
|
||||
d="M0,160 L40,150 L80,140 L120,148 L160,128 L200,134 L240,110 L280,118 L320,96 L360,82 L400,90 L440,68 L480,76 L520,52 L560,44 L600,30"/>
|
||||
<circle class="dot" cx="600" cy="30" r="6"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── stats triplet ───────── -->
|
||||
<section class="stats">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">◇ proof</span>
|
||||
<h2 class="section-head__title">Numbers that <em>don't</em> need a footnote.</h2>
|
||||
</div>
|
||||
<p class="section-head__desc">
|
||||
Tally has metered north of forty billion events for teams shipping
|
||||
to seven-figure customers. The math holds.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="stats__grid">
|
||||
<div class="stats__card">
|
||||
<div class="stats__num">42<small>B+</small></div>
|
||||
<div class="stats__label">events metered</div>
|
||||
<div class="stats__note">Across 14,000+ meters since 2024 — every one of them auditable.</div>
|
||||
</div>
|
||||
<div class="stats__card">
|
||||
<div class="stats__num">9.2<small>×</small></div>
|
||||
<div class="stats__label">faster month-end close</div>
|
||||
<div class="stats__note">From 4-day spreadsheet hell to 11 minutes of "looks good — send."</div>
|
||||
</div>
|
||||
<div class="stats__card">
|
||||
<div class="stats__num">$0.00</div>
|
||||
<div class="stats__label">in invoice disputes</div>
|
||||
<div class="stats__note">Per-call audit trails. Customers see the same numbers you do.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── three feature cards ───────── -->
|
||||
<section class="features">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">◇ how it works</span>
|
||||
<h2 class="section-head__title">Meter, price, invoice — <em>then</em> sleep.</h2>
|
||||
</div>
|
||||
<p class="section-head__desc">
|
||||
Three primitives, one console. Wire one event, configure one model,
|
||||
ship invoices that don't need anyone to "check the math."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="features__grid">
|
||||
<article class="feature" data-tilt>
|
||||
<div class="feature__art art-meter" aria-hidden="true"></div>
|
||||
<h3 class="feature__title">Meter anything in one line</h3>
|
||||
<p class="feature__desc">
|
||||
Drop a single SDK call wherever the value happens. We handle
|
||||
dedup, ordering, and replay so your meters never lie.
|
||||
</p>
|
||||
<a class="feature__link" href="#">Read the SDK docs</a>
|
||||
</article>
|
||||
|
||||
<article class="feature" data-tilt>
|
||||
<div class="feature__art art-tiers" aria-hidden="true">
|
||||
<i></i><i></i><i></i><i></i><i></i>
|
||||
</div>
|
||||
<h3 class="feature__title">Price without rebuilding</h3>
|
||||
<p class="feature__desc">
|
||||
Tiered, volume, package, hybrid — pick a model in the console
|
||||
and Tally retro-prices the meter. No deploy required.
|
||||
</p>
|
||||
<a class="feature__link" href="#">See price models</a>
|
||||
</article>
|
||||
|
||||
<article class="feature" data-tilt>
|
||||
<div class="feature__art art-invoice" aria-hidden="true">
|
||||
<div class="sheet"><b>INV-04210</b>Bramble · Apr<br/><em>$3,118.40</em></div>
|
||||
<div class="sheet"><b>INV-04211</b>Forge · Apr<br/><em>$8,940.20</em></div>
|
||||
<div class="sheet"><b>INV-04212</b>Acme · May<br/><em>$5,142.86</em></div>
|
||||
</div>
|
||||
<h3 class="feature__title">Invoices customers don't argue with</h3>
|
||||
<p class="feature__desc">
|
||||
Every line ties back to a metered event. When a customer asks
|
||||
"what's this?", you point at a clickable trail.
|
||||
</p>
|
||||
<a class="feature__link" href="#">Browse invoice templates</a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── pricing ───────── -->
|
||||
<section class="pricing" id="pricing">
|
||||
<div class="container">
|
||||
<div class="pricing__head">
|
||||
<span class="eyebrow">◇ pricing</span>
|
||||
<h2 class="section-head__title" style="text-align:center; margin-inline:auto;">Plans that scale with you, <em>not</em> against you.</h2>
|
||||
<div class="pricing__toggle" role="group" aria-label="Billing period">
|
||||
<button type="button" data-period="monthly" aria-pressed="true">Monthly</button>
|
||||
<button type="button" data-period="annual" aria-pressed="false">Annual <span class="save">save 20%</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing__grid">
|
||||
<article class="tier">
|
||||
<div class="tier__name">Starter</div>
|
||||
<div class="tier__price"><span data-price-monthly="$0" data-price-annual="$0">$0</span></div>
|
||||
<p class="tier__desc">Up to 100k events / month. For weekend builds and side projects.</p>
|
||||
<ul class="tier__features">
|
||||
<li>1 meter, 1 product</li>
|
||||
<li>Stripe sync</li>
|
||||
<li>Basic invoice PDF</li>
|
||||
<li>Community support</li>
|
||||
</ul>
|
||||
<button class="btn btn--ghost" type="button" style="margin-top: auto;">Start free</button>
|
||||
</article>
|
||||
|
||||
<article class="tier tier--featured">
|
||||
<span class="tier__badge">Most teams pick this</span>
|
||||
<div class="tier__name">Scale</div>
|
||||
<div class="tier__price">
|
||||
<span data-price-monthly="$249" data-price-annual="$199">$249</span>
|
||||
<small>/ mo</small>
|
||||
</div>
|
||||
<p class="tier__desc">For teams shipping to real customers with real revenue. No event ceiling.</p>
|
||||
<ul class="tier__features">
|
||||
<li>Unlimited meters & products</li>
|
||||
<li>Hybrid & tiered pricing</li>
|
||||
<li>Net-terms & multi-currency invoicing</li>
|
||||
<li>Audit log & SSO</li>
|
||||
<li>Dedicated solutions engineer</li>
|
||||
</ul>
|
||||
<button class="btn btn--primary" type="button" style="margin-top: auto;">Start 14-day trial</button>
|
||||
</article>
|
||||
|
||||
<article class="tier">
|
||||
<div class="tier__name">Enterprise</div>
|
||||
<div class="tier__price"><span>Custom</span></div>
|
||||
<p class="tier__desc">Self-hosted option, custom SLAs, procurement-friendly contracts.</p>
|
||||
<ul class="tier__features">
|
||||
<li>Self-host or VPC peering</li>
|
||||
<li>SOC 2 Type II + HIPAA</li>
|
||||
<li>Custom data residency</li>
|
||||
<li>White-glove onboarding</li>
|
||||
</ul>
|
||||
<button class="btn btn--ghost" type="button" style="margin-top: auto;">Talk to sales</button>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── testimonial ───────── -->
|
||||
<section class="testimonial">
|
||||
<div class="container">
|
||||
<p class="testimonial__quote">
|
||||
We went from a four-day month-end to closing the books before lunch.
|
||||
</p>
|
||||
<div class="testimonial__byline">
|
||||
<strong>Imani Okafor</strong>
|
||||
<span>VP Finance · Northsky · 1.4M API calls/day</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── CTA ───────── -->
|
||||
<section class="cta">
|
||||
<div class="container">
|
||||
<div class="cta__panel">
|
||||
<h2 class="cta__title">Ship usage billing this <em>afternoon</em>.</h2>
|
||||
<p class="cta__sub">
|
||||
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.
|
||||
</p>
|
||||
<div style="display:flex; gap: var(--space-sm); justify-content: center; flex-wrap: wrap;">
|
||||
<button class="btn btn--primary" type="button">Start free <span aria-hidden="true">→</span></button>
|
||||
<button class="btn btn--ghost" type="button">Book a 20-minute demo</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───────── footer · Ft5 Statement ───────── -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="footer__statement">
|
||||
Tally is a pricing engine for teams who'd rather <em>build</em> than reconcile.
|
||||
</p>
|
||||
<div class="footer__row">
|
||||
<div class="footer__col">
|
||||
<h5>Product</h5>
|
||||
<ul>
|
||||
<li><a href="#">Metering</a></li>
|
||||
<li><a href="#">Pricing models</a></li>
|
||||
<li><a href="#">Invoicing</a></li>
|
||||
<li><a href="#">Audit log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>Developers</h5>
|
||||
<ul>
|
||||
<li><a href="#">API reference</a></li>
|
||||
<li><a href="#">SDKs</a></li>
|
||||
<li><a href="#">Quickstarts</a></li>
|
||||
<li><a href="#">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>Company</h5>
|
||||
<ul>
|
||||
<li><a href="#">About</a></li>
|
||||
<li><a href="#">Customers</a></li>
|
||||
<li><a href="#">Careers</a></li>
|
||||
<li><a href="#">Press</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>Resources</h5>
|
||||
<ul>
|
||||
<li><a href="#">Pricing playbook</a></li>
|
||||
<li><a href="#">Changelog</a></li>
|
||||
<li><a href="#">Security</a></li>
|
||||
<li><a href="#">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__legal">
|
||||
<span class="wordmark">Tally</span>
|
||||
<span>© 2026 Tally Labs · Made in Brooklyn & Lisbon · v0.8.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,359 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Wayfare — Trips, set in motion.</title>
|
||||
<meta name="description" content="A small booking studio for slow trips. Six departures every Tuesday, hand-set by a person who has been there." />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,700;12..96,800;12..96,900&family=JetBrains+Mono:wght@400;500;600&family=Newsreader:ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;1,6..72,300;1,6..72,400&display=swap" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ───── Top ticker — IATA marquee ───── -->
|
||||
<div class="ticker" aria-hidden="true">
|
||||
<div class="ticker-track">
|
||||
<span><strong>LIS</strong> Lisbon 06:42</span>
|
||||
<span><strong>MEX</strong> Mexico City 09:15</span>
|
||||
<span><strong>RAK</strong> Marrakech 11:30</span>
|
||||
<span><strong>HND</strong> Tokyo 14:08</span>
|
||||
<span><strong>KEF</strong> Reykjavik 17:55</span>
|
||||
<span><strong>EZE</strong> Buenos Aires 21:20</span>
|
||||
<span><strong>CPH</strong> Copenhagen — next week</span>
|
||||
<span><strong>BKK</strong> Bangkok — next week</span>
|
||||
<span><strong>OAX</strong> Oaxaca — week 16</span>
|
||||
<span><strong>FCO</strong> Rome — week 17</span>
|
||||
<!-- duplicated track for seamless loop -->
|
||||
<span><strong>LIS</strong> Lisbon 06:42</span>
|
||||
<span><strong>MEX</strong> Mexico City 09:15</span>
|
||||
<span><strong>RAK</strong> Marrakech 11:30</span>
|
||||
<span><strong>HND</strong> Tokyo 14:08</span>
|
||||
<span><strong>KEF</strong> Reykjavik 17:55</span>
|
||||
<span><strong>EZE</strong> Buenos Aires 21:20</span>
|
||||
<span><strong>CPH</strong> Copenhagen — next week</span>
|
||||
<span><strong>BKK</strong> Bangkok — next week</span>
|
||||
<span><strong>OAX</strong> Oaxaca — week 16</span>
|
||||
<span><strong>FCO</strong> Rome — week 17</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───── Nav · N9 edge-aligned minimal ───── -->
|
||||
<header class="shell">
|
||||
<nav class="masthead" aria-label="Primary">
|
||||
<a href="#" class="wordmark" aria-label="Wayfare, home">WAYFARE</a>
|
||||
<span class="masthead-meta">Issue Nº 14 · Spring ’26</span>
|
||||
<div class="masthead-links">
|
||||
<a href="#picks">Trips</a>
|
||||
<a href="#process">How</a>
|
||||
<a href="#note">About</a>
|
||||
<a href="#book">Sign in</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- ───── Hero · Marquee with departure board ───── -->
|
||||
<main>
|
||||
<section class="shell hero" aria-labelledby="hero-title">
|
||||
|
||||
<div class="hero-strip" aria-hidden="false">
|
||||
<span class="live">LIVE · departures board</span>
|
||||
<span>Updated Tue 06:00 UTC</span>
|
||||
<span>06 picks · this week</span>
|
||||
</div>
|
||||
|
||||
<div class="hero-composition">
|
||||
<div>
|
||||
<h1 id="hero-title" class="hero-title">
|
||||
Pick a city.<br />
|
||||
<em>We’ll pack the rest.</em><span class="tonk">↗ vol.14</span>
|
||||
</h1>
|
||||
<p class="hero-sub">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<aside class="hero-stamp" aria-label="Departure stamp">
|
||||
<span class="sm">Departing</span>
|
||||
<span class="lg">TUESDAY</span>
|
||||
<span class="sm">Stamped in Lisbon</span>
|
||||
<span class="num">VOL. 14 · 06.MAY.26</span>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- Departure board -->
|
||||
<div class="board" role="table" aria-label="This week's departures">
|
||||
<div class="board-head" role="row">
|
||||
<span role="columnheader" aria-label="Local time of day"></span>
|
||||
<span role="columnheader">Departs</span>
|
||||
<span role="columnheader">Destination</span>
|
||||
<span role="columnheader">Code</span>
|
||||
<span role="columnheader">Status</span>
|
||||
<span role="columnheader" style="text-align:right">Fare from</span>
|
||||
</div>
|
||||
|
||||
<a href="#book" class="board-row" data-status="boarding" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="3" />
|
||||
<path d="M8 1v2M8 13v2M1 8h2M13 8h2M3.2 3.2l1.4 1.4M11.4 11.4l1.4 1.4M12.8 3.2l-1.4 1.4M4.6 11.4l-1.4 1.4" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">06:42</span>
|
||||
<span class="board-dest" role="cell">LISBON <small>· seven days, two coasts</small></span>
|
||||
<span class="board-code" role="cell">LIS</span>
|
||||
<span class="board-status" role="cell">Boarding</span>
|
||||
<span class="board-fare" role="cell">€387 <small>per person</small></span>
|
||||
</a>
|
||||
|
||||
<a href="#book" class="board-row" data-status="ontime" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="3" />
|
||||
<path d="M8 1v2M8 13v2M1 8h2M13 8h2" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">09:15</span>
|
||||
<span class="board-dest" role="cell">MEXICO CITY <small>· markets, mezcal, mole</small></span>
|
||||
<span class="board-code" role="cell">MEX</span>
|
||||
<span class="board-status" role="cell">On time</span>
|
||||
<span class="board-fare" role="cell">$612 <small>per person</small></span>
|
||||
</a>
|
||||
|
||||
<a href="#book" class="board-row" data-status="ontime" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="4" fill="currentColor" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">11:30</span>
|
||||
<span class="board-dest" role="cell">MARRAKECH <small>· five nights in the Medina</small></span>
|
||||
<span class="board-code" role="cell">RAK</span>
|
||||
<span class="board-status" role="cell">On time</span>
|
||||
<span class="board-fare" role="cell">€298 <small>per person</small></span>
|
||||
</a>
|
||||
|
||||
<a href="#book" class="board-row" data-status="ontime" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="4" fill="currentColor" />
|
||||
<line x1="2" y1="14" x2="14" y2="14" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">14:08</span>
|
||||
<span class="board-dest" role="cell">TOKYO <small>· nine days, late spring</small></span>
|
||||
<span class="board-code" role="cell">HND</span>
|
||||
<span class="board-status" role="cell">On time</span>
|
||||
<span class="board-fare" role="cell">¥84,500 <small>per person</small></span>
|
||||
</a>
|
||||
|
||||
<a href="#book" class="board-row" data-status="open" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<path d="M11 2.5A5.5 5.5 0 1 0 13.5 11 4.5 4.5 0 0 1 11 2.5Z" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">17:55</span>
|
||||
<span class="board-dest" role="cell">REYKJAVIK <small>· glacier week, room with a hot pool</small></span>
|
||||
<span class="board-code" role="cell">KEF</span>
|
||||
<span class="board-status" role="cell">Open</span>
|
||||
<span class="board-fare" role="cell">€445 <small>per person</small></span>
|
||||
</a>
|
||||
|
||||
<a href="#book" class="board-row" data-status="open" role="row">
|
||||
<svg class="board-glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<path d="M11 2.5A5.5 5.5 0 1 0 13.5 11 4.5 4.5 0 0 1 11 2.5Z" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
<span class="board-time" role="cell">21:20</span>
|
||||
<span class="board-dest" role="cell">BUENOS AIRES <small>· tango month, October ’26</small></span>
|
||||
<span class="board-code" role="cell">EZE</span>
|
||||
<span class="board-status" role="cell">Open</span>
|
||||
<span class="board-fare" role="cell">$898 <small>per person</small></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="board-foot">
|
||||
<span>Fares are placeholders until you tell us your dates and your budget.</span>
|
||||
<span>Press <kbd>/</kbd> to filter.</span>
|
||||
</p>
|
||||
|
||||
<div class="hero-cta">
|
||||
<a href="#book" class="btn">Book a chair · 15 min</a>
|
||||
<a href="#picks" class="btn-ghost">See the next eight weeks</a>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ───── Pitch ───── -->
|
||||
<section class="shell pitch" aria-labelledby="pitch-label">
|
||||
<div class="pitch-grid">
|
||||
<p id="pitch-label" class="pitch-label">01 — What this is</p>
|
||||
<div class="pitch-body">
|
||||
<p>
|
||||
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. <em>Six places.</em> Six trips someone here has actually taken
|
||||
in the last twelve months.
|
||||
</p>
|
||||
<p>
|
||||
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 <em>plus members save</em>.
|
||||
One flat planning fee, paid once you say go.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── Three picks ───── -->
|
||||
<section class="shell picks" id="picks" aria-labelledby="picks-title">
|
||||
<div class="picks-head">
|
||||
<h2 id="picks-title">Recently <em>set in motion.</em></h2>
|
||||
<span class="count">Three of forty — spring ’26</span>
|
||||
</div>
|
||||
|
||||
<div class="picks-grid">
|
||||
|
||||
<article class="pick">
|
||||
<p class="pick-num"><span>Nº 037</span><span>4 nights</span></p>
|
||||
<div class="pick-figure pick-figure-1" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 250" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="1.2">
|
||||
<path d="M20 200 Q60 140 100 180 T180 160" />
|
||||
<path d="M20 220 Q60 170 100 200 T180 190" stroke-dasharray="3 4" />
|
||||
<circle cx="148" cy="172" r="4" fill="currentColor" />
|
||||
<text x="100" y="60" text-anchor="middle" font-family="serif" font-style="italic" font-size="14" fill="currentColor">Bay of</text>
|
||||
<text x="100" y="80" text-anchor="middle" font-family="serif" font-style="italic" font-size="14" fill="currentColor">Cascais</text>
|
||||
<line x1="40" y1="100" x2="160" y2="100" stroke="currentColor" stroke-width="0.5" />
|
||||
<text x="40" y="115" font-family="monospace" font-size="7" fill="currentColor">38°41′N</text>
|
||||
<text x="120" y="115" font-family="monospace" font-size="7" fill="currentColor">9°25′W</text>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="pick-title">LISBON <small>+ Cascais</small></h3>
|
||||
<p class="pick-body">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.</p>
|
||||
<p class="pick-meta"><span>Apr — Jun</span><strong>From €387</strong></p>
|
||||
</article>
|
||||
|
||||
<article class="pick">
|
||||
<p class="pick-num"><span>Nº 038</span><span>9 days</span></p>
|
||||
<div class="pick-figure pick-figure-2" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 250" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="1.2">
|
||||
<rect x="40" y="60" width="120" height="120" stroke-dasharray="2 4" />
|
||||
<circle cx="100" cy="120" r="22" fill="currentColor" opacity="0.85" />
|
||||
<line x1="100" y1="60" x2="100" y2="180" stroke-width="0.5" />
|
||||
<line x1="40" y1="120" x2="160" y2="120" stroke-width="0.5" />
|
||||
<text x="100" y="200" text-anchor="middle" font-family="serif" font-style="italic" font-size="13" fill="currentColor">Late spring,</text>
|
||||
<text x="100" y="218" text-anchor="middle" font-family="serif" font-style="italic" font-size="13" fill="currentColor">cherry season</text>
|
||||
<text x="100" y="40" text-anchor="middle" font-family="monospace" font-size="7" fill="currentColor">35°41′N · 139°41′E</text>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="pick-title">TOKYO <small>+ a hot-spring detour</small></h3>
|
||||
<p class="pick-body">Nine days. A small inn in Yanaka, two nights in Hakone with the windows wide open, a bullet train back for one last izakaya.</p>
|
||||
<p class="pick-meta"><span>May — Jul</span><strong>From ¥84,500</strong></p>
|
||||
</article>
|
||||
|
||||
<article class="pick">
|
||||
<p class="pick-num"><span>Nº 039</span><span>7 nights</span></p>
|
||||
<div class="pick-figure pick-figure-3" aria-hidden="true">
|
||||
<svg viewBox="0 0 200 250" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="1.2">
|
||||
<path d="M30 180 L60 130 L90 160 L120 100 L150 150 L180 90" />
|
||||
<path d="M30 180 L180 180" stroke-dasharray="2 3" />
|
||||
<text x="100" y="40" text-anchor="middle" font-family="serif" font-style="italic" font-size="14" fill="currentColor">Volcanic</text>
|
||||
<text x="100" y="60" text-anchor="middle" font-family="serif" font-style="italic" font-size="14" fill="currentColor">interior</text>
|
||||
<text x="100" y="220" text-anchor="middle" font-family="monospace" font-size="7" fill="currentColor">64°08′N · 21°56′W</text>
|
||||
<circle cx="120" cy="100" r="3" fill="currentColor" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="pick-title">REYKJAVIK <small>+ the south coast</small></h3>
|
||||
<p class="pick-body">A car, a glacier, a small wooden cabin with a hot pool around the back. Aurora season runs late but the days are long.</p>
|
||||
<p class="pick-meta"><span>Sep — Mar</span><strong>From €445</strong></p>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── How it works ───── -->
|
||||
<section class="process" id="process" aria-labelledby="process-head">
|
||||
<div class="process-shell">
|
||||
<p id="process-head" class="process-head">02 — How it works</p>
|
||||
|
||||
<div class="process-grid">
|
||||
<div class="stage">
|
||||
<p class="stage-num">01<small>· Tue 06:00</small></p>
|
||||
<h3>The board goes up.</h3>
|
||||
<p>Six rows, six places, one a week. We pick what we’d book ourselves, never what pays best.</p>
|
||||
</div>
|
||||
<div class="stage">
|
||||
<p class="stage-num">02<small>· 15 min call</small></p>
|
||||
<h3>You pick the row that calls.</h3>
|
||||
<p>We talk dates, budget, and the one weird thing you want (a kettle, a left-handed surfboard, a quiet room).</p>
|
||||
</div>
|
||||
<div class="stage">
|
||||
<p class="stage-num">03<small>· Within 48h</small></p>
|
||||
<h3>We send the dossier.</h3>
|
||||
<p>Flights, beds, a hand-typed itinerary, restaurants you can ignore. Pay once. Go when you’re ready.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── A note ───── -->
|
||||
<section class="shell note" id="note" aria-labelledby="note-label">
|
||||
<p id="note-label" class="note-label">03 — A note</p>
|
||||
<p class="note-body">
|
||||
We started this because we were tired of <em>filtering</em> our way to a holiday.
|
||||
A good trip is closer to a good record — someone has to choose the order of the songs.
|
||||
<span class="note-sig">— Marta & Idris · Lisbon, est. 2024</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ───── Final CTA ───── -->
|
||||
<section class="shell final-cta" id="book">
|
||||
<h2>
|
||||
Tuesday’s board goes up <em>in 47 hours.</em>
|
||||
</h2>
|
||||
<a href="#" class="btn">Book a chair · 15 min</a>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ───── Footer · Ft4 dense colophon ───── -->
|
||||
<footer class="shell colophon">
|
||||
<div class="colophon-head">
|
||||
<a href="#" class="wordmark">WAYFARE</a>
|
||||
<em>Trips, set in motion. — Lisbon & Mexico City.</em>
|
||||
</div>
|
||||
|
||||
<dl class="colophon-grid">
|
||||
<div class="col-block">
|
||||
<dt>The studio</dt>
|
||||
<dd><a href="#">About us</a></dd>
|
||||
<dd><a href="#">The Tuesday letter</a></dd>
|
||||
<dd><a href="#">Working with us</a></dd>
|
||||
<dd><a href="#">Press & archive</a></dd>
|
||||
</div>
|
||||
|
||||
<div class="col-block">
|
||||
<dt>The board</dt>
|
||||
<dd><a href="#">This week (06)</a></dd>
|
||||
<dd><a href="#">Next eight weeks</a></dd>
|
||||
<dd><a href="#">Past trips, archived</a></dd>
|
||||
<dd><a href="#">Submit a city</a></dd>
|
||||
</div>
|
||||
|
||||
<div class="col-block">
|
||||
<dt>The fine print</dt>
|
||||
<dd><a href="#">Booking terms</a></dd>
|
||||
<dd><a href="#">Refund policy</a></dd>
|
||||
<dd><a href="#">Privacy</a></dd>
|
||||
<dd><a href="#">Cookies (we use two)</a></dd>
|
||||
</div>
|
||||
|
||||
<div class="col-block">
|
||||
<dt>Find us</dt>
|
||||
<dd>Rua das Flores 47</dd>
|
||||
<dd>1200–195 Lisbon</dd>
|
||||
<dd><a href="mailto:hello@wayfare.studio">hello@wayfare.studio</a></dd>
|
||||
<dd><a href="#">@wayfare.studio</a></dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="colophon-foot">
|
||||
<span class="lead">© 2026 Wayfare Studio, Lda.</span>
|
||||
<span>Set in Bricolage Grotesque & Newsreader. Built by hand.</span>
|
||||
<span>v1.0 · Spring ’26</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
+16
-16
@@ -199,61 +199,61 @@
|
||||
|
||||
<div class="examples-rail" role="list" aria-label="Example landing pages">
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="examples/tally/" target="_blank" rel="noopener" aria-label="Open the Tally example — SaaS product page, modern-minimal — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/SaaS-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a SaaS product page, modern-minimal</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="examples/wayfare/" target="_blank" rel="noopener" aria-label="Open the Wayfare example — travel booking site, atmospheric — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/wayfare-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a travel booking site, atmospheric</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="_tests/09-slow-pour/" target="_blank" rel="noopener" aria-label="Open the Slow Pour example — small-batch coffee subscription — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/slow-pour-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a small-batch coffee subscription page</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="examples/bananastudio/" target="_blank" rel="noopener" aria-label="Open the BananaStudio example — creative studio with playful motion — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/BananaStudio-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a creative studio with playful motion</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="_tests/06-anya-portfolio/" target="_blank" rel="noopener" aria-label="Open the Anya Reis example — software architect personal site — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/anya-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a software architect personal site</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="examples/najm/" target="_blank" rel="noopener" aria-label="Open the NAJM example — Moroccan fashion brand landing page — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/Najm-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a Moroccan fashion brand landing page</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="_tests/11-soroe-ceramics/" target="_blank" rel="noopener" aria-label="Open the Soroe example — small ceramics studio site — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/Soroe-loop.mp4" autoplay loop muted playsinline preload="metadata" width="1804" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a small ceramics studio site</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ex-card" role="listitem">
|
||||
<a class="ex-card" role="listitem" href="examples/hyperlane/" target="_blank" rel="noopener" aria-label="Open the Hyperlane example — developer infrastructure landing page — in a new tab">
|
||||
<div class="ex-card__thumb ex-card__thumb--video" aria-hidden="true">
|
||||
<video data-hover-play src="_tests/_thumbs/Hyperlane-example.mp4" autoplay loop muted playsinline preload="metadata" width="1784" height="1080"></video>
|
||||
</div>
|
||||
<p class="ex-card__prompt"><span class="ex-card__prompt-mark">$</span> /hallmark build a developer infrastructure landing page</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user