From 658afec491344f3baa38523a79120a86d9a9efa0 Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 14 Sep 2026 21:31:32 -0400 Subject: [PATCH] adding in design files --- design/ADMIN-SPEC.md | 136 +++++++++++++++++++++++++ design/DESIGN-HANDOFF.md | 82 +++++++++++++++ design/ENTITIES-AND-SERVICES.md | 117 ++++++++++++++++++++++ design/IMPLEMENTATION-ORDER.md | 80 +++++++++++++++ design/PANEL-SPEC.md | 170 ++++++++++++++++++++++++++++++++ 5 files changed, 585 insertions(+) create mode 100644 design/ADMIN-SPEC.md create mode 100644 design/DESIGN-HANDOFF.md create mode 100644 design/ENTITIES-AND-SERVICES.md create mode 100644 design/IMPLEMENTATION-ORDER.md create mode 100644 design/PANEL-SPEC.md diff --git a/design/ADMIN-SPEC.md b/design/ADMIN-SPEC.md new file mode 100644 index 0000000..1cb74a7 --- /dev/null +++ b/design/ADMIN-SPEC.md @@ -0,0 +1,136 @@ +# Admin spec — parent phone card + +Screens `1d`–`1i`. One card, `nestquest-admin-card`, four tabs. Reference viewport +390×844. Plain CubeCraft styling and plain domain words — no parchment, no Cinzel, no +quest vocabulary in this surface. + +## 1. Shell + +- Page `--nq-a-page`; cards `--nq-a-surface` with 1px `--nq-a-border`. +- **Header** white, 1px bottom border: screen title 24px/800, tracking −.02em, plus a + 12px/600 `--nq-a-ink-secondary` sub-line. Optional trailing action (icon square 36px, + or a gradient `New` button). +- **Tab bar** absolute bottom, `--nq-a-tabbar-height` 70px + 12px bottom padding, white, + 1px top border, 4 items: `house` Today · `list-checks` Tasks · `calendar-days` Schedule · + `history` History. Active icon and label `--nq-brand-blue` at 700; inactive icon + `--nq-a-ink-tertiary`, label `--nq-a-ink-secondary` 600. +- **Every scroll column ends with 92px bottom padding** so content clears the tab bar. + This was a real defect in review — do not omit it. +- Two densities exist. Ship **compact**; `--nq-a-density-*` carries the roomy values. +- Everything this card mutates goes through a NestQuest service, which enforces the admin + allowlist server-side. The card must assume it can be rendered by a non-admin and still + be safe — hiding controls is not the boundary (D-003). + +## 2. Today (1d compact, 1h roomy) + +### 2.1 Header +Title `Today`; sub `, · cycle day N of 14`; trailing `settings` square. + +### 2.2 Stat row +Three equal cards, 8px gap, padding 12px: numeral 22px/800 (done in `--nq-brand-blue`, +remaining in `--nq-a-ink`, overdue in `--nq-a-danger`), label 10px/600 uppercase tracking +.08em `--nq-a-ink-secondary` — `Done`, `Remaining`, `Overdue`. + +### 2.3 Child rows +One card, rows divided by `--nq-a-divider`, row padding 11px 13px: +30px gradient avatar square (radius 7px, initial 13px/800 white) · name 14px/700 · +meta 11px/600 (`3 of 7 · 1 overdue`; all-done in `--nq-a-success`; away reads +`Away · returns `) · 82×6px progress track with gradient fill. An away child's +avatar is flat `#9898A8` and the row carries a lowercase `override` pill when the absence +comes from a presence override rather than the pattern. + +### 2.4 Roomy variant (1h) +Header becomes a hero: kicker 11px/700 uppercase, `8 of 12 done` 32px/800, 10px gradient +progress bar, 13px/600 sub-line. Each child gets its own 12px-radius card with +`0 4px 12px rgba(0,0,0,.06)`, 48px avatar, 19px/800 name, right-aligned `3/7` + +status line, and its overdue item inlined as a `--nq-a-danger-bg` row with a +`Mark done` action. + +### 2.5 Needs attention +Section label 11px/700 uppercase, then one card of event rows: Lucide glyph 18px · +title ` · ` 13px/700 · meta 11px/600 (`Overdue since 9:00 AM` in +`--nq-a-danger`; `Completed 7:04 AM from panel` in `--nq-a-ink-secondary`) · trailing +text action `Mark done` (`--nq-brand-blue`) or `Undo` (`--nq-brand-purple`). + +### 2.6 Permission note +An info strip (`--nq-a-info-bg`, `info` glyph, 11.5px/600 `--nq-a-info-ink`): +"Undo is admin-only. A panel tap can never reverse a completion." Keep this copy — it is +the one place the security model is explained to the user. + +## 3. Definitions (1e) + +### 3.1 List +Header `Tasks` / `7 definitions · 3 children` + gradient `New` button. Filter chips row +(All / one per child): active `#18181D` fill white text, inactive white with border. +Rows: glyph 17px · title 13.5px/700 · meta 11px/600 ` · · ` +· `chevron-right`. An inactive definition renders the whole row at +`--nq-a-ink-tertiary` with `· inactive` in the meta — never hard-deleted (glossary). +The row being edited takes `--nq-a-selected` with brand-blue glyph, `Editing` meta and +`chevron-down`. + +### 3.2 Edit sheet +Bottom sheet, `--nq-a-radius-sheet`, `--nq-a-sheet-shadow`, 38×4px grabber, padding +14px 18px 24px. Title 19px/800. Fields, 11px gap, each with a 10px/700 uppercase label: + +| Field | Control | +|---|---| +| Assigned children | multi-select row → sheet; shows `Declan` or `All three` | +| Repeats | select (Daily / Weekly / Monthly / Yearly / Custom + interval) | +| Due time | time field per window | +| Window | 3-up segmented, **multi-select** — a definition may own several | +| Skip on away days | toggle, gradient when on, sub "No instance when the child is absent" | + +Footer: `Cancel` (flex 1, bordered) + `Save changes` (flex 2, gradient), both 13px vertical +padding. Sheet enters over the dimmed list in `--nq-dur-modal`. + +### 3.3 Icon picker +Each definition carries a Lucide glyph, shown on both surfaces. **The allowed subset is not +yet defined** — pick 24–40 household-relevant glyphs and ship them as a grid picker. + +## 4. Schedule (1f) + +- Header `Schedule` / `2-week cycle · anchor , `. +- **Month grid** card: month label 11px/700 uppercase + chevron pair; weekday initials + 10px/700 `--nq-a-ink-tertiary`; day cells `aspect-ratio: 1`, radius 6px — + rotation-home days `--nq-a-selected` with `#201F7A` numerals, away days + `--nq-a-page` with `--nq-a-ink-secondary`, override days `--nq-a-warning-bg` with + `--nq-a-warning-ink` and a 1px warning border, today filled with the brand gradient in + white 800. Legend row beneath, 10.5px/600, one swatch per state. +- **Presence pattern** card: one row per child, name 13px/700 + rule 12px/600 + (`Weeks 1 & 3 of cycle`, `Every day`). +- **Overrides** card: `Add` text action; rows with `calendar-x` / `calendar-check`, + title ` away · `, meta reason, trailing `trash-2`. +- Cycle position is always computed from the anchor date. **Never** from ISO week numbers + or week parity (D-004). + +## 5. History (1g) + +- Header `History` / `Append-only event log · last 14 days` + bordered `CSV` button + (`download` glyph) that exports the filtered range. +- Stat row: `91%` On time · `128` Completed · `7` Missed, numerals 20px/800. +- Filter chips: `All events` / `Reversals` / `Missed`. +- Day-grouped sections (label 11px/700 uppercase) of event rows: 7px status dot + (green completed, `--nq-a-reversal` un-completed, `--nq-a-danger` missed) · + title ` · ` 12.5px/700 · meta 10.5px/600 giving event type, actor and + on-time flag (`completed · panel (Declan) · on time`, `uncompleted · joshua (admin)`, + `missed · nightly sweep`, `completed · panel (Declan) · late 22 min`) · time 11px/600. +- Footer strip (`--nq-a-page`, `lock` glyph): "Events are never edited or deleted. A + reversal is its own row." Rows are never editable in the UI (D-005). + +## 6. Presence override editor (1i) + +Pushed screen with a back chevron; title `Presence override` / sub "Beats the custody +pattern for these dates". + +- Form card (roomy density, 18px padding, 16px gaps): + **Child** 3-up segmented, selected = gradient; + **Status for these dates** 2-up segmented (`Away` / `Home`), selected = `#18181D`; + **From** / **Through** date fields side by side, 13px padding, 14px/700 values; + **Reason (optional)** text field. +- **Consequence warning** — `--nq-a-warning-bg` card with `alert-circle`: + headline `This removes N upcoming tasks` 13.5px/700 `--nq-a-warning-ink`, body 12px/600 + `--nq-a-warning-ink-2` naming the child, the date range, and stating "Completed days are + untouched." The count must be computed live from the materialiser before saving. +- Footer: `Cancel` (flex 1, white, bordered) + `Save override` (flex 2, gradient), 15px + vertical padding. +- Saving must never delete an instance that already has a completion event. diff --git a/design/DESIGN-HANDOFF.md b/design/DESIGN-HANDOFF.md new file mode 100644 index 0000000..bacaa15 --- /dev/null +++ b/design/DESIGN-HANDOFF.md @@ -0,0 +1,82 @@ +# NestQuest — design handoff + +Approved UI design for the NestQuest Home Assistant integration, ready to implement. +Source of truth for visuals: the mockup file `NestQuest Mockups.dc.html` (9 screens, +option ids `1a`–`1j`). This folder is the written form of those screens. + +## What is in here + +| File | Contents | +|---|---| +| `PANEL-SPEC.md` | Kids' DAKOS wall panel: party board, quest log, confirm, quest complete | +| `ADMIN-SPEC.md` | Parent phone admin: today, definitions, schedule, history, overrides | +| `ENTITIES-AND-SERVICES.md` | Every screen mapped to the HA entities, services and events it needs | +| `IMPLEMENTATION-ORDER.md` | Build order, with the decisions that change existing code first | +| `tokens/nestquest-panel-tokens.css` | Panel card custom properties | +| `tokens/nestquest-admin-tokens.css` | Admin card custom properties | + +## Screen index + +| id | Screen | Surface | Spec | +|---|---|---|---| +| 1a | The Party (attract) | panel 1920×1080 | PANEL §2 | +| 1b | Quest Log | panel 1920×1080 | PANEL §3 | +| 1j | Complete confirm | panel 1920×1080 | PANEL §4 | +| 1c | Quest complete | panel 1920×1080 | PANEL §5 | +| 1d | Today (compact) | phone 390×844 | ADMIN §2 | +| 1e | Definitions + edit sheet | phone 390×844 | ADMIN §3 | +| 1f | Schedule + custody | phone 390×844 | ADMIN §4 | +| 1g | History + CSV | phone 390×844 | ADMIN §5 | +| 1h | Today (roomy) | phone 390×844 | ADMIN §2.4 | +| 1i | Presence override | phone 390×844 | ADMIN §6 | + +## Design decisions taken during this handoff + +These were open in CTXD and are now settled. Several contradict the current repo and +docs — read `IMPLEMENTATION-ORDER.md` §1 before writing code. + +1. **`quest_*` naming throughout.** The domain model is renamed from `task_*` to + `quest_*` in DB, services, entities and UI. Migration note in IMPLEMENTATION-ORDER §1.1. +2. **Instance key becomes `(definition_id, child_id, due_date, window)`.** Required by + multi-assignee definitions and by twice-daily quests. Supersedes the documented + `(definition_id, due_date)`. +3. **A definition may have multiple assignees**, and materialises one instance per + assigned child per date per window. Each child completes their own. +4. **A definition may declare more than one window** (morning / afternoon / evening), + producing one instance per window per day. +5. **Missed quests vanish from the panel at rollover.** Kids never see a wall of failure; + the missed event is in the parent's history only. +6. **Three Lovelace cards**: `nestquest-party-board-card`, `nestquest-quest-log-card`, + `nestquest-admin-card`. Board and log live in separate Lovelace views; a crest tap + navigates. +7. **Completion needs a confirm step** (screen 1j) because children cannot undo. +8. **The quest log owns idle return**: 40 s of no touch navigates back to the board. + TouchHub Auto-Return must be configured longer than 40 s so the two do not fight. +9. **Actor on a panel completion** is `panel` plus the tapped child profile, per glossary. +10. **Weather is a configured entity** on the party board card (`weather_entity`), not + NestQuest data. +11. **Fonts are bundled** as local woff2 in the card's `www/` folder — the panel may be + offline. Nunito (400/600/700/800) and Cinzel + Cinzel Decorative. + +## Vocabulary + +The panel speaks tabletop; the admin speaks plainly. Both sit on the same `quest_*` model. + +| Model / admin | Panel display string | +|---|---| +| quest instance | quest | +| child | adventurer | +| child's task list | Quest Log | +| household | The Party | +| all complete | Quest complete | +| absent | On travels | + +## Open questions + +- Empty and error states are unspecified by request (child with no quests, everyone away, + integration unconfigured, DB unreachable). Panel must not render a blank parchment — + decide before shipping the card. +- No gamification is in scope. XP, levels, streaks and rarity were explicitly deferred, + but the append-only event log already supports computing them later. +- Icon per quest definition is chosen by the parent from a Lucide subset; the subset is + not yet defined (`ADMIN-SPEC.md` §3.3). diff --git a/design/ENTITIES-AND-SERVICES.md b/design/ENTITIES-AND-SERVICES.md new file mode 100644 index 0000000..798926b --- /dev/null +++ b/design/ENTITIES-AND-SERVICES.md @@ -0,0 +1,117 @@ +# Screens → HA entities, services and events + +Planned surface from CTXD CONTEXT.MD, adjusted for the design decisions in +`DESIGN-HANDOFF.md`. Names use the `quest_*` model. + +## 1. Entities the cards read + +| Entity | Type | Used by | +|---|---|---| +| `sensor.nestquest__quests_due_today` | int | 1a plate progress, 1b counts, 1d stats | +| `sensor.nestquest__quests_completed_today` | int | 1a, 1b, 1c, 1d, 1h | +| `sensor.nestquest__quests_remaining_today` | int | 1b remaining block, 1d | +| `sensor.nestquest__completion_pct_today` | float | progress bars | +| `sensor.nestquest__next_quest` | str + attrs | optional panel hint | +| `binary_sensor.nestquest__all_done` | bool | 1c trigger, 1d "all done" | +| `binary_sensor.nestquest__present_today` | bool | 1a away plate, 1d away row | +| `sensor.nestquest_household_quests_due_today` | int | 1b party line, 1c party stat | +| `sensor.nestquest_household_quests_completed_today` | int | same | +| `sensor.nestquest_cycle_day` | int | 1d sub-line, 1f header | +| *configured* `weather.` | HA core | panel dock | + +Per-instance data is too granular for entity state. The cards read the day's instance list +from an attribute payload on the per-child sensor (or a websocket command — +implementer's call), shaped: + +```json +{ + "instances": [ + { "id": 4821, "definition_id": 12, "child_id": 3, "title": "Fill bird feeder", + "icon": "bird", "window": "afternoon", "due_time": "17:00", + "state": "open", "overdue": true, "completed_at": null, "on_time": null } + ] +} +``` + +`state` ∈ `open` | `completed`. Missed instances are **omitted** from panel payloads +(decision 5) and included in admin payloads. + +## 2. Services the cards call + +| Service | Admin only | Called from | +|---|---|---| +| `nestquest.complete_quest` | no (panel allowed) | 1j confirm | +| `nestquest.uncomplete_quest` | **yes** | 1d `Undo` | +| `nestquest.create_quest_definition` | **yes** | 1e `New` | +| `nestquest.update_quest_definition` | **yes** | 1e sheet `Save changes` | +| `nestquest.set_quest_definition_active` | **yes** | 1e row action | +| `nestquest.set_presence_pattern` | **yes** | 1f presence card | +| `nestquest.create_presence_override` | **yes** | 1i `Save override` | +| `nestquest.delete_presence_override` | **yes** | 1f override row `trash-2` | +| `nestquest.export_history_csv` | **yes** | 1g `CSV` | +| `nestquest.manage_child` | **yes** | settings | + +`complete_quest` payload from the panel: + +```yaml +service: nestquest.complete_quest +data: + instance_id: 4821 + actor: panel # kiosk user context + actor_child_id: 3 # the tapped profile (decision 9) +``` + +Every service resolves `call.context.user_id` against the admin allowlist and **fails +closed** with no user context (D-003). The panel's kiosk user is never allowlisted, so +`uncomplete_quest` is unreachable from the wall even though the card never renders the +control. + +## 3. Events the cards listen for + +| Event | Effect | +|---|---| +| `nestquest_quest_completed` | stamp the seal, re-sort the column, refresh counts | +| `nestquest_quest_uncompleted` | un-seal, restore the card (admin action, panel must reflect it) | +| `nestquest_quest_missed` | remove the instance from the panel; add a history row | +| `nestquest_child_day_complete` | show screen 1c | + +All four come off the `DataUpdateCoordinator`; cards should also refresh on +`state_changed` for their sensors so they recover if an event is dropped. + +## 4. Card configuration + +```yaml +# kids' panel, view 1 +type: custom:nestquest-party-board-card +weather_entity: weather.home # optional; dock falls back to date/time +quest_log_path: /nestquest/log # view the crest tap navigates to +child_order: [declan, jordyn, chloe] + +# kids' panel, view 2 +type: custom:nestquest-quest-log-card +board_path: /nestquest/board +idle_return_seconds: 40 # must be shorter than TouchHub Auto-Return +confirm_timeout_seconds: 15 +complete_screen_seconds: 12 + +# parent phone +type: custom:nestquest-admin-card +density: compact # compact | roomy +default_tab: today +``` + +The quest log reads the selected child from the view's URL/`hass` path parameter so the +board → log hop is a plain Lovelace navigation (decision 6). + +## 5. Data-shape consequences of the design + +1. Instance key is `(definition_id, child_id, due_date, window)` — the schema's + `task_instances` unique index must widen. +2. `quest_definitions` needs an **assignees** join table, replacing the single + `child_id` column. +3. `quest_definitions` needs a **windows** set (morning/afternoon/evening) plus an + optional due time per window. +4. The materialiser emits one instance per (assignee × window × firing date), still + idempotent on the widened key, still only when the child is present. +5. `completion_events` gains `actor_child_id` so a panel completion records which + profile was tapped, while `actor` stays `panel`. diff --git a/design/IMPLEMENTATION-ORDER.md b/design/IMPLEMENTATION-ORDER.md new file mode 100644 index 0000000..8764254 --- /dev/null +++ b/design/IMPLEMENTATION-ORDER.md @@ -0,0 +1,80 @@ +# Implementation order + +Written for an agent working in `code.cubecraftlabs.com/Maniacs_Mansion/NestQuest`. +Steps 1.x change existing code and must land first; the frontend is step 4 onward. + +## 1. Model changes (breaking, pre-release) + +### 1.1 Rename `task_*` → `quest_*` +Nothing is released (main/dev are at "first commit", version 0.1.0), so rename in place +rather than aliasing. + +- `schema.py`: `task_definitions` → `quest_definitions`, `task_instances` → + `quest_instances`. Keep `completion_events`, `children`, `admin_users`, + `schedule_rules`, `presence_schedules`, `presence_overrides`. +- `db.py` / `store.py`: rename DAO methods and any SQL literals. +- `const.py`: no table names live there today; keep it that way. +- `tests/test_schema.py`, `tests/test_store.py`, `tests/test_db.py`: update expectations. +- **Migration note:** the versioned migration runner has no released version to upgrade + from. Bump the schema version, and make migration 1 create the `quest_*` tables + directly. If any dev machine already holds a `nestquest.db` with `task_*` tables, the + runner should detect the legacy names and `ALTER TABLE ... RENAME TO` before applying + the widened index — a dev-only path, worth ~20 lines and a test. + +### 1.2 Widen the instance key +Unique index becomes `(definition_id, child_id, due_date, window)`. Materialisation stays +idempotent on that tuple. Add a test for the twice-daily case (one definition, two windows, +one child, one date → two rows) and the shared case (one definition, three assignees, one +window → three rows). + +### 1.3 Multi-assignee definitions +Add `quest_definition_assignees(definition_id, child_id)`. Drop the single `child_id` +column on the definition. The "exactly one assigned child" rule in GLOSSARY.MD no longer +holds — CTXD has been updated. + +### 1.4 Windows +Add `quest_definition_windows(definition_id, window, due_time)` with +`window ∈ ('morning','afternoon','evening')`. Window clock ranges are fixed for now: +morning ≤ 11:59, afternoon 12:00–17:00, evening 17:00–21:00. Store the ranges in +`const.py` so they are configurable later. + +### 1.5 `actor_child_id` on completion events +Nullable int. Set for panel completions, null for admin ones. Still append-only. + +## 2. Engines and materialisation +Unchanged in intent: pure recurrence engine, pure presence engine with anchor-date +arithmetic, daily materialiser over `horizon_days` at `day_rollover_time`. Only the +instance fan-out changes (assignees × windows). + +## 3. Services, permissions, entities +Implement the service table in `ENTITIES-AND-SERVICES.md` §2 behind the permission +decorator, then the sensors and binary sensors in §1, then the four events in §3. +`complete_quest` is the only service a non-admin context may call. + +## 4. Frontend groundwork +1. Create `custom_components/nestquest/www/`. +2. Bundle fonts as woff2: Nunito 400/600/700/800; Cinzel 600/700/900; Cinzel Decorative + 700/900. Declare `@font-face` with `font-display: swap` in a shared + `nestquest-fonts.css`. No CDN — the panel may be offline. +3. Copy `design/tokens/nestquest-panel-tokens.css` and + `design/tokens/nestquest-admin-tokens.css` in as the cards' `:host` blocks. +4. Register the three cards with `customCards` and ship editors later; hand-written YAML + is fine for v1. + +## 5. Cards, in this order +1. `nestquest-party-board-card` (screen 1a) — read-only, proves the entity payload. +2. `nestquest-quest-log-card` (1b) — read-only first, no completion. +3. Confirm dialog + `complete_quest` (1j) with optimistic seal and revert-on-failure. +4. Quest complete screen (1c) driven by `nestquest_child_day_complete`. +5. Idle return (40 s) and the documented TouchHub Auto-Return constraint. +6. `nestquest-admin-card`: Today (1d) → Definitions + sheet (1e) → Schedule (1f) → + History + CSV (1g) → Presence override (1i). + +## 6. Before shipping +- `use_x_frame_options: false` is required for TouchHub embedding — document the + clickjacking trade-off next to the setting, per D-006. +- Verify no NestQuest control in the dock strip is tappable. +- Verify the kiosk HA user is absent from the admin allowlist, and that + `uncomplete_quest` denies it with a logged denial. +- Empty and error states are still unspecified (see DESIGN-HANDOFF "Open questions"). + Do not ship a card that renders a blank parchment. diff --git a/design/PANEL-SPEC.md b/design/PANEL-SPEC.md new file mode 100644 index 0000000..4e5f14b --- /dev/null +++ b/design/PANEL-SPEC.md @@ -0,0 +1,170 @@ +# Panel spec — kids' DAKOS wall panel + +Screens `1a`, `1b`, `1j`, `1c` of the mockups. Two cards: +`nestquest-party-board-card` (§2) and `nestquest-quest-log-card` (§3–5). + +## 1. Frame and constraints + +- **Canvas** 1920×1080, landscape, fixed. No scrolling — content must fit. Every screen in + the mockups measures exactly 1920×1080 with no overflow; keep it that way. +- **Viewing distance** ~3 ft. **No text below 22px.** Meta lines are 22px/600 Nunito; + quest titles 30px/700 Cinzel; section heads 32px. +- **Contrast** every ink token is ≥4.5:1 on parchment. Do not introduce alpha-muted text. +- **Ground** `--nq-p-parchment` + `--nq-p-vignette`, CSS only, no image assets. +- **Double frame** inset 26px (`--nq-p-frame-outer`, radius 14px) and inset 36px + (`--nq-p-frame-inner`, radius 8px), both `pointer-events: none`. +- **Dock** bottom strip, 84px, inset 46px, radius 14px, `--nq-p-dock-bg`. Weather only. + **Nothing in the dock is tappable** (D-006). +- **No login.** Every action comes from the kiosk HA user; the kiosk user is never in the + admin allowlist. + +## 2. The Party — attract state (1a) + +Default view on the wall. Layout top to bottom: + +1. **Wordmark row** at y=64. Rule · d20 hexagon · `NestQuest` (66px/900 display, brand + gradient via `background-clip: text`) · d20 · rule. d20 marks are 34px hexagons + (`clip-path: polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)`) filled with + the brand gradient, numeral 15px/700 Cinzel. +2. **Kicker** `The Party · , ` — 21px/600 Cinzel, tracking .30em, uppercase. +3. **Adventurer plates**, 3-up grid, 56px gap, page inset 110px, top y=262. One plate per + child returned by the party sensor, in configured order. +4. **Hint line** `Tap your crest to open your Quest Log`, 27px/600 Nunito, y≈930. +5. **Dock.** + +### 2.1 Adventurer plate (present) + +Radius 20px, `--nq-p-card-panel`, 2px `--nq-p-panel-border`, `--nq-p-panel-shadow`, +padding 44px 28px 40px, contents centred, 26px gap. **The whole plate is the tap target** +(≥188px wide, ≥600px tall — far above minimum). + +- **Crest** 188×214, shield via `clip-path: polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)`, + `--nq-p-crest`, inset ring `inset 0 0 0 4px rgba(255,255,255,.22)`, child's initial + 82px/700 display in white. +- **Name** 48px/700 Cinzel, `--nq-p-ink`, tracking .02em. +- **Presence pill** radius 9999px, `#e8f4ea` on `rgba(22,120,60,.35)`, `check-circle` 22px + `#157a3c`, label `Home today` 19px/600 Cinzel uppercase `#146b36`. +- **Progress line** `N of M quests claimed` 26px/700 Cinzel `--nq-p-ink-secondary`. +- **Progress bar** full width, 16px, radius 9999px, track `rgba(92,62,26,.18)` with 1px + border, fill `--nq-brand-gradient-h`. + +### 2.2 Adventurer plate (away) + +Same geometry, muted: `linear-gradient(#f2ecdd, #e6dcc6)`, **2px dashed** border, no lift +shadow. Crest uses `--nq-p-crest-away`; initial stays **full-opacity white** (never alpha). +Name in `--nq-p-ink-away`. Pill: `tent` icon, label `On travels`, neutral fill. Progress +line reads `Returns , `; empty progress track, no fill. **Not tappable** — +the plate must not navigate, since the child has no instances. + +### 2.3 Dock + +`cloud-sun` 40px · temperature 38px/800 Nunito `--nq-p-dock-ink` · 1px divider · +condition + high/low 26px/600 `--nq-p-dock-ink-secondary` · divider · one short forecast +phrase. All read from the configured `weather_entity`; if unset, render the dock with date +and time only rather than removing it (the strip is part of the frame). + +## 3. Quest Log (1b) + +Reached by tapping a present crest. Header at y=56, page inset 62px: + +- Small crest 96×110 (initial 44px/700 display). +- Title `'s Quest Log` 56px/700 display. +- Sub `, · N of M claimed` 20px/600 Cinzel, tracking .24em, uppercase. +- **Remaining block** right-aligned: radius 14px panel, d20 mark 44px with the remaining + count, `N quests left` 28px/700 Cinzel, and a party line + `Party progress · X of Y today` 22px/600 Nunito. + +### 3.1 Window columns + +Three equal columns, 34px gap, top y=236, one per window — **Morning** (until 11:59 AM), +**Afternoon** (12:00–5:00 PM), **Evening** (5:00–9:00 PM). Column head: icon +(`sunrise` / `sun` / `moon`) 34px, name 32px/700 Cinzel, sub-label 21px/600 Cinzel +uppercase with the window's clock range, right-aligned `done/total` 26px/700 Cinzel, +14px bottom padding, `--nq-p-rule` underline. + +Cards stack at `--nq-p-quest-gap` (24px). Order within a column: **incomplete first, +sealed below**. A window with no quests renders its head and nothing else — do not +collapse the column, the three-column rhythm is the layout. + +### 3.2 Quest card — open + +`--nq-p-quest-min-height` 116px, radius 12px, `--nq-p-card-open`, 1px +`--nq-p-card-border`, `--nq-p-card-shadow`, padding 22px, 22px gap, row layout: + +1. **Icon tile** 64×64, radius 10px, `--nq-p-icon-tile`, Lucide glyph 34px white. +2. **Text** title 30px/700 Cinzel `--nq-p-ink`; meta 22px/600 Nunito + `--nq-p-ink-secondary` reading `Due by `. If past due: + `Overdue · due ` in 22px/700 Cinzel uppercase, tracking .10em, + `--nq-p-ink-late`. +3. **Complete button** `--nq-p-button-height` 72px × min 150px, radius 10px, + `--nq-brand-gradient`, `check` 30px + `Complete` 24px/700 Cinzel white, + `box-shadow: 0 3px 0 rgba(40,20,60,.35)`. + +Both the card body and the button open the confirm dialog (§4). Press feedback: +`scale(.98)`, `--nq-dur-micro`. + +### 3.3 Quest card — sealed + +`--nq-p-card-done`, `opacity: .82`, no shadow, **not tappable**. Icon tile +`--nq-p-icon-tile-done` with the glyph in `--nq-p-ink-secondary`. Title 30px/700 Cinzel +`--nq-p-ink-muted` with `text-decoration: line-through`. Meta reads +`Claimed ` 22px/600 Nunito `--nq-p-ink-secondary`. + +**Wax seal** `--nq-p-seal-size` 78px circle, `--nq-p-seal`, `--nq-p-seal-shadow`, +`check` 38px in `rgba(255,235,235,.95)`, absolutely positioned `right: 16px; top: -12px`, +rotated a few degrees — **vary the rotation per card** (−9°, +6°, −4° in the mockup) so the +board looks stamped by hand, not templated. Entrance: fade + `scale(1.15 → 1)`, +`--nq-dur-base`, `--nq-ease-out`. + +### 3.4 Party roll-up + +An optional dashed panel in the afternoon column: label `Party roll-up` 18px/600 Cinzel +uppercase, body 23px/600 Nunito `--nq-p-ink-secondary` — one or two sentences on the other +children ("Jordyn has finished her log. Chloe is on travels until Sunday."). Read-only. + +### 3.5 Idle return + +40 s with no touch → navigate back to the party board view. Reset on any pointer or touch +event. The card owns this; TouchHub Auto-Return must be set longer than 40 s. + +## 4. Complete confirm (1j) + +Modal over the dimmed log — scrim `rgba(30,20,10,.62)` over the live board. + +Dialog 1000px wide at `top: 196px; left: 460px`, radius 20px, parchment panel with 3px +`rgba(92,62,26,.5)`, `box-shadow: 0 26px 60px rgba(20,10,0,.5)`, padding 64px 68px 56px. + +- Header row: 104px icon tile (radius 12px, brand gradient, glyph 56px) · window kicker + 21px/600 Cinzel uppercase tracking .24em · quest title 58px/700 display. +- Body 32px/600 Nunito `#3f2f1c`: `Mark this quest complete? Once the seal is set, only a + parent can undo it.` +- Buttons, 24px gap, both `--nq-p-confirm-button` 108px tall: + **Not yet** flex 1, `rgba(92,62,26,.1)` with 2px border, `x` 34px + 30px/700 Cinzel; + **Complete** flex 2, brand gradient, `check` 38px + 32px/700 Cinzel white. +- Dialog enters with fade + `scale(.96 → 1)`, `--nq-dur-modal`, `--nq-ease-out`. +- Dismiss: **Not yet**, scrim tap, or 15 s of no input. + +On confirm, call the completion service optimistically, stamp the seal, and re-sort the +column. On service failure, revert the card and surface a single-line parchment toast. + +## 5. Quest complete (1c) + +Shown when the last instance for a child's day is completed; auto-returns to the board +after 12 s (countdown line `Returning to The Party in N seconds`, 25px/600 Nunito). + +- Large seal 168px at y=96 (`check` 86px), rotated −6°, `0 10px 24px rgba(60,10,20,.42)`. +- `Quest complete` 86px/900 display, brand-gradient text fill. +- Sub 34px/600 Nunito `--nq-p-ink-secondary`, max-width 1000px, centred, `text-wrap: pretty`: + names the child, the date, and how the day went. +- **Stat blocks** 4-up at y=540, inset 200px, 26px gap: radius 16px parchment panels, + numeral 58px/900 Cinzel, label 17px/600 Cinzel uppercase tracking .14em — + `Quests claimed`, `On time`, `Late`, `The Party` (household X/Y). +- Dock switches to the evening variant (`cloud-moon`). + +## 6. Accessibility and input + +- Minimum tap target on this surface is the quest card itself (116px) and the 72px button; + nothing interactive is smaller than 72px. +- 24px between adjacent tap targets so a stray finger cannot claim the wrong quest. +- No hover states — this is a touch-only surface. Provide `:active` feedback only. +- No animation loops. Entrances only, per the CubeCraft motion rules.