Compare commits
65 Commits
00121aaec9
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b355572a78 | |||
| 019a3363c5 | |||
| c794878e5e | |||
| 99b3fb9e59 | |||
| 25f698f067 | |||
| 59f5e6e0eb | |||
| 1320b1e4b2 | |||
| d554951077 | |||
| f27c00b965 | |||
| c0f6ba2651 | |||
| b8d120e74c | |||
| 828332f740 | |||
| d37f5a9538 | |||
| 7dab3fd7c5 | |||
| d7889be25a | |||
| 29cd5a4775 | |||
| 0bc6f389cc | |||
| 7081d2bee7 | |||
| 0489028486 | |||
| 0e4d78471b | |||
| 645291c724 | |||
| 4f78ed8897 | |||
| 77c51586e8 | |||
| 5e8d9efb7b | |||
| 8ae4a2fd5a | |||
| 76c72f69d8 | |||
| b53c0593c0 | |||
| a302ac09b4 | |||
| 5bb8289a54 | |||
| 81effca22b | |||
| e7b97561c1 | |||
| 3bd0b9a5a1 | |||
| 01b6007b1c | |||
| 6c9959c00d | |||
| 40581cb98b | |||
| a7e7b96465 | |||
| e7e9202a11 | |||
| 2637eb19cd | |||
| 9c29788210 | |||
| db80ea5262 | |||
| 62cd3f57df | |||
| dd89b3d934 | |||
| 38ea9b3d06 | |||
| f6321412aa | |||
| 409f51cc3e | |||
| 45e80cf4c0 | |||
| 84642ed13f | |||
| 9854688a49 | |||
| a511b89564 | |||
| ad0d6c572c | |||
| bea25098a0 | |||
| 89ba3515ac | |||
| 324f1b2641 | |||
| 488fc3d0f4 | |||
| a37edcb6c7 | |||
| 2f3527aaf9 | |||
| f3bf49ba1d | |||
| 1ebc9b615d | |||
| 82a42011d8 | |||
| e57e711fcc | |||
| 081ca83613 | |||
| b286a1f241 | |||
| 2adb07518c | |||
| bdb717de2e | |||
| 5a0db3cd60 |
@@ -45,7 +45,7 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
|
||||
|
||||
###> symfony/mailer ###
|
||||
MAILER_DSN=null://null
|
||||
MAILER_SENDER=noreply@pictureframe.edholm.me
|
||||
MAILER_SENDER=noreply@wevisto.com
|
||||
###< symfony/mailer ###
|
||||
|
||||
###> pictureframe ###
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# Library page — scaling to many frames
|
||||
|
||||
**Date:** 2026-05-14
|
||||
**Facilitated by:** BMAD design team
|
||||
**Active personas:** 🎨 Sally (UX), 🎨 Maya (Design Thinking), 📋 John (PM), 🏗️ Winston (Architect)
|
||||
**Brief from {{user_name}}:** Library doesn't scale beyond 1-2 frames. Need design ideas.
|
||||
|
||||
---
|
||||
|
||||
## 1. The problem, stated by the user reading the screen
|
||||
|
||||
🎨 **Sally:** *"Picture Aunt Carol. She's gifted PictureFrame units to her two grown kids and three grandkids — five frames in the wild. She opens the Library page, ready to approve last weekend's birthday photos. Each photo card stacks five 'Living Room', 'Kitchen', 'Bedroom', 'Mom's Place', 'Cabin' chips PLUS five lock chips. With twenty photos on screen that's two hundred buttons. She squints, taps the wrong one, undoes it. The page feels broken — and Aunt Carol is the literal target user."*
|
||||
|
||||
The current per-photo layout (`<button v-for="device in devicesStore.devices">` rendered **twice** — approval row + lock row) has O(photos × devices) chips on the page. Two of the most important user-facing screens (Library "My Photos" tab) breaks down at N=4+ frames.
|
||||
|
||||
---
|
||||
|
||||
## 2. Reframing — what job is the user really hiring this page for?
|
||||
|
||||
📋 **John:** *Cuts to the chase.* "Three jobs we keep conflating in the same view:
|
||||
|
||||
| # | Job | Frequency | Trigger |
|
||||
|---|-----|-----------|---------|
|
||||
| J1 | 'Send THIS new photo to ALL my family frames' | high | just uploaded |
|
||||
| J2 | 'Send THIS photo to ONE specific frame' | medium | birthday card for Mom |
|
||||
| J3 | 'What's on Grandma's frame right now? Lock it.' | medium | curating one frame |
|
||||
| J4 | 'Remove this old photo from my frames' | low | cleanup |
|
||||
|
||||
J1 is the bulk action — currently requires N taps. J3 is the per-frame view — currently requires scanning the chip column. The Library only directly serves J2 well, and only at small N."
|
||||
|
||||
🎨 **Maya:** *Sketching in the air.* "We're solving a **many-to-many curation** problem with a **per-cell grid of toggles**. Toggle grids are the worst answer at scale — visual noise scales with N×M. We should split the matrix: either device-first, photo-first-with-summary, or operation-first (bulk)."
|
||||
|
||||
---
|
||||
|
||||
## 3. Five design concepts
|
||||
|
||||
### 🅐 Photo card → photo-only + status badge (recommended baseline)
|
||||
|
||||
🎨 **Sally:** "Strip the card to the photo, plus a single line of status: `✓ on 3 of 5 frames · 1 lock`. Tap the photo opens a bottom sheet with the full grid of approvals + locks. The DevicePicker bottom sheet we already use during upload IS this exact UI — reuse it."
|
||||
|
||||
```
|
||||
┌───────────────┐
|
||||
│ │
|
||||
│ [ photo ] │
|
||||
│ │
|
||||
└───────────────┘
|
||||
✓ 3/5 frames · 🔒 Mom's
|
||||
[edit] [delete]
|
||||
```
|
||||
|
||||
- **Pros:** O(photos) widgets, not O(photos × devices). Reuses existing DevicePicker. Grid scales arbitrarily.
|
||||
- **Cons:** One more tap for the "approve to one specific frame" job (J2). One-frame and two-frame users see a regression.
|
||||
- **Effort:** Small. Mostly CSS + reusing DevicePicker. ~1 day.
|
||||
|
||||
### 🅑 Device-first column ("Per frame" tab)
|
||||
|
||||
📋 **John:** "Add a top-level toggle to the Library: `My Photos` / `By Frame`. The `By Frame` view is a horizontal swipe-able stack — one card per frame, each card shows its approved photos and the locked photo highlighted. Lets users curate per-frame without scanning a chip column."
|
||||
|
||||
```
|
||||
┌── Living Room (5 approved · 🔒 Birthday) ──┐
|
||||
│ [photo][photo][photo][photo][photo] [+] │
|
||||
└────────────────────────────────────────────┘
|
||||
┌── Kitchen (12 approved · 🔒 none) ─────┐
|
||||
│ [photo][photo][photo][photo][photo] [+] │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Pros:** Native answer to J3 ("what's on Grandma's frame?"). Each card is bounded — no matrix blow-up.
|
||||
- **Cons:** Doesn't help J1 (one photo → all frames) without leaving this view. Extra navigation cost.
|
||||
- **Effort:** Medium. New route/component + API filter param. ~2-3 days.
|
||||
|
||||
### 🅒 Multi-select + bulk action bar
|
||||
|
||||
🎨 **Maya:** *Jazz hands.* "Tap-and-hold a photo to enter selection mode. Top bar morphs into 'N selected — Send to… / Lock to… / Delete'. Tap-and-pick more photos. One trip to DevicePicker covers any batch. J1 in two gestures."
|
||||
|
||||
```
|
||||
[grid of photos with selection bubbles in corners]
|
||||
─────────────────────────────────────────
|
||||
3 selected [Send to →] [Cancel]
|
||||
```
|
||||
|
||||
- **Pros:** Solves J1 efficiently. Familiar pattern (iOS Photos, Google Photos).
|
||||
- **Cons:** Discoverability — first-time users won't know to long-press. Probably worth a hint on first run.
|
||||
- **Effort:** Medium. Selection-state in store + bulk API endpoint. ~2 days.
|
||||
|
||||
### 🅓 Device chip filter + photo dots
|
||||
|
||||
🎨 **Sally:** "Top of the Library page: a horizontal scrolling row of device chips. Tap one → grid filters to photos approved on that frame. Each photo's bottom shows tiny initial-dots for the OTHER frames it's on. Tap a photo to manage."
|
||||
|
||||
```
|
||||
[All] [Living Room ✓] [Kitchen] [Bedroom] [Mom's] [Cabin]
|
||||
─────────────────────────────────────────
|
||||
[photo · ●LR ●KT] [photo · ●LR]
|
||||
[photo · ●LR ●KT] [photo · ●LR ●MP]
|
||||
```
|
||||
|
||||
- **Pros:** Helpful for J3 by filter. Compact status without buttons. Works at any N.
|
||||
- **Cons:** Filter is implicit — user might not realise. Initial-dots crowd at high N.
|
||||
- **Effort:** Medium. Filter is purely client-side; dots are styling. ~1.5 days.
|
||||
|
||||
### 🅔 Device groups ("Family", "Kids", "All")
|
||||
|
||||
📋 **John:** *Skeptical.* "If users actually have 8 frames, they probably cluster: 'all kids' rooms', 'parents' places'. Let them define one-tap groups. Photo approval/lock takes group as a unit."
|
||||
|
||||
```
|
||||
Groups: [All Frames] [Kids' Rooms] [Parents] [+ Group]
|
||||
```
|
||||
|
||||
- **Pros:** Aligns to the user's mental model when they have many frames.
|
||||
- **Cons:** New entity to model (Group → Device many-to-many). New CRUD. Premature without evidence of cluster usage.
|
||||
- **Effort:** Large. Schema change + UI for group management. ~4-5 days.
|
||||
|
||||
---
|
||||
|
||||
## 4. Architect's gut-check
|
||||
|
||||
🏗️ **Winston:** *Pragmatic.* "From the data side:
|
||||
- **🅐 (photo-only card + sheet)** — zero schema changes, zero API changes. Pure frontend refactor. Ships fastest. Lowest risk to existing 7.3" beta units.
|
||||
- **🅑 (device-first tab)** — needs a `GET /api/devices/{id}/photos` endpoint. Trivial to add.
|
||||
- **🅒 (multi-select bulk)** — needs `PATCH /api/images/bulk-approve` with `[imageIds]` × `[deviceIds]`. Small server lift.
|
||||
- **🅓ⓔ** — purely frontend.
|
||||
|
||||
If we ship one thing, **🅐 is the foundation everything else builds on** — once the per-photo card is compact, layering bulk-select (🅒) or filtering (🅓) on top is additive, not a rewrite.
|
||||
|
||||
I would NOT do 🅔 yet — premature abstraction. Wait for actual users with 8+ frames asking for it."
|
||||
|
||||
---
|
||||
|
||||
## 5. Where the team lands
|
||||
|
||||
🎨 **Sally + 📋 John + 🏗️ Winston, in unison:**
|
||||
|
||||
**Ship 🅐 first.** It's the smallest change that unblocks scale and **costs nothing** for the existing 1-2-frame users (their card just looks cleaner). The DevicePicker sheet is already built — point the new "manage" tap at it.
|
||||
|
||||
**Then 🅒 (multi-select)** as a fast-follow for J1 efficiency. Tap-hold to enter selection, top action bar, batch send. Doesn't need a server endpoint immediately — can client-loop the existing per-image PATCH.
|
||||
|
||||
**Defer 🅑 (device-first tab)** until a user explicitly asks "how do I see what's on Grandma's frame?". The compact photo card + DevicePicker handles J3 well enough at N≤5.
|
||||
|
||||
**Defer 🅓 (chip filter) and 🅔 (groups)** until usage data says they're worth it.
|
||||
|
||||
---
|
||||
|
||||
## 6. Open questions for {{user_name}}
|
||||
|
||||
🎨 **Sally:** "Before we wireframe — three quick ones:
|
||||
|
||||
1. **Status badge wording.** `✓ on 3 of 5 frames` or `3 frames`? Subtle but it sets tone (`✓ on` reads as confirmation; bare `3 frames` reads as info).
|
||||
2. **Lock indicator placement.** Inside the badge line (`✓ 3/5 · 🔒 Mom's`) or as a corner overlay on the photo (lock icon top-right)?
|
||||
3. **Tap target.** Does tapping the *photo* open manage, or do we want a dedicated `Manage ▸` chip below it? Photo-tap is friendlier on mobile; dedicated chip is more discoverable."
|
||||
|
||||
🎨 **Maya:** "And one for after we wireframe — what's the maximum frame count we should *actually* design for? If it's 'family of five = five frames', design priorities differ from 'church group of forty'."
|
||||
|
||||
---
|
||||
|
||||
## 7. Next step
|
||||
|
||||
If you greenlight 🅐: I'll draft the wireframe + interaction spec, then implement. Estimated 1 working session for the refactor + DevicePicker rewiring.
|
||||
|
||||
If you want to riff on the concepts above first, just say the word — we can pull in Carson for a wild-ideas round, or Victor (Innovation Strategist) for a disruptive frame.
|
||||
@@ -0,0 +1,67 @@
|
||||
# WeVisto · atmospheric redesign
|
||||
|
||||
Iterative design proposal landed on 2026-05-15. Captures the **brand-as-surface**
|
||||
direction: the Camogli harbor photograph is the actual background of every
|
||||
authenticated view, frosted-glass cards float on it, and the six user themes
|
||||
get rebuilt as atmospheric **dusks** (tinted filters over the same photo)
|
||||
instead of cream variants.
|
||||
|
||||
## What's here
|
||||
|
||||
```
|
||||
atmospheric-redesign/
|
||||
├── index.html — entry point, links to every mockup
|
||||
├── login-cinematic.html — pre-auth canonical example
|
||||
├── spa/
|
||||
│ ├── home.html — HomeView with new chrome + frame card + up-next strip
|
||||
│ ├── library.html — LibraryView empty state (mark in halo)
|
||||
│ ├── settings.html — SettingsView with the dusk picker
|
||||
│ ├── _tokens.css — type + dusks (replaces cream theme system)
|
||||
│ └── _chrome.css — frosted-glass app bar / bottom nav / signature
|
||||
├── favicons/ — A/B/C/D icon directions (still iterating)
|
||||
└── assets/
|
||||
├── harbor.jpg — the brand photograph (extracted from logo.svg)
|
||||
├── wordmark.svg — full WeVisto wordmark + harbor
|
||||
├── mark-photo.png — split-W on harbor (512px)
|
||||
├── mark-photo-64.png — same, 64px for small uses
|
||||
└── mark-navy.svg — split-W on solid navy (lightweight vector mark)
|
||||
```
|
||||
|
||||
## To view
|
||||
|
||||
```bash
|
||||
cd webApp/_design/atmospheric-redesign
|
||||
python3 -m http.server 8766 --bind 0.0.0.0
|
||||
# → open http://<host>:8766/
|
||||
```
|
||||
|
||||
Every SPA mockup defaults to Ocean Dusk and has a floating dusk-switcher
|
||||
in the bottom-right — click any chip to flip the room to that dusk.
|
||||
|
||||
## The principle
|
||||
|
||||
**Brand layer (fixed):** the harbor photograph, the wordmark, the yellow V,
|
||||
the split-W mark. Permanent fixed backdrop. Never changes with theme.
|
||||
|
||||
**Theme layer (yours):** six dusks — Ocean, Amber, Sage, Rose, Mauve, Honey.
|
||||
Each is an `rgba` multiplied over the photo plus a theme-coloured accent for
|
||||
primary CTAs. Same room, different time of day.
|
||||
|
||||
## Open work
|
||||
|
||||
- **Favicon mark** — A/B/C/D directions saved; Matt's feedback was that the
|
||||
small mark needs more WeVisto presence "in the image itself", not just
|
||||
initials. Next iteration to integrate the wordmark into the mark.
|
||||
- **Logo usage across app** — currently the full wordmark logo only appears
|
||||
in emails. To propagate to login splash / library hero / welcome moments /
|
||||
upload success states.
|
||||
- **Implementation** — these are HTML/CSS mockups, not Vue components. The
|
||||
real port needs:
|
||||
- Migrate `frontend/src/styles/global.scss` from six cream themes to six
|
||||
dusks (replace `--color-*` cream tokens with the `--bg-tint / --glass /
|
||||
--accent` tokens shown in `spa/_tokens.css`).
|
||||
- Apply the harbor backdrop via `body::before` in global.scss.
|
||||
- Add the top app bar component (mark + wordmark + sub + actions).
|
||||
- Rebuild HomeView / LibraryView / SettingsView to use `.glass` surfaces.
|
||||
- Update the Twig login / register / setup templates to share the same
|
||||
backdrop + frosted-glass card pattern.
|
||||
|
After Width: | Height: | Size: 264 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<clipPath id="wv-left"><rect x="0" y="0" width="32" height="64"/></clipPath>
|
||||
<clipPath id="wv-right"><rect x="32" y="0" width="32" height="64"/></clipPath>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="12" fill="#1a3a5c"/>
|
||||
<g font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
|
||||
font-weight="900" font-size="56" text-anchor="middle">
|
||||
<text x="32" y="50" fill="#fafafa" clip-path="url(#wv-left)">W</text>
|
||||
<text x="32" y="50" fill="#f0d000" clip-path="url(#wv-right)">W</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 470 B |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="10" fill="#fdf6ee"/>
|
||||
<rect x="4" y="4" width="56" height="56" rx="7" fill="none" stroke="#1a3a5c" stroke-width="1.5"/>
|
||||
<!-- house silhouette: roof V over a small base, with bright yellow accent in negative -->
|
||||
<path d="M14 44 L32 18 L50 44 Z" fill="#1a3a5c"/>
|
||||
<path d="M22 44 L32 28 L42 44 Z" fill="#fdf6ee"/>
|
||||
<rect x="29" y="36" width="6" height="8" fill="#f0d000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 428 B |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 715 B |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="10" fill="#1a3a5c"/>
|
||||
<!-- inner frame -->
|
||||
<rect x="10" y="10" width="44" height="44" rx="3" fill="#fdf6ee"/>
|
||||
<rect x="13" y="13" width="38" height="38" fill="#cfe2ea"/>
|
||||
<!-- sea -->
|
||||
<rect x="13" y="35" width="38" height="16" fill="#4a7a9a"/>
|
||||
<!-- sun -->
|
||||
<circle cx="32" cy="32" r="9" fill="#f0d000"/>
|
||||
<!-- frame matt highlights -->
|
||||
<rect x="10" y="10" width="44" height="44" rx="3" fill="none" stroke="#0e2640" stroke-width="0.8" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
|
After Width: | Height: | Size: 482 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 586 B |
|
After Width: | Height: | Size: 733 B |
@@ -0,0 +1,29 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="10" fill="#1a3a5c"/>
|
||||
<!-- sky highlight -->
|
||||
<rect x="0" y="0" width="64" height="36" fill="#2a5478"/>
|
||||
<!-- buildings as colored vertical bars (Camogli houses are tall narrow row-houses in ochre/coral/cream) -->
|
||||
<g>
|
||||
<rect x="6" y="22" width="6" height="34" fill="#c97c3a"/>
|
||||
<rect x="13" y="18" width="7" height="38" fill="#dba068"/>
|
||||
<rect x="21" y="14" width="6" height="42" fill="#e8c889"/>
|
||||
<rect x="28" y="20" width="7" height="36" fill="#c45230"/>
|
||||
<rect x="36" y="16" width="6" height="40" fill="#dba068"/>
|
||||
<rect x="43" y="22" width="7" height="34" fill="#c97c3a"/>
|
||||
<rect x="51" y="19" width="6" height="37" fill="#e8c889"/>
|
||||
<!-- tiny dark windows for character (only readable at >=32) -->
|
||||
<g fill="#1a3a5c" opacity="0.85">
|
||||
<rect x="8" y="28" width="2" height="2"/><rect x="8" y="36" width="2" height="2"/><rect x="8" y="44" width="2" height="2"/>
|
||||
<rect x="15" y="24" width="2" height="2"/><rect x="15" y="32" width="2" height="2"/><rect x="15" y="40" width="2" height="2"/><rect x="15" y="48" width="2" height="2"/>
|
||||
<rect x="23" y="20" width="2" height="2"/><rect x="23" y="30" width="2" height="2"/><rect x="23" y="40" width="2" height="2"/><rect x="23" y="50" width="2" height="2"/>
|
||||
<rect x="30" y="26" width="2" height="2"/><rect x="30" y="36" width="2" height="2"/><rect x="30" y="46" width="2" height="2"/>
|
||||
<rect x="38" y="22" width="2" height="2"/><rect x="38" y="32" width="2" height="2"/><rect x="38" y="42" width="2" height="2"/><rect x="38" y="52" width="2" height="2"/>
|
||||
<rect x="45" y="28" width="2" height="2"/><rect x="45" y="38" width="2" height="2"/><rect x="45" y="48" width="2" height="2"/>
|
||||
<rect x="53" y="25" width="2" height="2"/><rect x="53" y="35" width="2" height="2"/><rect x="53" y="45" width="2" height="2"/>
|
||||
</g>
|
||||
</g>
|
||||
<!-- water reflection band -->
|
||||
<rect x="0" y="56" width="64" height="8" fill="#5e8cb0"/>
|
||||
<!-- yellow sun above buildings (the "V" accent reduced to a single dot) -->
|
||||
<circle cx="51" cy="10" r="4" fill="#f0d000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 672 B |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<radialGradient id="wax" cx="40%" cy="35%" r="65%">
|
||||
<stop offset="0%" stop-color="#b04a35"/>
|
||||
<stop offset="55%" stop-color="#8a2418"/>
|
||||
<stop offset="100%" stop-color="#5a140a"/>
|
||||
</radialGradient>
|
||||
<filter id="bevel" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feGaussianBlur stdDeviation="0.6"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="10" fill="#fdf6ee"/>
|
||||
<!-- wax circle with serrated edge -->
|
||||
<g transform="translate(32 33)">
|
||||
<circle r="24" fill="url(#wax)"/>
|
||||
<g fill="url(#wax)">
|
||||
<!-- subtle serration: tiny notches around perimeter -->
|
||||
<circle cx="0" cy="-24" r="1.6"/>
|
||||
<circle cx="17" cy="-17" r="1.6"/>
|
||||
<circle cx="24" cy="0" r="1.6"/>
|
||||
<circle cx="17" cy="17" r="1.6"/>
|
||||
<circle cx="0" cy="24" r="1.6"/>
|
||||
<circle cx="-17" cy="17" r="1.6"/>
|
||||
<circle cx="-24" cy="0" r="1.6"/>
|
||||
<circle cx="-17" cy="-17" r="1.6"/>
|
||||
</g>
|
||||
<circle r="20" fill="none" stroke="#5a140a" stroke-width="0.8" opacity="0.65"/>
|
||||
<!-- engraved V (slightly recessed/darker) -->
|
||||
<path d="M-10 -10 L0 12 L10 -10" fill="none" stroke="#3a0a04" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.85"/>
|
||||
<path d="M-10 -10 L0 12 L10 -10" fill="none" stroke="#d8a89a" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" opacity="0.5" transform="translate(-0.5 -0.5)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,262 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>WeVisto · atmospheric redesign</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--cream: #fdf6ee;
|
||||
--cream-deep: #f5ead4;
|
||||
--ink: #2a1f15;
|
||||
--ink-soft: #6e6450;
|
||||
--navy: #0e2740;
|
||||
--yellow: #f0d000;
|
||||
--rule: rgba(42,31,21,0.15);
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: var(--cream); color: var(--ink); }
|
||||
body {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.55;
|
||||
padding: 48px 40px 96px;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.top { display: grid; grid-template-columns: auto 1fr auto; align-items: end; gap: 24px; padding-bottom: 14px; border-bottom: 1px solid var(--rule); }
|
||||
.top .l, .top .r { font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.top .r { text-align: right; }
|
||||
.top .name { font-family: 'Marcellus', serif; font-size: 44px; color: var(--navy); text-align: center; letter-spacing: 0.01em; }
|
||||
.top .name .v { color: var(--yellow); }
|
||||
.subhead { margin-top: 12px; padding-bottom: 22px; border-bottom: 1px solid var(--rule); text-align: center; font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 22px; color: var(--ink); }
|
||||
|
||||
h2.section { margin: 56px 0 6px; font-family: 'Marcellus', serif; font-weight: 400; font-size: 30px; color: var(--navy); }
|
||||
h2.section .num { color: var(--yellow); margin-right: 8px; }
|
||||
.lede { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 19px; color: var(--ink); margin-bottom: 28px; max-width: 76ch; line-height: 1.5; }
|
||||
.lede em { color: var(--navy); }
|
||||
.lede strong { font-weight: 600; font-style: normal; color: var(--navy); }
|
||||
|
||||
.principles { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; margin: 32px 0; padding: 28px 32px; background: var(--cream-deep); border: 1px dashed rgba(42,31,21,0.25); border-radius: 8px; }
|
||||
.principle h3 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 18px; color: var(--navy); margin-bottom: 6px; }
|
||||
.principle h3 .yellow { color: var(--yellow); }
|
||||
.principle p { font-size: 14.5px; line-height: 1.55; color: var(--ink); }
|
||||
.principle p strong { color: var(--navy); font-weight: 700; }
|
||||
|
||||
.stack { display: grid; gap: 32px; margin-top: 24px; }
|
||||
.row-2 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 28px; }
|
||||
.row-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
|
||||
.card { background: #fff9ee; border: 1px solid var(--rule); display: flex; flex-direction: column; overflow: hidden; }
|
||||
.card .preview { position: relative; background: #06121f; }
|
||||
.card .preview iframe { border: 0; display: block; }
|
||||
.card.phone .preview { aspect-ratio: 9/16; }
|
||||
.card.phone .preview iframe { width: 250%; height: 250%; transform: scale(0.4); transform-origin: top left; }
|
||||
.card.wide .preview { aspect-ratio: 16/10; }
|
||||
.card.wide .preview iframe { width: 200%; height: 200%; transform: scale(0.5); transform-origin: top left; }
|
||||
.card .open { position: absolute; bottom: 12px; right: 12px; z-index: 5; font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase; color: #fff; background: rgba(7,23,42,0.75); padding: 8px 14px; text-decoration: none; backdrop-filter: blur(6px); transition: background 150ms; }
|
||||
.card .open:hover { background: var(--yellow); color: var(--navy); }
|
||||
.card .body { padding: 22px 24px 24px; }
|
||||
.card .body .roman { font-family: 'Marcellus', serif; font-size: 22px; color: var(--yellow); line-height: 1; margin-bottom: 4px; }
|
||||
.card .body h3 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 24px; color: var(--navy); margin-bottom: 6px; }
|
||||
.card .body .surface { font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 12px; }
|
||||
.card .body p { font-size: 15px; color: var(--ink); line-height: 1.5; }
|
||||
.card .body p em { color: var(--navy); font-style: italic; }
|
||||
|
||||
/* dusks reference strip */
|
||||
.dusks { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin-top: 24px; }
|
||||
.dusk { padding: 10px; border: 1px solid var(--rule); background: #fff9ee; text-align: center; }
|
||||
.dusk .swatch { aspect-ratio: 3/2; border-radius: 4px; background-image: url('assets/harbor.jpg'); background-size: cover; position: relative; margin-bottom: 8px; overflow: hidden; }
|
||||
.dusk .swatch::after { content:''; position: absolute; inset: 0; mix-blend-mode: multiply; }
|
||||
.dusk[data-d="ocean"] .swatch::after { background: rgba(8, 22, 38, 0.55); }
|
||||
.dusk[data-d="amber"] .swatch::after { background: rgba(60, 25, 8, 0.55); }
|
||||
.dusk[data-d="sage"] .swatch::after { background: rgba(20, 40, 22, 0.55); }
|
||||
.dusk[data-d="rose"] .swatch::after { background: rgba(56, 16, 38, 0.55); }
|
||||
.dusk[data-d="mauve"] .swatch::after { background: rgba(40, 18, 56, 0.55); }
|
||||
.dusk[data-d="honey"] .swatch::after { background: rgba(48, 36, 14, 0.55); }
|
||||
.dusk .name { font-family: 'Marcellus', serif; font-size: 14px; color: var(--navy); }
|
||||
.dusk .italic { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 12px; color: var(--ink-soft); }
|
||||
|
||||
/* favicon section (in-progress note) */
|
||||
.fav-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 24px; }
|
||||
.fav { background: #fff9ee; border: 1px solid var(--rule); padding: 18px; }
|
||||
.fav__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
||||
.fav__letter { font-family: 'Marcellus', serif; font-size: 22px; color: var(--yellow); line-height: 1; }
|
||||
.fav__label { font-family: 'DM Mono', monospace; font-size: 9px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.fav h4 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 18px; color: var(--navy); margin-bottom: 4px; }
|
||||
.fav .desc { font-size: 13px; color: var(--ink); line-height: 1.45; margin-bottom: 12px; min-height: 50px; }
|
||||
.fav .desc em { color: var(--navy); font-style: italic; }
|
||||
.fav .pips { display: flex; gap: 10px; align-items: flex-end; padding: 10px 0; border-top: 1px solid var(--rule); }
|
||||
.fav .pip { text-align: center; }
|
||||
.fav .pip img { display: block; image-rendering: pixelated; margin: 0 auto 4px; border-radius: 2px; }
|
||||
.fav .pip .cap { font-family: 'DM Mono', monospace; font-size: 9px; color: var(--ink-soft); }
|
||||
|
||||
.wip { padding: 16px 20px; background: rgba(240,208,0,0.18); border-left: 4px solid var(--yellow); margin-top: 16px; font-style: italic; color: var(--ink); font-size: 15px; }
|
||||
|
||||
footer { margin-top: 64px; padding-top: 22px; border-top: 1px solid var(--rule); text-align: center; font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 14px; color: var(--ink-soft); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
body { padding: 32px 20px; }
|
||||
.row-2, .row-3, .fav-row, .principles, .dusks { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="top">
|
||||
<div class="l">Design proposal · 2026-05-15 · settled</div>
|
||||
<div class="name">We<span class="v">V</span>isto</div>
|
||||
<div class="r">Atmospheric system<br>login → app, one room</div>
|
||||
</div>
|
||||
<div class="subhead">— the brand is the surface, the theme is the tint, the photograph runs through.</div>
|
||||
|
||||
<h2 class="section"><span class="num">○</span>The principle, after several mis-tries</h2>
|
||||
<p class="lede">The earlier proposals kept the app on cream while only the login was atmospheric. That made login feel like a different product. <strong>The right answer was to rebuild the theme system itself.</strong></p>
|
||||
|
||||
<div class="principles">
|
||||
<div class="principle">
|
||||
<h3><span class="yellow">●</span> Brand <em>is</em> the surface</h3>
|
||||
<p>The Camogli harbor photograph is the permanent fixed backdrop on every authenticated view — same as the login. Cards become <strong>frosted glass</strong> floating on it. Light text on dark, Marcellus / Cormorant italic / Nunito body, the same recipe as the login. The wordmark and split-W mark live in the persistent top app bar.</p>
|
||||
</div>
|
||||
<div class="principle">
|
||||
<h3><span class="yellow">●</span> Theme <em>is</em> the dusk</h3>
|
||||
<p>The six user themes get reborn as <strong>atmospheric dusks</strong>: a tinted overlay multiplied over the same harbor photo, plus a theme-coloured accent for primary CTAs. Same room, different time of day. The yellow V is the one brand colour that survives across every dusk — wordmark, hairlines, plate numbers.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="section"><span class="num">I.</span>The dusks (replaces the six cream themes)</h2>
|
||||
<p class="lede">Each chip below is the harbor photo with the dusk's tint multiplied over it — the actual recipe used in the body backdrop. Same photo, six moods.</p>
|
||||
<div class="dusks">
|
||||
<div class="dusk" data-d="ocean"><div class="swatch"></div><div class="name">Ocean</div><div class="italic">— the harbor</div></div>
|
||||
<div class="dusk" data-d="amber"><div class="swatch"></div><div class="name">Amber</div><div class="italic">— the workshop</div></div>
|
||||
<div class="dusk" data-d="sage"><div class="swatch"></div><div class="name">Sage</div><div class="italic">— the garden</div></div>
|
||||
<div class="dusk" data-d="rose"><div class="swatch"></div><div class="name">Rose</div><div class="italic">— the parlor</div></div>
|
||||
<div class="dusk" data-d="mauve"><div class="swatch"></div><div class="name">Mauve</div><div class="italic">— the study</div></div>
|
||||
<div class="dusk" data-d="honey"><div class="swatch"></div><div class="name">Honey</div><div class="italic">— the alcove</div></div>
|
||||
</div>
|
||||
|
||||
<h2 class="section"><span class="num">II.</span>The front door — login</h2>
|
||||
<p class="lede">The canonical example of the visual language. Same harbor backdrop, deep navy filter, frosted-glass card, staggered wordmark reveal. Now also: the home page lives in the same room.</p>
|
||||
<div class="stack">
|
||||
<div class="card wide">
|
||||
<div class="preview">
|
||||
<iframe src="login-cinematic.html" loading="lazy"></iframe>
|
||||
<a class="open" href="login-cinematic.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">II.</div>
|
||||
<h3>Login · cinematic arrival</h3>
|
||||
<div class="surface">templates/security/login.html.twig · register.html.twig</div>
|
||||
<p>Animated wordmark reveal, frosted-glass card, yellow CTA. Respects <em>prefers-reduced-motion</em>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="section"><span class="num">III.</span>The room — in-app views</h2>
|
||||
<p class="lede">All three default to Ocean Dusk. Use the floating chip cluster in the bottom-right of any mockup to switch dusks live — the photo re-tints, glass cards re-tint, accent button changes, the wordmark and yellow V stay constant.</p>
|
||||
|
||||
<div class="stack">
|
||||
<div class="row-3">
|
||||
|
||||
<div class="card phone">
|
||||
<div class="preview">
|
||||
<iframe src="spa/home.html" loading="lazy"></iframe>
|
||||
<a class="open" href="spa/home.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">III·a</div>
|
||||
<h3>Home</h3>
|
||||
<div class="surface">/ · HomeView.vue</div>
|
||||
<p>Top app bar; editorial hero ("Good morning, <em>Alice.</em>"); frame card as glass with the user's frame photo inside; up-next strip of queued thumbnails.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card phone">
|
||||
<div class="preview">
|
||||
<iframe src="spa/library.html" loading="lazy"></iframe>
|
||||
<a class="open" href="spa/library.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">III·b</div>
|
||||
<h3>Library · empty state</h3>
|
||||
<div class="surface">/library · LibraryView.vue</div>
|
||||
<p>A 112px mark in a yellow halo, "A library, <em>waiting.</em>" in display serif, three-step glass card explaining how it works, signature.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card phone">
|
||||
<div class="preview">
|
||||
<iframe src="spa/settings.html" loading="lazy"></iframe>
|
||||
<a class="open" href="spa/settings.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">III·c</div>
|
||||
<h3>Settings</h3>
|
||||
<div class="surface">/settings · SettingsView.vue</div>
|
||||
<p>Glass list rows. <em>Dusk picker</em> grid showing each dusk as a tinted harbor preview — click to switch the page live.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="section"><span class="num">IV.</span>The small mark — favicon directions (work in progress)</h2>
|
||||
<p class="lede">Four icon directions explored so far. Matt's still iterating here — wants WeVisto to be more present <em>inside the image itself</em>, not just initials beside the brand.</p>
|
||||
|
||||
<div class="wip">In progress — next iteration to integrate the wordmark into the mark and propagate the wordmark logo throughout more app surfaces.</div>
|
||||
|
||||
<div class="fav-row">
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__letter">A.</span><span class="fav__label">Warmth / home</span></div>
|
||||
<h4>Roof V</h4>
|
||||
<div class="desc">House silhouette where the rooftop is the V. Yellow door at centre.</div>
|
||||
<div class="pips">
|
||||
<div class="pip"><img src="favicons/A-roof-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/A-roof-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/A-roof-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/A-roof-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__letter">B.</span><span class="fav__label">Literal / poetic</span></div>
|
||||
<h4>Frame + horizon</h4>
|
||||
<div class="desc">A tiny photo frame with a sun over the sea. Most legible at 16.</div>
|
||||
<div class="pips">
|
||||
<div class="pip"><img src="favicons/B-frame-horizon-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/B-frame-horizon-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/B-frame-horizon-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/B-frame-horizon-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__letter">C.</span><span class="fav__label">Origin-specific</span></div>
|
||||
<h4>Camogli skyline</h4>
|
||||
<div class="desc">Camogli's coloured row-houses as silhouette. Risk: generic city at 16.</div>
|
||||
<div class="pips">
|
||||
<div class="pip"><img src="favicons/C-skyline-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/C-skyline-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/C-skyline-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/C-skyline-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__letter">D.</span><span class="fav__label">Gift / stationery</span></div>
|
||||
<h4>Wax seal V</h4>
|
||||
<div class="desc">Pressed-wax seal with V. Strong at 64+, softens at 16.</div>
|
||||
<div class="pips">
|
||||
<div class="pip"><img src="favicons/D-seal-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/D-seal-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/D-seal-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/D-seal-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Saved at <code>webApp/_design/atmospheric-redesign/</code>. Reload any mockup to replay animations.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Sign in — WeVisto</title>
|
||||
<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=Marcellus&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--navy: #0e2740;
|
||||
--navy-deep: #07172a;
|
||||
--yellow: #f0d000;
|
||||
--yellow-soft: #c4a700;
|
||||
--cream: #faf3e4;
|
||||
--cream-dim: #d8cbb0;
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { width: 100%; min-height: 100%; background: var(--navy-deep); color: var(--cream); }
|
||||
body {
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 56px 24px;
|
||||
}
|
||||
|
||||
/* Ken-Burns harbor backdrop */
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
inset: -4%;
|
||||
background: url('/assets/harbor.jpg') center/cover no-repeat;
|
||||
filter: brightness(0.5) saturate(0.85);
|
||||
opacity: 0;
|
||||
transform: scale(1.04);
|
||||
z-index: 0;
|
||||
animation:
|
||||
bd-fade 1.6s 0.1s cubic-bezier(.2,.7,.2,1) forwards,
|
||||
bd-zoom 28s 0.1s linear forwards;
|
||||
}
|
||||
@keyframes bd-fade { to { opacity: 0.42; } }
|
||||
@keyframes bd-zoom { from { transform: scale(1.04); } to { transform: scale(1.13) translate(-1.5%, -0.8%); } }
|
||||
|
||||
.vignette {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 65% at 50% 50%, transparent 0%, rgba(7,23,42,0.6) 65%, rgba(7,23,42,0.95) 100%);
|
||||
z-index: 1;
|
||||
}
|
||||
.grain {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.16 0'/></filter><rect width='320' height='320' filter='url(%23n)'/></svg>");
|
||||
mix-blend-mode: overlay;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* upper stamps */
|
||||
.stamp {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
left: 32px;
|
||||
font-family: 'Marcellus', serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.46em;
|
||||
color: var(--cream);
|
||||
opacity: 0;
|
||||
text-transform: uppercase;
|
||||
z-index: 10;
|
||||
animation: tag-in 1.4s 0.9s ease forwards;
|
||||
}
|
||||
.stamp .yellow { color: var(--yellow); }
|
||||
.corner {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
right: 32px;
|
||||
font-family: 'Marcellus', serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.4em;
|
||||
color: var(--cream);
|
||||
opacity: 0;
|
||||
text-transform: uppercase;
|
||||
z-index: 10;
|
||||
animation: tag-in 1.4s 1.0s ease forwards;
|
||||
}
|
||||
@keyframes tag-in { to { opacity: 0.85; transform: translateY(0); } }
|
||||
|
||||
/* the central composition */
|
||||
.stage {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 440px;
|
||||
}
|
||||
|
||||
/* wordmark — same staggered reveal as splash, but smaller */
|
||||
.wordmark {
|
||||
font-family: 'Marcellus', serif;
|
||||
font-weight: 400;
|
||||
font-size: clamp(48px, 10vw, 76px);
|
||||
line-height: 1;
|
||||
color: var(--cream);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.005em;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.wordmark .letter {
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
transform: translateY(0.35em) rotate(2deg);
|
||||
animation: letter-in 1.0s cubic-bezier(.2,.7,.2,1) forwards;
|
||||
}
|
||||
.wordmark .v {
|
||||
color: var(--yellow);
|
||||
text-shadow: 0 1px 0 rgba(0,0,0,0.25), 0 12px 24px rgba(240,208,0,0.18);
|
||||
}
|
||||
.wordmark .letter:nth-child(1) { animation-delay: 0.45s; }
|
||||
.wordmark .letter:nth-child(2) { animation-delay: 0.52s; }
|
||||
.wordmark .letter:nth-child(3) { animation-delay: 0.66s; }
|
||||
.wordmark .letter:nth-child(4) { animation-delay: 0.78s; }
|
||||
.wordmark .letter:nth-child(5) { animation-delay: 0.85s; }
|
||||
.wordmark .letter:nth-child(6) { animation-delay: 0.92s; }
|
||||
.wordmark .letter:nth-child(7) { animation-delay: 0.99s; }
|
||||
@keyframes letter-in { to { opacity: 1; transform: translateY(0) rotate(0); } }
|
||||
|
||||
/* tiny rule + tagline below wordmark */
|
||||
.underline {
|
||||
width: 0;
|
||||
height: 1px;
|
||||
margin: 6px auto 14px;
|
||||
background: linear-gradient(90deg, transparent, var(--yellow), transparent);
|
||||
animation: rule-grow 1.4s 1.25s cubic-bezier(.2,.7,.2,1) forwards;
|
||||
}
|
||||
@keyframes rule-grow { to { width: 180px; } }
|
||||
|
||||
.tagline {
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: 17px;
|
||||
color: var(--cream);
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
letter-spacing: 0.03em;
|
||||
margin-bottom: 40px;
|
||||
text-align: center;
|
||||
animation: tag-in 1.2s 1.7s cubic-bezier(.2,.7,.2,1) forwards;
|
||||
}
|
||||
|
||||
/* the sign-in panel — frosted glass card */
|
||||
.panel {
|
||||
width: 100%;
|
||||
background: rgba(7, 23, 42, 0.55);
|
||||
border: 1px solid rgba(255, 240, 200, 0.18);
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.06) inset,
|
||||
0 30px 60px -20px rgba(0,0,0,0.65);
|
||||
padding: 36px 36px 32px;
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
animation: panel-in 1.2s 2.0s cubic-bezier(.2,.7,.2,1) forwards;
|
||||
position: relative;
|
||||
}
|
||||
@keyframes panel-in { to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.panel h1 {
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: 26px;
|
||||
color: var(--cream);
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.panel .greeting {
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
color: var(--cream-dim);
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.field { margin-bottom: 18px; }
|
||||
.field label {
|
||||
display: block;
|
||||
font-family: 'Marcellus', serif;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--cream-dim);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.field input {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(255, 240, 200, 0.28);
|
||||
color: var(--cream);
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
padding: 8px 0 10px;
|
||||
outline: none;
|
||||
transition: border-color 0.25s ease;
|
||||
}
|
||||
.field input:focus {
|
||||
border-color: var(--yellow);
|
||||
}
|
||||
.field input::placeholder { color: rgba(216, 203, 176, 0.45); font-style: italic; }
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
margin-top: 18px;
|
||||
background: var(--yellow);
|
||||
color: var(--navy-deep);
|
||||
border: 0;
|
||||
padding: 16px 24px;
|
||||
font-family: 'Marcellus', serif;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.34em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: background 0.25s ease, transform 0.2s ease, box-shadow 0.25s ease;
|
||||
box-shadow: 0 8px 18px -4px rgba(240,208,0,0.35);
|
||||
}
|
||||
.btn:hover {
|
||||
background: var(--cream);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 24px -4px rgba(255,240,200,0.4);
|
||||
}
|
||||
|
||||
.register-link {
|
||||
margin-top: 22px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-style: italic;
|
||||
color: var(--cream-dim);
|
||||
}
|
||||
.register-link a {
|
||||
color: var(--yellow);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(240,208,0,0.4);
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.register-link a:hover { border-bottom-color: var(--yellow); }
|
||||
|
||||
/* bottom stamp */
|
||||
.footstamp {
|
||||
position: fixed;
|
||||
bottom: 28px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
font-family: 'Marcellus', serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.4em;
|
||||
color: var(--cream);
|
||||
opacity: 0;
|
||||
text-transform: uppercase;
|
||||
z-index: 10;
|
||||
animation: tag-in 1.4s 2.6s ease forwards;
|
||||
}
|
||||
.footstamp .sep { margin: 0 10px; color: var(--yellow); opacity: 0.7; }
|
||||
|
||||
/* loader dots top-right */
|
||||
.loader {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
right: 32px;
|
||||
display: none; /* mute loader on login — keep stamps only */
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.stamp, .corner { font-size: 10px; top: 20px; }
|
||||
.stamp { left: 20px; letter-spacing: 0.32em; }
|
||||
.corner { right: 20px; }
|
||||
.panel { padding: 28px 24px 24px; }
|
||||
.footstamp { bottom: 16px; font-size: 10px; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.backdrop, .wordmark .letter, .tagline, .underline, .panel, .stamp, .corner, .footstamp {
|
||||
animation: none !important; opacity: 1 !important; transform: none !important; width: auto !important;
|
||||
}
|
||||
.underline { width: 180px !important; }
|
||||
.backdrop { opacity: 0.42; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="backdrop"></div>
|
||||
<div class="vignette"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<div class="stamp">WeVisto <span class="yellow">·</span> Plate I</div>
|
||||
<div class="corner">Sign in</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="wordmark" aria-label="WeVisto">
|
||||
<span class="letter">W</span>
|
||||
<span class="letter">e</span>
|
||||
<span class="letter v">V</span>
|
||||
<span class="letter">i</span>
|
||||
<span class="letter">s</span>
|
||||
<span class="letter">t</span>
|
||||
<span class="letter">o</span>
|
||||
</div>
|
||||
<div class="underline"></div>
|
||||
<div class="tagline">Photographs, gifted. Quietly. Forever.</div>
|
||||
|
||||
<form class="panel" method="post" action="#" novalidate>
|
||||
<h1>Welcome back.</h1>
|
||||
<p class="greeting">— sign in to your account</p>
|
||||
|
||||
<div class="field">
|
||||
<label for="inputEmail">Email address</label>
|
||||
<input type="email" id="inputEmail" name="_username" autocomplete="email" placeholder="you@somewhere.com" autofocus>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="inputPassword">Password</label>
|
||||
<input type="password" id="inputPassword" name="_password" autocomplete="current-password" placeholder="••••••••">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">Sign in</button>
|
||||
|
||||
<p class="register-link">Don't have an account? <a href="/register">Create one</a></p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="footstamp">Edizione I <span class="sep">·</span> MMXXVI <span class="sep">·</span> A frame, by hand</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,183 @@
|
||||
/* Frosted-glass chrome — same vocabulary as the login.
|
||||
Every surface that holds content is a glass card over the harbor backdrop. */
|
||||
|
||||
/* ─── Top app bar — translucent dark band ────────────────────────────── */
|
||||
.app-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 40;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(18px);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(18px);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
padding: 12px 18px;
|
||||
padding-top: calc(12px + env(safe-area-inset-top));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.app-bar__mark {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.app-bar__mark img { width: 100%; height: 100%; display: block; }
|
||||
.app-bar__title-group { display: flex; flex-direction: column; }
|
||||
.app-bar__wordmark {
|
||||
font: 400 var(--text-lg)/1 var(--font-display);
|
||||
letter-spacing: 0.005em;
|
||||
color: var(--text);
|
||||
}
|
||||
.app-bar__wordmark .v { color: var(--brand-yellow); }
|
||||
.app-bar__sub {
|
||||
font: italic 400 13px/1 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.02em;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.app-bar__spacer { flex: 1; }
|
||||
.app-bar__icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
.app-bar__icon:hover { background: var(--glass); color: var(--text); }
|
||||
.app-bar__icon svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
||||
|
||||
/* ─── Glass surface mixin ──────────────────────────────────────────────── */
|
||||
.glass {
|
||||
background: var(--glass);
|
||||
backdrop-filter: saturate(160%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(160%) blur(20px);
|
||||
border: 1px solid var(--glass-bord);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.06) inset,
|
||||
0 20px 40px -16px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* ─── Bottom nav — translucent dark band ─────────────────────────────── */
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(20px);
|
||||
border-top: 1px solid var(--glass-bord);
|
||||
display: flex;
|
||||
z-index: 50;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.bottom-nav__tab {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
gap: 2px; height: 64px; color: var(--text-muted);
|
||||
text-decoration: none; min-height: 44px;
|
||||
transition: color var(--duration-fast);
|
||||
}
|
||||
.bottom-nav__tab.active {
|
||||
color: var(--brand-yellow);
|
||||
}
|
||||
.bottom-nav__tab.active .label { color: var(--text); }
|
||||
.bottom-nav__tab svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; }
|
||||
.bottom-nav__tab .label {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.main-scroll {
|
||||
padding-bottom: calc(64px + env(safe-area-inset-bottom) + 32px);
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
/* ─── Brand signature ─────────────────────────────────────────────────── */
|
||||
.signature {
|
||||
margin-top: 32px;
|
||||
padding: 32px 0 16px;
|
||||
border-top: 1px solid var(--glass-bord);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.signature__mark {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.signature__mark img { width: 100%; height: 100%; display: block; }
|
||||
.signature__text {
|
||||
font: 400 14px/1.2 var(--font-display);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.signature__text .v-mark { color: var(--brand-yellow); }
|
||||
.signature__version {
|
||||
font: 400 italic 14px/1.3 var(--font-accent);
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ─── Theme switcher (mockup only) ─────────────────────────────────────── */
|
||||
.theme-switcher {
|
||||
position: fixed;
|
||||
bottom: 84px;
|
||||
right: 16px;
|
||||
z-index: 60;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
border: 1px solid var(--glass-bord);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 12px 24px -8px rgba(0,0,0,0.5);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.theme-switcher__label {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.24em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.theme-switcher__chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.theme-switcher__chip {
|
||||
width: 26px; height: 26px;
|
||||
border: 2px solid var(--glass-bord);
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: transform var(--duration-fast);
|
||||
}
|
||||
.theme-switcher__chip:hover { transform: scale(1.1); }
|
||||
.theme-switcher__chip.active { box-shadow: 0 0 0 2px var(--brand-yellow); }
|
||||
/* each chip shows that theme's accent over its bg-base */
|
||||
.theme-switcher__chip[data-theme="ocean-dusk"] { background: linear-gradient(135deg, #06121f 50%, #4e9fc8 50%); }
|
||||
.theme-switcher__chip[data-theme="amber-dusk"] { background: linear-gradient(135deg, #1a0d05 50%, #e89048 50%); }
|
||||
.theme-switcher__chip[data-theme="sage-dusk"] { background: linear-gradient(135deg, #081208 50%, #88c068 50%); }
|
||||
.theme-switcher__chip[data-theme="rose-dusk"] { background: linear-gradient(135deg, #1a060f 50%, #d878a0 50%); }
|
||||
.theme-switcher__chip[data-theme="mauve-dusk"] { background: linear-gradient(135deg, #100618 50%, #b890d8 50%); }
|
||||
.theme-switcher__chip[data-theme="honey-dusk"] { background: linear-gradient(135deg, #18120a 50%, #e8c050 50%); }
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">User-pref theme</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="warm-craft" title="warm-craft"></button>
|
||||
<button class="theme-switcher__chip" data-theme="ocean-dusk" title="ocean-dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-cream" title="sage-cream"></button>
|
||||
<button class="theme-switcher__chip" data-theme="playful-pop" title="playful-pop"></button>
|
||||
<button class="theme-switcher__chip" data-theme="dusty-mauve" title="dusty-mauve"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-slate" title="honey-slate"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Atmospheric design tokens — themes as dusks layered over the harbor photo.
|
||||
The whole app shares the login's surface vocabulary. */
|
||||
|
||||
:root {
|
||||
/* Type — same recipe as login */
|
||||
--font-family: 'Nunito', system-ui, sans-serif;
|
||||
--font-display: 'Marcellus', Georgia, serif;
|
||||
--font-accent: 'Cormorant Garamond', Georgia, serif;
|
||||
--font-mono: 'DM Mono', ui-monospace, monospace;
|
||||
|
||||
--text-xs: 11px; --text-sm: 13px; --text-base: 15px; --text-md: 17px;
|
||||
--text-lg: 20px; --text-xl: 24px; --text-2xl: 28px; --text-3xl: 36px;
|
||||
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
|
||||
--space-5: 20px; --space-6: 24px; --space-8: 32px;
|
||||
--radius-sm: 6px; --radius-md: 12px; --radius-lg: 20px; --radius-full: 9999px;
|
||||
--duration-fast: 150ms; --duration-base: 250ms;
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
|
||||
/* Brand layer — fixed regardless of theme. */
|
||||
--brand-yellow: #f0d000;
|
||||
--brand-yellow-soft: #c4a700;
|
||||
|
||||
/* Editorial recipes */
|
||||
--type-display-xl: 400 var(--text-3xl)/1.05 var(--font-display);
|
||||
--type-display-lg: 400 var(--text-2xl)/1.1 var(--font-display);
|
||||
--type-display-md: 400 var(--text-xl)/1.15 var(--font-display);
|
||||
--type-accent-md: 400 italic var(--text-md)/1.4 var(--font-accent);
|
||||
--type-label: 700 var(--text-xs)/1 var(--font-mono);
|
||||
}
|
||||
|
||||
/* ─── DUSKS ──────────────────────────────────────────────────────────────
|
||||
Each dusk is:
|
||||
--bg-base : the deep base color behind everything
|
||||
--bg-tint : an rgba multiplied over the harbor photo (the "filter")
|
||||
--glass : rgba background of cards (frosted, sits on photo)
|
||||
--glass-2 : a more opaque variant for nav strips
|
||||
--glass-bord : hairline border on glass surfaces
|
||||
--accent : theme accent (was --color-primary) — CTAs that aren't brand
|
||||
--accent-fg : text on accent
|
||||
--text : main text on glass (always light)
|
||||
--text-muted : muted text on glass
|
||||
|
||||
The yellow V brand constant survives across all dusks.
|
||||
*/
|
||||
|
||||
/* OCEAN DUSK — default, closest to login */
|
||||
[data-theme="ocean-dusk"], :root {
|
||||
--bg-base: #06121f;
|
||||
--bg-tint: rgba(8, 22, 38, 0.45);
|
||||
--glass: rgba(10, 28, 48, 0.55);
|
||||
--glass-2: rgba(8, 22, 38, 0.72);
|
||||
--glass-bord: rgba(180, 210, 235, 0.18);
|
||||
--accent: #4e9fc8;
|
||||
--accent-fg: #06121f;
|
||||
--text: #f4eed8;
|
||||
--text-muted: #b8c8d8;
|
||||
}
|
||||
|
||||
/* AMBER DUSK — successor to warm-craft */
|
||||
[data-theme="amber-dusk"] {
|
||||
--bg-base: #1a0d05;
|
||||
--bg-tint: rgba(60, 25, 8, 0.45);
|
||||
--glass: rgba(50, 22, 8, 0.55);
|
||||
--glass-2: rgba(35, 14, 5, 0.72);
|
||||
--glass-bord: rgba(230, 180, 130, 0.2);
|
||||
--accent: #e89048;
|
||||
--accent-fg: #1a0d05;
|
||||
--text: #faecd0;
|
||||
--text-muted: #d8b890;
|
||||
}
|
||||
|
||||
/* SAGE DUSK — successor to sage-cream */
|
||||
[data-theme="sage-dusk"] {
|
||||
--bg-base: #081208;
|
||||
--bg-tint: rgba(20, 40, 22, 0.45);
|
||||
--glass: rgba(18, 38, 22, 0.55);
|
||||
--glass-2: rgba(12, 26, 14, 0.72);
|
||||
--glass-bord: rgba(180, 220, 180, 0.18);
|
||||
--accent: #88c068;
|
||||
--accent-fg: #081208;
|
||||
--text: #ecf3e0;
|
||||
--text-muted: #a8c0a0;
|
||||
}
|
||||
|
||||
/* ROSE DUSK — warmer, classier replacement for playful-pop */
|
||||
[data-theme="rose-dusk"] {
|
||||
--bg-base: #1a060f;
|
||||
--bg-tint: rgba(56, 16, 38, 0.45);
|
||||
--glass: rgba(48, 14, 36, 0.55);
|
||||
--glass-2: rgba(32, 8, 22, 0.72);
|
||||
--glass-bord: rgba(230, 180, 200, 0.2);
|
||||
--accent: #d878a0;
|
||||
--accent-fg: #1a060f;
|
||||
--text: #f8e8ec;
|
||||
--text-muted: #c898ac;
|
||||
}
|
||||
|
||||
/* MAUVE DUSK — replaces dusty-mauve */
|
||||
[data-theme="mauve-dusk"] {
|
||||
--bg-base: #100618;
|
||||
--bg-tint: rgba(40, 18, 56, 0.45);
|
||||
--glass: rgba(36, 14, 50, 0.55);
|
||||
--glass-2: rgba(24, 10, 34, 0.72);
|
||||
--glass-bord: rgba(210, 190, 230, 0.18);
|
||||
--accent: #b890d8;
|
||||
--accent-fg: #100618;
|
||||
--text: #f0e8f8;
|
||||
--text-muted: #b8a8c8;
|
||||
}
|
||||
|
||||
/* HONEY DUSK — replaces honey-slate */
|
||||
[data-theme="honey-dusk"] {
|
||||
--bg-base: #18120a;
|
||||
--bg-tint: rgba(48, 36, 14, 0.45);
|
||||
--glass: rgba(42, 32, 12, 0.55);
|
||||
--glass-2: rgba(28, 22, 8, 0.72);
|
||||
--glass-bord: rgba(232, 200, 130, 0.22);
|
||||
--accent: #e8c050;
|
||||
--accent-fg: #18120a;
|
||||
--text: #faf0d8;
|
||||
--text-muted: #c8b888;
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: var(--bg-base); color: var(--text); }
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
line-height: 1.5;
|
||||
min-height: 100dvh;
|
||||
position: relative;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ─── PERMANENT ATMOSPHERIC BACKDROP ──────────────────────────────────── */
|
||||
/* harbor photo fixed under everything */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: url('../assets/harbor.jpg') center/cover no-repeat;
|
||||
filter: brightness(0.6) saturate(0.85);
|
||||
z-index: -3;
|
||||
}
|
||||
/* theme tint multiplied over photo */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--bg-tint);
|
||||
mix-blend-mode: multiply;
|
||||
z-index: -2;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* global vignette + film grain via one extra layer */
|
||||
.atmosphere {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 70% at 50% 45%, transparent 0%, rgba(0,0,0,0.35) 70%, rgba(0,0,0,0.65) 100%),
|
||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.1 0'/></filter><rect width='320' height='320' filter='url(%23n)'/></svg>");
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Home — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
/* ─── hero — editorial moment, sits directly on the photo ──────────── */
|
||||
.hero {
|
||||
padding: var(--space-6) var(--space-5) var(--space-5);
|
||||
position: relative;
|
||||
}
|
||||
.hero__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-3);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.hero__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.hero__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
.hero__greeting {
|
||||
font: var(--type-display-lg);
|
||||
color: var(--text);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.hero__greeting em {
|
||||
font-family: var(--font-accent);
|
||||
font-style: italic;
|
||||
color: var(--brand-yellow);
|
||||
}
|
||||
.hero__sub {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
max-width: 38ch;
|
||||
}
|
||||
.hero__sub strong { color: var(--text); font-style: normal; font-family: var(--font-display); font-weight: 400; }
|
||||
.hero__rule {
|
||||
margin-top: var(--space-4);
|
||||
width: 80px; height: 1px;
|
||||
background: linear-gradient(90deg, var(--brand-yellow), transparent);
|
||||
}
|
||||
|
||||
/* ─── frame card — frosted glass, the user's photo is the inner content ── */
|
||||
.frame-card {
|
||||
margin: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
/* uses .glass class for the frost effect */
|
||||
}
|
||||
.frame-card__hero {
|
||||
aspect-ratio: 4/3;
|
||||
background-image: url('../assets/harbor.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
/* the user's frame photo — keep at full saturation (it's user content, not brand backdrop) */
|
||||
}
|
||||
.frame-card__status {
|
||||
position: absolute;
|
||||
top: 14px; left: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(8, 18, 30, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 6px 12px;
|
||||
border-radius: var(--radius-full);
|
||||
font: var(--type-label);
|
||||
color: var(--text);
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.frame-card__status .dot {
|
||||
width: 7px; height: 7px;
|
||||
background: #6cd498;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 3px rgba(108, 212, 152, 0.25);
|
||||
}
|
||||
.frame-card__plate {
|
||||
position: absolute;
|
||||
bottom: 14px; left: 14px;
|
||||
font: var(--type-label);
|
||||
background: rgba(8, 18, 30, 0.7);
|
||||
color: var(--text);
|
||||
padding: 5px 10px;
|
||||
letter-spacing: 0.28em;
|
||||
text-transform: uppercase;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.frame-card__body { padding: var(--space-5) var(--space-5) var(--space-4); }
|
||||
.frame-card__name {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.frame-card__meta {
|
||||
font: italic 400 15px/1.4 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.frame-card__meta .sep { margin: 0 8px; opacity: 0.4; }
|
||||
.frame-card__schedule {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid var(--glass-bord);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-3);
|
||||
font-size: var(--text-sm);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.frame-card__schedule svg {
|
||||
width: 18px; height: 18px;
|
||||
stroke: var(--brand-yellow);
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.frame-card__schedule strong { font-family: var(--font-display); font-size: var(--text-base); font-weight: 400; color: var(--text); display: block; margin-bottom: 2px; }
|
||||
.frame-card__schedule span { color: var(--text-muted); }
|
||||
.frame-card__actions { display: flex; gap: var(--space-3); }
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 14px 20px;
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--glass-bord);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast);
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
||||
}
|
||||
.btn:hover { background: rgba(255, 255, 255, 0.12); }
|
||||
.btn--primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
border-color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
.btn--primary:hover { filter: brightness(1.08); }
|
||||
.btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
|
||||
|
||||
/* ─── upcoming photos strip — quiet teaser of what's queued ─────────── */
|
||||
.upcoming {
|
||||
margin: var(--space-5);
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.upcoming__head {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.upcoming__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
}
|
||||
.upcoming__hint {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.upcoming__row {
|
||||
display: flex; gap: var(--space-3); overflow-x: auto; padding-bottom: 4px;
|
||||
}
|
||||
.upcoming__row::-webkit-scrollbar { display: none; }
|
||||
.upcoming__row { scrollbar-width: none; }
|
||||
.thumb {
|
||||
flex-shrink: 0;
|
||||
width: 92px; height: 92px;
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
background: rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
border: 1px solid var(--glass-bord);
|
||||
}
|
||||
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.thumb .when {
|
||||
position: absolute;
|
||||
bottom: 4px; left: 4px;
|
||||
font: var(--type-label);
|
||||
background: rgba(0,0,0,0.7);
|
||||
color: var(--text);
|
||||
padding: 2px 6px;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— Margaret's frame</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<section class="hero">
|
||||
<div class="hero__plate"><span class="roman">I.</span><span class="sep"></span><span>Plate · today</span></div>
|
||||
<h1 class="hero__greeting">Good morning, <em>Alice.</em></h1>
|
||||
<p class="hero__sub">Margaret's frame is in sync. The next photograph will arrive at <strong>7:00 AM</strong>, quietly, on its own.</p>
|
||||
<div class="hero__rule"></div>
|
||||
</section>
|
||||
|
||||
<article class="frame-card glass">
|
||||
<div class="frame-card__hero">
|
||||
<div class="frame-card__status">
|
||||
<span class="dot"></span><span>Synced 12 min ago</span>
|
||||
</div>
|
||||
<div class="frame-card__plate">Plate XII</div>
|
||||
</div>
|
||||
<div class="frame-card__body">
|
||||
<h2 class="frame-card__name">Margaret's frame</h2>
|
||||
<p class="frame-card__meta">7.3″ landscape <span class="sep">·</span> Camden, Maine <span class="sep">·</span> est. May 2026</p>
|
||||
|
||||
<div class="frame-card__schedule">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
<div>
|
||||
<strong>Next photograph at 7:00 AM</strong>
|
||||
<span>then again at 12:00 PM and 6:00 PM</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="frame-card__actions">
|
||||
<button class="btn btn--primary">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Add photograph
|
||||
</button>
|
||||
<button class="btn">
|
||||
<svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section class="upcoming glass">
|
||||
<div class="upcoming__head">
|
||||
<h3 class="upcoming__title">Up next</h3>
|
||||
<span class="upcoming__hint">— in the library queue</span>
|
||||
</div>
|
||||
<div class="upcoming__row">
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt=""><span class="when">7am</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: hue-rotate(40deg);"><span class="when">noon</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: hue-rotate(-30deg) saturate(1.2);"><span class="when">6pm</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: brightness(0.85);"><span class="when">tmrw</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: sepia(0.4);"><span class="when">+5</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab active" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,249 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Library — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
.library-empty {
|
||||
padding: 56px 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.library-empty__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-5);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.library-empty__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.library-empty__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
|
||||
.library-empty__halo { position: relative; margin-bottom: var(--space-5); }
|
||||
.library-empty__halo::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
width: 240px; height: 240px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: radial-gradient(circle, color-mix(in srgb, var(--brand-yellow) 25%, transparent), transparent 65%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
.library-empty__mark {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.18) inset,
|
||||
0 20px 40px -10px rgba(0,0,0,0.6),
|
||||
0 8px 20px -6px rgba(0,0,0,0.4);
|
||||
}
|
||||
.library-empty__mark img { width: 100%; height: 100%; display: block; }
|
||||
|
||||
.library-empty__title {
|
||||
font: var(--type-display-xl);
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-2);
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.library-empty__title em {
|
||||
font-family: var(--font-accent);
|
||||
font-style: italic;
|
||||
color: var(--brand-yellow);
|
||||
font-weight: 400;
|
||||
}
|
||||
.library-empty__sub {
|
||||
font: italic 400 18px/1.55 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
max-width: 36ch;
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
.library-empty__rule {
|
||||
width: 96px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
margin: 0 auto var(--space-6);
|
||||
}
|
||||
.library-empty__cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
padding: 14px 28px;
|
||||
border-radius: var(--radius-full);
|
||||
font-family: var(--font-family);
|
||||
font-weight: 700;
|
||||
font-size: var(--text-base);
|
||||
text-decoration: none;
|
||||
box-shadow: 0 8px 22px -4px color-mix(in srgb, var(--accent) 50%, transparent);
|
||||
transition: transform var(--duration-fast) var(--ease-out), filter var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
.library-empty__cta:hover { transform: translateY(-1px); filter: brightness(1.08); }
|
||||
.library-empty__cta svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; }
|
||||
.library-empty__or {
|
||||
margin-top: var(--space-5);
|
||||
font: italic 400 var(--text-md)/1.4 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.library-empty__or a {
|
||||
color: var(--text);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
text-decoration: none;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
/* steps card — frosted */
|
||||
.intro-steps {
|
||||
margin: 48px var(--space-5) 0;
|
||||
padding: var(--space-5) var(--space-5) var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
position: relative;
|
||||
}
|
||||
.intro-steps__rule {
|
||||
position: absolute;
|
||||
top: -1px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60px; height: 1px;
|
||||
background: var(--brand-yellow);
|
||||
}
|
||||
.intro-steps__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.intro-steps__title .v-mark { color: var(--brand-yellow); }
|
||||
.intro-steps__sub {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.intro-steps__list { display: flex; gap: var(--space-3); }
|
||||
.step { flex: 1; text-align: center; padding: var(--space-3); }
|
||||
.step__num {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-2xl);
|
||||
color: var(--brand-yellow);
|
||||
line-height: 1;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.step__label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
line-height: 1.4;
|
||||
}
|
||||
.step__label strong {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 400;
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text);
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.signature { margin: 64px var(--space-5) 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— the library</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Filter">
|
||||
<svg viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="library-empty">
|
||||
<div class="library-empty__plate"><span class="roman">○</span><span class="sep"></span><span>Accession no. 001</span></div>
|
||||
<div class="library-empty__halo">
|
||||
<div class="library-empty__mark"><img src="../assets/mark-photo.png" alt=""></div>
|
||||
</div>
|
||||
<h1 class="library-empty__title">A library, <em>waiting.</em></h1>
|
||||
<p class="library-empty__sub">Photographs you upload will rotate through Margaret's frame, one at a time. We'll hold them here until the frame is ready.</p>
|
||||
<div class="library-empty__rule"></div>
|
||||
<a class="library-empty__cta" href="#">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Upload the first photograph
|
||||
</a>
|
||||
<div class="library-empty__or">— or <a href="#">invite someone else</a> to send one</div>
|
||||
</div>
|
||||
|
||||
<div class="intro-steps glass">
|
||||
<div class="intro-steps__rule"></div>
|
||||
<div class="intro-steps__title">How <span class="v-mark">V</span>isto works, briefly</div>
|
||||
<div class="intro-steps__sub">— three steps, then forever</div>
|
||||
<div class="intro-steps__list">
|
||||
<div class="step"><div class="step__num">I.</div><div class="step__label"><strong>You upload</strong>family photos</div></div>
|
||||
<div class="step"><div class="step__num">II.</div><div class="step__label"><strong>We hold</strong>them in the library</div></div>
|
||||
<div class="step"><div class="step__num">III.</div><div class="step__label"><strong>Frame picks</strong>one each interval</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Edizione I · MMXXVI</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab active" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Settings — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
.page-title {
|
||||
padding: var(--space-6) var(--space-5) var(--space-3);
|
||||
text-align: center;
|
||||
}
|
||||
.page-title__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.page-title__plate .v-mark { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); margin: 0 8px; }
|
||||
.page-title__title {
|
||||
font: var(--type-display-lg);
|
||||
color: var(--text);
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.page-title__rule {
|
||||
width: 60px; height: 1px;
|
||||
margin: var(--space-3) auto 0;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
}
|
||||
|
||||
.section { margin: var(--space-6) var(--space-5) 0; }
|
||||
.section__head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
margin-bottom: var(--space-3);
|
||||
padding: 0 var(--space-2);
|
||||
}
|
||||
.section__roman {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-lg);
|
||||
color: var(--brand-yellow);
|
||||
line-height: 1;
|
||||
}
|
||||
.section__label {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.section__rule { flex: 1; height: 1px; background: var(--glass-bord); }
|
||||
|
||||
.list {
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-4) var(--space-5);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast);
|
||||
}
|
||||
.row:last-child { border-bottom: 0; }
|
||||
.row:hover { background: rgba(255,255,255,0.05); }
|
||||
.row__icon { width: 22px; height: 22px; flex-shrink: 0; color: var(--text-muted); }
|
||||
.row__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2; }
|
||||
.row__label {
|
||||
flex: 1;
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
.row__value {
|
||||
font: italic 400 14px/1.3 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.row__chevron { color: var(--text-muted); opacity: 0.6; }
|
||||
|
||||
/* DUSK PICKER */
|
||||
.theme-picker {
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
.theme-picker__head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.theme-picker__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
}
|
||||
.theme-picker__hint {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
|
||||
.theme-swatch {
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3);
|
||||
border: 1px solid var(--glass-bord);
|
||||
background: rgba(0,0,0,0.25);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: transform var(--duration-fast), box-shadow var(--duration-fast);
|
||||
font-family: inherit;
|
||||
color: var(--text);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.theme-swatch.active {
|
||||
border-color: var(--brand-yellow);
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand-yellow) 35%, transparent);
|
||||
}
|
||||
.theme-swatch:hover { transform: translateY(-2px); }
|
||||
/* a tinted preview of harbor inside each swatch */
|
||||
.theme-swatch__preview {
|
||||
aspect-ratio: 3/2;
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-2);
|
||||
background-image: url('../assets/harbor.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
}
|
||||
.theme-swatch__preview::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
.theme-swatch[data-pref="ocean-dusk"] .theme-swatch__preview::after { background: rgba(8, 22, 38, 0.55); }
|
||||
.theme-swatch[data-pref="amber-dusk"] .theme-swatch__preview::after { background: rgba(60, 25, 8, 0.55); }
|
||||
.theme-swatch[data-pref="sage-dusk"] .theme-swatch__preview::after { background: rgba(20, 40, 22, 0.55); }
|
||||
.theme-swatch[data-pref="rose-dusk"] .theme-swatch__preview::after { background: rgba(56, 16, 38, 0.55); }
|
||||
.theme-swatch[data-pref="mauve-dusk"] .theme-swatch__preview::after { background: rgba(40, 18, 56, 0.55); }
|
||||
.theme-swatch[data-pref="honey-dusk"] .theme-swatch__preview::after { background: rgba(48, 36, 14, 0.55); }
|
||||
.theme-swatch__name {
|
||||
font-family: var(--font-display);
|
||||
font-size: 13px;
|
||||
color: var(--text);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.theme-swatch__italic {
|
||||
font: italic 400 12px/1.2 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sign-out { margin: var(--space-6) var(--space-5); text-align: center; }
|
||||
.sign-out a {
|
||||
color: #e08070;
|
||||
font-family: var(--font-family);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
font-size: var(--text-base);
|
||||
padding: var(--space-4) var(--space-5);
|
||||
display: inline-block;
|
||||
}
|
||||
.sign-out a:hover { text-decoration: underline; }
|
||||
|
||||
.signature { margin: 0 var(--space-5); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— settings</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="page-title">
|
||||
<div class="page-title__plate">Settings <span class="v-mark">·</span> Plate i</div>
|
||||
<h1 class="page-title__title">A few quiet choices.</h1>
|
||||
<div class="page-title__rule"></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">I.</span><span class="section__label">Account</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/></svg></span>
|
||||
<span class="row__label">Alice Wexler</span>
|
||||
<span class="row__value">alice@example.com</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg></span>
|
||||
<span class="row__label">Notification email</span>
|
||||
<span class="row__value">daily digest</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">II.</span><span class="section__label">Appearance</span><span class="section__rule"></span></div>
|
||||
<div class="theme-picker glass">
|
||||
<div class="theme-picker__head">
|
||||
<div class="theme-picker__title">Dusk</div>
|
||||
<div class="theme-picker__hint">— pick a tint for the room</div>
|
||||
</div>
|
||||
<div class="theme-grid">
|
||||
<button class="theme-swatch active" data-pref="ocean-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Ocean dusk</div>
|
||||
<span class="theme-swatch__italic">— the harbor</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="amber-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Amber dusk</div>
|
||||
<span class="theme-swatch__italic">— the workshop</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="sage-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Sage dusk</div>
|
||||
<span class="theme-swatch__italic">— the garden</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="rose-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Rose dusk</div>
|
||||
<span class="theme-swatch__italic">— the parlor</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="mauve-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Mauve dusk</div>
|
||||
<span class="theme-swatch__italic">— the study</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="honey-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Honey dusk</div>
|
||||
<span class="theme-swatch__italic">— the alcove</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">III.</span><span class="section__label">Frames</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21,15 16,10 5,21"/></svg></span>
|
||||
<span class="row__label">Margaret's frame</span>
|
||||
<span class="row__value">7.3″ landscape</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></span>
|
||||
<span class="row__label">Add another frame</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">IV.</span><span class="section__label">Help</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>
|
||||
<span class="row__label">How WeVisto works</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg></span>
|
||||
<span class="row__label">Contact us</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sign-out"><a href="#">Sign out</a></div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Edizione I · MMXXVI · Camogli</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab active" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
const t = chip.dataset.theme;
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
document.querySelectorAll('.theme-swatch').forEach(s => s.classList.toggle('active', s.dataset.pref === t));
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.theme-swatch').forEach(sw => {
|
||||
sw.addEventListener('click', () => {
|
||||
const t = sw.dataset.pref;
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
document.querySelectorAll('.theme-swatch').forEach(s => s.classList.toggle('active', s === sw));
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c.dataset.theme === t));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
# WeVisto · favicons v2 + logo placements
|
||||
|
||||
2026-05-15 iteration following the atmospheric-redesign checkpoint.
|
||||
Two threads:
|
||||
|
||||
1. **Favicons rebuilt** — scrapped the previous A/B/C/D (roof, frame+horizon,
|
||||
skyline, wax seal) as generic invented icons. The new set reduces to the
|
||||
brand's own glyph: the yellow V from the wordmark, presented four ways.
|
||||
2. **The wordmark logo placed at hero scale** — three mockups giving the
|
||||
full `wordmark.svg` (harbor photo + "WeVisto") room to live in the PWA
|
||||
beyond the existing email + small-badge usage.
|
||||
|
||||
## Picked direction (favicons)
|
||||
|
||||
**3 · V-viewfinder.** The V cut out of navy with the harbor photo visible
|
||||
inside — *you are looking at a photograph framed by the V.* Strongest
|
||||
refinement is **3b** (`favicons/3b-viewfinder-buildings-*.png`) which tightens
|
||||
the inner photo crop onto Camogli's coloured row-houses for a more
|
||||
brand-specific small-size reading.
|
||||
|
||||
Runner-up: **4 · V-cropped** (`favicons/4-V-cropped-*.png`) — an actual
|
||||
tight crop of the published wordmark. Most "WeVisto in the image itself"
|
||||
of any direction.
|
||||
|
||||
## What's here
|
||||
|
||||
```
|
||||
favicons-and-logo-v2/
|
||||
├── index.html — entry point, shows everything
|
||||
├── favicons/
|
||||
│ ├── 1-V-pure-{16,32,64,180}.png — yellow V on navy, isolated
|
||||
│ ├── 2-V-horizon-{...}.png — V plus horizon + sun
|
||||
│ ├── 3-V-viewfinder-{...}.png — original V cut over full harbor
|
||||
│ ├── 3a-viewfinder-full-thin-{...}.png — refinement A (full harbor, thin frame)
|
||||
│ ├── 3b-viewfinder-buildings-{...}.png — recommended: Camogli row-houses
|
||||
│ ├── 3c-viewfinder-noframe-{...}.png — no yellow outline
|
||||
│ ├── 3d-viewfinder-thick-{...}.png — thick yellow frame
|
||||
│ ├── 4-V-cropped-{...}.png — cropped from actual wordmark
|
||||
│ └── 2-V-horizon.svg — SVG source for direction 2
|
||||
├── logo-moments/
|
||||
│ ├── splash.html — PWA cold-launch full-screen
|
||||
│ ├── library-with-logo.html — library hero w/ full wordmark
|
||||
│ └── about.html — settings → about page
|
||||
├── assets/ — harbor.jpg, wordmark.svg, mark images
|
||||
└── spa/ — shared atmospheric tokens + chrome
|
||||
(so logo-moments can render)
|
||||
```
|
||||
|
||||
## To view
|
||||
|
||||
```bash
|
||||
cd webApp/_design/favicons-and-logo-v2
|
||||
python3 -m http.server 8766 --bind 0.0.0.0
|
||||
# → open http://<host>:8766/
|
||||
```
|
||||
|
||||
## Open work
|
||||
|
||||
- Decide between **3b** (recommended) and **4** (also "promising").
|
||||
- Logo-moments not yet picked — Matt's review pending on which of the
|
||||
three placements (splash / library hero / about page) to wire up first.
|
||||
- Once selected, rasterize 3b at the standard PWA sizes (16/32/64 favicons,
|
||||
180 apple-touch-icon, 192/512 PWA manifest icons, 512 maskable variant
|
||||
with safe-zone padding) and ship into `webApp/frontend/public/icons/`
|
||||
replacing the current split-W-on-photo set.
|
||||
|
After Width: | Height: | Size: 264 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<clipPath id="wv-left"><rect x="0" y="0" width="32" height="64"/></clipPath>
|
||||
<clipPath id="wv-right"><rect x="32" y="0" width="32" height="64"/></clipPath>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="12" fill="#1a3a5c"/>
|
||||
<g font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
|
||||
font-weight="900" font-size="56" text-anchor="middle">
|
||||
<text x="32" y="50" fill="#fafafa" clip-path="url(#wv-left)">W</text>
|
||||
<text x="32" y="50" fill="#f0d000" clip-path="url(#wv-right)">W</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 365 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 377 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 857 B |
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" fill="#0e2740"/>
|
||||
|
||||
<!-- full V — both strokes clearly meeting at bottom -->
|
||||
<path d="M 14 14 L 32 50 L 50 14"
|
||||
stroke="#f0d000" stroke-width="9"
|
||||
stroke-linejoin="round" stroke-linecap="round" fill="none"/>
|
||||
|
||||
<!-- horizon line + sun below the V, like the sea visible past the wordmark -->
|
||||
<line x1="6" y1="58" x2="58" y2="58"
|
||||
stroke="#f0d000" stroke-width="1.4" opacity="0.5"/>
|
||||
<circle cx="50" cy="58" r="2.4" fill="#f0d000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 558 B |
|
After Width: | Height: | Size: 647 B |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 647 B |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 648 B |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 491 B |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 675 B |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 795 B |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,301 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>WeVisto — favicons v2 + logo moments</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--cream: #fdf6ee; --cream-deep: #f5ead4;
|
||||
--ink: #2a1f15; --ink-soft: #6e6450;
|
||||
--navy: #0e2740; --yellow: #f0d000;
|
||||
--rule: rgba(42,31,21,0.15);
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: var(--cream); color: var(--ink); }
|
||||
body { font-family: 'Nunito', sans-serif; font-size: 16px; line-height: 1.55; padding: 48px 40px 96px; max-width: 1280px; margin: 0 auto; }
|
||||
|
||||
.top { display: grid; grid-template-columns: auto 1fr auto; align-items: end; gap: 24px; padding-bottom: 14px; border-bottom: 1px solid var(--rule); }
|
||||
.top .l, .top .r { font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.top .r { text-align: right; }
|
||||
.top .name { font-family: 'Marcellus', serif; font-size: 44px; color: var(--navy); text-align: center; letter-spacing: 0.01em; }
|
||||
.top .name .v { color: var(--yellow); }
|
||||
.subhead { margin-top: 12px; padding-bottom: 22px; border-bottom: 1px solid var(--rule); text-align: center; font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 21px; color: var(--ink); }
|
||||
|
||||
h2.section { margin: 56px 0 8px; font-family: 'Marcellus', serif; font-weight: 400; font-size: 30px; color: var(--navy); }
|
||||
h2.section .num { color: var(--yellow); margin-right: 8px; }
|
||||
.lede { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 19px; color: var(--ink); margin-bottom: 28px; max-width: 76ch; line-height: 1.5; }
|
||||
.lede em { color: var(--navy); }
|
||||
.lede strong { font-weight: 600; font-style: normal; color: var(--navy); }
|
||||
|
||||
/* favicon grid — 2x2, each shown at all four sizes + browser-tab + ios-tile */
|
||||
.fav-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 28px; margin-top: 32px; }
|
||||
.fav { background: #fff9ee; border: 1px solid var(--rule); padding: 24px; }
|
||||
.fav__head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; }
|
||||
.fav__num { font-family: 'Marcellus', serif; font-size: 28px; color: var(--yellow); line-height: 1; }
|
||||
.fav__tag { font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.fav h3 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 24px; color: var(--navy); margin-bottom: 6px; }
|
||||
.fav .desc { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 17px; color: var(--ink); margin-bottom: 18px; line-height: 1.45; }
|
||||
.fav .desc em { color: var(--navy); font-style: italic; }
|
||||
.fav .row {
|
||||
display: flex; align-items: flex-end; gap: 18px;
|
||||
padding: 14px 0; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.fav .pip { text-align: center; }
|
||||
.fav .pip img { display: block; image-rendering: pixelated; margin: 0 auto 6px; border-radius: 3px; }
|
||||
.fav .pip .cap { font-family: 'DM Mono', monospace; font-size: 9px; letter-spacing: 0.2em; color: var(--ink-soft); }
|
||||
.fav .ctx { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.fav .ctx .box { padding: 12px; background: var(--cream); border: 1px solid var(--rule); }
|
||||
.fav .ctx .box-label { font-family: 'DM Mono', monospace; font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 8px; }
|
||||
.tab-strip { background: #2a2a2a; border-radius: 4px 4px 0 0; padding: 4px 4px 0; display: inline-block; }
|
||||
.tab-strip .tab { display: inline-flex; align-items: center; gap: 6px; background: #3a3a3a; padding: 5px 10px 5px 8px; border-radius: 4px 4px 0 0; font-family: -apple-system, sans-serif; font-size: 11px; color: #ddd; }
|
||||
.tab-strip .tab img { width: 14px; height: 14px; }
|
||||
.ios-tile { width: 56px; height: 56px; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.25); }
|
||||
.ios-tile img { width: 100%; height: 100%; display: block; }
|
||||
|
||||
/* logo-moment cards */
|
||||
.stack { display: grid; gap: 32px; margin-top: 24px; grid-template-columns: repeat(3, 1fr); }
|
||||
.moment { background: #fff9ee; border: 1px solid var(--rule); overflow: hidden; }
|
||||
.moment .preview { position: relative; aspect-ratio: 9/16; background: #06121f; }
|
||||
.moment .preview iframe { border: 0; width: 250%; height: 250%; transform: scale(0.4); transform-origin: top left; display: block; }
|
||||
.moment .open { position: absolute; bottom: 12px; right: 12px; z-index: 5; font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase; color: #fff; background: rgba(7,23,42,0.75); padding: 8px 14px; text-decoration: none; backdrop-filter: blur(6px); }
|
||||
.moment .open:hover { background: var(--yellow); color: var(--navy); }
|
||||
.moment .body { padding: 22px 24px 24px; }
|
||||
.moment .body .roman { font-family: 'Marcellus', serif; font-size: 22px; color: var(--yellow); margin-bottom: 4px; }
|
||||
.moment .body h3 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 22px; color: var(--navy); margin-bottom: 6px; }
|
||||
.moment .body p { font-size: 15px; line-height: 1.5; color: var(--ink); }
|
||||
.moment .body p em { color: var(--navy); font-style: italic; }
|
||||
|
||||
.also { margin-top: 56px; padding: 16px 20px; background: rgba(240,208,0,0.18); border-left: 4px solid var(--yellow); font-style: italic; color: var(--ink); font-size: 15px; }
|
||||
.also a { color: var(--navy); border-bottom: 1px solid var(--navy); text-decoration: none; }
|
||||
|
||||
footer { margin-top: 64px; padding-top: 22px; border-top: 1px solid var(--rule); text-align: center; font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 14px; color: var(--ink-soft); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
body { padding: 32px 20px; }
|
||||
.fav-grid, .stack { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="top">
|
||||
<div class="l">v2 · 2026-05-15</div>
|
||||
<div class="name">We<span class="v">V</span>isto</div>
|
||||
<div class="r">Favicons rethought<br>+ logo placed</div>
|
||||
</div>
|
||||
<div class="subhead">— scrapping the previous favicon set; the brand's own V is the answer, not invented icons.</div>
|
||||
|
||||
<!-- FAVICONS — V2 -->
|
||||
<h2 class="section"><span class="num">I.</span>Favicons, rebuilt around the V — <span style="color: var(--navy); font-style: italic; font-family: 'Cormorant Garamond', serif;">V-viewfinder picked, refining</span></h2>
|
||||
<p class="lede">Direction 3 (V-viewfinder) is the chosen path — the V as a window onto the harbor. Four refinements of it below: <strong>3b is the strongest</strong> (tighter crop on Camogli's coloured row-houses + thin yellow frame). 3a, 3c, 3d shown for comparison.</p>
|
||||
|
||||
<style>
|
||||
.v3-variants { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-top: 24px; margin-bottom: 48px; }
|
||||
.v3-card { background: #fff9ee; border: 1px solid var(--rule); padding: 18px; }
|
||||
.v3-card.pick { background: #fffbe0; border-color: var(--yellow); box-shadow: 0 0 0 1px var(--yellow); }
|
||||
.v3-card .pick-tag { display: inline-block; font-family: 'DM Mono', monospace; font-size: 9px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--yellow); background: var(--navy); padding: 3px 8px; margin-bottom: 8px; }
|
||||
.v3-card h4 { font-family: 'Marcellus', serif; font-weight: 400; font-size: 18px; color: var(--navy); margin-bottom: 4px; }
|
||||
.v3-card .desc { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 14px; line-height: 1.4; color: var(--ink); margin-bottom: 12px; min-height: 56px; }
|
||||
.v3-card .row { display: flex; align-items: flex-end; gap: 10px; padding: 10px 0; border-top: 1px solid var(--rule); }
|
||||
.v3-card .row img { display: block; image-rendering: pixelated; border-radius: 2px; }
|
||||
.v3-card .row .cap { font-family: 'DM Mono', monospace; font-size: 8px; letter-spacing: 0.18em; color: var(--ink-soft); text-align: center; margin-top: 4px; }
|
||||
</style>
|
||||
|
||||
<div class="v3-variants">
|
||||
<div class="v3-card">
|
||||
<h4>3a · full harbor</h4>
|
||||
<div class="desc">Full harbor view inside the V. Detailed but busy at small sizes.</div>
|
||||
<div class="row">
|
||||
<div><img src="favicons/3a-viewfinder-full-thin-16.png" width="28" height="28"><div class="cap">16</div></div>
|
||||
<div><img src="favicons/3a-viewfinder-full-thin-32.png" width="44" height="44"><div class="cap">32</div></div>
|
||||
<div><img src="favicons/3a-viewfinder-full-thin-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div><img src="favicons/3a-viewfinder-full-thin-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="v3-card pick">
|
||||
<span class="pick-tag">Recommended</span>
|
||||
<h4>3b · Camogli row-houses</h4>
|
||||
<div class="desc">Tighter crop on the colored row-houses. Most brand-specific — <em>Camogli is the brand origin.</em></div>
|
||||
<div class="row">
|
||||
<div><img src="favicons/3b-viewfinder-buildings-16.png" width="28" height="28"><div class="cap">16</div></div>
|
||||
<div><img src="favicons/3b-viewfinder-buildings-32.png" width="44" height="44"><div class="cap">32</div></div>
|
||||
<div><img src="favicons/3b-viewfinder-buildings-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div><img src="favicons/3b-viewfinder-buildings-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="v3-card">
|
||||
<h4>3c · no frame</h4>
|
||||
<div class="desc">No yellow outline. Cleanest silhouette but <em>loses the brand yellow at small sizes.</em></div>
|
||||
<div class="row">
|
||||
<div><img src="favicons/3c-viewfinder-noframe-16.png" width="28" height="28"><div class="cap">16</div></div>
|
||||
<div><img src="favicons/3c-viewfinder-noframe-32.png" width="44" height="44"><div class="cap">32</div></div>
|
||||
<div><img src="favicons/3c-viewfinder-noframe-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div><img src="favicons/3c-viewfinder-noframe-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="v3-card">
|
||||
<h4>3d · thick frame</h4>
|
||||
<div class="desc">Heavier yellow border, more "picture frame" reading. Veers toward decorative.</div>
|
||||
<div class="row">
|
||||
<div><img src="favicons/3d-viewfinder-thick-16.png" width="28" height="28"><div class="cap">16</div></div>
|
||||
<div><img src="favicons/3d-viewfinder-thick-32.png" width="44" height="44"><div class="cap">32</div></div>
|
||||
<div><img src="favicons/3d-viewfinder-thick-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div><img src="favicons/3d-viewfinder-thick-180.png" width="90" height="90"><div class="cap">180</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="lede">Original first-round directions below for reference (1 V-pure, 2 V-horizon, 4 V-cropped).</p>
|
||||
|
||||
<div class="fav-grid">
|
||||
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__num">1.</span><span class="fav__tag">Brand · isolated</span></div>
|
||||
<h3>V-pure</h3>
|
||||
<div class="desc">The yellow V on solid navy, drawn confidently. <em>The brand's own glyph, alone in a room.</em> Reads at every size, no decoration.</div>
|
||||
<div class="row">
|
||||
<div class="pip"><img src="favicons/1-V-pure-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/1-V-pure-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/1-V-pure-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/1-V-pure-180.png" width="96" height="96"><div class="cap">180</div></div>
|
||||
</div>
|
||||
<div class="ctx">
|
||||
<div class="box">
|
||||
<div class="box-label">browser tab</div>
|
||||
<div class="tab-strip"><span class="tab"><img src="favicons/1-V-pure-32.png">WeVisto</span></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-label">iOS home screen</div>
|
||||
<div class="ios-tile"><img src="favicons/1-V-pure-180.png"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__num">2.</span><span class="fav__tag">Brand · with horizon</span></div>
|
||||
<h3>V-horizon</h3>
|
||||
<div class="desc">The V plus a hairline horizon and a single sun dot below — <em>the harbor is implied,</em> not depicted. Editorial echo of the rest of the brand.</div>
|
||||
<div class="row">
|
||||
<div class="pip"><img src="favicons/2-V-horizon-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/2-V-horizon-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/2-V-horizon-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/2-V-horizon-180.png" width="96" height="96"><div class="cap">180</div></div>
|
||||
</div>
|
||||
<div class="ctx">
|
||||
<div class="box">
|
||||
<div class="box-label">browser tab</div>
|
||||
<div class="tab-strip"><span class="tab"><img src="favicons/2-V-horizon-32.png">WeVisto</span></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-label">iOS home screen</div>
|
||||
<div class="ios-tile"><img src="favicons/2-V-horizon-180.png"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__num">3.</span><span class="fav__tag">Brand · viewfinder</span></div>
|
||||
<h3>V-viewfinder</h3>
|
||||
<div class="desc">The V cut out of navy, with the harbor photo visible inside — <em>you are looking at a photograph framed by the V.</em> Strong at 64+, silhouette holds at 16.</div>
|
||||
<div class="row">
|
||||
<div class="pip"><img src="favicons/3-V-viewfinder-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/3-V-viewfinder-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/3-V-viewfinder-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/3-V-viewfinder-180.png" width="96" height="96"><div class="cap">180</div></div>
|
||||
</div>
|
||||
<div class="ctx">
|
||||
<div class="box">
|
||||
<div class="box-label">browser tab</div>
|
||||
<div class="tab-strip"><span class="tab"><img src="favicons/3-V-viewfinder-32.png">WeVisto</span></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-label">iOS home screen</div>
|
||||
<div class="ios-tile"><img src="favicons/3-V-viewfinder-180.png"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fav">
|
||||
<div class="fav__head"><span class="fav__num">4.</span><span class="fav__tag">Brand · cropped</span></div>
|
||||
<h3>V-cropped (the wordmark itself)</h3>
|
||||
<div class="desc">A tight crop of the actual published wordmark. The favicon <em>is</em> a slice of the logo — V centered, "e" and "i" partially visible at large sizes. The most "WeVisto in the image."</div>
|
||||
<div class="row">
|
||||
<div class="pip"><img src="favicons/4-V-cropped-16.png" width="32" height="32"><div class="cap">16</div></div>
|
||||
<div class="pip"><img src="favicons/4-V-cropped-32.png" width="48" height="48"><div class="cap">32</div></div>
|
||||
<div class="pip"><img src="favicons/4-V-cropped-64.png" width="64" height="64"><div class="cap">64</div></div>
|
||||
<div class="pip"><img src="favicons/4-V-cropped-180.png" width="96" height="96"><div class="cap">180</div></div>
|
||||
</div>
|
||||
<div class="ctx">
|
||||
<div class="box">
|
||||
<div class="box-label">browser tab</div>
|
||||
<div class="tab-strip"><span class="tab"><img src="favicons/4-V-cropped-32.png">WeVisto</span></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-label">iOS home screen</div>
|
||||
<div class="ios-tile"><img src="favicons/4-V-cropped-180.png"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- LOGO MOMENTS -->
|
||||
<h2 class="section"><span class="num">II.</span>The wordmark logo, given room to live</h2>
|
||||
<p class="lede">Currently the full <em>wordmark + harbor</em> logo only appears in email templates and as a small badge on Twig pages. These three mockups give it three new homes in the PWA at <strong>hero scale</strong> — each one a moment where the brand earns the room.</p>
|
||||
|
||||
<div class="stack">
|
||||
|
||||
<div class="moment">
|
||||
<div class="preview">
|
||||
<iframe src="logo-moments/splash.html" loading="lazy"></iframe>
|
||||
<a class="open" href="logo-moments/splash.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">II·a</div>
|
||||
<h3>PWA cold-launch splash</h3>
|
||||
<p>Shown for ~1.5s on app cold-launch. Wordmark logo at ~520px, Ken-Burns harbor backdrop, three-dot loader. Then it fades to <em>HomeView.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="moment">
|
||||
<div class="preview">
|
||||
<iframe src="logo-moments/library-with-logo.html" loading="lazy"></iframe>
|
||||
<a class="open" href="logo-moments/library-with-logo.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">II·b</div>
|
||||
<h3>Library hero (revised)</h3>
|
||||
<p>The empty state's 112px mark replaced by the <em>full wordmark logo</em> at 320px. Same headline / sub / CTA below. The library page becomes a brand moment.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="moment">
|
||||
<div class="preview">
|
||||
<iframe src="logo-moments/about.html" loading="lazy"></iframe>
|
||||
<a class="open" href="logo-moments/about.html" target="_blank">Open ⟶</a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="roman">II·c</div>
|
||||
<h3>About · the brand story</h3>
|
||||
<p>A settings → about page with the wordmark logo as page hero, a two-section editorial about the frame and the V, then a credits card. <em>The place where the brand explains itself.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="also">Saved iteration: this is in <code>/tmp/wevisto-mockups/v2/</code> for now. Approve a direction (or set of directions) and I'll save it into the repo alongside <a href="../">the previous checkpoint</a>.</div>
|
||||
|
||||
<footer>
|
||||
Open each tile to interact at full scale.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,173 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>About — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../spa/_tokens.css">
|
||||
<link rel="stylesheet" href="../spa/_chrome.css">
|
||||
<style>
|
||||
.about-hero {
|
||||
padding: var(--space-5) var(--space-5) var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.about-hero__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-4);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.about-hero__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.about-hero__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
/* Wordmark logo as the page hero */
|
||||
.about-hero__logo {
|
||||
width: min(70vw, 280px);
|
||||
aspect-ratio: 1;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-5);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.18) inset,
|
||||
0 22px 44px -12px rgba(0,0,0,0.6);
|
||||
}
|
||||
.about-hero__logo img { width: 100%; height: 100%; display: block; }
|
||||
.about-hero__title {
|
||||
font: var(--type-display-lg);
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.about-hero__sub {
|
||||
font: italic 400 17px/1.55 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
max-width: 38ch;
|
||||
}
|
||||
.about-hero__rule {
|
||||
width: 80px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
margin-top: var(--space-5);
|
||||
}
|
||||
|
||||
.story {
|
||||
margin: var(--space-6) var(--space-5);
|
||||
padding: var(--space-5) var(--space-5) var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.story h2 {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-3);
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
}
|
||||
.story h2 .roman {
|
||||
font-family: var(--font-display);
|
||||
color: var(--brand-yellow);
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
.story p {
|
||||
font-size: var(--text-base);
|
||||
line-height: 1.65;
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
.story p em { font-family: var(--font-accent); font-style: italic; color: var(--brand-yellow); }
|
||||
.story p strong { font-family: var(--font-display); font-weight: 400; color: var(--text); }
|
||||
|
||||
/* the credit list as a glass card */
|
||||
.credits {
|
||||
margin: var(--space-6) var(--space-5);
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.credits h3 {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.credits dl { display: grid; grid-template-columns: auto 1fr; gap: var(--space-3) var(--space-5); }
|
||||
.credits dt {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--brand-yellow);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.credits dd { font-family: var(--font-accent); font-style: italic; font-size: var(--text-base); color: var(--text); }
|
||||
|
||||
.signature { margin: var(--space-6) var(--space-5) 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— about</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Close">
|
||||
<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="about-hero">
|
||||
<div class="about-hero__plate"><span class="roman">○</span><span class="sep"></span><span>Edizione I</span></div>
|
||||
<div class="about-hero__logo">
|
||||
<img src="../assets/wordmark.svg" alt="WeVisto">
|
||||
</div>
|
||||
<h1 class="about-hero__title">A frame, gifted.</h1>
|
||||
<p class="about-hero__sub">Handmade e-ink picture frames, given to the people who keep our photographs.</p>
|
||||
<div class="about-hero__rule"></div>
|
||||
</div>
|
||||
|
||||
<article class="story glass">
|
||||
<h2><span class="roman">I.</span>The thing itself</h2>
|
||||
<p>A WeVisto frame is built by hand in <strong>Camogli</strong>, on the Ligurian coast. The display uses paper-like ink — the same family of screens as a Kindle — so it has the <em>quiet of a photograph</em> rather than the glow of a phone.</p>
|
||||
<p>It connects to your wifi once. After that, nothing. It collects new photographs on its own and changes them as quietly as a clock.</p>
|
||||
|
||||
<h2><span class="roman">II.</span>The thing about the V</h2>
|
||||
<p>The yellow V is the only thing in the wordmark that isn't white. It's how we remember the people we make these for — <em>the recipient is the V,</em> we are the letters around them.</p>
|
||||
</article>
|
||||
|
||||
<div class="credits glass">
|
||||
<h3>Edition I · MMXXVI</h3>
|
||||
<dl>
|
||||
<dt>Made by</dt> <dd>Matt Edholm</dd>
|
||||
<dt>From</dt> <dd>Camogli, Liguria</dd>
|
||||
<dt>For</dt> <dd>Margaret, Alice, and the rest</dd>
|
||||
<dt>Version</dt> <dd>0.4 · pre-release</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Plate i · v 0.4</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab"><svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg><span class="label">Home</span></a>
|
||||
<a class="bottom-nav__tab"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg><span class="label">Library</span></a>
|
||||
<a class="bottom-nav__tab active"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="label">Settings</span></a>
|
||||
</nav>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,161 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Library — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../spa/_tokens.css">
|
||||
<link rel="stylesheet" href="../spa/_chrome.css">
|
||||
<style>
|
||||
/* This is the library empty state — but the FULL WORDMARK LOGO replaces the 112px mark */
|
||||
.library-empty {
|
||||
padding: 48px 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.library-empty__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-5);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.library-empty__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.library-empty__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
|
||||
/* THE LOGO MOMENT — the actual wordmark+harbor logo, hero scale */
|
||||
.library-empty__logo {
|
||||
width: min(78vw, 320px);
|
||||
aspect-ratio: 1;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-5);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.18) inset,
|
||||
0 24px 48px -12px rgba(0,0,0,0.65),
|
||||
0 10px 24px -8px rgba(0,0,0,0.4);
|
||||
position: relative;
|
||||
}
|
||||
.library-empty__logo img { width: 100%; height: 100%; display: block; }
|
||||
/* soft halo behind the logo */
|
||||
.library-empty__logo::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -40px;
|
||||
background: radial-gradient(circle, color-mix(in srgb, var(--brand-yellow) 22%, transparent), transparent 60%);
|
||||
border-radius: 50%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.library-empty__title {
|
||||
font: var(--type-display-xl);
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-2);
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.library-empty__title em {
|
||||
font-family: var(--font-accent);
|
||||
font-style: italic;
|
||||
color: var(--brand-yellow);
|
||||
font-weight: 400;
|
||||
}
|
||||
.library-empty__sub {
|
||||
font: italic 400 18px/1.55 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
max-width: 34ch;
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
.library-empty__rule {
|
||||
width: 96px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
margin: 0 auto var(--space-6);
|
||||
}
|
||||
.library-empty__cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
padding: 14px 28px;
|
||||
border-radius: var(--radius-full);
|
||||
font-family: var(--font-family);
|
||||
font-weight: 700;
|
||||
font-size: var(--text-base);
|
||||
text-decoration: none;
|
||||
box-shadow: 0 8px 22px -4px color-mix(in srgb, var(--accent) 50%, transparent);
|
||||
transition: transform var(--duration-fast), filter var(--duration-fast);
|
||||
}
|
||||
.library-empty__cta:hover { transform: translateY(-1px); filter: brightness(1.08); }
|
||||
.library-empty__cta svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; }
|
||||
.library-empty__or {
|
||||
margin-top: var(--space-5);
|
||||
font: italic 400 var(--text-md)/1.4 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.library-empty__or a {
|
||||
color: var(--text);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
text-decoration: none;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.signature { margin: 64px var(--space-5) 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— the library</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Filter">
|
||||
<svg viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="library-empty">
|
||||
<div class="library-empty__plate"><span class="roman">○</span><span class="sep"></span><span>Accession no. 001</span></div>
|
||||
<div class="library-empty__logo">
|
||||
<img src="../assets/wordmark.svg" alt="WeVisto">
|
||||
</div>
|
||||
<h1 class="library-empty__title">A library, <em>waiting.</em></h1>
|
||||
<p class="library-empty__sub">Photographs you upload will rotate through Margaret's frame, one at a time.</p>
|
||||
<div class="library-empty__rule"></div>
|
||||
<a class="library-empty__cta" href="#">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Upload the first photograph
|
||||
</a>
|
||||
<div class="library-empty__or">— or <a href="#">invite someone else</a> to send one</div>
|
||||
</div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Edizione I · MMXXVI</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab"><svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg><span class="label">Home</span></a>
|
||||
<a class="bottom-nav__tab active"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg><span class="label">Library</span></a>
|
||||
<a class="bottom-nav__tab"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="label">Settings</span></a>
|
||||
</nav>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>WeVisto — loading</title>
|
||||
<style>
|
||||
:root { --yellow: #f0d000; --cream: #faf3e4; --navy: #07172a; }
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { width: 100%; height: 100%; overflow: hidden; background: var(--navy); color: var(--cream); }
|
||||
body { position: relative; display: flex; align-items: center; justify-content: center; }
|
||||
|
||||
/* harbor backdrop, Ken-Burns */
|
||||
.bg {
|
||||
position: absolute; inset: -4%;
|
||||
background: url('../assets/harbor.jpg') center/cover no-repeat;
|
||||
filter: brightness(0.45) saturate(0.85);
|
||||
opacity: 0;
|
||||
transform: scale(1.04);
|
||||
animation:
|
||||
bd-fade 1.4s 0.1s cubic-bezier(.2,.7,.2,1) forwards,
|
||||
bd-zoom 26s 0.1s linear forwards;
|
||||
}
|
||||
@keyframes bd-fade { to { opacity: 0.5; } }
|
||||
@keyframes bd-zoom { from { transform: scale(1.04); } to { transform: scale(1.14) translate(-2%, -1%); } }
|
||||
|
||||
.vignette {
|
||||
position: absolute; inset: 0; pointer-events: none;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 65% at 50% 50%, transparent 0%, rgba(7,23,42,0.6) 65%, rgba(7,23,42,0.95) 100%);
|
||||
}
|
||||
.grain {
|
||||
position: absolute; inset: 0; pointer-events: none;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.16 0'/></filter><rect width='320' height='320' filter='url(%23n)'/></svg>");
|
||||
mix-blend-mode: overlay;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* THE LOGO — the full wordmark logo at hero scale */
|
||||
.logo {
|
||||
position: relative; z-index: 5;
|
||||
width: min(78vw, 520px);
|
||||
aspect-ratio: 1;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,240,200,0.15) inset,
|
||||
0 32px 64px -16px rgba(0,0,0,0.7),
|
||||
0 12px 28px -8px rgba(0,0,0,0.5);
|
||||
opacity: 0;
|
||||
transform: translateY(12px) scale(0.98);
|
||||
animation: logo-in 1.6s 0.4s cubic-bezier(.2,.7,.2,1) forwards;
|
||||
}
|
||||
@keyframes logo-in { to { opacity: 1; transform: translateY(0) scale(1); } }
|
||||
.logo img { width: 100%; height: 100%; display: block; }
|
||||
|
||||
/* loader pulse below — dim brand confirmation */
|
||||
.loader {
|
||||
position: absolute; bottom: 56px; left: 0; right: 0;
|
||||
text-align: center;
|
||||
z-index: 5;
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
opacity: 0;
|
||||
animation: tag-in 1.2s 1.8s ease forwards;
|
||||
}
|
||||
.loader .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--yellow); animation: pulse 1.4s infinite ease-in-out; }
|
||||
.loader .dot:nth-child(2) { animation-delay: 0.18s; }
|
||||
.loader .dot:nth-child(3) { animation-delay: 0.36s; }
|
||||
@keyframes pulse { 0%, 80%, 100% { opacity: 0.25; transform: scale(0.85); } 40% { opacity: 1; transform: scale(1.2); } }
|
||||
@keyframes tag-in { to { opacity: 0.85; transform: translateY(0); } }
|
||||
|
||||
.stamp {
|
||||
position: absolute; top: 32px; right: 32px;
|
||||
font-family: Georgia, serif;
|
||||
font-size: 11px; letter-spacing: 0.46em; color: var(--cream);
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
animation: tag-in 1.4s 2.0s ease forwards;
|
||||
}
|
||||
.stamp .y { color: var(--yellow); }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.bg, .logo, .loader, .stamp { animation: none !important; opacity: 1 !important; transform: none !important; }
|
||||
.bg { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="bg"></div>
|
||||
<div class="vignette"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<div class="stamp">Edizione I <span class="y">·</span> Plate i</div>
|
||||
|
||||
<div class="logo">
|
||||
<img src="../assets/wordmark.svg" alt="WeVisto">
|
||||
</div>
|
||||
|
||||
<div class="loader"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,183 @@
|
||||
/* Frosted-glass chrome — same vocabulary as the login.
|
||||
Every surface that holds content is a glass card over the harbor backdrop. */
|
||||
|
||||
/* ─── Top app bar — translucent dark band ────────────────────────────── */
|
||||
.app-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 40;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(18px);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(18px);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
padding: 12px 18px;
|
||||
padding-top: calc(12px + env(safe-area-inset-top));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.app-bar__mark {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.app-bar__mark img { width: 100%; height: 100%; display: block; }
|
||||
.app-bar__title-group { display: flex; flex-direction: column; }
|
||||
.app-bar__wordmark {
|
||||
font: 400 var(--text-lg)/1 var(--font-display);
|
||||
letter-spacing: 0.005em;
|
||||
color: var(--text);
|
||||
}
|
||||
.app-bar__wordmark .v { color: var(--brand-yellow); }
|
||||
.app-bar__sub {
|
||||
font: italic 400 13px/1 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.02em;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.app-bar__spacer { flex: 1; }
|
||||
.app-bar__icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
.app-bar__icon:hover { background: var(--glass); color: var(--text); }
|
||||
.app-bar__icon svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
||||
|
||||
/* ─── Glass surface mixin ──────────────────────────────────────────────── */
|
||||
.glass {
|
||||
background: var(--glass);
|
||||
backdrop-filter: saturate(160%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(160%) blur(20px);
|
||||
border: 1px solid var(--glass-bord);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.06) inset,
|
||||
0 20px 40px -16px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* ─── Bottom nav — translucent dark band ─────────────────────────────── */
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(20px);
|
||||
border-top: 1px solid var(--glass-bord);
|
||||
display: flex;
|
||||
z-index: 50;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.bottom-nav__tab {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
gap: 2px; height: 64px; color: var(--text-muted);
|
||||
text-decoration: none; min-height: 44px;
|
||||
transition: color var(--duration-fast);
|
||||
}
|
||||
.bottom-nav__tab.active {
|
||||
color: var(--brand-yellow);
|
||||
}
|
||||
.bottom-nav__tab.active .label { color: var(--text); }
|
||||
.bottom-nav__tab svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; }
|
||||
.bottom-nav__tab .label {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.main-scroll {
|
||||
padding-bottom: calc(64px + env(safe-area-inset-bottom) + 32px);
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
/* ─── Brand signature ─────────────────────────────────────────────────── */
|
||||
.signature {
|
||||
margin-top: 32px;
|
||||
padding: 32px 0 16px;
|
||||
border-top: 1px solid var(--glass-bord);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.signature__mark {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.signature__mark img { width: 100%; height: 100%; display: block; }
|
||||
.signature__text {
|
||||
font: 400 14px/1.2 var(--font-display);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.signature__text .v-mark { color: var(--brand-yellow); }
|
||||
.signature__version {
|
||||
font: 400 italic 14px/1.3 var(--font-accent);
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ─── Theme switcher (mockup only) ─────────────────────────────────────── */
|
||||
.theme-switcher {
|
||||
position: fixed;
|
||||
bottom: 84px;
|
||||
right: 16px;
|
||||
z-index: 60;
|
||||
background: var(--glass-2);
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
border: 1px solid var(--glass-bord);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 12px 24px -8px rgba(0,0,0,0.5);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.theme-switcher__label {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.24em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.theme-switcher__chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.theme-switcher__chip {
|
||||
width: 26px; height: 26px;
|
||||
border: 2px solid var(--glass-bord);
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: transform var(--duration-fast);
|
||||
}
|
||||
.theme-switcher__chip:hover { transform: scale(1.1); }
|
||||
.theme-switcher__chip.active { box-shadow: 0 0 0 2px var(--brand-yellow); }
|
||||
/* each chip shows that theme's accent over its bg-base */
|
||||
.theme-switcher__chip[data-theme="ocean-dusk"] { background: linear-gradient(135deg, #06121f 50%, #4e9fc8 50%); }
|
||||
.theme-switcher__chip[data-theme="amber-dusk"] { background: linear-gradient(135deg, #1a0d05 50%, #e89048 50%); }
|
||||
.theme-switcher__chip[data-theme="sage-dusk"] { background: linear-gradient(135deg, #081208 50%, #88c068 50%); }
|
||||
.theme-switcher__chip[data-theme="rose-dusk"] { background: linear-gradient(135deg, #1a060f 50%, #d878a0 50%); }
|
||||
.theme-switcher__chip[data-theme="mauve-dusk"] { background: linear-gradient(135deg, #100618 50%, #b890d8 50%); }
|
||||
.theme-switcher__chip[data-theme="honey-dusk"] { background: linear-gradient(135deg, #18120a 50%, #e8c050 50%); }
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">User-pref theme</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="warm-craft" title="warm-craft"></button>
|
||||
<button class="theme-switcher__chip" data-theme="ocean-dusk" title="ocean-dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-cream" title="sage-cream"></button>
|
||||
<button class="theme-switcher__chip" data-theme="playful-pop" title="playful-pop"></button>
|
||||
<button class="theme-switcher__chip" data-theme="dusty-mauve" title="dusty-mauve"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-slate" title="honey-slate"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Atmospheric design tokens — themes as dusks layered over the harbor photo.
|
||||
The whole app shares the login's surface vocabulary. */
|
||||
|
||||
:root {
|
||||
/* Type — same recipe as login */
|
||||
--font-family: 'Nunito', system-ui, sans-serif;
|
||||
--font-display: 'Marcellus', Georgia, serif;
|
||||
--font-accent: 'Cormorant Garamond', Georgia, serif;
|
||||
--font-mono: 'DM Mono', ui-monospace, monospace;
|
||||
|
||||
--text-xs: 11px; --text-sm: 13px; --text-base: 15px; --text-md: 17px;
|
||||
--text-lg: 20px; --text-xl: 24px; --text-2xl: 28px; --text-3xl: 36px;
|
||||
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
|
||||
--space-5: 20px; --space-6: 24px; --space-8: 32px;
|
||||
--radius-sm: 6px; --radius-md: 12px; --radius-lg: 20px; --radius-full: 9999px;
|
||||
--duration-fast: 150ms; --duration-base: 250ms;
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
|
||||
/* Brand layer — fixed regardless of theme. */
|
||||
--brand-yellow: #f0d000;
|
||||
--brand-yellow-soft: #c4a700;
|
||||
|
||||
/* Editorial recipes */
|
||||
--type-display-xl: 400 var(--text-3xl)/1.05 var(--font-display);
|
||||
--type-display-lg: 400 var(--text-2xl)/1.1 var(--font-display);
|
||||
--type-display-md: 400 var(--text-xl)/1.15 var(--font-display);
|
||||
--type-accent-md: 400 italic var(--text-md)/1.4 var(--font-accent);
|
||||
--type-label: 700 var(--text-xs)/1 var(--font-mono);
|
||||
}
|
||||
|
||||
/* ─── DUSKS ──────────────────────────────────────────────────────────────
|
||||
Each dusk is:
|
||||
--bg-base : the deep base color behind everything
|
||||
--bg-tint : an rgba multiplied over the harbor photo (the "filter")
|
||||
--glass : rgba background of cards (frosted, sits on photo)
|
||||
--glass-2 : a more opaque variant for nav strips
|
||||
--glass-bord : hairline border on glass surfaces
|
||||
--accent : theme accent (was --color-primary) — CTAs that aren't brand
|
||||
--accent-fg : text on accent
|
||||
--text : main text on glass (always light)
|
||||
--text-muted : muted text on glass
|
||||
|
||||
The yellow V brand constant survives across all dusks.
|
||||
*/
|
||||
|
||||
/* OCEAN DUSK — default, closest to login */
|
||||
[data-theme="ocean-dusk"], :root {
|
||||
--bg-base: #06121f;
|
||||
--bg-tint: rgba(8, 22, 38, 0.45);
|
||||
--glass: rgba(10, 28, 48, 0.55);
|
||||
--glass-2: rgba(8, 22, 38, 0.72);
|
||||
--glass-bord: rgba(180, 210, 235, 0.18);
|
||||
--accent: #4e9fc8;
|
||||
--accent-fg: #06121f;
|
||||
--text: #f4eed8;
|
||||
--text-muted: #b8c8d8;
|
||||
}
|
||||
|
||||
/* AMBER DUSK — successor to warm-craft */
|
||||
[data-theme="amber-dusk"] {
|
||||
--bg-base: #1a0d05;
|
||||
--bg-tint: rgba(60, 25, 8, 0.45);
|
||||
--glass: rgba(50, 22, 8, 0.55);
|
||||
--glass-2: rgba(35, 14, 5, 0.72);
|
||||
--glass-bord: rgba(230, 180, 130, 0.2);
|
||||
--accent: #e89048;
|
||||
--accent-fg: #1a0d05;
|
||||
--text: #faecd0;
|
||||
--text-muted: #d8b890;
|
||||
}
|
||||
|
||||
/* SAGE DUSK — successor to sage-cream */
|
||||
[data-theme="sage-dusk"] {
|
||||
--bg-base: #081208;
|
||||
--bg-tint: rgba(20, 40, 22, 0.45);
|
||||
--glass: rgba(18, 38, 22, 0.55);
|
||||
--glass-2: rgba(12, 26, 14, 0.72);
|
||||
--glass-bord: rgba(180, 220, 180, 0.18);
|
||||
--accent: #88c068;
|
||||
--accent-fg: #081208;
|
||||
--text: #ecf3e0;
|
||||
--text-muted: #a8c0a0;
|
||||
}
|
||||
|
||||
/* ROSE DUSK — warmer, classier replacement for playful-pop */
|
||||
[data-theme="rose-dusk"] {
|
||||
--bg-base: #1a060f;
|
||||
--bg-tint: rgba(56, 16, 38, 0.45);
|
||||
--glass: rgba(48, 14, 36, 0.55);
|
||||
--glass-2: rgba(32, 8, 22, 0.72);
|
||||
--glass-bord: rgba(230, 180, 200, 0.2);
|
||||
--accent: #d878a0;
|
||||
--accent-fg: #1a060f;
|
||||
--text: #f8e8ec;
|
||||
--text-muted: #c898ac;
|
||||
}
|
||||
|
||||
/* MAUVE DUSK — replaces dusty-mauve */
|
||||
[data-theme="mauve-dusk"] {
|
||||
--bg-base: #100618;
|
||||
--bg-tint: rgba(40, 18, 56, 0.45);
|
||||
--glass: rgba(36, 14, 50, 0.55);
|
||||
--glass-2: rgba(24, 10, 34, 0.72);
|
||||
--glass-bord: rgba(210, 190, 230, 0.18);
|
||||
--accent: #b890d8;
|
||||
--accent-fg: #100618;
|
||||
--text: #f0e8f8;
|
||||
--text-muted: #b8a8c8;
|
||||
}
|
||||
|
||||
/* HONEY DUSK — replaces honey-slate */
|
||||
[data-theme="honey-dusk"] {
|
||||
--bg-base: #18120a;
|
||||
--bg-tint: rgba(48, 36, 14, 0.45);
|
||||
--glass: rgba(42, 32, 12, 0.55);
|
||||
--glass-2: rgba(28, 22, 8, 0.72);
|
||||
--glass-bord: rgba(232, 200, 130, 0.22);
|
||||
--accent: #e8c050;
|
||||
--accent-fg: #18120a;
|
||||
--text: #faf0d8;
|
||||
--text-muted: #c8b888;
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: var(--bg-base); color: var(--text); }
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
line-height: 1.5;
|
||||
min-height: 100dvh;
|
||||
position: relative;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ─── PERMANENT ATMOSPHERIC BACKDROP ──────────────────────────────────── */
|
||||
/* harbor photo fixed under everything */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: url('../assets/harbor.jpg') center/cover no-repeat;
|
||||
filter: brightness(0.6) saturate(0.85);
|
||||
z-index: -3;
|
||||
}
|
||||
/* theme tint multiplied over photo */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--bg-tint);
|
||||
mix-blend-mode: multiply;
|
||||
z-index: -2;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* global vignette + film grain via one extra layer */
|
||||
.atmosphere {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 70% at 50% 45%, transparent 0%, rgba(0,0,0,0.35) 70%, rgba(0,0,0,0.65) 100%),
|
||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.1 0'/></filter><rect width='320' height='320' filter='url(%23n)'/></svg>");
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Home — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
/* ─── hero — editorial moment, sits directly on the photo ──────────── */
|
||||
.hero {
|
||||
padding: var(--space-6) var(--space-5) var(--space-5);
|
||||
position: relative;
|
||||
}
|
||||
.hero__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-3);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.hero__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.hero__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
.hero__greeting {
|
||||
font: var(--type-display-lg);
|
||||
color: var(--text);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.hero__greeting em {
|
||||
font-family: var(--font-accent);
|
||||
font-style: italic;
|
||||
color: var(--brand-yellow);
|
||||
}
|
||||
.hero__sub {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
max-width: 38ch;
|
||||
}
|
||||
.hero__sub strong { color: var(--text); font-style: normal; font-family: var(--font-display); font-weight: 400; }
|
||||
.hero__rule {
|
||||
margin-top: var(--space-4);
|
||||
width: 80px; height: 1px;
|
||||
background: linear-gradient(90deg, var(--brand-yellow), transparent);
|
||||
}
|
||||
|
||||
/* ─── frame card — frosted glass, the user's photo is the inner content ── */
|
||||
.frame-card {
|
||||
margin: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
/* uses .glass class for the frost effect */
|
||||
}
|
||||
.frame-card__hero {
|
||||
aspect-ratio: 4/3;
|
||||
background-image: url('../assets/harbor.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
/* the user's frame photo — keep at full saturation (it's user content, not brand backdrop) */
|
||||
}
|
||||
.frame-card__status {
|
||||
position: absolute;
|
||||
top: 14px; left: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(8, 18, 30, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 6px 12px;
|
||||
border-radius: var(--radius-full);
|
||||
font: var(--type-label);
|
||||
color: var(--text);
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.frame-card__status .dot {
|
||||
width: 7px; height: 7px;
|
||||
background: #6cd498;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 3px rgba(108, 212, 152, 0.25);
|
||||
}
|
||||
.frame-card__plate {
|
||||
position: absolute;
|
||||
bottom: 14px; left: 14px;
|
||||
font: var(--type-label);
|
||||
background: rgba(8, 18, 30, 0.7);
|
||||
color: var(--text);
|
||||
padding: 5px 10px;
|
||||
letter-spacing: 0.28em;
|
||||
text-transform: uppercase;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.frame-card__body { padding: var(--space-5) var(--space-5) var(--space-4); }
|
||||
.frame-card__name {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.frame-card__meta {
|
||||
font: italic 400 15px/1.4 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.frame-card__meta .sep { margin: 0 8px; opacity: 0.4; }
|
||||
.frame-card__schedule {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid var(--glass-bord);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-3);
|
||||
font-size: var(--text-sm);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.frame-card__schedule svg {
|
||||
width: 18px; height: 18px;
|
||||
stroke: var(--brand-yellow);
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.frame-card__schedule strong { font-family: var(--font-display); font-size: var(--text-base); font-weight: 400; color: var(--text); display: block; margin-bottom: 2px; }
|
||||
.frame-card__schedule span { color: var(--text-muted); }
|
||||
.frame-card__actions { display: flex; gap: var(--space-3); }
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 14px 20px;
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--glass-bord);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast);
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
||||
}
|
||||
.btn:hover { background: rgba(255, 255, 255, 0.12); }
|
||||
.btn--primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
border-color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
.btn--primary:hover { filter: brightness(1.08); }
|
||||
.btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
|
||||
|
||||
/* ─── upcoming photos strip — quiet teaser of what's queued ─────────── */
|
||||
.upcoming {
|
||||
margin: var(--space-5);
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.upcoming__head {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.upcoming__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
}
|
||||
.upcoming__hint {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.upcoming__row {
|
||||
display: flex; gap: var(--space-3); overflow-x: auto; padding-bottom: 4px;
|
||||
}
|
||||
.upcoming__row::-webkit-scrollbar { display: none; }
|
||||
.upcoming__row { scrollbar-width: none; }
|
||||
.thumb {
|
||||
flex-shrink: 0;
|
||||
width: 92px; height: 92px;
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
background: rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
border: 1px solid var(--glass-bord);
|
||||
}
|
||||
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.thumb .when {
|
||||
position: absolute;
|
||||
bottom: 4px; left: 4px;
|
||||
font: var(--type-label);
|
||||
background: rgba(0,0,0,0.7);
|
||||
color: var(--text);
|
||||
padding: 2px 6px;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— Margaret's frame</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<section class="hero">
|
||||
<div class="hero__plate"><span class="roman">I.</span><span class="sep"></span><span>Plate · today</span></div>
|
||||
<h1 class="hero__greeting">Good morning, <em>Alice.</em></h1>
|
||||
<p class="hero__sub">Margaret's frame is in sync. The next photograph will arrive at <strong>7:00 AM</strong>, quietly, on its own.</p>
|
||||
<div class="hero__rule"></div>
|
||||
</section>
|
||||
|
||||
<article class="frame-card glass">
|
||||
<div class="frame-card__hero">
|
||||
<div class="frame-card__status">
|
||||
<span class="dot"></span><span>Synced 12 min ago</span>
|
||||
</div>
|
||||
<div class="frame-card__plate">Plate XII</div>
|
||||
</div>
|
||||
<div class="frame-card__body">
|
||||
<h2 class="frame-card__name">Margaret's frame</h2>
|
||||
<p class="frame-card__meta">7.3″ landscape <span class="sep">·</span> Camden, Maine <span class="sep">·</span> est. May 2026</p>
|
||||
|
||||
<div class="frame-card__schedule">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
<div>
|
||||
<strong>Next photograph at 7:00 AM</strong>
|
||||
<span>then again at 12:00 PM and 6:00 PM</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="frame-card__actions">
|
||||
<button class="btn btn--primary">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Add photograph
|
||||
</button>
|
||||
<button class="btn">
|
||||
<svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section class="upcoming glass">
|
||||
<div class="upcoming__head">
|
||||
<h3 class="upcoming__title">Up next</h3>
|
||||
<span class="upcoming__hint">— in the library queue</span>
|
||||
</div>
|
||||
<div class="upcoming__row">
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt=""><span class="when">7am</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: hue-rotate(40deg);"><span class="when">noon</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: hue-rotate(-30deg) saturate(1.2);"><span class="when">6pm</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: brightness(0.85);"><span class="when">tmrw</span></div>
|
||||
<div class="thumb"><img src="../assets/harbor.jpg" alt="" style="filter: sepia(0.4);"><span class="when">+5</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab active" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,249 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Library — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
.library-empty {
|
||||
padding: 56px 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.library-empty__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-5);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.library-empty__plate .roman { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); }
|
||||
.library-empty__plate .sep { width: 24px; height: 1px; background: var(--glass-bord); }
|
||||
|
||||
.library-empty__halo { position: relative; margin-bottom: var(--space-5); }
|
||||
.library-empty__halo::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
width: 240px; height: 240px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: radial-gradient(circle, color-mix(in srgb, var(--brand-yellow) 25%, transparent), transparent 65%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
.library-empty__mark {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.18) inset,
|
||||
0 20px 40px -10px rgba(0,0,0,0.6),
|
||||
0 8px 20px -6px rgba(0,0,0,0.4);
|
||||
}
|
||||
.library-empty__mark img { width: 100%; height: 100%; display: block; }
|
||||
|
||||
.library-empty__title {
|
||||
font: var(--type-display-xl);
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-2);
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.library-empty__title em {
|
||||
font-family: var(--font-accent);
|
||||
font-style: italic;
|
||||
color: var(--brand-yellow);
|
||||
font-weight: 400;
|
||||
}
|
||||
.library-empty__sub {
|
||||
font: italic 400 18px/1.55 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
max-width: 36ch;
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
.library-empty__rule {
|
||||
width: 96px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
margin: 0 auto var(--space-6);
|
||||
}
|
||||
.library-empty__cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
padding: 14px 28px;
|
||||
border-radius: var(--radius-full);
|
||||
font-family: var(--font-family);
|
||||
font-weight: 700;
|
||||
font-size: var(--text-base);
|
||||
text-decoration: none;
|
||||
box-shadow: 0 8px 22px -4px color-mix(in srgb, var(--accent) 50%, transparent);
|
||||
transition: transform var(--duration-fast) var(--ease-out), filter var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
.library-empty__cta:hover { transform: translateY(-1px); filter: brightness(1.08); }
|
||||
.library-empty__cta svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; }
|
||||
.library-empty__or {
|
||||
margin-top: var(--space-5);
|
||||
font: italic 400 var(--text-md)/1.4 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.library-empty__or a {
|
||||
color: var(--text);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
text-decoration: none;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
/* steps card — frosted */
|
||||
.intro-steps {
|
||||
margin: 48px var(--space-5) 0;
|
||||
padding: var(--space-5) var(--space-5) var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
position: relative;
|
||||
}
|
||||
.intro-steps__rule {
|
||||
position: absolute;
|
||||
top: -1px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60px; height: 1px;
|
||||
background: var(--brand-yellow);
|
||||
}
|
||||
.intro-steps__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.intro-steps__title .v-mark { color: var(--brand-yellow); }
|
||||
.intro-steps__sub {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.intro-steps__list { display: flex; gap: var(--space-3); }
|
||||
.step { flex: 1; text-align: center; padding: var(--space-3); }
|
||||
.step__num {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-2xl);
|
||||
color: var(--brand-yellow);
|
||||
line-height: 1;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.step__label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
line-height: 1.4;
|
||||
}
|
||||
.step__label strong {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 400;
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text);
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.signature { margin: 64px var(--space-5) 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— the library</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
<button class="app-bar__icon" aria-label="Filter">
|
||||
<svg viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="library-empty">
|
||||
<div class="library-empty__plate"><span class="roman">○</span><span class="sep"></span><span>Accession no. 001</span></div>
|
||||
<div class="library-empty__halo">
|
||||
<div class="library-empty__mark"><img src="../assets/mark-photo.png" alt=""></div>
|
||||
</div>
|
||||
<h1 class="library-empty__title">A library, <em>waiting.</em></h1>
|
||||
<p class="library-empty__sub">Photographs you upload will rotate through Margaret's frame, one at a time. We'll hold them here until the frame is ready.</p>
|
||||
<div class="library-empty__rule"></div>
|
||||
<a class="library-empty__cta" href="#">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Upload the first photograph
|
||||
</a>
|
||||
<div class="library-empty__or">— or <a href="#">invite someone else</a> to send one</div>
|
||||
</div>
|
||||
|
||||
<div class="intro-steps glass">
|
||||
<div class="intro-steps__rule"></div>
|
||||
<div class="intro-steps__title">How <span class="v-mark">V</span>isto works, briefly</div>
|
||||
<div class="intro-steps__sub">— three steps, then forever</div>
|
||||
<div class="intro-steps__list">
|
||||
<div class="step"><div class="step__num">I.</div><div class="step__label"><strong>You upload</strong>family photos</div></div>
|
||||
<div class="step"><div class="step__num">II.</div><div class="step__label"><strong>We hold</strong>them in the library</div></div>
|
||||
<div class="step"><div class="step__num">III.</div><div class="step__label"><strong>Frame picks</strong>one each interval</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Edizione I · MMXXVI</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab active" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', chip.dataset.theme);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="ocean-dusk">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Settings — WeVisto</title>
|
||||
<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=Nunito:wght@400;600;700;900&family=Marcellus&family=Cormorant+Garamond:ital,wght@1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="_tokens.css">
|
||||
<link rel="stylesheet" href="_chrome.css">
|
||||
<style>
|
||||
.page-title {
|
||||
padding: var(--space-6) var(--space-5) var(--space-3);
|
||||
text-align: center;
|
||||
}
|
||||
.page-title__plate {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.page-title__plate .v-mark { color: var(--brand-yellow); font-family: var(--font-display); font-size: var(--text-md); margin: 0 8px; }
|
||||
.page-title__title {
|
||||
font: var(--type-display-lg);
|
||||
color: var(--text);
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.page-title__rule {
|
||||
width: 60px; height: 1px;
|
||||
margin: var(--space-3) auto 0;
|
||||
background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
|
||||
}
|
||||
|
||||
.section { margin: var(--space-6) var(--space-5) 0; }
|
||||
.section__head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
margin-bottom: var(--space-3);
|
||||
padding: 0 var(--space-2);
|
||||
}
|
||||
.section__roman {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-lg);
|
||||
color: var(--brand-yellow);
|
||||
line-height: 1;
|
||||
}
|
||||
.section__label {
|
||||
font: var(--type-label);
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.section__rule { flex: 1; height: 1px; background: var(--glass-bord); }
|
||||
|
||||
.list {
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-4) var(--space-5);
|
||||
border-bottom: 1px solid var(--glass-bord);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition: background var(--duration-fast);
|
||||
}
|
||||
.row:last-child { border-bottom: 0; }
|
||||
.row:hover { background: rgba(255,255,255,0.05); }
|
||||
.row__icon { width: 22px; height: 22px; flex-shrink: 0; color: var(--text-muted); }
|
||||
.row__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2; }
|
||||
.row__label {
|
||||
flex: 1;
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
.row__value {
|
||||
font: italic 400 14px/1.3 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.row__chevron { color: var(--text-muted); opacity: 0.6; }
|
||||
|
||||
/* DUSK PICKER */
|
||||
.theme-picker {
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
.theme-picker__head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.theme-picker__title {
|
||||
font: var(--type-display-md);
|
||||
color: var(--text);
|
||||
}
|
||||
.theme-picker__hint {
|
||||
font: var(--type-accent-md);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
|
||||
.theme-swatch {
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3);
|
||||
border: 1px solid var(--glass-bord);
|
||||
background: rgba(0,0,0,0.25);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: transform var(--duration-fast), box-shadow var(--duration-fast);
|
||||
font-family: inherit;
|
||||
color: var(--text);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.theme-swatch.active {
|
||||
border-color: var(--brand-yellow);
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand-yellow) 35%, transparent);
|
||||
}
|
||||
.theme-swatch:hover { transform: translateY(-2px); }
|
||||
/* a tinted preview of harbor inside each swatch */
|
||||
.theme-swatch__preview {
|
||||
aspect-ratio: 3/2;
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-2);
|
||||
background-image: url('../assets/harbor.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
}
|
||||
.theme-swatch__preview::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
.theme-swatch[data-pref="ocean-dusk"] .theme-swatch__preview::after { background: rgba(8, 22, 38, 0.55); }
|
||||
.theme-swatch[data-pref="amber-dusk"] .theme-swatch__preview::after { background: rgba(60, 25, 8, 0.55); }
|
||||
.theme-swatch[data-pref="sage-dusk"] .theme-swatch__preview::after { background: rgba(20, 40, 22, 0.55); }
|
||||
.theme-swatch[data-pref="rose-dusk"] .theme-swatch__preview::after { background: rgba(56, 16, 38, 0.55); }
|
||||
.theme-swatch[data-pref="mauve-dusk"] .theme-swatch__preview::after { background: rgba(40, 18, 56, 0.55); }
|
||||
.theme-swatch[data-pref="honey-dusk"] .theme-swatch__preview::after { background: rgba(48, 36, 14, 0.55); }
|
||||
.theme-swatch__name {
|
||||
font-family: var(--font-display);
|
||||
font-size: 13px;
|
||||
color: var(--text);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.theme-swatch__italic {
|
||||
font: italic 400 12px/1.2 var(--font-accent);
|
||||
color: var(--text-muted);
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sign-out { margin: var(--space-6) var(--space-5); text-align: center; }
|
||||
.sign-out a {
|
||||
color: #e08070;
|
||||
font-family: var(--font-family);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
font-size: var(--text-base);
|
||||
padding: var(--space-4) var(--space-5);
|
||||
display: inline-block;
|
||||
}
|
||||
.sign-out a:hover { text-decoration: underline; }
|
||||
|
||||
.signature { margin: 0 var(--space-5); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<header class="app-bar">
|
||||
<div class="app-bar__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="app-bar__title-group">
|
||||
<div class="app-bar__wordmark">We<span class="v">V</span>isto</div>
|
||||
<div class="app-bar__sub">— settings</div>
|
||||
</div>
|
||||
<div class="app-bar__spacer"></div>
|
||||
</header>
|
||||
|
||||
<main class="main-scroll">
|
||||
|
||||
<div class="page-title">
|
||||
<div class="page-title__plate">Settings <span class="v-mark">·</span> Plate i</div>
|
||||
<h1 class="page-title__title">A few quiet choices.</h1>
|
||||
<div class="page-title__rule"></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">I.</span><span class="section__label">Account</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/></svg></span>
|
||||
<span class="row__label">Alice Wexler</span>
|
||||
<span class="row__value">alice@example.com</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg></span>
|
||||
<span class="row__label">Notification email</span>
|
||||
<span class="row__value">daily digest</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">II.</span><span class="section__label">Appearance</span><span class="section__rule"></span></div>
|
||||
<div class="theme-picker glass">
|
||||
<div class="theme-picker__head">
|
||||
<div class="theme-picker__title">Dusk</div>
|
||||
<div class="theme-picker__hint">— pick a tint for the room</div>
|
||||
</div>
|
||||
<div class="theme-grid">
|
||||
<button class="theme-swatch active" data-pref="ocean-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Ocean dusk</div>
|
||||
<span class="theme-swatch__italic">— the harbor</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="amber-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Amber dusk</div>
|
||||
<span class="theme-swatch__italic">— the workshop</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="sage-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Sage dusk</div>
|
||||
<span class="theme-swatch__italic">— the garden</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="rose-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Rose dusk</div>
|
||||
<span class="theme-swatch__italic">— the parlor</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="mauve-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Mauve dusk</div>
|
||||
<span class="theme-swatch__italic">— the study</span>
|
||||
</button>
|
||||
<button class="theme-swatch" data-pref="honey-dusk">
|
||||
<div class="theme-swatch__preview"></div>
|
||||
<div class="theme-swatch__name">Honey dusk</div>
|
||||
<span class="theme-swatch__italic">— the alcove</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">III.</span><span class="section__label">Frames</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21,15 16,10 5,21"/></svg></span>
|
||||
<span class="row__label">Margaret's frame</span>
|
||||
<span class="row__value">7.3″ landscape</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></span>
|
||||
<span class="row__label">Add another frame</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section__head"><span class="section__roman">IV.</span><span class="section__label">Help</span><span class="section__rule"></span></div>
|
||||
<div class="list glass">
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>
|
||||
<span class="row__label">How WeVisto works</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
<a class="row">
|
||||
<span class="row__icon"><svg viewBox="0 0 24 24"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg></span>
|
||||
<span class="row__label">Contact us</span>
|
||||
<span class="row__chevron">›</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sign-out"><a href="#">Sign out</a></div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature__mark"><img src="../assets/mark-photo-64.png" alt=""></div>
|
||||
<div class="signature__text">WeVisto <span class="v-mark">·</span> a frame, gifted</div>
|
||||
<div class="signature__version">Edizione I · MMXXVI · Camogli</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<a class="bottom-nav__tab" href="home.html">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
|
||||
<span class="label">Home</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab" href="library.html">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
<span class="label">Library</span>
|
||||
</a>
|
||||
<a class="bottom-nav__tab active" href="settings.html">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="theme-switcher" role="region" aria-label="Theme switcher (mockup only)">
|
||||
<span class="theme-switcher__label">Dusk</span>
|
||||
<div class="theme-switcher__chips">
|
||||
<button class="theme-switcher__chip active" data-theme="ocean-dusk" title="Ocean dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="amber-dusk" title="Amber dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="sage-dusk" title="Sage dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="rose-dusk" title="Rose dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="mauve-dusk" title="Mauve dusk"></button>
|
||||
<button class="theme-switcher__chip" data-theme="honey-dusk" title="Honey dusk"></button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
const t = chip.dataset.theme;
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c === chip));
|
||||
document.querySelectorAll('.theme-swatch').forEach(s => s.classList.toggle('active', s.dataset.pref === t));
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.theme-swatch').forEach(sw => {
|
||||
sw.addEventListener('click', () => {
|
||||
const t = sw.dataset.pref;
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
document.querySelectorAll('.theme-swatch').forEach(s => s.classList.toggle('active', s === sw));
|
||||
document.querySelectorAll('.theme-switcher__chip').forEach(c => c.classList.toggle('active', c.dataset.theme === t));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,8 +21,11 @@
|
||||
# the backgrounded SSE subscriber in step 5 sometimes gets killed before it
|
||||
# receives data. Reproduces inside this script, not in interactive shell.
|
||||
|
||||
HOST="pictureframe.edholm.me"
|
||||
SSH_HOST="pictureframe"
|
||||
# Defaults to the new wevisto.com host; override with HOST=… to smoke-test
|
||||
# the legacy pictureframe.edholm.me alias kept alive under the dual-domain
|
||||
# migration (Option C).
|
||||
HOST="${HOST:-wevisto.com}"
|
||||
SSH_HOST="${SSH_HOST:-pictureframe}"
|
||||
TESTBOT_EMAIL="testbot@example.com"
|
||||
TESTBOT_PASS="testpass123"
|
||||
RED="\033[31m"; GREEN="\033[32m"; RESET="\033[0m"
|
||||
|
||||
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 15 MiB |
@@ -0,0 +1,8 @@
|
||||
# Brand source assets
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `IMG_2524.jpg` | Raw full-quality original (Camogli harbour, 5712×4284). The hero photo behind the WeVisto wordmark. Use this for any marketing, social, or print work. |
|
||||
| `IMG_2524-square900.jpg` | 900×900 centred crop (vbias 0.42) — the exact image embedded as base64 in `frontend/public/logo.svg`. Regenerate the logo from this if you ever tweak the wordmark. |
|
||||
|
||||
The composed logo SVG lives at `frontend/public/logo.svg` (Vite copies it to `public/build/logo.svg` at build time).
|
||||
@@ -3,17 +3,20 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<title>pictureFrame</title>
|
||||
<title>WeVisto</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/build/favicon.svg?v=20260515-vviewfinder" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/build/icons/favicon-32.png?v=20260515-vviewfinder" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/build/icons/favicon-16.png?v=20260515-vviewfinder" />
|
||||
<link rel="manifest" href="/build/manifest.webmanifest" />
|
||||
<link rel="apple-touch-icon" href="/build/icons/apple-touch-icon.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/build/icons/apple-touch-icon.png?v=20260515-vviewfinder" />
|
||||
|
||||
<link rel="stylesheet" href="/css/wevisto-design.css">
|
||||
<meta name="theme-color" content="#fdf6ee" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="pictureFrame" />
|
||||
<meta name="apple-mobile-web-app-title" content="WeVisto" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
|
After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 271 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 647 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 352 KiB |