Add FamDone Brand component

This commit is contained in:
JCEEE
2026-09-02 11:50:51 +01:00
parent 1d4d6be20b
commit f288df5b9c
18 changed files with 657 additions and 493 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
--- ---
# FamChore v2 — AI Agent Reference # FamDone v2 — AI Agent Reference
## Stack ## Stack
+1 -1
View File
@@ -1,4 +1,4 @@
# FamChore v2 — Architecture & Developer Reference # FamDone v2 — Architecture & Developer Reference
## Overview ## Overview
+2 -2
View File
@@ -1,4 +1,4 @@
# FamChore v2 — Development Memory # FamDone v2 — Development Memory
## 2026-08-17 — Schema collapse to single source of truth (Option 1) ## 2026-08-17 — Schema collapse to single source of truth (Option 1)
@@ -34,7 +34,7 @@
``` ```
### Sidebar (collapsible to mini-mode) ### Sidebar (collapsible to mini-mode)
- Header: app name (FamChore) - Header: app name (FamDone)
- Admin CTAs: Dashboard, Chores, Rewards (badge count), Bonuses - Admin CTAs: Dashboard, Chores, Rewards (badge count), Bonuses
- Member CTAs: Dashboard, Preferences - Member CTAs: Dashboard, Preferences
- Footer: family name, Settings (admin only), Log out - Footer: family name, Settings (admin only), Log out
+1 -1
View File
@@ -1,4 +1,4 @@
# FamChore v2 — Rules # FamDone v2 — Rules
## Auth ## Auth
- Every collection query includes `famId = @request.auth.famId` filter - Every collection query includes `famId = @request.auth.famId` filter
+1 -1
View File
@@ -1,4 +1,4 @@
# FamChore — TODO # FamDone — TODO
(Completed work is tracked in MEMORY.md / git history.) (Completed work is tracked in MEMORY.md / git history.)
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>

After

Width:  |  Height:  |  Size: 328 B

+15 -4
View File
@@ -1,5 +1,11 @@
<script lang="ts"> <script lang="ts">
let { title, subtitle, children }: { import FamDone from './FamDone.svelte';
let {
title,
subtitle,
children
}: {
title: string; title: string;
subtitle?: string; subtitle?: string;
children?: any; children?: any;
@@ -7,7 +13,7 @@
</script> </script>
<main class="auth"> <main class="auth">
<a class="brand" href="/">FamChore</a> <a class="brand" href="/"><FamDone /></a>
<div class="card"> <div class="card">
<h1>{title}</h1> <h1>{title}</h1>
{#if subtitle} {#if subtitle}
@@ -26,7 +32,10 @@
justify-content: center; justify-content: center;
padding: 2rem 1rem; padding: 2rem 1rem;
background: linear-gradient(135deg, #eef2ff 0%, #ffffff 55%, #eef2ff 100%); background: linear-gradient(135deg, #eef2ff 0%, #ffffff 55%, #eef2ff 100%);
font-family: system-ui, -apple-system, sans-serif; font-family:
system-ui,
-apple-system,
sans-serif;
color: #1f2937; color: #1f2937;
} }
.brand { .brand {
@@ -56,6 +65,8 @@
line-height: 1.5; line-height: 1.5;
} }
@media (max-width: 480px) { @media (max-width: 480px) {
.card { padding: 1.5rem; } .card {
padding: 1.5rem;
}
} }
</style> </style>
@@ -0,0 +1,33 @@
<script lang="ts">
let { tag = 'span' }: { tag?: 'span' | 'h1' | 'h2' | 'h3' | 'a' | 'strong' | 'p' } = $props();
</script>
<svelte:element this={tag} class="fam-done">
FamDone<svg
class="fam-done-check"
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="#10b981"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" /><polyline points="22 4 12 14.01 9 11.01" /></svg
></svelte:element
>
<style>
.fam-done {
font-weight: inherit;
color: inherit;
}
.fam-done-check {
vertical-align: super;
position: relative;
top: -0.3em;
margin-left: 1px;
display: inline-block;
}
</style>
+5 -1
View File
@@ -1,5 +1,9 @@
<script lang="ts">
import FamDone from './FamDone.svelte';
</script>
<footer class="app-footer"> <footer class="app-footer">
<span class="footer-text">FamChore</span> <span class="footer-text"><FamDone /></span>
</footer> </footer>
<style> <style>
+2 -1
View File
@@ -10,6 +10,7 @@
logoutIcon, logoutIcon,
prefsIcon prefsIcon
} from './icons'; } from './icons';
import FamDone from './FamDone.svelte';
let { famName = '', session = null, isParent = false, role = 'child' } = $props(); let { famName = '', session = null, isParent = false, role = 'child' } = $props();
@@ -53,7 +54,7 @@
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<span class="app-icon">✦</span> <span class="app-icon">✦</span>
<span class="app-name">FamChore</span> <span class="app-name"><FamDone /></span>
</div> </div>
<nav class="sidebar-nav"> <nav class="sidebar-nav">
+1
View File
@@ -1,4 +1,5 @@
export { default as Sidebar } from './Sidebar.svelte'; export { default as Sidebar } from './Sidebar.svelte';
export { default as FamDone } from './FamDone.svelte';
export { default as TopNav } from './TopNav.svelte'; export { default as TopNav } from './TopNav.svelte';
export { default as Footer } from './Footer.svelte'; export { default as Footer } from './Footer.svelte';
export { default as ViewHeader } from './ViewHeader.svelte'; export { default as ViewHeader } from './ViewHeader.svelte';
+1 -1
View File
@@ -34,7 +34,7 @@ export async function sendParentInviteEmail(opts: {
await resend.emails.send({ await resend.emails.send({
from: 'no-reply@famchamp.ai', from: 'no-reply@famchamp.ai',
to, to,
subject: `You're invited to ${famName} on FamChore`, subject: `You're invited to ${famName} on FamDone`,
html: ` html: `
<p>You've been invited as a parent on <strong>${famName}</strong>.</p> <p>You've been invited as a parent on <strong>${famName}</strong>.</p>
<p><a href="${inviteLink}">Click here to join</a> and set up your password.</p> <p><a href="${inviteLink}">Click here to join</a> and set up your password.</p>
+4 -3
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Footer from '$lib/components/Footer.svelte'; import Footer from '$lib/components/Footer.svelte';
import FamDone from '$lib/components/FamDone.svelte';
import { homeIcon, checkCircleIcon, chevronRightIcon } from '$lib/components/icons'; import { homeIcon, checkCircleIcon, chevronRightIcon } from '$lib/components/icons';
import { readShortcut, shortcutTarget, type Shortcut } from '$lib/shortcut'; import { readShortcut, shortcutTarget, type Shortcut } from '$lib/shortcut';
@@ -13,7 +14,7 @@
</script> </script>
<svelte:head> <svelte:head>
<title>FamChore — Chores done. Allowance earned.</title> <title>FamDone — Chores done. Allowance earned.</title>
<meta <meta
name="description" name="description"
content="Turn family chores into points and pocket money. Assign chores, kids complete them, allowances are calculated automatically." content="Turn family chores into points and pocket money. Assign chores, kids complete them, allowances are calculated automatically."
@@ -40,8 +41,8 @@
<span class="pill">For busy families</span> <span class="pill">For busy families</span>
<h1>Chores done. <span class="accent">Allowance earned.</span></h1> <h1>Chores done. <span class="accent">Allowance earned.</span></h1>
<p class="lede"> <p class="lede">
FamChore turns everyday household chores into points and pocket money. Assign the chores, <FamDone /> turns everyday household chores into points and pocket money. Assign the chores,
let your kids see their progress live, and FamChore calculates the allowance automatically let your kids see their progress live, and <FamDone /> calculates the allowance automatically
— no spreadsheets, no nagging. — no spreadsheets, no nagging.
</p> </p>
<ul class="hero-ticks"> <ul class="hero-ticks">
+2 -2
View File
@@ -39,7 +39,7 @@
if (returnHandled || page.url.searchParams.get('checkout') !== 'return') return; if (returnHandled || page.url.searchParams.get('checkout') !== 'return') return;
returnHandled = true; returnHandled = true;
if (!disabled) { if (!disabled) {
notices.success('Welcome to FamChore!', 'Your subscription is active.'); notices.success('Welcome to FamDone!', 'Your subscription is active.');
return; return;
} }
startActivating(); startActivating();
@@ -195,7 +195,7 @@
<strong>Access paused</strong> <strong>Access paused</strong>
<span> <span>
{isParent {isParent
? 'Add an access code or resume your subscription to keep using FamChore.' ? 'Add an access code or resume your subscription to keep using FamDone.'
: 'Your family access is paused.'} : 'Your family access is paused.'}
</span> </span>
{/if} {/if}
+159 -48
View File
@@ -3,6 +3,7 @@
import { goto, invalidateAll } from '$app/navigation'; import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/state'; import { page } from '$app/state';
import { ViewHeader, CardGrid, Card, Button } from '$lib/components'; import { ViewHeader, CardGrid, Card, Button } from '$lib/components';
import FamDone from '$lib/components/FamDone.svelte';
import TemplateIcon from '$lib/components/TemplateIcon.svelte'; import TemplateIcon from '$lib/components/TemplateIcon.svelte';
import { ICON_NAMES, TEMPLATE_COLORS } from '$lib/templateIcons'; import { ICON_NAMES, TEMPLATE_COLORS } from '$lib/templateIcons';
import Footer from '$lib/components/Footer.svelte'; import Footer from '$lib/components/Footer.svelte';
@@ -82,7 +83,9 @@
<div class="shell"> <div class="shell">
<header class="topbar"> <header class="topbar">
<a class="brand" href="/"><span class="brand-icon">{@html homeIcon}</span> <strong>FamChore</strong></a> <a class="brand" href="/"
><span class="brand-icon">{@html homeIcon}</span> <strong><FamDone /></strong></a
>
<nav> <nav>
{#if data.authenticated} {#if data.authenticated}
<form method="POST" action="?/logout" use:enhance> <form method="POST" action="?/logout" use:enhance>
@@ -95,7 +98,7 @@
</header> </header>
<main> <main>
{#if !data.authenticated} {#if !data.authenticated}
<div class="login-card"> <div class="login-card">
<h1>Platform Admin</h1> <h1>Platform Admin</h1>
<p class="subtitle">Sign in to access the admin dashboard</p> <p class="subtitle">Sign in to access the admin dashboard</p>
@@ -135,8 +138,12 @@
<Button type="submit" variant="primary" size="lg">Sign In</Button> <Button type="submit" variant="primary" size="lg">Sign In</Button>
</form> </form>
</div> </div>
{:else} {:else}
<ViewHeader title="Platform Admin" subtitle="Families, access codes and platform settings" hero /> <ViewHeader
title="Platform Admin"
subtitle="Families, access codes and platform settings"
hero
/>
<CardGrid> <CardGrid>
<Card cols={2} title="Overview" accent="#6366f1"> <Card cols={2} title="Overview" accent="#6366f1">
@@ -174,7 +181,11 @@
<span class="flag-name">debug</span> <span class="flag-name">debug</span>
<form method="POST" action="?/togglePlatformFlag" use:enhance> <form method="POST" action="?/togglePlatformFlag" use:enhance>
<input type="hidden" name="flag" value="debug" /> <input type="hidden" name="flag" value="debug" />
<button type="submit" class="toggle-btn" class:active={page.data.platformFlags?.debug}> <button
type="submit"
class="toggle-btn"
class:active={page.data.platformFlags?.debug}
>
{page.data.platformFlags?.debug ? 'ON' : 'OFF'} {page.data.platformFlags?.debug ? 'ON' : 'OFF'}
</button> </button>
</form> </form>
@@ -254,7 +265,9 @@
<td class="row-actions" data-label="Actions"> <td class="row-actions" data-label="Actions">
<form method="POST" action="?/toggleCode" use:enhance> <form method="POST" action="?/toggleCode" use:enhance>
<input type="hidden" name="id" value={c.id} /> <input type="hidden" name="id" value={c.id} />
<button type="submit" class="link-btn">{c.active ? 'Disable' : 'Enable'}</button> <button type="submit" class="link-btn"
>{c.active ? 'Disable' : 'Enable'}</button
>
</form> </form>
{#if !c.usedBy.length} {#if !c.usedBy.length}
<form method="POST" action="?/deleteCode" use:enhance> <form method="POST" action="?/deleteCode" use:enhance>
@@ -297,8 +310,16 @@
<td data-label="Parent">{fam.parentEmail || '—'}</td> <td data-label="Parent">{fam.parentEmail || '—'}</td>
<td data-label="Members">{fam.memberCount}</td> <td data-label="Members">{fam.memberCount}</td>
<td data-label="Plan"> <td data-label="Plan">
<span class="badge {fam.paymentMode === 'sub' ? 'on' : fam.active ? 'dur' : 'off'}"> <span
{modeLabel[fam.paymentMode] || fam.paymentMode}{fam.active ? '' : ' · paused'} class="badge {fam.paymentMode === 'sub'
? 'on'
: fam.active
? 'dur'
: 'off'}"
>
{modeLabel[fam.paymentMode] || fam.paymentMode}{fam.active
? ''
: ' · paused'}
</span> </span>
</td> </td>
<td data-label="Claims"> <td data-label="Claims">
@@ -308,18 +329,21 @@
<span class="badge none">None</span> <span class="badge none">None</span>
{/if} {/if}
</td> </td>
<td class="row-actions" data-label="Actions"><a href="/{fam.slug}" class="link">View {@html chevronRightIcon}</a></td> <td class="row-actions" data-label="Actions"
><a href="/{fam.slug}" class="link">View {@html chevronRightIcon}</a></td
>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
</table> </table>
</Card> </div></Card
>
</CardGrid> </CardGrid>
<CardGrid> <CardGrid>
<Card cols={2} title="Chore Templates" accent="#2563eb"> <Card cols={2} title="Chore Templates" accent="#2563eb">
<div class="tpl-grid"> <div class="tpl-grid">
{#each (data.choreTemplates || []) as t} {#each data.choreTemplates || [] as t}
<div class="tpl-card" style="border-left-color:{t.color}"> <div class="tpl-card" style="border-left-color:{t.color}">
<div class="tpl-head"> <div class="tpl-head">
<TemplateIcon name={t.icon} size={20} color={t.color} /> <TemplateIcon name={t.icon} size={20} color={t.color} />
@@ -364,7 +388,7 @@
<Card cols={1} title="Reward Templates" accent="#8b5cf6"> <Card cols={1} title="Reward Templates" accent="#8b5cf6">
<div class="tpl-grid"> <div class="tpl-grid">
{#each (data.bonusTemplates || []) as t} {#each data.bonusTemplates || [] as t}
<div class="tpl-card" style="border-left-color:{t.color}"> <div class="tpl-card" style="border-left-color:{t.color}">
<div class="tpl-head"> <div class="tpl-head">
<TemplateIcon name={t.icon} size={20} color={t.color} /> <TemplateIcon name={t.icon} size={20} color={t.color} />
@@ -429,7 +453,9 @@
{/if} {/if}
<input type="hidden" name="type" value={tplType} /> <input type="hidden" name="type" value={tplType} />
<label>Name <input name="name" bind:value={tplVals.name} required /></label> <label>Name <input name="name" bind:value={tplVals.name} required /></label>
<label>Description <input name="description" bind:value={tplVals.description} /></label> <label
>Description <input name="description" bind:value={tplVals.description} /></label
>
<div class="row"> <div class="row">
<label> <label>
Icon Icon
@@ -437,7 +463,9 @@
{#each ICON_NAMES as n}<option value={n}>{n}</option>{/each} {#each ICON_NAMES as n}<option value={n}>{n}</option>{/each}
</select> </select>
</label> </label>
<span class="icon-preview"><TemplateIcon name={tplVals.icon} size={22} color={tplVals.color} /></span> <span class="icon-preview"
><TemplateIcon name={tplVals.icon} size={22} color={tplVals.color} /></span
>
<div class="color-field"> <div class="color-field">
Colour Colour
<div class="color-swatches"> <div class="color-swatches">
@@ -451,7 +479,8 @@
? '#e5e7eb' ? '#e5e7eb'
: c.value}" : c.value}"
title={c.label} title={c.label}
onclick={() => (tplVals.color = c.value)}></button> onclick={() => (tplVals.color = c.value)}
></button>
{/each} {/each}
</div> </div>
<input type="hidden" name="color" value={tplVals.color} /> <input type="hidden" name="color" value={tplVals.color} />
@@ -475,7 +504,12 @@
</label> </label>
<label> <label>
Default Value Default Value
<input type="number" step="any" name="defaultValue" bind:value={tplVals.defaultValue} /> <input
type="number"
step="any"
name="defaultValue"
bind:value={tplVals.defaultValue}
/>
</label> </label>
{:else} {:else}
<label> <label>
@@ -535,7 +569,12 @@
</select> </select>
</label> </label>
<label class="check"> <label class="check">
<input type="checkbox" name="isPocketMoney" value="true" bind:checked={tplVals.isPocketMoney} /> <input
type="checkbox"
name="isPocketMoney"
value="true"
bind:checked={tplVals.isPocketMoney}
/>
Pocket Money Pocket Money
</label> </label>
{/if} {/if}
@@ -548,8 +587,7 @@
</div> </div>
</div> </div>
{/if} {/if}
{/if}
{/if}
</main> </main>
<Footer /> <Footer />
@@ -560,9 +598,7 @@
min-height: 100vh; min-height: 100vh;
display: grid; display: grid;
grid-template-rows: auto 1fr auto; grid-template-rows: auto 1fr auto;
background: background: radial-gradient(60rem 30rem at 50% -10rem, #eef2ff 0%, transparent 65%), #fafafa;
radial-gradient(60rem 30rem at 50% -10rem, #eef2ff 0%, transparent 65%),
#fafafa;
} }
.topbar { .topbar {
display: flex; display: flex;
@@ -581,7 +617,11 @@
color: #111827; color: #111827;
text-decoration: none; text-decoration: none;
} }
.topbar nav { display: flex; align-items: center; gap: 1rem; } .topbar nav {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-link { .nav-link {
font-size: 0.9rem; font-size: 0.9rem;
color: #4b5563; color: #4b5563;
@@ -594,7 +634,9 @@
padding: 0; padding: 0;
font-weight: 500; font-weight: 500;
} }
.link-btn-nav:hover { color: #111827; } .link-btn-nav:hover {
color: #111827;
}
main { main {
display: flex; display: flex;
@@ -608,8 +650,12 @@
margin: 0 auto; margin: 0 auto;
} }
main :global(.card-grid), main :global(.card-grid),
main :global(.hero) { width: 100%; } main :global(.hero) {
.head { text-align: center; } width: 100%;
}
.head {
text-align: center;
}
.head h1 { .head h1 {
margin: 0 0 0.35rem; margin: 0 0 0.35rem;
font-size: clamp(1.5rem, 3vw, 2rem); font-size: clamp(1.5rem, 3vw, 2rem);
@@ -775,10 +821,18 @@
vertical-align: middle; vertical-align: middle;
color: #374151; color: #374151;
} }
.fam-table tbody tr:last-child td { border-bottom: none; } .fam-table tbody tr:last-child td {
.fam-table tbody tr { transition: background 0.12s ease; } border-bottom: none;
.fam-table tbody tr:hover { background: #f8faff; } }
.th-actions { text-align: right; } .fam-table tbody tr {
transition: background 0.12s ease;
}
.fam-table tbody tr:hover {
background: #f8faff;
}
.th-actions {
text-align: right;
}
.code-value { .code-value {
font-family: ui-monospace, monospace; font-family: ui-monospace, monospace;
@@ -894,7 +948,9 @@
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
} }
.fam-link:hover { color: #4338ca; } .fam-link:hover {
color: #4338ca;
}
.link { .link {
color: #4338ca; color: #4338ca;
font-weight: 500; font-weight: 500;
@@ -919,7 +975,11 @@
/* ── Responsive: stack rows into cards with labels ── */ /* ── Responsive: stack rows into cards with labels ── */
@media (max-width: 720px) { @media (max-width: 720px) {
.table-wrap { border: none; overflow: visible; background: transparent; } .table-wrap {
border: none;
overflow: visible;
background: transparent;
}
.fam-table, .fam-table,
.fam-table tbody, .fam-table tbody,
.fam-table tr, .fam-table tr,
@@ -927,7 +987,9 @@
display: block; display: block;
width: 100%; width: 100%;
} }
.fam-table thead { display: none; } .fam-table thead {
display: none;
}
.fam-table tbody tr { .fam-table tbody tr {
background: #fff; background: #fff;
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
@@ -954,7 +1016,9 @@
text-align: left; text-align: left;
flex-shrink: 0; flex-shrink: 0;
} }
.fam-table tbody td:empty { display: none; } .fam-table tbody td:empty {
display: none;
}
.row-actions { .row-actions {
justify-content: flex-end !important; justify-content: flex-end !important;
border-bottom: none !important; border-bottom: none !important;
@@ -983,11 +1047,30 @@
gap: 0.5rem; gap: 0.5rem;
margin-bottom: 0.35rem; margin-bottom: 0.35rem;
} }
.tpl-head strong { font-size: 0.9rem; } .tpl-head strong {
.tpl-meta { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.4rem; } font-size: 0.9rem;
.tpl-actions { display: flex; gap: 0.75rem; } }
.tpl-new { display: flex; gap: 0.6rem; margin-top: 0.5rem; } .tpl-meta {
.row { display: flex; align-items: flex-end; gap: 0.75rem; flex-wrap: wrap; } display: flex;
gap: 0.3rem;
flex-wrap: wrap;
margin-bottom: 0.4rem;
}
.tpl-actions {
display: flex;
gap: 0.75rem;
}
.tpl-new {
display: flex;
gap: 0.6rem;
margin-top: 0.5rem;
}
.row {
display: flex;
align-items: flex-end;
gap: 0.75rem;
flex-wrap: wrap;
}
.color-field { .color-field {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -996,7 +1079,11 @@
font-weight: 600; font-weight: 600;
color: #6b7280; color: #6b7280;
} }
.color-swatches { display: flex; flex-wrap: wrap; gap: 0.4rem; } .color-swatches {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.swatch { .swatch {
width: 26px; width: 26px;
height: 26px; height: 26px;
@@ -1006,7 +1093,10 @@
padding: 0; padding: 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
} }
.swatch.selected { outline: 2px solid #111827; outline-offset: 1px; } .swatch.selected {
outline: 2px solid #111827;
outline-offset: 1px;
}
.icon-preview { .icon-preview {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -1016,10 +1106,23 @@
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
border-radius: 8px; border-radius: 8px;
} }
.badge.chore { background: #dbeafe; color: #1e40af; } .badge.chore {
.badge.reward { background: #ede9fe; color: #6d28d9; } background: #dbeafe;
.badge.pocket { background: #dcfce7; color: #166534; } color: #1e40af;
.check { display: flex; align-items: center; gap: 0.4rem; } }
.badge.reward {
background: #ede9fe;
color: #6d28d9;
}
.badge.pocket {
background: #dcfce7;
color: #166534;
}
.check {
display: flex;
align-items: center;
gap: 0.4rem;
}
/* ── Modal (shared) ── */ /* ── Modal (shared) ── */
.overlay { .overlay {
@@ -1041,7 +1144,9 @@
overflow: auto; overflow: auto;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
} }
.modal h3 { margin: 0 0 0.75rem; } .modal h3 {
margin: 0 0 0.75rem;
}
.modal label { .modal label {
display: block; display: block;
margin-bottom: 0.6rem; margin-bottom: 0.6rem;
@@ -1072,6 +1177,12 @@
cursor: pointer; cursor: pointer;
font-size: 0.9rem; font-size: 0.9rem;
} }
.modal-actions button[type='button'] { background: #e5e7eb; color: #374151; } .modal-actions button[type='button'] {
.modal-actions button[type='submit'] { background: #6366f1; color: #fff; } background: #e5e7eb;
color: #374151;
}
.modal-actions button[type='submit'] {
background: #6366f1;
color: #fff;
}
</style> </style>
+2 -2
View File
@@ -45,9 +45,9 @@ export async function POST({ request, url }) {
await resend.emails.send({ await resend.emails.send({
from: 'no-reply@famchamp.ai', from: 'no-reply@famchamp.ai',
to: email, to: email,
subject: 'Reset your FamChore password', subject: 'Reset your FamDone password',
html: ` html: `
<p>You requested a password reset for your FamChore account.</p> <p>You requested a password reset for your FamDone account.</p>
<p><a href="${resetUrl}">Click here to reset your password</a></p> <p><a href="${resetUrl}">Click here to reset your password</a></p>
<p>This link expires in 1 hour.</p> <p>This link expires in 1 hour.</p>
<p>If you didn't request this, you can ignore this email.</p> <p>If you didn't request this, you can ignore this email.</p>
+1 -1
View File
@@ -27,7 +27,7 @@
} }
</script> </script>
<AuthShell title="Log in" subtitle="Welcome back to FamChore."> <AuthShell title="Log in" subtitle="Welcome back to FamDone.">
{#if showForgot} {#if showForgot}
{#if forgotSuccess} {#if forgotSuccess}
<p class="form-ok"> <p class="form-ok">
+3 -2
View File
@@ -5,6 +5,7 @@
import { PUBLIC_STRIPE_PUBLISHABLE_KEY } from '$app/env/public'; import { PUBLIC_STRIPE_PUBLISHABLE_KEY } from '$app/env/public';
import { loadStripe, type StripeEmbeddedCheckout } from '@stripe/stripe-js'; import { loadStripe, type StripeEmbeddedCheckout } from '@stripe/stripe-js';
import { Button, PricingPlans } from '$lib/components'; import { Button, PricingPlans } from '$lib/components';
import FamDone from '$lib/components/FamDone.svelte';
import Footer from '$lib/components/Footer.svelte'; import Footer from '$lib/components/Footer.svelte';
import { homeIcon, chevronLeftIcon, chevronRightIcon } from '$lib/components/icons'; import { homeIcon, chevronLeftIcon, chevronRightIcon } from '$lib/components/icons';
@@ -56,12 +57,12 @@
} }
</script> </script>
<svelte:head><title>Pricing — FamChore</title></svelte:head> <svelte:head><title>Pricing — FamDone</title></svelte:head>
<div class="shell"> <div class="shell">
<header class="topbar"> <header class="topbar">
<a class="brand" href="/" <a class="brand" href="/"
><span class="brand-icon">{@html homeIcon}</span> <strong>FamChore</strong></a ><span class="brand-icon">{@html homeIcon}</span> <strong><FamDone /></strong></a
> >
<nav> <nav>
<a href="/login" class="nav-link">Log in</a> <a href="/login" class="nav-link">Log in</a>