Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da59b52738 | ||
|
|
ee2c54d664 | ||
|
|
60a55107f8 | ||
|
|
9bddc5722c | ||
|
|
7be9a85754 | ||
|
|
d69ec605dc |
No files matched your search
@@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
name: website-builder-setup
|
||||||
|
description: "Install the full AI website builder stack — UI/UX Pro Max, Framer Motion animations, and 21st.dev components. One skill, three tools, zero coding experience needed."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Website Builder Setup
|
||||||
|
|
||||||
|
This skill walks you through installing everything you need to build professional, animated websites with Claude Code. No coding experience required.
|
||||||
|
|
||||||
|
## What Gets Installed
|
||||||
|
|
||||||
|
| Tool | What it does |
|
||||||
|
|------|-------------|
|
||||||
|
| **UI/UX Pro Max** | Gives Claude access to 50+ design styles, 161 color palettes, 57 font pairings. Your sites look designed, not generated. |
|
||||||
|
| **Framer Motion** | Adds smooth animations — page transitions, hover effects, scroll reveals. Makes sites feel alive. |
|
||||||
|
| **21st.dev Magic** | A library of 100+ polished React components Claude can pull from. Production-quality building blocks. |
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
When this skill is triggered, walk the user through each step one at a time. Be encouraging and clear — assume they have zero coding experience. If any step fails, don't stop. Acknowledge it, give them the manual command, and keep moving.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 1: Check Prerequisites
|
||||||
|
|
||||||
|
> Before we start, let me make sure you have what we need.
|
||||||
|
|
||||||
|
Run this silently:
|
||||||
|
```bash
|
||||||
|
node --version 2>&1 && npm --version 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
- If Node.js is installed → say "You're good — Node.js is installed. Let's go."
|
||||||
|
- If NOT installed → say:
|
||||||
|
|
||||||
|
> You need Node.js first. Go to https://nodejs.org and download the LTS version. Install it, restart your terminal, then come back and run `/website-builder-setup` again. Takes 2 minutes.
|
||||||
|
|
||||||
|
Stop here if Node is missing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2: Install UI/UX Pro Max
|
||||||
|
|
||||||
|
> **Step 1 of 3: UI/UX Pro Max**
|
||||||
|
>
|
||||||
|
> This gives me a massive design library — 50+ styles, 161 color palettes, 57 font pairings. When you ask me to build a website, I'm pulling from a real design system instead of guessing. This is why the output won't look like generic AI slop.
|
||||||
|
>
|
||||||
|
> Installing now...
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
npm install -g uipro-cli 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
```bash
|
||||||
|
uipro init --ai claude 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
- On success → "UI/UX Pro Max is installed. Your design stack is ready."
|
||||||
|
- On failure → "Hit a snag. You can try manually later: `npm install -g uipro-cli && uipro init --ai claude`. Let's keep going."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3: Install Framer Motion
|
||||||
|
|
||||||
|
> **Step 2 of 3: Framer Motion**
|
||||||
|
>
|
||||||
|
> This teaches me how to add real animations to your websites — smooth page transitions, hover effects, scroll-triggered reveals. The stuff that makes a $500 site look like a $10,000 site.
|
||||||
|
>
|
||||||
|
> Installing now...
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
npm install framer-motion 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
- On success → "Framer Motion is installed. Your sites will have real animations now."
|
||||||
|
- On failure → "Hit a snag. You can try manually later: `npm install framer-motion`. Moving on."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4: Set Up 21st.dev Magic
|
||||||
|
|
||||||
|
> **Step 3 of 3: 21st.dev Components**
|
||||||
|
>
|
||||||
|
> This connects me to a library of 100+ beautifully designed React components. Instead of building everything from scratch, I pull from production-quality building blocks — buttons, navbars, hero sections, cards, footers — all pre-designed and ready to use.
|
||||||
|
>
|
||||||
|
> This one needs a free API key. Here's how to get it:
|
||||||
|
>
|
||||||
|
> 1. Go to **https://21st.dev/magic/console**
|
||||||
|
> 2. Sign up or log in (it's free)
|
||||||
|
> 3. Copy your API key
|
||||||
|
> 4. Paste it here when I ask for it
|
||||||
|
|
||||||
|
Wait for the user to provide their API key.
|
||||||
|
|
||||||
|
Once they provide it, add the MCP server to their Claude Code config:
|
||||||
|
|
||||||
|
Read `~/.claude.json`, find the `mcpServers` object, and add:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"21st-dev-magic": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@21st-dev/magic@latest"],
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "THEIR_KEY_HERE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After writing the config:
|
||||||
|
|
||||||
|
> 21st.dev Magic is connected. You'll need to restart Claude Code for this one to kick in — just close and reopen your terminal after we're done.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 5: Done
|
||||||
|
|
||||||
|
> **You're all set.** Here's what you just installed:
|
||||||
|
>
|
||||||
|
> - **UI/UX Pro Max** — 50+ styles, 161 palettes, 57 font pairings
|
||||||
|
> - **Framer Motion** — smooth, professional animations
|
||||||
|
> - **21st.dev Magic** — 100+ production-ready components
|
||||||
|
>
|
||||||
|
> **To build your first website, just tell me:**
|
||||||
|
> - What your business does
|
||||||
|
> - Who it's for
|
||||||
|
> - What vibe you want (dark, minimal, bold, playful, etc.)
|
||||||
|
>
|
||||||
|
> I'll handle the rest. Try something like:
|
||||||
|
>
|
||||||
|
> *"Build me a landing page for my consulting business targeting small business owners. Dark theme, modern, with animations."*
|
||||||
|
>
|
||||||
|
> **Important:** Restart Claude Code first so 21st.dev loads in. Then let's build something.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
- Walk through each step ONE AT A TIME
|
||||||
|
- Never dump all instructions at once
|
||||||
|
- If any install fails, don't stop — acknowledge, give manual command, keep moving
|
||||||
|
- Be encouraging and casual throughout
|
||||||
|
- Assume zero coding experience
|
||||||
|
- After everything is installed, prompt them to build their first site
|
||||||
@@ -1 +0,0 @@
|
|||||||
ff37d1cd
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
.hallmark/shots/
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
shots/
|
|
||||||
before/
|
|
||||||
before.json
|
|
||||||
before.txt
|
|
||||||
compare.html
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"shape": "Marquee Hero",
|
|
||||||
"theme": "squeeze",
|
|
||||||
"axes": "mid/geometric-sans/warm",
|
|
||||||
"hero": "Banner",
|
|
||||||
"nav": "edge-aligned",
|
|
||||||
"footer": "letter close",
|
|
||||||
"imagery": "photographs, library 1, natural",
|
|
||||||
"photos": [
|
|
||||||
"worm-bay"
|
|
||||||
],
|
|
||||||
"entry": "BeingOne front page",
|
|
||||||
"sections": [
|
|
||||||
"Hero",
|
|
||||||
"Path",
|
|
||||||
"Finder",
|
|
||||||
"Checks",
|
|
||||||
"Privacy",
|
|
||||||
"Talk",
|
|
||||||
"Family+Safety",
|
|
||||||
"Questions+Join",
|
|
||||||
"Footer"
|
|
||||||
],
|
|
||||||
"signature": "match finder with a tappable map of Australia and a moving score",
|
|
||||||
"waived": [],
|
|
||||||
"prompt": "BeingOne marriage site for Australians",
|
|
||||||
"date": "2026-10-23",
|
|
||||||
"mode": "build"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# BeingOne: plan (PRD)
|
|
||||||
|
|
||||||
Tagline: Real people. Real profiles. Real marriage.
|
|
||||||
For: Australians, and the families of Australians, who want to find a life partner. English only. Australia only.
|
|
||||||
Written for: Keith. Plain words on purpose.
|
|
||||||
|
|
||||||
## The big idea
|
|
||||||
|
|
||||||
BeingOne is a marriage website where every profile is checked to be a real person. People find matches, talk safely, and families can be part of it.
|
|
||||||
|
|
||||||
## The 13 core features (from Keith's plan)
|
|
||||||
|
|
||||||
1. Sign up and log in with email.
|
|
||||||
2. Make a profile: photos, age, city, job, things you love, a short "about me".
|
|
||||||
3. Check the profile is real: phone check, photo check, ID check, and a quick look by a person on the team.
|
|
||||||
4. Search by Australian city, age and shared interests.
|
|
||||||
5. Match score: a number showing how well two people fit.
|
|
||||||
6. Photo privacy: you decide who sees your photos.
|
|
||||||
7. Send an interest. The other person accepts or says no thanks.
|
|
||||||
8. Private chat that opens only when both say yes.
|
|
||||||
9. Family view: a parent or sibling helps look at profiles.
|
|
||||||
10. Safety tools: block, report, hide your profile.
|
|
||||||
11. Free plan and paid plan (payments).
|
|
||||||
12. Admin area for the BeingOne team: review profiles, handle reports, see numbers.
|
|
||||||
13. Emails and phone alerts: new matches, interests, messages.
|
|
||||||
|
|
||||||
## Phase 1 (today): the new look, with working demos
|
|
||||||
|
|
||||||
What you get: one front page in a new design. Everything on it is a demo that runs in your browser, with made-up sample people.
|
|
||||||
|
|
||||||
| Feature | Where it shows up on the page |
|
|
||||||
| --- | --- |
|
|
||||||
| 1 Sign up, log in | The join card at the bottom (Join and Log in tabs). It does not save anything yet. |
|
|
||||||
| 2 Profile | Shown through the sample people and the checks demo. The real profile editor is Phase 3. |
|
|
||||||
| 3 Checks | "Every profile is checked": run the four checks on a sample profile. |
|
|
||||||
| 4 Search | "Find your match across Australia": map of Australia, city, ages, interests. |
|
|
||||||
| 5 Match score | The score and bar on every sample person. |
|
|
||||||
| 6 Photo privacy | "You choose who sees your photos": pick who can see, pick who is looking. |
|
|
||||||
| 7 Interest, accept | "Chat opens when both say yes": accept or say no thanks. |
|
|
||||||
| 8 Private chat | Same section: the chat unlocks after Accept. |
|
|
||||||
| 9 Family view | "Family view" tile: pick who helps you look. |
|
|
||||||
| 10 Safety tools | "Safety tools" tile: block, report, hide. |
|
|
||||||
| 11 Plans | The "What does it cost?" answer. The paid price is left blank until you give it. |
|
|
||||||
| 12 Admin | Mentioned in "Who runs the site?". The real admin area is Phase 7 and is a separate private page. |
|
|
||||||
| 13 Alerts | The "Will I get alerts?" answer. Real alerts are Phase 6. |
|
|
||||||
|
|
||||||
Tests, in this order:
|
|
||||||
1. The page loads with no errors.
|
|
||||||
2. Every button, chip, slider, tab and form reacts.
|
|
||||||
3. Real connections: none yet, so nothing to test.
|
|
||||||
4. Security: no passwords, no keys, no calls to other servers for data. The forms only show a message on screen.
|
|
||||||
|
|
||||||
## Phases 2 to 8 (one at a time, each must work before the next)
|
|
||||||
|
|
||||||
2. Real sign-up and login (needs a host, a domain, a privacy policy and a consent step).
|
|
||||||
3. Real profiles and photos, with private photo storage.
|
|
||||||
4. Real checks (phone code, ID check through a trusted provider, team review screen).
|
|
||||||
5. Real search and matching on real profiles.
|
|
||||||
6. Private chat, block and report, email and phone alerts.
|
|
||||||
7. Payments (test mode first) and the admin area.
|
|
||||||
8. Launch checks: speed, phones, privacy rules, backups, a trial with friends and family.
|
|
||||||
|
|
||||||
## Important
|
|
||||||
|
|
||||||
Marriage sites hold very private details. In Australia some of them are "sensitive information" under the Privacy Act 1988, so people must clearly agree to how they are used. Plan the privacy policy and consent step in Phase 2. Please check with a lawyer before launch. This is not legal advice.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# BeingOne
|
|
||||||
|
|
||||||
Real people. Real profiles. Real marriage.
|
|
||||||
|
|
||||||
BeingOne is a marriage website for Australians and their families. Every profile is checked to be a real person. People find matches, talk safely, and families can be part of it.
|
|
||||||
|
|
||||||
This is **Phase 1**: the front page, in its new design, with working demos that run in the browser. The people on it are made up. Nothing is saved and nothing is sent anywhere yet.
|
|
||||||
|
|
||||||
## See it
|
|
||||||
|
|
||||||
There is no build step. Open `index.html` in a browser.
|
|
||||||
|
|
||||||
## What is in this folder
|
|
||||||
|
|
||||||
| File | What it is |
|
|
||||||
| --- | --- |
|
|
||||||
| `index.html` | The page |
|
|
||||||
| `style.css` | How it looks (colours, fonts, spacing, motion) |
|
|
||||||
| `tokens.css`, `base.css` | The colour and font settings, and the basic rules |
|
|
||||||
| `app.js` | What the buttons, map, search, checks, chat and forms do |
|
|
||||||
| `images/hero.jpg` | The photo at the top of the page (swap in the original) |
|
|
||||||
| `PRD.md` | The plan: 13 features and the 8 phases |
|
|
||||||
|
|
||||||
## What the demos do
|
|
||||||
|
|
||||||
- **Search bar and map of Australia:** pick a city, ages and interests. Made-up people appear with a match score.
|
|
||||||
- **Four checks:** run the phone, photo, ID and team checks on a sample profile.
|
|
||||||
- **Photo privacy:** choose who can see a photo, and who is looking.
|
|
||||||
- **Interest and chat:** accept an interest and a private chat opens.
|
|
||||||
- **Family view and safety tools:** pick who helps you look; block, report and hide.
|
|
||||||
- **Join and log in:** forms that do not send anything yet.
|
|
||||||
|
|
||||||
## Before it goes live
|
|
||||||
|
|
||||||
- The fonts load from Google Fonts. The beach photo loads from Wikimedia Commons (CC BY-SA 4.0, credited in the footer). Download both into this folder if you want the page to make no outside calls.
|
|
||||||
- Replace `images/hero.jpg` with the original photo file.
|
|
||||||
- The paid-plan price is a blank box on the page until you decide it.
|
|
||||||
- Marriage sites hold very private details. In Australia some of them count as "sensitive information" under the Privacy Act 1988, so people must clearly agree to how it is used. Plan the privacy policy and consent step in Phase 2, and check with a lawyer before launch. This is not legal advice.
|
|
||||||
|
|
||||||
## Next: Phase 2
|
|
||||||
|
|
||||||
Real sign-up and login. This needs a hosting place (for example Vercel and Supabase) and a domain name.
|
|
||||||
-313
@@ -1,313 +0,0 @@
|
|||||||
(() => {
|
|
||||||
'use strict';
|
|
||||||
const $ = (s, r = document) => r.querySelector(s);
|
|
||||||
const $$ = (s, r = document) => Array.from(r.querySelectorAll(s));
|
|
||||||
const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
||||||
const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
||||||
|
|
||||||
/* made-up sample people; nothing here is a real person (g: w = woman, m = man) */
|
|
||||||
const PEOPLE = [
|
|
||||||
{ n: 'Mia', g: 'w', a: 28, c: 'Sydney', l: ['beach', 'coffee', 'travel'], t: 'a' },
|
|
||||||
{ n: 'Arjun', g: 'm', a: 31, c: 'Melbourne', l: ['footy', 'cooking', 'music'], t: 'b' },
|
|
||||||
{ n: 'Hannah', g: 'w', a: 27, c: 'Brisbane', l: ['beach', 'hiking', 'pets'], t: 'c' },
|
|
||||||
{ n: 'Wei', g: 'm', a: 30, c: 'Perth', l: ['coffee', 'travel', 'hiking'], t: 'd' },
|
|
||||||
{ n: 'Sana', g: 'w', a: 29, c: 'Adelaide', l: ['cooking', 'music', 'pets'], t: 'a' },
|
|
||||||
{ n: 'Tom', g: 'm', a: 33, c: 'Hobart', l: ['hiking', 'footy', 'coffee'], t: 'b' },
|
|
||||||
{ n: 'Priya', g: 'w', a: 26, c: 'Sydney', l: ['music', 'cooking', 'beach'], t: 'c' },
|
|
||||||
{ n: 'Jack', g: 'm', a: 35, c: 'Melbourne', l: ['footy', 'beach', 'pets'], t: 'd' },
|
|
||||||
{ n: 'Elena', g: 'w', a: 32, c: 'Brisbane', l: ['travel', 'coffee', 'music'], t: 'a' },
|
|
||||||
{ n: 'Dev', g: 'm', a: 34, c: 'Perth', l: ['footy', 'cooking', 'hiking'], t: 'b' },
|
|
||||||
{ n: 'Grace', g: 'w', a: 30, c: 'Canberra', l: ['hiking', 'coffee', 'pets'], t: 'c' },
|
|
||||||
{ n: 'Sam', g: 'm', a: 28, c: 'Darwin', l: ['beach', 'footy', 'travel'], t: 'd' },
|
|
||||||
{ n: 'Lena', g: 'w', a: 38, c: 'Sydney', l: ['travel', 'music', 'pets'], t: 'a' },
|
|
||||||
{ n: 'Omar', g: 'm', a: 29, c: 'Melbourne', l: ['coffee', 'cooking', 'hiking'], t: 'c' }
|
|
||||||
];
|
|
||||||
|
|
||||||
/* ---------- scroll reveal, once ---------- */
|
|
||||||
const revealEls = $$('[data-reveal]');
|
|
||||||
if ('IntersectionObserver' in window) {
|
|
||||||
const io = new IntersectionObserver((es) => es.forEach((e) => {
|
|
||||||
if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); }
|
|
||||||
}), { rootMargin: '0px 0px -10% 0px' });
|
|
||||||
revealEls.forEach((el) => io.observe(el));
|
|
||||||
} else {
|
|
||||||
revealEls.forEach((el) => el.classList.add('is-in'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- mobile menu ---------- */
|
|
||||||
const menu = $('.nav__menu'), links = $('#nav-links');
|
|
||||||
menu.addEventListener('click', () => {
|
|
||||||
const open = links.classList.toggle('is-open');
|
|
||||||
menu.setAttribute('aria-expanded', String(open));
|
|
||||||
menu.textContent = open ? 'Close' : 'Menu';
|
|
||||||
});
|
|
||||||
links.addEventListener('click', (e) => {
|
|
||||||
if (e.target.closest('a')) { links.classList.remove('is-open'); menu.setAttribute('aria-expanded', 'false'); menu.textContent = 'Menu'; }
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ---------- match finder (the quick search bar drives it too) ---------- */
|
|
||||||
const st = { city: 'Anywhere', gender: 'any', from: 25, to: 40, likes: new Set(), sent: new Set() };
|
|
||||||
const from = $('#f-from'), to = $('#f-to'), oFrom = $('#o-from'), oTo = $('#o-to');
|
|
||||||
const results = $('#results'), status = $('#finder-status'), mapCity = $('#map-city');
|
|
||||||
const qFor = $('#q-for'), qAge = $('#q-age'), qCity = $('#q-city');
|
|
||||||
const pins = $$('.pin');
|
|
||||||
|
|
||||||
function scoreOf(p) {
|
|
||||||
const mid = (st.from + st.to) / 2, half = Math.max((st.to - st.from) / 2, 1);
|
|
||||||
const ageFit = Math.max(0, 1 - Math.abs(p.a - mid) / (half + 4));
|
|
||||||
const shared = p.l.filter((x) => st.likes.has(x)).length;
|
|
||||||
const likeFit = st.likes.size ? Math.min(1, shared / Math.min(st.likes.size, 3)) : 0.5;
|
|
||||||
const cityFit = st.city === 'Anywhere' ? 0.7 : 1;
|
|
||||||
return Math.round(55 + 45 * (0.5 * likeFit + 0.3 * ageFit + 0.2 * cityFit));
|
|
||||||
}
|
|
||||||
|
|
||||||
function tile(r, i) {
|
|
||||||
const p = r.p, sent = st.sent.has(p.n);
|
|
||||||
const tags = p.l.map((x) => '<li' + (st.likes.has(x) ? ' class="is-shared"' : '') + '>' + cap(x) + '</li>').join('');
|
|
||||||
return '<article class="person" style="--i:' + i + '">' +
|
|
||||||
'<div class="person__who"><span class="avatar" style="--tone:var(--tone-' + p.t + ')" aria-hidden="true">' + p.n.charAt(0) + '</span>' +
|
|
||||||
'<div><h3>' + p.n + ', ' + p.a + '</h3><p class="label">' + p.c + '</p></div></div>' +
|
|
||||||
'<div><div class="score"><b data-to="' + r.score + '">' + r.score + '</b><span class="label" title="Counts shared interests, city and age">match</span></div>' +
|
|
||||||
'<div class="bar" role="img" aria-label="Match score ' + r.score + ' out of 100"><i style="--s:' + r.score + '"></i></div></div>' +
|
|
||||||
'<ul class="tags">' + tags + '</ul>' +
|
|
||||||
'<button class="btn btn--sm" type="button" data-interest="' + p.n + '" aria-pressed="' + sent + '">' + (sent ? 'Interest sent' : 'Send interest') + '</button>' +
|
|
||||||
'</article>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* scores count up from 55 to their value, once per render */
|
|
||||||
function countUp(el) {
|
|
||||||
const to = +el.dataset.to;
|
|
||||||
if (reduced) { el.textContent = to; return; }
|
|
||||||
const t0 = performance.now(), dur = 900, start = 55;
|
|
||||||
el.textContent = start;
|
|
||||||
(function step(now) {
|
|
||||||
const k = Math.min(1, (now - t0) / dur), e = 1 - Math.pow(1 - k, 3);
|
|
||||||
el.textContent = Math.round(start + (to - start) * e);
|
|
||||||
if (k < 1) requestAnimationFrame(step);
|
|
||||||
})(t0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderFinder() {
|
|
||||||
const pool = PEOPLE
|
|
||||||
.filter((p) => (st.city === 'Anywhere' || p.c === st.city) && (st.gender === 'any' || p.g === st.gender) && p.a >= st.from && p.a <= st.to)
|
|
||||||
.map((p) => ({ p, score: scoreOf(p) }))
|
|
||||||
.sort((x, y) => y.score - x.score || x.p.n.localeCompare(y.p.n));
|
|
||||||
const top = pool.slice(0, 4);
|
|
||||||
results.innerHTML = top.length
|
|
||||||
? top.map(tile).join('')
|
|
||||||
: '<p class="results__empty">No one fits yet. Widen the ages or try another city.</p>';
|
|
||||||
$$('.score b', results).forEach(countUp);
|
|
||||||
status.textContent = top.length ? top.length + ' of ' + pool.length + ' shown.' : '';
|
|
||||||
mapCity.textContent = st.city === 'Anywhere' ? 'All of Australia' : st.city;
|
|
||||||
qCity.value = st.city;
|
|
||||||
pins.forEach((b) => b.setAttribute('aria-pressed', String(b.dataset.city === st.city)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAges(a, b) {
|
|
||||||
st.from = a; st.to = b; from.value = a; to.value = b;
|
|
||||||
oFrom.textContent = a; oTo.textContent = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
pins.forEach((b) => b.addEventListener('click', () => {
|
|
||||||
st.city = st.city === b.dataset.city ? 'Anywhere' : b.dataset.city;
|
|
||||||
if (st.city === b.dataset.city && !reduced) {
|
|
||||||
const ping = document.createElement('span');
|
|
||||||
ping.className = 'ping';
|
|
||||||
ping.addEventListener('animationend', () => ping.remove(), { once: true });
|
|
||||||
b.appendChild(ping);
|
|
||||||
}
|
|
||||||
renderFinder();
|
|
||||||
}));
|
|
||||||
from.addEventListener('input', () => {
|
|
||||||
st.from = +from.value;
|
|
||||||
if (st.from > st.to) { st.to = st.from; to.value = st.to; }
|
|
||||||
oFrom.textContent = st.from; oTo.textContent = st.to; renderFinder();
|
|
||||||
});
|
|
||||||
to.addEventListener('input', () => {
|
|
||||||
st.to = +to.value;
|
|
||||||
if (st.to < st.from) { st.from = st.to; from.value = st.from; }
|
|
||||||
oFrom.textContent = st.from; oTo.textContent = st.to; renderFinder();
|
|
||||||
});
|
|
||||||
$$('#f-likes input').forEach((c) => c.addEventListener('change', () => {
|
|
||||||
c.checked ? st.likes.add(c.value) : st.likes.delete(c.value);
|
|
||||||
renderFinder();
|
|
||||||
}));
|
|
||||||
results.addEventListener('click', (e) => {
|
|
||||||
const b = e.target.closest('[data-interest]');
|
|
||||||
if (!b) return;
|
|
||||||
const name = b.dataset.interest;
|
|
||||||
st.sent.has(name) ? st.sent.delete(name) : st.sent.add(name);
|
|
||||||
const on = st.sent.has(name);
|
|
||||||
b.setAttribute('aria-pressed', String(on));
|
|
||||||
b.textContent = on ? 'Interest sent' : 'Send interest';
|
|
||||||
});
|
|
||||||
|
|
||||||
/* quick search bar: each choice updates the finder, the button also scrolls to the people */
|
|
||||||
function applyQuick() {
|
|
||||||
st.gender = qFor.value;
|
|
||||||
st.city = qCity.value;
|
|
||||||
const [a, b] = qAge.value.split('-').map(Number);
|
|
||||||
setAges(a, b);
|
|
||||||
renderFinder();
|
|
||||||
}
|
|
||||||
[qFor, qAge, qCity].forEach((el) => el.addEventListener('change', applyQuick));
|
|
||||||
$('#quick').addEventListener('submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
applyQuick();
|
|
||||||
results.scrollIntoView({ behavior: reduced ? 'auto' : 'smooth', block: 'center' });
|
|
||||||
});
|
|
||||||
renderFinder();
|
|
||||||
|
|
||||||
/* ---------- the four checks ---------- */
|
|
||||||
const rows = $$('[data-check]'), vStatus = $('#v-status'), vText = $('#v-status-text'), vBar = $('#v-progress');
|
|
||||||
const timers = new Map();
|
|
||||||
function updateChecks() {
|
|
||||||
const n = rows.filter((r) => r.classList.contains('is-done')).length;
|
|
||||||
vStatus.dataset.state = n === rows.length ? 'done' : 'idle';
|
|
||||||
vText.textContent = n === rows.length ? 'Verified' : n ? n + ' of 4 passed' : 'Not verified yet';
|
|
||||||
vBar.style.setProperty('--p', n / rows.length);
|
|
||||||
}
|
|
||||||
function runCheck(row) {
|
|
||||||
if (row.classList.contains('is-done') || timers.has(row)) return;
|
|
||||||
const btn = $('[data-run]', row);
|
|
||||||
btn.setAttribute('aria-busy', 'true'); btn.disabled = true; btn.textContent = 'Checking…';
|
|
||||||
timers.set(row, setTimeout(() => {
|
|
||||||
timers.delete(row);
|
|
||||||
row.classList.add('is-done');
|
|
||||||
btn.hidden = true; btn.removeAttribute('aria-busy');
|
|
||||||
$('.passed', row).hidden = false;
|
|
||||||
updateChecks();
|
|
||||||
}, 800));
|
|
||||||
}
|
|
||||||
rows.forEach((r) => $('[data-run]', r).addEventListener('click', () => runCheck(r)));
|
|
||||||
$('#v-all').addEventListener('click', () => rows.forEach((r, i) => setTimeout(() => runCheck(r), i * 250)));
|
|
||||||
$('#v-reset').addEventListener('click', () => {
|
|
||||||
timers.forEach((t) => clearTimeout(t)); timers.clear();
|
|
||||||
rows.forEach((r) => {
|
|
||||||
r.classList.remove('is-done');
|
|
||||||
const btn = $('[data-run]', r);
|
|
||||||
btn.hidden = false; btn.disabled = false; btn.removeAttribute('aria-busy'); btn.textContent = 'Run';
|
|
||||||
$('.passed', r).hidden = true;
|
|
||||||
});
|
|
||||||
updateChecks();
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ---------- photo privacy ---------- */
|
|
||||||
const photo = $('#photo'), photoState = $('#photo-state');
|
|
||||||
const rank = { stranger: 0, verified: 1, accepted: 2 };
|
|
||||||
function renderPhoto() {
|
|
||||||
const setting = $('input[name="setting"]:checked').value;
|
|
||||||
const viewer = $('input[name="viewer"]:checked').value;
|
|
||||||
const can = setting === 'everyone' ||
|
|
||||||
(setting === 'verified' && rank[viewer] >= 1) ||
|
|
||||||
(setting === 'accepted' && rank[viewer] >= 2);
|
|
||||||
photo.classList.toggle('is-hidden', !can);
|
|
||||||
photoState.textContent = can ? 'Visible to this visitor' : 'Hidden from this visitor';
|
|
||||||
}
|
|
||||||
$$('input[name="setting"], input[name="viewer"]').forEach((r) => r.addEventListener('change', renderPhoto));
|
|
||||||
renderPhoto();
|
|
||||||
|
|
||||||
/* ---------- interest, accept, chat ---------- */
|
|
||||||
const chat = $('#chat'), log = $('#chat-log'), tResult = $('#t-result');
|
|
||||||
const accept = $('#t-accept'), decline = $('#t-decline'), tReset = $('#t-reset');
|
|
||||||
const chatForm = $('#chat-form'), chatInput = $('#chat-input');
|
|
||||||
function addLine(text, cls) {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
if (cls) li.className = cls;
|
|
||||||
li.textContent = text;
|
|
||||||
log.appendChild(li);
|
|
||||||
log.scrollTop = log.scrollHeight;
|
|
||||||
}
|
|
||||||
function decide(yes) {
|
|
||||||
accept.hidden = true; decline.hidden = true; tReset.hidden = false;
|
|
||||||
tResult.textContent = yes ? 'You both said yes. Say hello.' : 'You said no thanks. Nothing opens.';
|
|
||||||
if (yes) {
|
|
||||||
chat.classList.remove('is-locked');
|
|
||||||
addLine('You and Priya both said yes.', 'note');
|
|
||||||
chatInput.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accept.addEventListener('click', () => decide(true));
|
|
||||||
decline.addEventListener('click', () => decide(false));
|
|
||||||
tReset.addEventListener('click', () => {
|
|
||||||
accept.hidden = false; decline.hidden = false; tReset.hidden = true;
|
|
||||||
tResult.textContent = ''; log.textContent = '';
|
|
||||||
chat.classList.add('is-locked'); chatInput.value = '';
|
|
||||||
});
|
|
||||||
chatForm.addEventListener('submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const v = chatInput.value.trim();
|
|
||||||
if (!v) { chatInput.focus(); return; }
|
|
||||||
addLine(v, 'me'); chatInput.value = ''; chatInput.focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ---------- family view and safety tools ---------- */
|
|
||||||
const famStatus = $('#fam-status');
|
|
||||||
function renderFam() {
|
|
||||||
const on = $$('#fam input:checked').map((c) => c.value);
|
|
||||||
famStatus.textContent = !on.length ? 'Just you for now.'
|
|
||||||
: (on.length === 1 ? on[0] : on.slice(0, -1).join(', ') + ' and ' + on[on.length - 1]) +
|
|
||||||
(on.length === 1 ? ' is' : ' are') + ' looking with you.';
|
|
||||||
}
|
|
||||||
$$('#fam input').forEach((c) => c.addEventListener('change', renderFam));
|
|
||||||
|
|
||||||
const safe = $('#safe-status'), sBlock = $('#s-block'), sHide = $('#s-hide'), sReport = $('#s-report');
|
|
||||||
let blocked = false;
|
|
||||||
sBlock.addEventListener('click', () => {
|
|
||||||
blocked = !blocked;
|
|
||||||
$('span', sBlock).textContent = blocked ? 'Unblock' : 'Block';
|
|
||||||
sBlock.setAttribute('aria-pressed', String(blocked));
|
|
||||||
safe.textContent = blocked ? 'Jack is blocked. He cannot message you.' : 'Jack is unblocked.';
|
|
||||||
});
|
|
||||||
sReport.addEventListener('click', () => { safe.textContent = 'Demo only: nothing was sent to our team.'; });
|
|
||||||
sHide.addEventListener('click', () => {
|
|
||||||
const on = sHide.getAttribute('aria-pressed') !== 'true';
|
|
||||||
sHide.setAttribute('aria-pressed', String(on));
|
|
||||||
$('span', sHide).textContent = on ? 'Show my profile' : 'Hide my profile';
|
|
||||||
safe.textContent = on ? 'Your profile is hidden from search.' : 'Your profile shows in search.';
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ---------- join card: tabs and forms that post nowhere yet ---------- */
|
|
||||||
const tabs = $$('[role="tab"]', $('#join-card'));
|
|
||||||
const panels = { join: $('#panel-join'), login: $('#panel-login') };
|
|
||||||
function openTab(name, focus) {
|
|
||||||
tabs.forEach((t) => {
|
|
||||||
const on = t.id === 'tab-' + name;
|
|
||||||
t.setAttribute('aria-selected', String(on));
|
|
||||||
t.tabIndex = on ? 0 : -1;
|
|
||||||
if (on && focus) t.focus();
|
|
||||||
});
|
|
||||||
Object.keys(panels).forEach((k) => { panels[k].hidden = k !== name; });
|
|
||||||
}
|
|
||||||
tabs.forEach((t, i) => {
|
|
||||||
t.addEventListener('click', () => openTab(t.id.replace('tab-', '')));
|
|
||||||
t.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
|
|
||||||
e.preventDefault();
|
|
||||||
const next = tabs[(i + (e.key === 'ArrowRight' ? 1 : tabs.length - 1)) % tabs.length];
|
|
||||||
openTab(next.id.replace('tab-', ''), true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$$('[data-open-tab]').forEach((a) => a.addEventListener('click', () => openTab(a.dataset.openTab)));
|
|
||||||
openTab('join');
|
|
||||||
|
|
||||||
$$('form[data-endpoint]').forEach((form) => {
|
|
||||||
const help = $('.form-help', form);
|
|
||||||
form.addEventListener('input', (e) => { e.target.removeAttribute && e.target.removeAttribute('aria-invalid'); });
|
|
||||||
form.addEventListener('submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
help.classList.remove('is-error');
|
|
||||||
const bad = $$('input', form).filter((i) => !i.checkValidity());
|
|
||||||
$$('input', form).forEach((i) => i.toggleAttribute('aria-invalid', bad.includes(i)));
|
|
||||||
if (bad.length) {
|
|
||||||
help.classList.add('is-error');
|
|
||||||
help.textContent = bad[0].type === 'email'
|
|
||||||
? 'That email looks incomplete. Write it as name@example.com.'
|
|
||||||
: 'Please fill in the highlighted field.';
|
|
||||||
bad[0].focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!form.dataset.endpoint) { help.textContent = 'Nothing was sent: this form is not connected yet.'; return; }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
*, *::before, *::after { box-sizing: border-box; }
|
|
||||||
html, body { overflow-x: clip; }
|
|
||||||
html { scrollbar-color: var(--color-muted) var(--color-paper); scroll-padding-top: var(--space-3xl); }
|
|
||||||
body { margin: 0; background: var(--color-paper); color: var(--color-ink); font-family: var(--font-body); font-size: var(--text-md); line-height: 1.55; -webkit-font-smoothing: antialiased; caret-color: var(--color-accent); }
|
|
||||||
::selection { background: color-mix(in oklab, var(--color-accent) 22%, transparent); color: var(--color-ink); }
|
|
||||||
:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
|
|
||||||
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.16em; }
|
|
||||||
h1, h2, h3 { margin: 0; font-family: var(--font-display); font-synthesis: none; overflow-wrap: break-word; min-width: 0; text-wrap: balance; }
|
|
||||||
p { margin: 0; text-wrap: pretty; }
|
|
||||||
p:not([class]) { max-width: 65ch; }
|
|
||||||
img, video { max-width: 100%; height: auto; display: block; }
|
|
||||||
svg { display: block; }
|
|
||||||
[hidden] { display: none !important; }
|
|
||||||
button, input, select, textarea { font: inherit; }
|
|
||||||
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); box-sizing: border-box; }
|
|
||||||
.num, .figure, td.num { font-variant-numeric: tabular-nums; }
|
|
||||||
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
||||||
[data-slot] { position: relative; overflow: hidden; background: linear-gradient(160deg, var(--color-paper-2, var(--color-paper)), var(--color-paper-3, var(--color-paper))); }
|
|
||||||
[data-slot] > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
|
|
||||||
[data-slot]:has(> img) > :not(img):not([data-keep]) { display: none !important; }
|
|
||||||
@media (max-width: 60rem) { :root { --gutter: 24px; --section-gap-s: 80px; --section-gap: 96px; --section-gap-l: 120px; } }
|
|
||||||
@media (max-width: 40rem) { :root { --gutter: 16px; --section-gap-s: 56px; --section-gap: 72px; --section-gap-l: 96px; --gap-columns: 16px; } }
|
|
||||||
@media (prefers-reduced-motion: reduce) { *:not(.keep-motion), *:not(.keep-motion)::before, *:not(.keep-motion)::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB |
@@ -1,346 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en-AU">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>BeingOne: marriage profiles for Australians, every one checked</title>
|
|
||||||
<meta name="description" content="BeingOne is a marriage website for Australians and their families where every profile is checked to be a real person.">
|
|
||||||
<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=DM+Serif+Display:ital@0;1&family=Figtree:wght@500;700&display=swap" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script>document.documentElement.classList.add('js');</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
|
|
||||||
<symbol id="i-check" viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></symbol>
|
|
||||||
<symbol id="i-arrow" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></symbol>
|
|
||||||
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></symbol>
|
|
||||||
<symbol id="i-lock" viewBox="0 0 24 24"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
|
|
||||||
<symbol id="i-eye" viewBox="0 0 24 24"><path d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"/><circle cx="12" cy="12" r="3"/></symbol>
|
|
||||||
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></symbol>
|
|
||||||
<symbol id="i-phone" viewBox="0 0 24 24"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/></symbol>
|
|
||||||
<symbol id="i-camera" viewBox="0 0 24 24"><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></symbol>
|
|
||||||
<symbol id="i-id" viewBox="0 0 24 24"><rect x="2" y="5" width="20" height="14" rx="2"/><circle cx="9" cy="11" r="2"/><path d="M5.5 16c.5-1.5 2-2.5 3.5-2.5s3 1 3.5 2.5M15 10h4M15 14h3"/></symbol>
|
|
||||||
<symbol id="i-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
|
|
||||||
<symbol id="i-ban" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m4.9 4.9 14.2 14.2"/></symbol>
|
|
||||||
<symbol id="i-flag" viewBox="0 0 24 24"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><path d="M4 22v-7"/></symbol>
|
|
||||||
<symbol id="i-send" viewBox="0 0 24 24"><path d="m22 2-7 20-4-9-9-4z"/><path d="M22 2 11 13"/></symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<a class="skip" href="#main">Skip to content</a>
|
|
||||||
|
|
||||||
<header class="nav">
|
|
||||||
<div class="container nav__bar">
|
|
||||||
<a class="wordmark" href="#top" aria-label="BeingOne, back to the top">
|
|
||||||
<svg class="mark" viewBox="0 0 32 32" aria-hidden="true"><circle cx="12" cy="16" r="8"/><circle cx="20" cy="16" r="8"/></svg>
|
|
||||||
<span>Being<b>One</b></span>
|
|
||||||
</a>
|
|
||||||
<nav class="nav__links" id="nav-links" aria-label="Main">
|
|
||||||
<a href="#find">Discover</a>
|
|
||||||
<a href="#results">Curated matches</a>
|
|
||||||
<a href="#talk">Messages</a>
|
|
||||||
<a href="#safety">Safety centre</a>
|
|
||||||
<a href="#help">Help</a>
|
|
||||||
<a class="only-sm" href="#join" data-open-tab="login">Sign in</a>
|
|
||||||
</nav>
|
|
||||||
<div class="nav__actions">
|
|
||||||
<a class="nav__signin" href="#join" data-open-tab="login">Sign in</a>
|
|
||||||
<a class="btn btn--sm" href="#join" data-open-tab="join">Join</a>
|
|
||||||
<button class="nav__menu btn btn--sm" type="button" aria-expanded="false" aria-controls="nav-links">Menu</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main id="main">
|
|
||||||
|
|
||||||
<section class="hero" id="top" aria-labelledby="hero-title">
|
|
||||||
<figure class="hero__photo" data-slot="hero" data-alt="A bride and groom holding hands outdoors as guests throw petals">
|
|
||||||
<img src="images/hero.jpg" width="998" height="770" alt="A bride and groom holding hands outdoors as guests throw petals" fetchpriority="high">
|
|
||||||
</figure>
|
|
||||||
<div class="container hero__inner">
|
|
||||||
<div class="hero__text">
|
|
||||||
<h1 id="hero-title">
|
|
||||||
<span class="line" style="--i:0"><span>Real people.</span></span>
|
|
||||||
<span class="line" style="--i:1"><span>Real profiles.</span></span>
|
|
||||||
<span class="line line--it" style="--i:2"><span>Real marriage.</span></span>
|
|
||||||
</h1>
|
|
||||||
<p class="lead hero__lead">Every profile is checked, so you meet real people across Australia.</p>
|
|
||||||
<a class="btn btn--primary hero__cta" href="#join" data-open-tab="join">Create your profile<svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container quick__wrap">
|
|
||||||
<form class="quick" id="quick" onsubmit="return false" aria-label="Quick search">
|
|
||||||
<div class="quick__field">
|
|
||||||
<label for="q-for">I’m looking for</label>
|
|
||||||
<select id="q-for"><option value="any">Anyone</option><option value="w">A woman</option><option value="m">A man</option></select>
|
|
||||||
</div>
|
|
||||||
<div class="quick__field">
|
|
||||||
<label for="q-age">Age</label>
|
|
||||||
<select id="q-age"><option value="21-60">Any age</option><option value="21-29">21 to 29</option><option value="30-39">30 to 39</option><option value="40-49">40 to 49</option><option value="50-60">50 to 60</option></select>
|
|
||||||
</div>
|
|
||||||
<div class="quick__field">
|
|
||||||
<label for="q-city">Location</label>
|
|
||||||
<select id="q-city"><option>Anywhere</option><option>Adelaide</option><option>Brisbane</option><option>Canberra</option><option>Darwin</option><option>Hobart</option><option>Melbourne</option><option>Perth</option><option>Sydney</option></select>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn--primary quick__go" type="submit"><svg class="icon" aria-hidden="true"><use href="#i-search"/></svg>Search</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="band band--path" id="path" aria-label="How it goes">
|
|
||||||
<div class="container">
|
|
||||||
<ol class="path">
|
|
||||||
<li data-reveal style="--i:0"><a href="#join">Join</a></li>
|
|
||||||
<li data-reveal style="--i:1"><a href="#verified">Get checked</a></li>
|
|
||||||
<li data-reveal style="--i:2"><a href="#find">Find</a></li>
|
|
||||||
<li data-reveal style="--i:3"><a href="#talk">Say yes</a></li>
|
|
||||||
<li data-reveal style="--i:4"><a href="#talk">Chat</a></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="sec finder" id="find" aria-labelledby="find-title">
|
|
||||||
<div class="container">
|
|
||||||
<div class="grid finder__top">
|
|
||||||
<h2 id="find-title" data-reveal>Find your match across Australia</h2>
|
|
||||||
<p class="lead" data-reveal style="--i:1">Pick a city, ages and what you love.</p>
|
|
||||||
</div>
|
|
||||||
<div class="grid finder__body">
|
|
||||||
<div class="map" data-reveal role="group" aria-label="Map of Australia. Tap a city to search there.">
|
|
||||||
<svg class="map__svg" viewBox="-40 -20 605 516" role="img" aria-label="Outline of Australia">
|
|
||||||
<path d="M381.3 9.8L393.8 56.0L416.3 70.0L422.5 96.6L435.0 130.2L458.7 144.2L465.0 155.4L485.0 187.6L505.0 208.6L513.7 224.0L512.5 245.0L520.0 260.4L512.5 284.2L506.3 308.0L490.0 334.6L485.0 348.6L476.2 366.8L473.8 385.0L450.0 392.0L430.0 407.4L412.5 397.6L393.8 403.2L362.5 393.4L350.0 385.0L343.8 358.4L326.2 359.1L331.3 347.2L321.2 336.0L300.0 347.2L277.5 322.0L237.5 301.0L212.5 303.8L175.0 312.2L143.8 333.2L123.8 334.6L87.5 348.6L73.8 350.0L38.7 341.6L46.3 326.2L47.5 308.0L32.5 263.2L17.5 224.0L18.8 196.0L26.2 165.2L58.8 149.8L82.5 144.2L118.8 126.0L127.5 112.0L147.5 91.0L168.8 77.0L186.3 54.6L212.5 70.0L221.2 68.6L225.0 46.2L235.0 35.0L256.3 21.0L275.0 28.0L310.0 30.8L306.3 49.0L297.5 72.8L322.5 91.0L343.8 105.0L360.0 103.6L370.0 77.0L368.8 49.0L376.2 30.8ZM408.7 429.8L453.8 432.6L453.8 450.8L441.3 469.0L425.0 470.4L415.0 450.8Z"/>
|
|
||||||
</svg>
|
|
||||||
<button class="pin" type="button" data-city="Perth" aria-pressed="false" aria-label="Perth" title="Perth" style="--x:14.6%;--y:63.4%;--i:0"></button>
|
|
||||||
<button class="pin" type="button" data-city="Adelaide" aria-pressed="false" aria-label="Adelaide" title="Adelaide" style="--x:61.6%;--y:71.5%;--i:1"></button>
|
|
||||||
<button class="pin" type="button" data-city="Darwin" aria-pressed="false" aria-label="Darwin" title="Darwin" style="--x:45.5%;--y:10.5%;--i:2"></button>
|
|
||||||
<button class="pin" type="button" data-city="Brisbane" aria-pressed="false" aria-label="Brisbane" title="Brisbane" style="--x:91.4%;--y:51.3%;--i:3"></button>
|
|
||||||
<button class="pin" type="button" data-city="Sydney" aria-pressed="false" aria-label="Sydney" title="Sydney" style="--x:87.6%;--y:68.6%;--i:4"></button>
|
|
||||||
<button class="pin" type="button" data-city="Canberra" aria-pressed="false" aria-label="Canberra" title="Canberra" style="--x:83.3%;--y:72.5%;--i:5"></button>
|
|
||||||
<button class="pin" type="button" data-city="Melbourne" aria-pressed="false" aria-label="Melbourne" title="Melbourne" style="--x:74.7%;--y:79.3%;--i:6"></button>
|
|
||||||
<button class="pin" type="button" data-city="Hobart" aria-pressed="false" aria-label="Hobart" title="Hobart" style="--x:79.6%;--y:93.1%;--i:7"></button>
|
|
||||||
<p class="map__city" id="map-city">All of Australia</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="controls" id="finder-form" data-reveal style="--i:1" onsubmit="return false" aria-label="Match finder">
|
|
||||||
<div class="controls__ages">
|
|
||||||
<div class="field">
|
|
||||||
<div class="field__row"><label for="f-from">From</label><output id="o-from" for="f-from">25</output></div>
|
|
||||||
<input id="f-from" type="range" min="21" max="60" value="25">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="field__row"><label for="f-to">To</label><output id="o-to" for="f-to">40</output></div>
|
|
||||||
<input id="f-to" type="range" min="21" max="60" value="40">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<fieldset class="choices" id="f-likes">
|
|
||||||
<legend>You love</legend>
|
|
||||||
<input type="checkbox" id="l-beach" value="beach"><label for="l-beach">Beach</label>
|
|
||||||
<input type="checkbox" id="l-footy" value="footy"><label for="l-footy">Footy</label>
|
|
||||||
<input type="checkbox" id="l-coffee" value="coffee"><label for="l-coffee">Coffee</label>
|
|
||||||
<input type="checkbox" id="l-hiking" value="hiking"><label for="l-hiking">Hiking</label>
|
|
||||||
<input type="checkbox" id="l-cooking" value="cooking"><label for="l-cooking">Cooking</label>
|
|
||||||
<input type="checkbox" id="l-travel" value="travel"><label for="l-travel">Travel</label>
|
|
||||||
<input type="checkbox" id="l-music" value="music"><label for="l-music">Music</label>
|
|
||||||
<input type="checkbox" id="l-pets" value="pets"><label for="l-pets">Pets</label>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="results" id="results" aria-live="polite"></div>
|
|
||||||
<p class="visually-hidden" id="finder-status" role="status"></p>
|
|
||||||
<p class="finder__note">Sample people, made up for this demo.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="sec band--navy on-dark checks" id="verified" aria-labelledby="verified-title">
|
|
||||||
<div class="container grid">
|
|
||||||
<div class="head">
|
|
||||||
<h2 id="verified-title" data-reveal>Every profile is checked</h2>
|
|
||||||
<p class="lead" data-reveal style="--i:1">Try the four checks.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checks__panel" data-reveal style="--i:2">
|
|
||||||
<div class="checks__who">
|
|
||||||
<span class="avatar" style="--tone:var(--tone-a)" aria-hidden="true">M</span>
|
|
||||||
<div><h3>Mia, 28</h3><p class="label">Sydney</p></div>
|
|
||||||
<span class="status" id="v-status" data-state="idle" role="status"><svg class="icon icon--sm" aria-hidden="true"><use href="#i-shield"/></svg><span id="v-status-text">Not verified yet</span></span>
|
|
||||||
</div>
|
|
||||||
<div class="progress" aria-hidden="true"><i id="v-progress" style="--p:0"></i></div>
|
|
||||||
<ul class="rows" id="v-rows">
|
|
||||||
<li data-check>
|
|
||||||
<svg class="icon idle" aria-hidden="true"><use href="#i-phone"/></svg><svg class="icon tick" aria-hidden="true"><use href="#i-check"/></svg>
|
|
||||||
<div><h3>Phone check</h3><p>A code arrives by text.</p></div>
|
|
||||||
<button class="btn btn--sm" type="button" data-run>Run</button><span class="passed" hidden>Passed</span>
|
|
||||||
</li>
|
|
||||||
<li data-check>
|
|
||||||
<svg class="icon idle" aria-hidden="true"><use href="#i-camera"/></svg><svg class="icon tick" aria-hidden="true"><use href="#i-check"/></svg>
|
|
||||||
<div><h3>Photo check</h3><p>Checked as real.</p></div>
|
|
||||||
<button class="btn btn--sm" type="button" data-run>Run</button><span class="passed" hidden>Passed</span>
|
|
||||||
</li>
|
|
||||||
<li data-check>
|
|
||||||
<svg class="icon idle" aria-hidden="true"><use href="#i-id"/></svg><svg class="icon tick" aria-hidden="true"><use href="#i-check"/></svg>
|
|
||||||
<div><h3>ID check</h3><p>Matched to your photo.</p></div>
|
|
||||||
<button class="btn btn--sm" type="button" data-run>Run</button><span class="passed" hidden>Passed</span>
|
|
||||||
</li>
|
|
||||||
<li data-check>
|
|
||||||
<svg class="icon idle" aria-hidden="true"><use href="#i-users"/></svg><svg class="icon tick" aria-hidden="true"><use href="#i-check"/></svg>
|
|
||||||
<div><h3>Team look</h3><p>A quick look by our team.</p></div>
|
|
||||||
<button class="btn btn--sm" type="button" data-run>Run</button><span class="passed" hidden>Passed</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="checks__foot">
|
|
||||||
<button class="btn btn--sm" type="button" id="v-all">Run all four</button>
|
|
||||||
<button class="btn btn--sm" type="button" id="v-reset">Start again</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="sec sec--tall" id="privacy" aria-labelledby="privacy-title">
|
|
||||||
<div class="container grid">
|
|
||||||
<figure class="privacy__photo" id="photo" data-reveal>
|
|
||||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg/1280px-Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg" srcset="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg/960px-Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg 960w, https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg/1280px-Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg 1280w, https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg/1920px-Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg 1920w" sizes="(min-width: 60rem) 58vw, 100vw" width="6718" height="4479" alt="A beach below orange sandstone cliffs, standing in for a profile photo" loading="lazy">
|
|
||||||
<img class="blur" src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg/960px-Peterborough_%28AU%29%2C_Port_Campbell_National_Park%2C_Worm_Bay_--_2019_--_0863.jpg" width="6718" height="4479" alt="" aria-hidden="true" loading="lazy">
|
|
||||||
<span class="privacy__lock" aria-hidden="true"><span class="privacy__disc"><svg class="icon"><use href="#i-lock"/></svg></span></span>
|
|
||||||
<span class="privacy__badge" role="status"><svg class="icon icon--sm icon-open" aria-hidden="true"><use href="#i-eye"/></svg><svg class="icon icon--sm icon-shut" aria-hidden="true"><use href="#i-lock"/></svg><span id="photo-state">Hidden from this visitor</span></span>
|
|
||||||
</figure>
|
|
||||||
<form class="privacy__panel" onsubmit="return false" aria-label="Photo privacy">
|
|
||||||
<h2 id="privacy-title" data-reveal>You choose who sees your photos</h2>
|
|
||||||
<fieldset class="choices choices--stack" id="p-setting">
|
|
||||||
<legend>Photos are visible to</legend>
|
|
||||||
<input type="radio" name="setting" id="s-everyone" value="everyone"><label for="s-everyone">Everyone</label>
|
|
||||||
<input type="radio" name="setting" id="s-verified" value="verified" checked><label for="s-verified">Verified members</label>
|
|
||||||
<input type="radio" name="setting" id="s-accepted" value="accepted"><label for="s-accepted">People I accept</label>
|
|
||||||
<input type="radio" name="setting" id="s-me" value="me"><label for="s-me">Only me</label>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="choices" id="p-viewer">
|
|
||||||
<legend>Right now, a visitor is</legend>
|
|
||||||
<input type="radio" name="viewer" id="v-stranger" value="stranger" checked><label for="v-stranger">A stranger</label>
|
|
||||||
<input type="radio" name="viewer" id="v-member" value="verified"><label for="v-member">A verified member</label>
|
|
||||||
<input type="radio" name="viewer" id="v-accepted" value="accepted"><label for="v-accepted">Someone I accepted</label>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="band talk" id="talk" aria-labelledby="talk-title">
|
|
||||||
<div class="container">
|
|
||||||
<div class="talk__top"><h2 id="talk-title" data-reveal>Chat opens when both say yes</h2></div>
|
|
||||||
<div class="grid talk__body">
|
|
||||||
<div class="invite" data-reveal>
|
|
||||||
<div class="invite__who">
|
|
||||||
<span class="avatar" style="--tone:var(--tone-c)" aria-hidden="true">P</span>
|
|
||||||
<div><h3>Priya sent you an interest</h3><p class="label">26, Sydney</p></div>
|
|
||||||
</div>
|
|
||||||
<div class="invite__actions" id="invite-actions">
|
|
||||||
<button class="btn" type="button" id="t-accept">Accept</button>
|
|
||||||
<button class="btn" type="button" id="t-decline">No thanks</button>
|
|
||||||
<button class="btn btn--sm" type="button" id="t-reset" hidden>Start again</button>
|
|
||||||
</div>
|
|
||||||
<p class="invite__result" id="t-result" role="status"></p>
|
|
||||||
</div>
|
|
||||||
<div class="chat on-dark is-locked" id="chat" data-reveal style="--i:1">
|
|
||||||
<ul class="chat__log" id="chat-log" role="log" aria-label="Chat with Priya"></ul>
|
|
||||||
<form class="chat__form" id="chat-form" autocomplete="off">
|
|
||||||
<label class="visually-hidden" for="chat-input">Message</label>
|
|
||||||
<input id="chat-input" name="message" type="text" maxlength="300" enterkeyhint="send">
|
|
||||||
<button class="btn btn--sm btn--primary" type="submit"><svg class="icon" aria-hidden="true"><use href="#i-send"/></svg>Send</button>
|
|
||||||
</form>
|
|
||||||
<div class="chat__lock"><svg class="icon" aria-hidden="true"><use href="#i-lock"/></svg><span>Chat opens when you both say yes.</span></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="sec sec--tall" id="family" aria-label="Family view and safety tools">
|
|
||||||
<div class="container grid together">
|
|
||||||
<div class="tile tile--wide" data-reveal>
|
|
||||||
<h2>Family view</h2>
|
|
||||||
<p class="lead">A parent or sibling can look at profiles with you.</p>
|
|
||||||
<fieldset class="choices" id="fam">
|
|
||||||
<legend>Who helps you look</legend>
|
|
||||||
<input type="checkbox" id="h-mum" value="Mum"><label for="h-mum">Mum</label>
|
|
||||||
<input type="checkbox" id="h-dad" value="Dad"><label for="h-dad">Dad</label>
|
|
||||||
<input type="checkbox" id="h-sister" value="Sister"><label for="h-sister">Sister</label>
|
|
||||||
<input type="checkbox" id="h-brother" value="Brother"><label for="h-brother">Brother</label>
|
|
||||||
</fieldset>
|
|
||||||
<p class="live" id="fam-status" role="status">Just you for now.</p>
|
|
||||||
</div>
|
|
||||||
<div class="tile tile--narrow on-dark" id="safety" data-reveal style="--i:1">
|
|
||||||
<h2>Safety tools</h2>
|
|
||||||
<div class="safety__who">
|
|
||||||
<span class="avatar" style="--tone:var(--tone-d)" aria-hidden="true">J</span>
|
|
||||||
<div><h3>Jack, 35</h3><p class="label">Melbourne</p></div>
|
|
||||||
</div>
|
|
||||||
<div class="safety__actions">
|
|
||||||
<button class="btn btn--sm" type="button" id="s-block"><svg class="icon" aria-hidden="true"><use href="#i-ban"/></svg><span>Block</span></button>
|
|
||||||
<button class="btn btn--sm" type="button" id="s-report"><svg class="icon" aria-hidden="true"><use href="#i-flag"/></svg><span>Report</span></button>
|
|
||||||
<button class="btn btn--sm" type="button" id="s-hide" aria-pressed="false"><svg class="icon" aria-hidden="true"><use href="#i-lock"/></svg><span>Hide my profile</span></button>
|
|
||||||
</div>
|
|
||||||
<p class="live" id="safe-status" role="status"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="band join" id="join" aria-labelledby="join-title">
|
|
||||||
<div class="container grid">
|
|
||||||
<div class="head">
|
|
||||||
<h2 id="join-title" data-reveal>Make your profile</h2>
|
|
||||||
<dl class="qa" id="help" data-reveal style="--i:1">
|
|
||||||
<div><dt>What does it cost?</dt><dd>A free plan and a paid plan. Paid price: <span class="fill" data-fill="paid-price">to be announced</span></dd></div>
|
|
||||||
<div><dt>Will I get alerts?</dt><dd>Email and phone alerts for matches, interests and messages.</dd></div>
|
|
||||||
<div><dt>Who runs the site?</dt><dd>Our team reviews profiles and reports.</dd></div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="card" id="join-card" data-reveal style="--i:2">
|
|
||||||
<div class="tabs" role="tablist" aria-label="Join or log in">
|
|
||||||
<button type="button" role="tab" id="tab-join" aria-selected="true" aria-controls="panel-join">Join</button>
|
|
||||||
<button type="button" role="tab" id="tab-login" aria-selected="false" aria-controls="panel-login" tabindex="-1">Log in</button>
|
|
||||||
</div>
|
|
||||||
<div class="panel" role="tabpanel" id="panel-join" aria-labelledby="tab-join">
|
|
||||||
<form data-endpoint="" novalidate class="panel">
|
|
||||||
<div class="field"><label for="j-name">First name</label><input id="j-name" name="name" type="text" autocomplete="given-name" required></div>
|
|
||||||
<div class="field"><label for="j-email">Email address</label><input id="j-email" name="email" type="email" autocomplete="email" inputmode="email" required></div>
|
|
||||||
<div class="field"><label for="j-for">Joining for</label>
|
|
||||||
<select id="j-for" name="for"><option>Myself</option><option>My son</option><option>My daughter</option><option>My brother or sister</option></select>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn--primary" type="submit">Create your profile<svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></button>
|
|
||||||
<p class="form-help" aria-live="polite"></p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel" role="tabpanel" id="panel-login" aria-labelledby="tab-login">
|
|
||||||
<form data-endpoint="" novalidate class="panel">
|
|
||||||
<div class="field"><label for="l-email">Email address</label><input id="l-email" name="email" type="email" autocomplete="email" inputmode="email" required></div>
|
|
||||||
<button class="btn btn--primary" type="submit">Log in<svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></button>
|
|
||||||
<p class="form-help" aria-live="polite"></p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer class="foot on-dark">
|
|
||||||
<div class="container">
|
|
||||||
<p class="foot__sign">Warmly, the BeingOne team.</p>
|
|
||||||
<div class="foot__row">
|
|
||||||
<p>For Australians and their families. <time datetime="2026-10">October 2026</time></p>
|
|
||||||
<ul class="foot__links">
|
|
||||||
<li><a href="#find">Discover</a></li>
|
|
||||||
<li><a href="#verified">Checks</a></li>
|
|
||||||
<li><a href="#privacy">Photos</a></li>
|
|
||||||
<li><a href="#talk">Messages</a></li>
|
|
||||||
<li><a href="#join">Join</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p data-credits>Photographs: beach by <a href="https://commons.wikimedia.org/wiki/File:Peterborough_(AU),_Port_Campbell_National_Park,_Worm_Bay_--_2019_--_0863.jpg">Dietmar Rabich</a> (CC BY-SA 4.0, Wikimedia Commons)</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script>document.querySelectorAll('[data-slot] img').forEach((i) => { if (i.complete && !i.naturalWidth) i.remove(); else i.addEventListener('error', () => i.remove(), { once: true }); });</script>
|
|
||||||
<script src="app.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
/* Hallmark · shape: Split Hero With Floating Search · genre: editorial · theme: custom (by request: the user's own reference screenshot) · job: sign up · idea: "a marriage site that opens like a wedding morning: warm paper, a serif that speaks plainly, and every demo a visitor can touch" · vibe: "warm cream, navy ink, quietly lively"
|
|
||||||
* paper: oklch(98.4% 0.012 85) · accent: oklch(56% 0.15 38) · display: DM Serif Display · body: Figtree · hero: text left, the couple's photo bleeding off the right edge, a search bar floating across its foot · nav: edge-aligned · footer: letter close · imagery: photographs, user's own 1, library 1, natural · signature: the floating search bar that drives the Australia map and the scored matches
|
|
||||||
* axes: light/roman-serif/warm · posture: restrained · contrast: pass · fold: pass · v2.0.0 */
|
|
||||||
/* Hallmark · pre-emit critique: P4 H4 E4 S4 R3 V3 · weakest: R, the page now holds a lot of motion; cut every loop and kept only one-time entrances, hover lifts and state changes */
|
|
||||||
/* Hallmark · waive 65 · the user's own reference screenshot pins cream paper, a high-contrast serif and a terracotta accent for BeingOne · the look is the client's brief, not a model default */
|
|
||||||
/* Hallmark · waive 38ii · guard: true italic of the display face is loaded, font-synthesis none, one line of one h1 only · the closing line of the hero head mirrors the BeingOne reference the user supplied */
|
|
||||||
/* Hallmark · waive 38iii · one italic closing line of the hero head, once on the page · it mirrors the BeingOne reference the user supplied */
|
|
||||||
/* Hallmark · dismissed 59 · the path strip under the search bar is one row of five links that shows how the visitor moves through BeingOne, so it is wayfinding and not a content section */
|
|
||||||
/* Hallmark · dismissed F7 · the pill radius sits on controls only: nav links, choice chips, map pins and their ping ring are all pressed to act */
|
|
||||||
@import url("./tokens.css");
|
|
||||||
@import url("./base.css");
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-navy-3: oklch(20% 0.06 255);
|
|
||||||
--color-accent-on-dark: oklch(70% 0.14 38);
|
|
||||||
--nav-h: 76px;
|
|
||||||
--color-rule-on-navy: oklch(44% 0.05 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* type */
|
|
||||||
h1, h2 { font-weight: 400; letter-spacing: var(--tracking-display); }
|
|
||||||
h1 { max-width: none; font-size: clamp(3.25rem, 6.4vw, 6rem); line-height: 1; }
|
|
||||||
h2 { max-width: 22ch; font-size: clamp(2.25rem, 3.6vw, 3.5rem); line-height: 1.04; }
|
|
||||||
h3 { max-width: 40ch; font-weight: 400; font-size: var(--text-title); line-height: 1.2; letter-spacing: -0.01em; }
|
|
||||||
.lead { font-size: var(--text-lead); line-height: 1.45; max-width: 46ch; color: var(--color-ink-2); }
|
|
||||||
.label { font-size: var(--text-sm); font-weight: 700; color: var(--color-muted); }
|
|
||||||
.muted { color: var(--color-muted); }
|
|
||||||
body { font-weight: 500; }
|
|
||||||
|
|
||||||
/* local dark surfaces: components keep using the same tokens */
|
|
||||||
.on-dark { --color-ink: var(--color-on-navy); --color-ink-2: var(--color-on-navy); --color-muted: var(--color-on-navy-muted); --color-paper-2: var(--color-navy-2); --color-paper-3: var(--color-navy-3); --color-rule: var(--color-rule-on-navy); --color-card: var(--color-navy-2); --color-card-ink: var(--color-on-navy); background: var(--color-navy); color: var(--color-on-navy); }
|
|
||||||
|
|
||||||
/* frame */
|
|
||||||
.skip { position: absolute; left: var(--space-md); top: calc(-1 * var(--space-3xl)); z-index: var(--z-toast); padding: var(--space-xs) var(--space-md); background: var(--color-card); color: var(--color-card-ink); border-radius: var(--radius-control); font-weight: 700; }
|
|
||||||
.skip:focus-visible { top: var(--space-md); }
|
|
||||||
.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gap-columns); }
|
|
||||||
.sec { padding-block: var(--space-3xl); }
|
|
||||||
.sec--tall { padding-block: var(--space-4xl); }
|
|
||||||
.band { background: var(--color-paper-2); padding-block: var(--section-gap-s); }
|
|
||||||
.band--navy { background: var(--color-navy); color: var(--color-on-navy); padding-block: var(--section-gap-s); }
|
|
||||||
.head { display: grid; gap: var(--space-md); align-content: start; }
|
|
||||||
.icon { width: 24px; height: 24px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
||||||
.icon--sm { width: 20px; height: 20px; }
|
|
||||||
.fill { padding: 0 0.25em; border: 1px dashed color-mix(in oklch, var(--color-muted) 70%, transparent); border-radius: 3px; color: var(--color-muted); -webkit-box-decoration-break: clone; box-decoration-break: clone; }
|
|
||||||
|
|
||||||
/* nav: edge-aligned */
|
|
||||||
.nav { position: sticky; top: 0; z-index: var(--z-sticky-nav); height: var(--nav-h); background: var(--color-paper); border-bottom: var(--rule-hair) solid var(--color-rule); }
|
|
||||||
.nav__bar { height: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); }
|
|
||||||
.wordmark { display: inline-flex; align-items: center; gap: var(--space-xs); font: 400 var(--text-2xl)/1 var(--font-display); letter-spacing: -0.01em; text-decoration: none; color: var(--color-ink); }
|
|
||||||
.wordmark b { font-weight: 400; color: var(--color-accent); }
|
|
||||||
.mark { width: 34px; height: 34px; fill: none; stroke: var(--color-accent); stroke-width: 2.5; transition: transform var(--dur-long) var(--ease-out); }
|
|
||||||
.wordmark:hover .mark { transform: rotate(-18deg); }
|
|
||||||
.nav__links { display: flex; align-items: center; gap: var(--space-xs); }
|
|
||||||
.nav__links a { padding: var(--space-xs) var(--space-md); border-radius: var(--radius-pill); text-decoration: none; font-weight: 700; white-space: nowrap; transition: background-color var(--dur-short) var(--ease-out); }
|
|
||||||
.nav__links a:hover { background: var(--color-paper-2); }
|
|
||||||
.nav__actions { display: flex; align-items: center; gap: var(--space-md); }
|
|
||||||
.nav__signin { white-space: nowrap; font-weight: 700; color: var(--color-accent); text-decoration: none; padding: var(--space-xs); }
|
|
||||||
.nav__signin:hover { text-decoration: underline; text-decoration-thickness: 2px; }
|
|
||||||
.nav__actions .nav__menu { display: none; }
|
|
||||||
.nav__links .only-sm { display: none; }
|
|
||||||
@media (max-width: 68rem) {
|
|
||||||
.nav__actions .nav__menu { display: inline-flex; }
|
|
||||||
.nav__links { position: absolute; top: var(--nav-h); left: 0; right: 0; flex-direction: column; align-items: stretch; gap: 0; padding: var(--space-md) var(--gutter); background: var(--color-paper); border-bottom: var(--rule-hair) solid var(--color-rule); display: none; }
|
|
||||||
.nav__links.is-open { display: flex; }
|
|
||||||
.nav__links a { padding: var(--space-md) 0; border-radius: 0; border-top: var(--rule-hair) solid var(--color-rule); }
|
|
||||||
.nav__links a:first-child { border-top: 0; }
|
|
||||||
}
|
|
||||||
@media (max-width: 40rem) { .nav__signin { display: none; } .nav__links .only-sm { display: block; } .nav__actions { gap: var(--space-xs); } }
|
|
||||||
|
|
||||||
/* controls */
|
|
||||||
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-xs); min-height: 52px; padding: 0 var(--space-lg); border: var(--rule-bar) solid var(--color-ink); border-radius: var(--radius-control); background: transparent; color: var(--color-ink); font: 700 var(--text-md)/1.25 var(--font-body); text-decoration: none; white-space: nowrap; cursor: pointer; transition: transform var(--dur-short) var(--ease-out), background-color var(--dur-short) var(--ease-out), color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out), box-shadow var(--dur-short) var(--ease-out); }
|
|
||||||
.btn:hover { background: var(--color-paper-3); }
|
|
||||||
.btn:active { transform: translateY(1px); }
|
|
||||||
.btn--sm { min-height: 44px; padding: 0 var(--space-md); font-size: var(--text-sm); }
|
|
||||||
.btn--primary { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); box-shadow: 0 14px 24px -14px color-mix(in oklab, var(--color-accent) 80%, transparent); }
|
|
||||||
.btn--primary:hover { background: var(--color-accent-2); border-color: var(--color-accent-2); transform: translateY(-2px); }
|
|
||||||
.btn--primary:active { transform: translateY(1px); }
|
|
||||||
.btn .icon { width: 20px; height: 20px; transition: transform var(--dur-short) var(--ease-out); }
|
|
||||||
.btn:hover .icon { transform: translateX(4px); }
|
|
||||||
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; transform: none; }
|
|
||||||
.btn[aria-busy="true"] { cursor: progress; }
|
|
||||||
.btn[aria-pressed="true"] { background: var(--color-paper-3); box-shadow: inset 0 0 0 2px var(--color-ink); }
|
|
||||||
.on-dark .btn--primary { color: var(--color-accent-ink); }
|
|
||||||
.on-dark .btn--primary:hover { background: var(--color-accent-2); }
|
|
||||||
|
|
||||||
.choices { position: relative; display: flex; flex-wrap: wrap; gap: var(--space-xs); border: 0; margin: 0; padding: 0; min-width: 0; }
|
|
||||||
.choices legend { padding: 0; margin-bottom: var(--space-sm); font-size: var(--text-sm); font-weight: 700; color: var(--color-muted); }
|
|
||||||
.choices input { position: absolute; opacity: 0; width: 1px; height: 1px; }
|
|
||||||
.choices label { display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--space-md); border: var(--rule-bar) solid var(--color-rule); border-radius: var(--radius-pill); background: var(--color-card); font-weight: 700; font-size: var(--text-sm); cursor: pointer; transition: transform var(--dur-short) var(--ease-out), background-color var(--dur-short) var(--ease-out), color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out); }
|
|
||||||
.choices label:hover { border-color: var(--color-ink); }
|
|
||||||
.choices label:active { transform: translateY(1px); }
|
|
||||||
.choices input:checked + label { background: var(--color-ink); border-color: var(--color-ink); color: var(--color-paper); }
|
|
||||||
.on-dark .choices input:checked + label { color: var(--color-navy); }
|
|
||||||
.choices input:focus-visible + label { outline: 2px solid var(--color-focus); outline-offset: 3px; }
|
|
||||||
.choices--stack { flex-direction: column; flex-wrap: nowrap; }
|
|
||||||
.choices--stack label { border-radius: var(--radius-control); width: 100%; }
|
|
||||||
|
|
||||||
.field { display: grid; gap: var(--space-xs); }
|
|
||||||
.field label { font-size: var(--text-sm); font-weight: 700; }
|
|
||||||
.field input:not([type="range"]), .field select, .chat__form input { width: 100%; min-height: 52px; padding: 0 var(--space-md); border: var(--rule-bar) solid var(--color-muted); border-radius: var(--radius-control); background: var(--color-card); color: var(--color-card-ink); outline: 2px solid transparent; outline-offset: -3px; transition: border-color var(--dur-short) var(--ease-out); }
|
|
||||||
.field input:not([type="range"]):hover, .field select:hover { border-color: var(--color-ink); }
|
|
||||||
.field input:not([type="range"]):focus-visible, .field select:focus-visible, .chat__form input:focus-visible { outline-color: var(--color-focus); border-color: var(--color-focus); }
|
|
||||||
.field input[aria-invalid] { border-color: var(--color-error); }
|
|
||||||
.field input[type="range"] { width: 100%; accent-color: var(--color-accent); min-height: 32px; cursor: pointer; }
|
|
||||||
.field output { font: 400 var(--text-xl)/1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
||||||
.field__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md); }
|
|
||||||
|
|
||||||
/* hero: photo bleeds off the right edge, head on the left */
|
|
||||||
.hero { position: relative; background: var(--color-paper); }
|
|
||||||
.hero__inner { position: relative; z-index: 2; padding-block: var(--space-2xl) var(--space-xl); }
|
|
||||||
.hero__text { display: grid; gap: var(--space-lg); justify-items: start; max-width: 46%; }
|
|
||||||
.hero h1 .line { display: block; overflow: hidden; padding: 0 0.12em 0.12em 0; margin-bottom: -0.12em; }
|
|
||||||
.hero h1 .line > span { display: block; animation: line-up 1000ms var(--ease-out) backwards; animation-delay: calc(var(--i) * 120ms + 120ms); }
|
|
||||||
.hero h1 .line--it > span { font-style: italic; }
|
|
||||||
.hero__lead { animation: rise 800ms var(--ease-out) 650ms backwards; }
|
|
||||||
.hero__cta { animation: rise 800ms var(--ease-out) 780ms backwards; }
|
|
||||||
.hero__photo { position: absolute; z-index: 1; top: 0; right: 0; bottom: var(--space-2xl); width: 50%; margin: 0; border-radius: var(--radius-card) 0 0 var(--radius-card); animation: curtain 1300ms var(--ease-in-out) 200ms backwards; }
|
|
||||||
.hero__photo > img { object-position: 60% 40%; animation: settle 2200ms var(--ease-out) 200ms backwards; }
|
|
||||||
@keyframes line-up { from { transform: translateY(110%); } }
|
|
||||||
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }
|
|
||||||
@keyframes curtain { from { clip-path: inset(0 0 0 100% round var(--radius-card)); } to { clip-path: inset(0 0 0 0 round var(--radius-card)); } }
|
|
||||||
@keyframes settle { from { transform: scale(1.16); } }
|
|
||||||
@media (max-width: 60rem) {
|
|
||||||
.hero__inner { padding-block: var(--space-2xl) var(--space-xl); }
|
|
||||||
.hero__text { max-width: none; }
|
|
||||||
.hero__photo { position: relative; width: 100%; aspect-ratio: 4 / 3; border-radius: 0; }
|
|
||||||
.hero { display: flex; flex-direction: column; }
|
|
||||||
.hero__inner { order: 1; }
|
|
||||||
.hero__photo { order: 2; }
|
|
||||||
.quick__wrap { order: 3; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* floating search bar */
|
|
||||||
.quick__wrap { position: relative; z-index: 5; margin-bottom: calc(-1 * var(--space-2xl)); }
|
|
||||||
.quick { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) auto; gap: var(--space-md); align-items: end; max-width: 1120px; margin-inline: auto; padding: var(--space-lg); background: var(--color-card); border-radius: var(--radius-card); box-shadow: var(--shadow-card); animation: rise-lg 900ms var(--ease-out) 900ms backwards; }
|
|
||||||
.quick__field { display: grid; gap: var(--space-2xs); }
|
|
||||||
.quick__field label { font-size: var(--text-sm); font-weight: 700; color: var(--color-muted); }
|
|
||||||
.quick select { width: 100%; min-height: 52px; padding: 0 var(--space-md); border: var(--rule-bar) solid var(--color-muted); border-radius: var(--radius-control); background: var(--color-card); color: var(--color-card-ink); font-weight: 700; transition: border-color var(--dur-short) var(--ease-out); }
|
|
||||||
.quick select:hover { border-color: var(--color-ink); }
|
|
||||||
.quick select:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
|
|
||||||
.quick__go { min-width: 160px; }
|
|
||||||
@keyframes rise-lg { from { opacity: 0; transform: translateY(40px); } }
|
|
||||||
@media (max-width: 60rem) { .quick__wrap { margin-bottom: 0; padding-block: var(--space-lg); } .quick { grid-template-columns: minmax(0, 1fr); } }
|
|
||||||
|
|
||||||
/* path: a real ol */
|
|
||||||
.band--path { padding-block: calc(var(--space-2xl) + var(--space-md)) var(--space-2xl); }
|
|
||||||
.path { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--gap-columns); counter-reset: step; }
|
|
||||||
.path li { counter-increment: step; position: relative; display: grid; gap: var(--space-2xs); align-content: start; padding-top: var(--space-lg); }
|
|
||||||
.path li::after { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--color-ink); transform-origin: left; transition: transform 1000ms var(--ease-out); transition-delay: calc(var(--i, 0) * 120ms + 150ms); }
|
|
||||||
.js .path li:not(.is-in)::after { transform: scaleX(0); }
|
|
||||||
.path li::before { content: counter(step); font: 400 clamp(3.5rem, 6vw, 5.25rem)/0.95 var(--font-display); color: var(--color-accent); font-variant-numeric: lining-nums tabular-nums; }
|
|
||||||
.path a { font: 700 var(--text-lg)/1.15 var(--font-body); text-decoration: none; }
|
|
||||||
.path a:hover { text-decoration: underline; text-decoration-color: var(--color-accent); text-decoration-thickness: 3px; text-underline-offset: 0.25em; }
|
|
||||||
@media (max-width: 60rem) { .path { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); } .path li { grid-template-columns: 72px minmax(0, 1fr); align-items: center; } .path li::before { font-size: var(--text-3xl); } }
|
|
||||||
|
|
||||||
/* finder */
|
|
||||||
.finder__top { align-items: end; margin-bottom: var(--space-2xl); }
|
|
||||||
.finder__top h2 { grid-column: 1 / 8; }
|
|
||||||
.finder__top .lead { grid-column: 8 / 13; }
|
|
||||||
.finder__body { align-items: stretch; }
|
|
||||||
.map { grid-column: 1 / 6; position: relative; aspect-ratio: 605 / 516; background: var(--color-paper-2); border-radius: var(--radius-card); }
|
|
||||||
.map svg { position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
||||||
.map path { fill: var(--color-land); stroke: var(--color-card); stroke-width: 3; stroke-linejoin: round; }
|
|
||||||
.pin { position: absolute; left: var(--x); top: var(--y); width: 36px; height: 36px; transform: translate(-50%, -50%); padding: 0; border: 0; background: transparent; cursor: pointer; border-radius: var(--radius-pill); }
|
|
||||||
.pin::before { content: ""; position: absolute; inset: 10px; border-radius: var(--radius-pill); background: var(--color-ink); box-shadow: 0 0 0 3px var(--color-card); transition: transform var(--dur-short) var(--ease-out), background-color var(--dur-short) var(--ease-out); }
|
|
||||||
.js .map.is-in .pin::before { animation: pop 600ms var(--ease-out) backwards; animation-delay: calc(var(--i) * 90ms + 250ms); }
|
|
||||||
.pin:hover::before { transform: scale(1.4); }
|
|
||||||
.pin[aria-pressed="true"]::before { background: var(--color-accent); transform: scale(1.9); }
|
|
||||||
.pin:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 0; }
|
|
||||||
.ping { position: absolute; inset: 8px; border-radius: var(--radius-pill); border: 2px solid var(--color-accent); pointer-events: none; animation: ping 800ms var(--ease-out) both; }
|
|
||||||
.map__city { position: absolute; left: var(--space-md); bottom: var(--space-md); padding: var(--space-xs) var(--space-md); border-radius: var(--radius-control); background: var(--color-card); box-shadow: var(--shadow-card); font-size: var(--text-sm); font-weight: 700; }
|
|
||||||
@keyframes pop { from { transform: scale(0); } }
|
|
||||||
@keyframes ping { from { opacity: 0.9; transform: scale(0.6); } to { opacity: 0; transform: scale(2.8); } }
|
|
||||||
.controls { grid-column: 6 / 13; display: grid; gap: var(--space-xl); align-content: center; padding: var(--space-xl); background: var(--color-paper-2); border-radius: var(--radius-card); }
|
|
||||||
.controls__ages { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap-columns); }
|
|
||||||
.results { margin-top: var(--space-xl); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap-columns); min-height: 300px; scroll-margin-top: var(--space-3xl); }
|
|
||||||
.person { display: grid; gap: var(--space-md); align-content: space-between; padding: var(--space-lg); background: var(--color-card); border-radius: var(--radius-card); box-shadow: var(--shadow-card); animation: rise var(--dur-long) var(--ease-out) backwards; animation-delay: calc(var(--i, 0) * 80ms); transition: transform var(--dur-short) var(--ease-out), box-shadow var(--dur-short) var(--ease-out); }
|
|
||||||
.person:hover { transform: translateY(-5px); box-shadow: var(--shadow-lift); }
|
|
||||||
.person__who { display: flex; align-items: center; gap: var(--space-md); }
|
|
||||||
.person h3 { font-size: var(--text-xl); }
|
|
||||||
.avatar { width: 56px; height: 56px; flex-shrink: 0; display: grid; place-items: center; border-radius: var(--radius-pill); background: var(--tone, var(--tone-a)); color: var(--color-navy); font: 400 var(--text-xl)/1 var(--font-display); }
|
|
||||||
.score { display: flex; align-items: baseline; gap: var(--space-xs); }
|
|
||||||
.score b { font: 400 var(--text-3xl)/1 var(--font-display); font-variant-numeric: lining-nums tabular-nums; }
|
|
||||||
.bar { height: 8px; margin-top: var(--space-xs); border-radius: var(--radius-pill); background: var(--color-paper-3); overflow: hidden; }
|
|
||||||
.bar i { display: block; height: 100%; background: var(--color-accent); transform-origin: left; transform: scaleX(calc(var(--s, 0) / 100)); animation: grow 1100ms var(--ease-out) backwards; animation-delay: calc(var(--i, 0) * 80ms + 200ms); }
|
|
||||||
.tags { display: flex; flex-wrap: wrap; gap: var(--space-xs); padding: 0; margin: 0; list-style: none; }
|
|
||||||
.tags li { padding: var(--space-3xs) var(--space-sm); border-radius: var(--radius-pill); border: var(--rule-bar) solid var(--color-rule); font-size: var(--text-sm); font-weight: 700; transition: background-color var(--dur-short) var(--ease-out); }
|
|
||||||
.tags li.is-shared { background: var(--color-paper-3); border-color: var(--color-paper-3); }
|
|
||||||
.results__empty { grid-column: 1 / -1; align-self: center; font: 400 var(--text-2xl)/1.2 var(--font-display); max-width: 30ch; animation: rise var(--dur-long) var(--ease-out) backwards; }
|
|
||||||
.finder__note { margin-top: var(--space-md); font-size: var(--text-sm); color: var(--color-muted); }
|
|
||||||
@keyframes grow { from { transform: scaleX(0); } }
|
|
||||||
@media (max-width: 60rem) {
|
|
||||||
.finder__top h2, .finder__top .lead, .map, .controls { grid-column: 1 / -1; }
|
|
||||||
.finder__top .lead { margin-top: var(--space-md); }
|
|
||||||
.controls { margin-top: var(--space-lg); padding: var(--space-lg); }
|
|
||||||
.results { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
}
|
|
||||||
@media (max-width: 40rem) { .results { grid-template-columns: minmax(0, 1fr); } .controls__ages { grid-template-columns: minmax(0, 1fr); } }
|
|
||||||
|
|
||||||
/* checks */
|
|
||||||
.checks .head { grid-column: 1 / 6; }
|
|
||||||
.checks__panel { grid-column: 7 / 13; background: var(--color-paper-2); border-radius: var(--radius-card); padding: var(--space-lg); display: grid; gap: var(--space-md); }
|
|
||||||
.checks__who { display: flex; align-items: center; gap: var(--space-md); }
|
|
||||||
.checks__who h3 { font-size: var(--text-xl); }
|
|
||||||
.status { margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-xs); padding: var(--space-xs) var(--space-md); border-radius: var(--radius-pill); background: var(--color-paper-3); font-size: var(--text-sm); font-weight: 700; }
|
|
||||||
.status .icon { display: none; }
|
|
||||||
.status[data-state="done"] { background: var(--color-on-navy); color: var(--color-navy); animation: settle-in 600ms var(--ease-out) both; }
|
|
||||||
.status[data-state="done"] .icon { display: block; }
|
|
||||||
@keyframes settle-in { from { transform: scale(0.9); opacity: 0.4; } }
|
|
||||||
.progress { height: 6px; border-radius: var(--radius-pill); background: var(--color-paper-3); overflow: hidden; }
|
|
||||||
.progress i { display: block; height: 100%; background: var(--color-accent-on-dark); transform-origin: left; transform: scaleX(var(--p, 0)); transition: transform 700ms var(--ease-out); }
|
|
||||||
.rows { list-style: none; margin: 0; padding: 0; }
|
|
||||||
.rows > li { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; gap: var(--space-md); align-items: center; padding-block: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); }
|
|
||||||
.rows h3 { font: 700 var(--text-md)/1.3 var(--font-body); letter-spacing: 0; }
|
|
||||||
.rows p { font-size: var(--text-sm); color: var(--color-muted); }
|
|
||||||
.rows .tick { display: none; color: var(--color-accent-on-dark); }
|
|
||||||
.rows .is-done .tick { display: block; animation: pop 500ms var(--ease-out) backwards; }
|
|
||||||
.rows .is-done .idle { display: none; }
|
|
||||||
.rows .passed { font-weight: 700; font-size: var(--text-sm); }
|
|
||||||
.checks__foot { display: flex; justify-content: space-between; align-items: center; gap: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); padding-top: var(--space-md); }
|
|
||||||
@media (max-width: 60rem) { .checks .head, .checks__panel { grid-column: 1 / -1; } .checks__panel { margin-top: var(--space-xl); } }
|
|
||||||
@media (max-width: 40rem) { .rows > li { grid-template-columns: 24px minmax(0, 1fr); } .rows .btn, .rows .passed { grid-column: 2; justify-self: start; } .checks__who { flex-wrap: wrap; } }
|
|
||||||
|
|
||||||
/* photo privacy */
|
|
||||||
.privacy__photo { grid-column: 1 / 8; position: relative; aspect-ratio: 4 / 3; border-radius: var(--radius-card); overflow: hidden; background: var(--color-paper-3); box-shadow: var(--shadow-card); margin: 0; }
|
|
||||||
.privacy__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 35% 40%; filter: saturate(1.06) contrast(1.04); }
|
|
||||||
.privacy__photo .blur { filter: blur(28px) saturate(1.06); transform: scale(1.15); transition: opacity var(--dur-long) var(--ease-in-out); }
|
|
||||||
.js .privacy__photo:not(.is-hidden) .blur { opacity: 0; }
|
|
||||||
.privacy__badge { position: absolute; left: var(--space-lg); bottom: var(--space-lg); display: inline-flex; align-items: center; gap: var(--space-xs); padding: var(--space-xs) var(--space-md); border-radius: var(--radius-pill); background: var(--color-card); font-size: var(--text-sm); font-weight: 700; z-index: 2; box-shadow: var(--shadow-card); }
|
|
||||||
.privacy__lock { position: absolute; inset: 0; display: grid; place-items: center; z-index: 2; transition: opacity var(--dur-long) var(--ease-in-out); }
|
|
||||||
.privacy__disc { display: grid; place-items: center; width: 112px; height: 112px; border-radius: var(--radius-pill); background: var(--color-card); box-shadow: var(--shadow-card); transition: transform var(--dur-long) var(--ease-out); }
|
|
||||||
.privacy__disc .icon { width: 44px; height: 44px; }
|
|
||||||
.js .privacy__photo:not(.is-hidden) .privacy__lock { opacity: 0; }
|
|
||||||
.js .privacy__photo:not(.is-hidden) .privacy__disc { transform: scale(0.7); }
|
|
||||||
.privacy__badge .icon-open, .privacy__badge .icon-shut { display: none; }
|
|
||||||
.privacy__photo:not(.is-hidden) .icon-open { display: block; }
|
|
||||||
.privacy__photo.is-hidden .icon-shut { display: block; }
|
|
||||||
.privacy__panel { grid-column: 9 / 13; display: grid; gap: var(--space-lg); align-content: center; }
|
|
||||||
@media (max-width: 60rem) { .privacy__photo, .privacy__panel { grid-column: 1 / -1; } .privacy__panel { margin-top: var(--space-xl); } }
|
|
||||||
|
|
||||||
/* talk */
|
|
||||||
.talk__top { margin-bottom: var(--space-2xl); }
|
|
||||||
.talk__body { align-items: stretch; }
|
|
||||||
.invite { grid-column: 1 / 6; display: grid; gap: var(--space-lg); align-content: space-between; padding: var(--space-xl); background: var(--color-card); border-radius: var(--radius-card); box-shadow: var(--shadow-card); }
|
|
||||||
.invite__who { display: flex; align-items: center; gap: var(--space-md); }
|
|
||||||
.invite h3 { font-size: var(--text-xl); }
|
|
||||||
.invite__actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
|
|
||||||
.invite__result { min-height: 1lh; font-weight: 700; }
|
|
||||||
.chat { grid-column: 6 / 13; position: relative; display: grid; grid-template-rows: minmax(0, 1fr) auto; min-height: 380px; padding: var(--space-lg); border-radius: var(--radius-card); }
|
|
||||||
.chat__log { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; justify-content: flex-end; gap: var(--space-xs); overflow-y: auto; }
|
|
||||||
.chat__log li { max-width: 80%; padding: var(--space-xs) var(--space-md); border-radius: var(--radius-control); background: var(--color-paper-2); animation: rise var(--dur-short) var(--ease-out) backwards; overflow-wrap: break-word; }
|
|
||||||
.chat__log li.me { align-self: flex-end; background: var(--color-accent); color: var(--color-accent-ink); }
|
|
||||||
.chat__log li.note { align-self: center; max-width: 100%; background: transparent; color: var(--color-muted); font-size: var(--text-sm); font-weight: 700; }
|
|
||||||
.chat__form { display: flex; gap: var(--space-sm); margin-top: var(--space-md); }
|
|
||||||
.chat__form input { flex: 1; min-width: 0; }
|
|
||||||
.chat.is-locked .chat__log, .chat.is-locked .chat__form { visibility: hidden; }
|
|
||||||
.chat__lock { position: absolute; inset: 0; display: none; place-items: center; align-content: center; gap: var(--space-sm); text-align: center; padding: var(--space-lg); font: 400 var(--text-2xl)/1.2 var(--font-display); }
|
|
||||||
.chat__lock .icon { width: 48px; height: 48px; margin-inline: auto; }
|
|
||||||
.chat.is-locked .chat__lock { display: grid; }
|
|
||||||
@media (max-width: 60rem) { .invite, .chat { grid-column: 1 / -1; } .chat { margin-top: var(--space-lg); } }
|
|
||||||
|
|
||||||
/* family and safety */
|
|
||||||
.together { align-items: stretch; row-gap: var(--gap-columns); }
|
|
||||||
.tile { display: grid; gap: var(--space-lg); align-content: start; padding: var(--space-xl); background: var(--color-paper-2); border-radius: var(--radius-card); }
|
|
||||||
.tile--wide { grid-column: 1 / 8; }
|
|
||||||
.tile--narrow { grid-column: 8 / 13; }
|
|
||||||
.tile h2 { font-size: clamp(2rem, 3vw, 3rem); }
|
|
||||||
.tile .lead { color: var(--color-ink-2); }
|
|
||||||
.safety__who { display: flex; align-items: center; gap: var(--space-md); }
|
|
||||||
.safety__who h3 { font-size: var(--text-xl); }
|
|
||||||
.safety__actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
|
|
||||||
.live { min-height: 1lh; font-weight: 700; }
|
|
||||||
@media (max-width: 60rem) { .tile--wide, .tile--narrow { grid-column: 1 / -1; } .tile { padding: var(--space-lg); } }
|
|
||||||
|
|
||||||
/* questions and join */
|
|
||||||
.join .head { grid-column: 1 / 7; align-content: start; gap: var(--space-xl); }
|
|
||||||
.qa { margin: 0; display: grid; }
|
|
||||||
.qa > div { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-md); padding-block: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); }
|
|
||||||
.qa dt { font: 700 var(--text-md)/1.3 var(--font-body); }
|
|
||||||
.qa dd { margin: 0; color: var(--color-ink-2); }
|
|
||||||
.card { grid-column: 8 / 13; background: var(--color-card); color: var(--color-card-ink); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: var(--space-xl); display: grid; gap: var(--space-lg); align-content: start; }
|
|
||||||
.tabs { display: flex; gap: var(--space-xs); border-bottom: var(--rule-bar) solid var(--color-rule); }
|
|
||||||
.tabs button { min-height: 44px; padding: 0 var(--space-md); border: 0; border-bottom: 4px solid transparent; margin-bottom: calc(-1 * var(--space-3xs)); background: transparent; color: inherit; font-weight: 700; cursor: pointer; transition: border-color var(--dur-short) var(--ease-out); }
|
|
||||||
.tabs button:hover { border-bottom-color: var(--color-rule); }
|
|
||||||
.tabs button[aria-selected="true"] { border-bottom-color: var(--color-accent); }
|
|
||||||
.form-help { min-height: 1lh; font-size: var(--text-sm); }
|
|
||||||
.form-help.is-error { color: var(--color-error); font-weight: 700; }
|
|
||||||
.panel { display: grid; gap: var(--space-md); }
|
|
||||||
@media (max-width: 60rem) { .join .head, .card { grid-column: 1 / -1; } .card { margin-top: var(--space-xl); padding: var(--space-lg); } .qa > div { grid-template-columns: minmax(0, 1fr); gap: var(--space-2xs); } }
|
|
||||||
|
|
||||||
/* footer: letter close */
|
|
||||||
.foot { padding-block: var(--space-3xl) var(--space-xl); }
|
|
||||||
.foot__sign { font: 400 var(--text-display-s)/1.05 var(--font-display); letter-spacing: var(--tracking-display); max-width: 18ch; text-wrap: balance; }
|
|
||||||
.foot__row { margin-top: var(--space-2xl); display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-lg); font-size: var(--text-sm); }
|
|
||||||
.foot__links { display: flex; flex-wrap: wrap; gap: var(--space-lg); list-style: none; margin: 0; padding: 0; }
|
|
||||||
.foot__links a { font-weight: 700; }
|
|
||||||
.foot__links a:hover { text-decoration-color: var(--color-accent-on-dark); text-decoration-thickness: 3px; }
|
|
||||||
.foot [data-credits] { margin-top: var(--space-lg); color: var(--color-muted); font-size: var(--text-xs); max-width: 80ch; }
|
|
||||||
|
|
||||||
/* one scroll reveal, visible without script */
|
|
||||||
.js [data-reveal] { opacity: 0; transform: translateY(18px); transition: opacity 800ms var(--ease-out), transform 800ms var(--ease-out); transition-delay: calc(var(--i, 0) * 110ms); }
|
|
||||||
.js [data-reveal].is-in { opacity: 1; transform: none; }
|
|
||||||
.js .person[data-reveal], .js li[data-reveal] { transition-property: opacity, transform; }
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.js [data-reveal] { opacity: 1; transform: none; transition: none; }
|
|
||||||
.hero h1 .line > span, .hero__lead, .hero__cta, .hero__photo, .hero__photo > img, .quick, .person, .bar i, .chat__log li, .ping, .status, .rows .is-done .tick, .js .map.is-in .pin::before { animation: none; }
|
|
||||||
.bar i { transform: scaleX(calc(var(--s, 0) / 100)); }
|
|
||||||
.js .path li:not(.is-in)::after { transform: none; }
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
:root {
|
|
||||||
--space-3xs: 2px; --space-2xs: 4px; --space-xs: 8px; --space-sm: 12px; --space-md: 16px; --space-lg: 24px; --space-xl: 40px; --space-2xl: 64px; --space-3xl: 96px; --space-4xl: 144px;
|
|
||||||
--text-xs: 0.8125rem; --text-sm: 0.875rem; --text-md: 1rem; --text-lg: 1.25rem; --text-xl: 1.5625rem; --text-2xl: 1.953rem; --text-3xl: 2.441rem;
|
|
||||||
--text-display-s: clamp(2.25rem, 4vw + 0.5rem, 3.75rem); --text-display: clamp(2.75rem, 5vw + 1rem, 5.25rem);
|
|
||||||
--text-section: clamp(2rem, 2.5vw + 1rem, 3.5rem); --text-title: clamp(1.25rem, 0.75vw + 0.875rem, 1.625rem); --text-lead: clamp(1.125rem, 0.35vw + 1rem, 1.375rem);
|
|
||||||
--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: 220ms; --dur-long: 420ms;
|
|
||||||
--color-error: oklch(52% 0.19 27); --color-success: oklch(50% 0.12 150);
|
|
||||||
--container: 1440px; --gutter: 64px; --gap-columns: 32px; --section-gap-s: 120px; --section-gap: 144px; --section-gap-l: 176px;
|
|
||||||
--z-base: 1; --z-raised: 10; --z-dropdown: 100; --z-sticky: 200; --z-sticky-nav: 300; --z-modal: 400; --z-toast: 500; --z-tooltip: 600;
|
|
||||||
--color-paper: oklch(98.4% 0.012 85); --color-paper-2: oklch(95.6% 0.02 80); --color-paper-3: oklch(91.5% 0.03 75);
|
|
||||||
--color-ink: oklch(25% 0.07 255); --color-ink-2: oklch(36% 0.065 255); --color-muted: oklch(46% 0.045 255);
|
|
||||||
--color-rule: oklch(87% 0.025 80);
|
|
||||||
--color-accent: oklch(56% 0.15 38); --color-accent-2: oklch(49% 0.15 38); --color-accent-ink: oklch(99% 0.008 85); --color-focus: oklch(58% 0.19 38);
|
|
||||||
--color-card: oklch(99.6% 0.006 85); --color-card-ink: oklch(25% 0.07 255);
|
|
||||||
--color-navy: oklch(25% 0.07 255); --color-navy-2: oklch(31% 0.07 255); --color-on-navy: oklch(97% 0.015 85); --color-on-navy-muted: oklch(82% 0.03 255);
|
|
||||||
--color-land: oklch(88% 0.04 250);
|
|
||||||
--font-display: "DM Serif Display", ui-serif, Georgia, serif; --font-body: "Figtree", ui-sans-serif, system-ui, sans-serif; --font-outlier: var(--font-display);
|
|
||||||
--radius-card: 24px; --radius-control: 14px; --radius-pill: 999px; --rule-hair: 1px; --rule-bar: 2px;
|
|
||||||
--shadow-card: 0 18px 40px -22px oklch(25% 0.07 255 / 0.35), 0 2px 6px oklch(25% 0.07 255 / 0.06);
|
|
||||||
--shadow-lift: 0 26px 48px -24px oklch(25% 0.07 255 / 0.45), 0 3px 8px oklch(25% 0.07 255 / 0.08);
|
|
||||||
--tracking-display: -0.025em;
|
|
||||||
--tone-a: oklch(86% 0.08 20); --tone-b: oklch(92% 0.09 95); --tone-c: oklch(88% 0.07 150); --tone-d: oklch(86% 0.06 235);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# LookAtU: Product Plan (PRD)
|
||||||
|
|
||||||
|
## What is it? (the 4th-grade version)
|
||||||
|
LookAtU is a helper for parents. The parent has an app. The child has an app. The parent can see where the child is, see what the child does on the phone, choose which apps the child can use, and check calls and messages. If the child is in danger, the child taps a big **panic button** and the parent gets an alert right away.
|
||||||
|
|
||||||
|
## Who is it for?
|
||||||
|
- **Parents** of kids who have their own phone.
|
||||||
|
- **Kids**, who need a simple way to ask for help.
|
||||||
|
|
||||||
|
## Core features
|
||||||
|
1. **Live location**: the parent sees where the child is, any time.
|
||||||
|
2. **Activity monitor**: which apps are used and for how long.
|
||||||
|
3. **App control**: the parent can allow, block, or limit apps, and approve new installs.
|
||||||
|
4. **Calls and messages**: the parent can check call history and messages.
|
||||||
|
5. **Panic button**: the child taps once. The parent gets an alert with the child's location.
|
||||||
|
|
||||||
|
## Trust and safety rules (built in from day one)
|
||||||
|
- The child **knows** the app is on their phone. No hidden spying. This is the honest way to do it, and app stores require it.
|
||||||
|
- The parent signs in with a real account and a strong password.
|
||||||
|
- Data is private and encrypted. Only the parent sees it.
|
||||||
|
- Rules about monitoring messages differ by country and by the child's age. We must check the law for each country before launch.
|
||||||
|
|
||||||
|
## Phased roadmap
|
||||||
|
|
||||||
|
### Phase 1: Quick win (TODAY)
|
||||||
|
A good-looking website (one page) that explains LookAtU and collects sign-ups.
|
||||||
|
- Hero, 5 feature cards, "how it works", trust section, FAQ
|
||||||
|
- Waitlist form that checks the email is real-looking
|
||||||
|
- Works on phones and computers
|
||||||
|
- **Done when:** page loads with no errors and the form and buttons work.
|
||||||
|
|
||||||
|
### Phase 2: Make it real
|
||||||
|
- Connect the waitlist form to a real service so emails are saved.
|
||||||
|
- Add animations (Framer Motion) and better pictures.
|
||||||
|
- Add privacy and terms pages.
|
||||||
|
|
||||||
|
### Phase 3: Parent accounts
|
||||||
|
- Sign up and log in.
|
||||||
|
- A simple parent dashboard (website).
|
||||||
|
- Link a parent to a child.
|
||||||
|
|
||||||
|
### Phase 4: Phone apps
|
||||||
|
- Child app and parent app (Android first, then iPhone).
|
||||||
|
- Live location and the panic button with alerts (push notifications and SMS).
|
||||||
|
|
||||||
|
### Phase 5: Monitoring and control
|
||||||
|
- App usage, app blocking and install approval.
|
||||||
|
- Call and message history (after legal review).
|
||||||
|
|
||||||
|
## How we test at every phase
|
||||||
|
1. Page loads with **no errors**.
|
||||||
|
2. Buttons and forms **work**.
|
||||||
|
3. Real services (database, email, maps, alerts) **work**.
|
||||||
|
4. **Security check**: passwords, private data, no secrets in code.
|
||||||
|
|
||||||
|
## Open questions for the owner
|
||||||
|
- Which countries first?
|
||||||
|
- Free or paid? (price)
|
||||||
|
- Android first, or iPhone first?
|
||||||
|
- Final name and logo?
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>LookAtU – Know your child is safe</title>
|
||||||
|
<meta name="description" content="LookAtU helps parents see where their child is, guide their phone use, and gives kids a panic button for emergencies.">
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--bg:#0b1220; --bg2:#111a2e; --card:#16213a; --text:#eaf0ff; --muted:#a3b1d1;
|
||||||
|
--brand:#4f8cff; --brand2:#7c5cff; --danger:#ff4d5e; --ok:#31d0a0; --line:#243253;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html{scroll-behavior:smooth}
|
||||||
|
body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--text);line-height:1.6}
|
||||||
|
a{color:inherit}
|
||||||
|
.wrap{max-width:1080px;margin:0 auto;padding:0 20px}
|
||||||
|
header{position:sticky;top:0;z-index:10;background:rgba(11,18,32,.85);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||||
|
.nav{display:flex;align-items:center;justify-content:space-between;height:64px}
|
||||||
|
.logo{font-weight:800;font-size:1.3rem;text-decoration:none}
|
||||||
|
.logo span{color:var(--brand)}
|
||||||
|
.nav a.link{margin-left:22px;text-decoration:none;color:var(--muted);font-size:.95rem}
|
||||||
|
.nav a.link:hover{color:var(--text)}
|
||||||
|
.btn{display:inline-block;border:0;cursor:pointer;font:inherit;font-weight:700;padding:12px 22px;border-radius:12px;color:#fff;text-decoration:none;background:linear-gradient(135deg,var(--brand),var(--brand2));transition:transform .15s}
|
||||||
|
.btn:hover{transform:translateY(-2px)}
|
||||||
|
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--text)}
|
||||||
|
.hero{padding:80px 0 60px;text-align:center;background:radial-gradient(900px 400px at 50% -10%,rgba(79,140,255,.25),transparent)}
|
||||||
|
.hero h1{font-size:clamp(2.2rem,6vw,3.8rem);line-height:1.1;margin:0 0 18px}
|
||||||
|
.hero h1 em{font-style:normal;background:linear-gradient(135deg,var(--brand),var(--brand2));-webkit-background-clip:text;background-clip:text;color:transparent}
|
||||||
|
.hero p{max-width:620px;margin:0 auto 28px;color:var(--muted);font-size:1.15rem}
|
||||||
|
.cta{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
|
||||||
|
.phone{margin:48px auto 0;width:240px;border:2px solid var(--line);border-radius:34px;padding:18px;background:var(--bg2);box-shadow:0 20px 60px rgba(0,0,0,.4)}
|
||||||
|
.map{height:130px;border-radius:16px;background:linear-gradient(135deg,#1c2b4d,#22365f);position:relative;overflow:hidden}
|
||||||
|
.pin{position:absolute;left:50%;top:50%;width:16px;height:16px;margin:-8px;border-radius:50%;background:var(--ok)}
|
||||||
|
.pin::after{content:"";position:absolute;inset:-10px;border-radius:50%;border:2px solid var(--ok);animation:pulse 2s infinite}
|
||||||
|
@keyframes pulse{from{transform:scale(.6);opacity:1}to{transform:scale(1.8);opacity:0}}
|
||||||
|
.phone p{margin:12px 0 4px;font-size:.9rem;color:var(--muted)}
|
||||||
|
.panic{margin-top:6px;width:100%;padding:12px;border:0;border-radius:14px;background:var(--danger);color:#fff;font-weight:800;font-size:1rem;cursor:pointer}
|
||||||
|
section{padding:70px 0}
|
||||||
|
section.alt{background:var(--bg2)}
|
||||||
|
h2{font-size:clamp(1.6rem,4vw,2.4rem);margin:0 0 10px;text-align:center}
|
||||||
|
.sub{text-align:center;color:var(--muted);max-width:620px;margin:0 auto 40px}
|
||||||
|
.grid{display:grid;gap:18px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
|
||||||
|
.card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:24px}
|
||||||
|
.card .ic{font-size:2rem}
|
||||||
|
.card h3{margin:10px 0 6px}
|
||||||
|
.card p{margin:0;color:var(--muted)}
|
||||||
|
.card.sos{border-color:var(--danger)}
|
||||||
|
.steps{counter-reset:s}
|
||||||
|
.steps .card{position:relative;padding-top:50px}
|
||||||
|
.steps .card::before{counter-increment:s;content:counter(s);position:absolute;top:14px;left:20px;width:28px;height:28px;border-radius:50%;background:var(--brand);display:grid;place-items:center;font-weight:800}
|
||||||
|
.trust li{margin:10px 0;color:var(--muted)}
|
||||||
|
.trust b{color:var(--text)}
|
||||||
|
details{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:16px 20px;margin:10px auto;max-width:720px}
|
||||||
|
summary{cursor:pointer;font-weight:700}
|
||||||
|
details p{color:var(--muted);margin:10px 0 0}
|
||||||
|
form{max-width:480px;margin:0 auto;display:flex;gap:10px;flex-wrap:wrap}
|
||||||
|
input[type=email]{flex:1 1 220px;padding:13px 14px;border-radius:12px;border:1px solid var(--line);background:var(--bg);color:var(--text);font:inherit}
|
||||||
|
input[type=email]:focus-visible,.btn:focus-visible,summary:focus-visible,button:focus-visible{outline:3px solid var(--brand);outline-offset:2px}
|
||||||
|
#msg{text-align:center;margin-top:14px;min-height:1.5em}
|
||||||
|
#msg.ok{color:var(--ok)} #msg.err{color:var(--danger)}
|
||||||
|
.note{text-align:center;color:var(--muted);font-size:.85rem;margin-top:8px}
|
||||||
|
.sr{position:absolute;left:-9999px}
|
||||||
|
.trap{position:absolute;left:-9999px;height:0;overflow:hidden}
|
||||||
|
select{padding:13px 14px;border-radius:12px;border:1px solid var(--line);background:var(--bg);color:var(--text);font:inherit}
|
||||||
|
.consent{flex:1 1 100%;font-size:.9rem;color:var(--muted)}
|
||||||
|
.consent input{margin-right:8px}
|
||||||
|
.consent a{color:var(--brand)}
|
||||||
|
footer a{margin:0 8px}
|
||||||
|
footer{padding:30px 0;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem;text-align:center}
|
||||||
|
@media (prefers-reduced-motion:reduce){.pin::after{animation:none}html{scroll-behavior:auto}}
|
||||||
|
@media (max-width:640px){.nav a.link{display:none}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="wrap nav">
|
||||||
|
<a class="logo" href="#top">Look<span>At</span>U</a>
|
||||||
|
<nav>
|
||||||
|
<a class="link" href="#features">Features</a>
|
||||||
|
<a class="link" href="#how">How it works</a>
|
||||||
|
<a class="link" href="#trust">Trust</a>
|
||||||
|
<a class="link" href="#faq">FAQ</a>
|
||||||
|
<a class="btn" style="margin-left:22px;padding:9px 16px" href="#join">Join waitlist</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main id="top">
|
||||||
|
<div class="hero">
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>Know your child is <em>safe</em>,<br>anytime you want.</h1>
|
||||||
|
<p>See where they are, guide how they use their phone, and give them a big red button to call for help when it matters most.</p>
|
||||||
|
<div class="cta">
|
||||||
|
<a class="btn" href="#join">Join the waitlist</a>
|
||||||
|
<a class="btn ghost" href="#features">See what it does</a>
|
||||||
|
</div>
|
||||||
|
<div class="phone" aria-hidden="true">
|
||||||
|
<div class="map"><div class="pin"></div></div>
|
||||||
|
<p>Alex is at school · just now</p>
|
||||||
|
<button class="panic" tabindex="-1">SOS</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section id="features">
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Everything a parent needs</h2>
|
||||||
|
<p class="sub">Five simple tools, in one app.</p>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card"><div class="ic">📍</div><h3>Live location</h3><p>Open the app and see where your child is right now.</p></div>
|
||||||
|
<div class="card"><div class="ic">📊</div><h3>Activity monitor</h3><p>See which apps they use and for how long, every day.</p></div>
|
||||||
|
<div class="card"><div class="ic">🔒</div><h3>App control</h3><p>Allow, block or limit apps. Approve new installs before they happen.</p></div>
|
||||||
|
<div class="card"><div class="ic">💬</div><h3>Calls & messages</h3><p>Check call history and messages so you can spot trouble early.</p></div>
|
||||||
|
<div class="card sos"><div class="ic">🚨</div><h3>Panic button</h3><p>One tap and you get an alert with their location, right away.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="how" class="alt">
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>How it works</h2>
|
||||||
|
<p class="sub">Set up takes a few minutes.</p>
|
||||||
|
<div class="grid steps">
|
||||||
|
<div class="card"><h3>Install on both phones</h3><p>You get the parent app. Your child gets the child app.</p></div>
|
||||||
|
<div class="card"><h3>Link them together</h3><p>Scan a code on your child's phone to connect the two.</p></div>
|
||||||
|
<div class="card"><h3>Choose your rules</h3><p>Pick which apps are allowed and what you want to see.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="trust">
|
||||||
|
<div class="wrap" style="max-width:720px">
|
||||||
|
<h2>Built on trust, not secrets</h2>
|
||||||
|
<p class="sub">Safety works best when families talk about it.</p>
|
||||||
|
<ul class="trust">
|
||||||
|
<li><b>Your child knows.</b> The app is always visible on their phone. LookAtU is never hidden.</li>
|
||||||
|
<li><b>Private by design.</b> Data is encrypted and only the parent account can see it.</li>
|
||||||
|
<li><b>You stay in charge.</b> Choose what to watch and turn features on or off at any time.</li>
|
||||||
|
<li><b>Follows the law.</b> Rules about monitoring differ by country and a child's age. We check them before we launch anywhere.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="faq" class="alt">
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Questions</h2>
|
||||||
|
<p class="sub"> </p>
|
||||||
|
<details><summary>Is LookAtU available now?</summary><p>Not yet. This page is for our waitlist. Join and we will email you when it opens.</p></details>
|
||||||
|
<details><summary>Can my child hide the app?</summary><p>No. The app is always visible, and your child knows it is there.</p></details>
|
||||||
|
<details><summary>What happens when my child presses the panic button?</summary><p>You get an instant alert with your child's location, so you can call them or get help.</p></details>
|
||||||
|
<details><summary>Which phones will it work on?</summary><p>We plan to start with Android, then iPhone. Tell us which one you need when you join.</p></details>
|
||||||
|
<details><summary>How much will it cost?</summary><p>We have not set the price yet. Waitlist members will hear first.</p></details>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="join">
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Join the waitlist</h2>
|
||||||
|
<p class="sub">Be the first to know when LookAtU opens.</p>
|
||||||
|
<form id="waitlist" novalidate>
|
||||||
|
<label for="email" class="sr">Email address</label>
|
||||||
|
<input id="email" name="email" type="email" placeholder="you@example.com" autocomplete="email" required>
|
||||||
|
<label for="phone" class="sr">Your child's phone</label>
|
||||||
|
<select id="phone" name="phone">
|
||||||
|
<option value="Android">Android phone</option>
|
||||||
|
<option value="iPhone">iPhone</option>
|
||||||
|
<option value="Both">Both</option>
|
||||||
|
</select>
|
||||||
|
<div class="trap" aria-hidden="true"><label for="website">Leave this empty</label><input id="website" name="website" type="text" tabindex="-1" autocomplete="off"></div>
|
||||||
|
<label class="consent" for="agree"><input id="agree" name="agree" type="checkbox"> I agree to the <a href="privacy/">Privacy Policy</a> and want emails about LookAtU.</label>
|
||||||
|
<button class="btn" id="send" type="submit">Notify me</button>
|
||||||
|
</form>
|
||||||
|
<div id="msg" role="status" aria-live="polite"></div>
|
||||||
|
<p class="note" id="note">We only use your email to tell you when LookAtU opens. No spam.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer><div class="wrap">© <span id="yr">2026</span> LookAtU. Early preview. <a href="privacy/">Privacy</a> <a href="terms/">Terms</a></div></footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// PASTE YOUR FORM URL BETWEEN THE QUOTES (example: https://formspree.io/f/abcdwxyz). While empty, the form runs in demo mode.
|
||||||
|
var FORM_ENDPOINT = 'https://formspree.io/f/mljgogkr';
|
||||||
|
(function(){
|
||||||
|
var form=document.getElementById('waitlist'),email=document.getElementById('email'),msg=document.getElementById('msg'),
|
||||||
|
btn=document.getElementById('send'),note=document.getElementById('note');
|
||||||
|
document.getElementById('yr').textContent=new Date().getFullYear();
|
||||||
|
if(!FORM_ENDPOINT){note.textContent='Demo mode: emails are not saved yet.';}
|
||||||
|
function say(cls,t){msg.className=cls;msg.textContent=t;msg.scrollIntoView({block:'nearest'});}
|
||||||
|
form.addEventListener('submit',function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var v=email.value.trim();
|
||||||
|
if(!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v)){say('err','Please enter a real email address, like you@example.com.');email.focus();return;}
|
||||||
|
if(!document.getElementById('agree').checked){say('err','Please tick the box to agree to the Privacy Policy.');return;}
|
||||||
|
if(document.getElementById('website').value){say('ok','Thank you! You are on the list.');return;}
|
||||||
|
if(!FORM_ENDPOINT){say('ok','Thank you! You are on the list (demo, not saved).');form.reset();return;}
|
||||||
|
btn.disabled=true;say('','Sending...');
|
||||||
|
var data=new FormData();data.append('email',v);data.append('phone',document.getElementById('phone').value);
|
||||||
|
fetch(FORM_ENDPOINT,{method:'POST',headers:{'Accept':'application/json'},body:data})
|
||||||
|
.then(function(r){
|
||||||
|
if(!r.ok){return r.text().then(function(t){throw new Error('Error '+r.status+' '+t.slice(0,120));});}
|
||||||
|
say('ok','Thank you! You are on the list.');form.reset();
|
||||||
|
})
|
||||||
|
.catch(function(err){
|
||||||
|
say('err','Could not send. ('+(err&&err.message?err.message:'network problem')+') Please try again in a minute.');
|
||||||
|
if(window.console)console.error('Waitlist error:',err);
|
||||||
|
})
|
||||||
|
.then(function(){btn.disabled=false;msg.scrollIntoView({block:'nearest'});});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
|
// Framer Motion ("motion") adds the animations. If it fails to load, the page still shows everything.
|
||||||
|
try{
|
||||||
|
var m=await import('https://cdn.jsdelivr.net/npm/motion@11.11.13/+esm');
|
||||||
|
if(!matchMedia('(prefers-reduced-motion: reduce)').matches){
|
||||||
|
m.animate('.hero h1,.hero p,.hero .cta',{opacity:[0,1],y:[24,0]},{duration:.7,delay:m.stagger(.12)});
|
||||||
|
m.animate('.phone',{opacity:[0,1],y:[40,0]},{duration:.8,delay:.4});
|
||||||
|
document.querySelectorAll('.card').forEach(function(c){
|
||||||
|
m.inView(c,function(){m.animate(c,{opacity:[0,1],y:[24,0]},{duration:.5});});
|
||||||
|
c.addEventListener('mouseenter',function(){m.animate(c,{y:-4},{duration:.2});});
|
||||||
|
c.addEventListener('mouseleave',function(){m.animate(c,{y:0},{duration:.2});});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}catch(e){}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Privacy Policy – LookAtU</title>
|
||||||
|
<style>
|
||||||
|
body{margin:0;background:#0b1220;color:#eaf0ff;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;line-height:1.7}
|
||||||
|
main{max-width:720px;margin:0 auto;padding:40px 20px}
|
||||||
|
a{color:#4f8cff}h1{margin-top:0}h2{margin-top:32px}
|
||||||
|
.draft{background:#16213a;border:1px solid #ff4d5e;border-radius:12px;padding:14px 18px;color:#a3b1d1}
|
||||||
|
</style></head><body><main>
|
||||||
|
<p><a href="../">← Back to LookAtU</a></p>
|
||||||
|
<h1>Privacy Policy</h1>
|
||||||
|
<p class="draft"><strong>Draft.</strong> This page is a plain-language starting point. A lawyer must review it before LookAtU launches, because rules for children's data differ by country.</p>
|
||||||
|
|
||||||
|
<h2>What we collect today</h2>
|
||||||
|
<p>This website collects only the email address and phone type (Android, iPhone or both) that you type into the waitlist form.</p>
|
||||||
|
<h2>Why we collect it</h2>
|
||||||
|
<p>We use it only to tell you when LookAtU opens. We do not sell it. We do not use it for anything else.</p>
|
||||||
|
<h2>Who sees it</h2>
|
||||||
|
<p>The LookAtU team, and the service that stores the waitlist for us.</p>
|
||||||
|
<h2>Your choices</h2>
|
||||||
|
<p>You can ask us to delete your email at any time. Every email we send will have a way to unsubscribe.</p>
|
||||||
|
<h2>The LookAtU app (coming later)</h2>
|
||||||
|
<p>The app will handle location, activity and messages of children. Before launch we will publish full details here: what is collected, how it is protected, how long it is kept, and how a parent can delete it. The child will always be able to see that the app is installed.</p>
|
||||||
|
<h2>Contact</h2>
|
||||||
|
<p>Contact email: <em>to be added before launch</em>.</p>
|
||||||
|
</main></body></html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Terms of Use – LookAtU</title>
|
||||||
|
<style>
|
||||||
|
body{margin:0;background:#0b1220;color:#eaf0ff;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;line-height:1.7}
|
||||||
|
main{max-width:720px;margin:0 auto;padding:40px 20px}
|
||||||
|
a{color:#4f8cff}h1{margin-top:0}h2{margin-top:32px}
|
||||||
|
.draft{background:#16213a;border:1px solid #ff4d5e;border-radius:12px;padding:14px 18px;color:#a3b1d1}
|
||||||
|
</style></head><body><main>
|
||||||
|
<p><a href="../">← Back to LookAtU</a></p>
|
||||||
|
<h1>Terms of Use</h1>
|
||||||
|
<p class="draft"><strong>Draft.</strong> This page is a plain-language starting point. A lawyer must review it before LookAtU launches, because rules for children's data differ by country.</p>
|
||||||
|
|
||||||
|
<h2>This is an early preview</h2>
|
||||||
|
<p>This website is a preview of a product that is not released yet. Nothing here is an offer to sell.</p>
|
||||||
|
<h2>Waitlist</h2>
|
||||||
|
<p>Joining the waitlist is free and does not oblige you to buy anything. You can leave at any time.</p>
|
||||||
|
<h2>Using LookAtU responsibly (when it launches)</h2>
|
||||||
|
<ul>
|
||||||
|
<li>LookAtU is for parents and legal guardians of their own children.</li>
|
||||||
|
<li>It must not be used to watch another adult or someone else's child.</li>
|
||||||
|
<li>The child must be able to see that the app is installed.</li>
|
||||||
|
<li>Parents are responsible for following the laws in their country.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Emergencies</h2>
|
||||||
|
<p>The panic button helps a child alert a parent. It does not replace the police or emergency services. In an emergency, call your local emergency number.</p>
|
||||||
|
<h2>Contact</h2>
|
||||||
|
<p>Contact email: <em>to be added before launch</em>.</p>
|
||||||
|
</main></body></html>
|
||||||
Reference in new issue
Block a user