init project

This commit is contained in:
root
2026-06-25 19:06:59 -04:00
parent c5dfee6efb
commit 5d017f533a
349 changed files with 31330 additions and 0 deletions
@@ -0,0 +1,19 @@
Event,Trigger,Properties,PII_Allowed,Purpose
page_view,Prototype loaded,"locale,theme,path,hash,session_id,test_mode,referrer_origin",No,Session start and denominator
section_view,Section reaches 35% visibility,"section,locale,theme,hash,session_id",No,Discovery sequence
navigation_click,Primary/mobile nav link used,"target,locale,theme,hash,session_id",No,Navigation path
product_tour_open,Tour opened,"locale,theme,hash,session_id",No,Tour discovery
product_tour_step,Tour step rendered,"step,locale,theme,session_id",No,Progress/drop-off
product_tour_complete,Final tour step completed,"locale,theme,session_id",No,Completion
product_tour_close,Tour closed,"reason,last_step,locale,theme,session_id",No,Abandonment point
locale_change,Language selected,"from,to,context,session_id",No,Locale use/context preservation
theme_change,Theme selected,"from,to,locale,session_id",No,Theme use
mobile_nav_open,Drawer opened,"locale,theme,session_id",No,Mobile discovery
mobile_nav_close,Drawer closed,"locale,theme,session_id",No,Drawer completion
demo_open,Demo opened,"source,locale,theme,session_id",No,CTA source
demo_validation_error,Validation blocked submit,"source,error_count,locale,theme,session_id",No,Error rate; never field values
demo_submit_attempt,Submit activated,"source,locale,theme,session_id",No,Funnel attempt
demo_submit_failure,Simulated service failure,"source,scenario,locale,theme,session_id",No,Failure recovery
demo_submit_success,Prototype confirmation reached,"source,locale,theme,session_id",No,Completion
demo_close,Demo closed,"reason,locale,theme,session_id",No,Abandonment
faq_open,FAQ disclosure opened,"index,locale,theme,session_id",No,Question discovery
1 Event Trigger Properties PII_Allowed Purpose
2 page_view Prototype loaded locale,theme,path,hash,session_id,test_mode,referrer_origin No Session start and denominator
3 section_view Section reaches 35% visibility section,locale,theme,hash,session_id No Discovery sequence
4 navigation_click Primary/mobile nav link used target,locale,theme,hash,session_id No Navigation path
5 product_tour_open Tour opened locale,theme,hash,session_id No Tour discovery
6 product_tour_step Tour step rendered step,locale,theme,session_id No Progress/drop-off
7 product_tour_complete Final tour step completed locale,theme,session_id No Completion
8 product_tour_close Tour closed reason,last_step,locale,theme,session_id No Abandonment point
9 locale_change Language selected from,to,context,session_id No Locale use/context preservation
10 theme_change Theme selected from,to,locale,session_id No Theme use
11 mobile_nav_open Drawer opened locale,theme,session_id No Mobile discovery
12 mobile_nav_close Drawer closed locale,theme,session_id No Drawer completion
13 demo_open Demo opened source,locale,theme,session_id No CTA source
14 demo_validation_error Validation blocked submit source,error_count,locale,theme,session_id No Error rate; never field values
15 demo_submit_attempt Submit activated source,locale,theme,session_id No Funnel attempt
16 demo_submit_failure Simulated service failure source,scenario,locale,theme,session_id No Failure recovery
17 demo_submit_success Prototype confirmation reached source,locale,theme,session_id No Completion
18 demo_close Demo closed reason,locale,theme,session_id No Abandonment
19 faq_open FAQ disclosure opened index,locale,theme,session_id No Question discovery
@@ -0,0 +1,33 @@
# Interaction Notes
## Navigation
Anchor navigation updates the URL hash and lands the target below the sticky header using `scroll-margin-block-start`. Locale switching preserves only hashes listed for the route. Mobile navigation closes after an internal destination activates. Focus is returned only when the close action does not immediately navigate.
## Drawer
`closed → opening → open → closing → closed`. Opening stores the active element, sets `aria-expanded=true`, reveals backdrop, makes page landmarks inert, locks body scroll without layout shift, and focuses the close control. Tab and Shift+Tab remain inside. Escape and backdrop close unless a nested blocking dialog is active.
## Dialogs
Use native `<dialog>` when browser support and QA pass; otherwise use a proven accessible primitive. The trigger regains focus on close. Backdrop click is permitted when the form is idle. During submission, closing requires an explicit cancel decision so an in-flight request is not silently duplicated.
## Demo validation
Validation occurs on submit, then on change/blur for fields that are already invalid. A form summary uses `role=alert` or an announced live region. First invalid field receives focus after the summary is updated. Native input semantics remain. Email syntax validation is deliberately modest; the server remains authoritative.
## Theme
Apply preference immediately. Persist cookie and storage in one transaction. Broadcast changes through the `storage` event. In System mode, respond to `prefers-color-scheme` changes. Never animate the entire page between themes.
## Product tour
Opening starts at step one. Back is disabled on the first step. Next becomes Finish on the last. Step heading receives programmatic focus after each change. Progress text is localized and announced politely. Completion does not imply a video was watched.
## FAQ
Native `details/summary` is the baseline. Multiple items may remain open. Do not introduce accordion exclusivity without a content reason and a new decision.
## Analytics
Events are emitted through a vendor-neutral adapter after consent. Event payloads contain route, locale, resolved theme, direction, component/source ID, and non-PII state. Form field values and free text are forbidden. Development logs must be disabled in production.
@@ -0,0 +1,9 @@
export type AnalyticsEventName =
| "page_view" | "section_view" | "navigation_click"
| "locale_change" | "theme_change" | "mobile_nav_open" | "mobile_nav_close"
| "product_tour_open" | "product_tour_step" | "product_tour_complete" | "product_tour_close"
| "demo_open" | "demo_submit_attempt" | "demo_validation_error" | "demo_submit_success" | "demo_submit_failure" | "demo_close"
| "faq_open";
export interface AnalyticsContext { routeId:string; locale:"en"|"fr"|"ar"; direction:"ltr"|"rtl"; resolvedTheme:"light"|"dark"; source?:string; step?:number; errorCode?:string; }
export interface AnalyticsAdapter { consentGranted():boolean; track(name:AnalyticsEventName, context:AnalyticsContext):void; }
export const forbiddenPayloadKeys = ["name","email","company","message","phone","fieldValue","serverResponse"] as const;
@@ -0,0 +1,5 @@
export const fleetSizeValues = ["1-9","10-24","25-49","50-99","100-249","250+"] as const;
export type FleetSize = typeof fleetSizeValues[number];
export interface DemoRequest { fullName:string; workEmail:string; company:string; fleetSize:FleetSize; market?:string; preferredLanguage?:"en"|"fr"|"ar"; message?:string; idempotencyKey:string; source:string; }
export interface DemoSuccess { requestId:string; acceptedAt:string; nextStepCode:"sales_followup"|"manual_review"; }
export interface DemoFailure { code:"validation"|"rate_limited"|"duplicate"|"unavailable"|"unknown"; retryable:boolean; fieldErrors?:Record<string,string>; }
@@ -0,0 +1,14 @@
(function () {
var allowed = { light: true, dark: true, system: true };
var cookieMatch = document.cookie.match(/(?:^|; )hpc-theme=([^;]+)/);
var cookieValue = cookieMatch ? decodeURIComponent(cookieMatch[1]) : null;
var stored = null;
try { stored = localStorage.getItem("hpc.theme.preference"); } catch (_) {}
var preference = allowed[cookieValue] ? cookieValue : (allowed[stored] ? stored : "system");
var dark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
var resolved = preference === "system" ? (dark ? "dark" : "light") : preference;
var root = document.documentElement;
root.dataset.themePreference = preference;
root.dataset.theme = resolved;
root.style.colorScheme = resolved;
})();