chore: fix prettier formatting, regenerate phase9 manifest, add missing config files (.env.example, .node-version, .nvmrc, CI workflow)
CI / test (push) Failing after 28s
CI / test (push) Failing after 28s
This commit is contained in:
@@ -37,11 +37,11 @@ The implementation must:
|
||||
|
||||
Breakpoints are layout decisions, not device labels.
|
||||
|
||||
| Mode | CSS range | Reference frames | Required behavior |
|
||||
|---|---:|---|---|
|
||||
| Compact | 0–767 px | 320, 390 | One-column sections; mobile drawer; stacked CTAs; product preview simplified; no horizontal page scroll |
|
||||
| Medium | 768–1119 px | 768, 1024 | Two-column where specified; mobile drawer remains; 4-column workflow allowed; balanced content widths |
|
||||
| Wide | 1120 px and above | 1280, 1440 | Desktop navigation and controls; 12-column composition; hero 7/5 split; max container 1280 px |
|
||||
| Mode | CSS range | Reference frames | Required behavior |
|
||||
| ------- | ----------------: | ---------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| Compact | 0–767 px | 320, 390 | One-column sections; mobile drawer; stacked CTAs; product preview simplified; no horizontal page scroll |
|
||||
| Medium | 768–1119 px | 768, 1024 | Two-column where specified; mobile drawer remains; 4-column workflow allowed; balanced content widths |
|
||||
| Wide | 1120 px and above | 1280, 1440 | Desktop navigation and controls; 12-column composition; hero 7/5 split; max container 1280 px |
|
||||
|
||||
Do not infer new behavior between breakpoints. Follow `components/RESPONSIVE_BEHAVIOR_MATRIX_v1.0.csv`.
|
||||
|
||||
|
||||
@@ -3,164 +3,164 @@
|
||||
**Version:** 9.0
|
||||
**Phases:** 1 — Design Foundation through 9 — Developer Handoff
|
||||
|
||||
| ID | Decision | Status | Rationale | Consequence |
|
||||
|---|---|---|---|---|
|
||||
| D-001 | Use restrained operational blue as the main brand family | Approved | Supports trust, clarity, and product-led presentation | Blue carries navigation, links, selection, and routine primary actions |
|
||||
| D-002 | Reserve orange for demo conversion and tightly related emphasis | Approved | Preserves conversion salience and prevents visual noise | Orange is not a routine navigation or generic action color |
|
||||
| D-003 | Use Inter Variable for English and French | Approved | Screen-oriented, broad Latin support, strong UI and marketing utility | One Latin family across website and product-facing UI |
|
||||
| D-004 | Use Noto Sans Arabic for Arabic | Approved | Broad Arabic-script support and suitable sans-serif structure | Arabic receives separate metrics and locale loading |
|
||||
| D-005 | Use one token system with separate display and UI roles | Approved | Avoids two unrelated systems while respecting different density needs | Marketing headings and application labels do not share identical sizing |
|
||||
| D-006 | Adopt blue-600 `#1B5DD8` as brand anchor | Approved | Clear operational blue with useful lighter and darker neighbors | Accessible action background uses blue-700 |
|
||||
| D-007 | Adopt orange-600 `#EA580C` as accent anchor | Approved | Strong conversion accent without becoming the whole brand | White-text conversion CTA uses orange-700 |
|
||||
| D-008 | Ban orange-600 with white normal text | Approved restriction | 3.56:1 contrast fails normal-text AA | Use orange-700 in light theme or orange-400 with dark text in dark theme |
|
||||
| D-009 | Use a 4 px spacing base | Approved | Predictable, scalable, compatible with compact and spacious layouts | Exceptions require explicit optical rationale |
|
||||
| D-010 | Use 4/8/12 column grids for mobile/tablet/desktop | Approved | Distinct responsive composition without device-specific hacks | Reference frames: 390, 1024, 1440 px |
|
||||
| D-011 | Cap primary container at 1280 px | Approved | Protects readability while allowing strong product visuals | Wider screens gain outer breathing room, not stretched text |
|
||||
| D-012 | Use CSS logical properties | Approved | Required for real LTR/RTL parity | Physical left/right spacing tokens are rejected |
|
||||
| D-013 | Use Lucide as the base icon family | Approved | Consistent 24 px grid and 2 px outline language | Mixing icon families requires a documented exception |
|
||||
| D-014 | Prefer 44 × 44 px interactive targets | Approved | Exceeds the WCAG 2.2 minimum and improves usability | Compact exceptions must remain accessible |
|
||||
| D-015 | Require formal Modern Standard Arabic | Approved | Prevents inconsistent regional dialect and preserves professional tone | All Arabic product and marketing copy must be linguistically reviewed |
|
||||
| D-016 | Avoid automotive racing clichés | Approved | Product is operational software, not performance-car entertainment | No speedometers, racing stripes, carbon fiber, or aggressive neon styling |
|
||||
| R-001 | Use orange for every primary button | Rejected | Destroys conversion hierarchy | Routine primary actions remain blue |
|
||||
| R-002 | Force identical line-height across scripts | Rejected | Damages Arabic readability and vertical rhythm | Arabic uses script-specific line heights |
|
||||
| R-003 | Use fixed-width translated buttons | Rejected | Breaks under French expansion and Arabic content | Buttons are content-driven |
|
||||
| R-004 | Mirror all icons in RTL | Rejected | Many icons are semantically direction-neutral | Mirror only directional meaning |
|
||||
| R-005 | Create one type scale for every context | Rejected | Marketing and dense UI have different jobs | Shared foundations, separate role scales |
|
||||
| D-017 | Use English as source, default, and fallback locale | Approved | Provides one controlled authoring source while all public locales remain independently addressable | Missing translations fail CI; production fallback is logged |
|
||||
| D-018 | Use locale-prefixed public URLs for every indexable page | Approved | Makes language state shareable, crawlable, and deterministic | All navigation and SEO generation use a central route manifest |
|
||||
| D-019 | Limit automatic locale detection to root `/` | Approved | Respects explicit URLs and prevents redirect loops or crawler interference | Locale-prefixed pages are never overridden by browser language |
|
||||
| D-020 | Persist explicit language selection in a first-party cookie | Approved | Allows SSR to honor user choice without relying on client-only state | Manual choice outranks Accept-Language |
|
||||
| D-021 | Use localized public slugs backed by stable route IDs | Approved | Improves localized readability while preventing routing logic from depending on translated strings | Switchers, sitemaps, and canonicals map by route ID |
|
||||
| D-022 | Use English/French LTR and Arabic document-level RTL | Approved | Correct browser bidi and layout behavior requires root direction | Arabic pages render `html lang=ar dir=rtl` |
|
||||
| D-023 | Use formal Modern Standard Arabic only | Approved | Maintains professional, region-neutral baseline | Dialect and colloquial copy fail linguistic review |
|
||||
| D-024 | Use Latin digits and Gregorian dates in the language-only baseline | Approved | Keeps operational data and identifiers consistent before market-specific preferences exist | Revisit only after Arabic market research |
|
||||
| D-025 | Use `فحص المركبة` for vehicle inspection | Approved | Clear operational term with broad formal comprehension | `معاينة المركبة` remains a documented secondary term |
|
||||
| D-026 | Use `عقد الإيجار` for rental agreement | Approved | Concise and standard legal-operational label | Jurisdictional legal review remains required |
|
||||
| D-027 | Use `مبلغ الضمان` for security deposit | Approved | Avoids confusion between deposit and insurance | Arabic `تأمين` alone is rejected |
|
||||
| D-028 | Use `معدل استخدام الأسطول` for fleet utilization rate | Approved | Describes use rather than occupancy | Occupancy language is rejected |
|
||||
| D-029 | Keep currency, country, timezone, tax, and phone behavior independent from language | Approved | Language-only locales do not define business jurisdiction | Formatting consumes explicit data and context |
|
||||
| D-030 | Use semantic namespaced translation keys | Approved | Supports reuse, context, QA, and refactoring | Source sentences, positions, and visual styles are not keys |
|
||||
| D-031 | Fail missing translations in CI | Approved | Prevents silent partial localization | Production fallback is a logged safety net, not the workflow |
|
||||
| D-032 | Use self-canonical locale pages and reciprocal language-only hreflang | Approved | Prevents English from swallowing localized indexing | `x-default` points to `/en` until a neutral selector exists |
|
||||
| D-033 | Omit regional Open Graph locale until target markets are approved | Approved | Avoids inventing country-region assumptions | Localized OG copy still ships |
|
||||
| R-006 | Use country flags as language controls | Rejected | Languages are not equivalent to countries | Display language names in their own language |
|
||||
| R-007 | Redirect explicit locale URLs based on browser language | Rejected | Breaks shareability, intent, crawling, and testing | Detection is root-only |
|
||||
| R-008 | Use IP geolocation to choose language | Rejected | Country is not language and geolocation is unreliable | Use explicit preference and Accept-Language |
|
||||
| R-009 | Use English source sentences as translation keys | Rejected | Copy edits become code changes and context is lost | Use semantic namespaced keys |
|
||||
| R-010 | Simulate Arabic by right-aligning an LTR document | Rejected | Does not provide correct bidi or layout behavior | Set document-level RTL |
|
||||
| R-011 | Translate check-in/check-out like hotel terminology | Rejected | Misstates rental operational processes | Use pickup, handover, and return terms by perspective |
|
||||
| R-012 | Infer currency or legal jurisdiction from locale | Rejected | English, French, and Arabic span multiple markets | Use explicit business data |
|
||||
| R-013 | Publish placeholder logos, metrics, testimonials, or certifications | Rejected | Creates unsupported evidence | Hide evidence sections until approved sources exist |
|
||||
| D-034 | Organize the library into foundations, behavior primitives, and marketing composites | Approved | Prevents page-specific composition from masquerading as a reusable primitive | Component APIs and ownership follow explicit tiers |
|
||||
| D-035 | Share behavior primitives with the future product dashboard, not marketing composites by default | Approved | Marketing and operational interfaces have different density and interaction requirements | Dashboard reuse requires a later density specification |
|
||||
| D-036 | Use one Button component with primary-blue and conversion-orange intents | Approved | Preserves hierarchy and avoids duplicated behavior | Orange remains restricted to demo and related conversion |
|
||||
| D-037 | Require content-driven dimensions and prohibit truncation of important copy | Approved | French/Arabic expansion and zoom invalidate fixed text geometry | Components change structure before collision |
|
||||
| D-038 | Require visible labels for all form fields | Approved | Placeholders disappear and are unreliable accessible context | Placeholder-only fields fail review |
|
||||
| D-039 | Use native HTML semantics as the baseline for controls, tables, disclosures, and dialogs | Approved | Native behavior reduces accessibility defects | Custom widgets require separate interaction contracts |
|
||||
| D-040 | Use a native date input as the Phase 3 Gregorian baseline | Approved | Custom date pickers are complex and requirements are unresolved | Custom calendar work is deferred |
|
||||
| D-041 | Use one culturally neutral full-name field in the demo form | Approved | First/last assumptions fail across naming systems | CRM mapping preserves the complete name |
|
||||
| D-042 | Keep the demo form minimally qualified | Approved | Extra fields increase abandonment and privacy burden | Required: full name, work email, company, fleet-size range |
|
||||
| D-043 | Evidence-gate metrics, testimonials, case studies, trust marks, and pricing | Approved | A visual component must not make unsupported content publishable | Missing or expired approval suppresses rendering |
|
||||
| D-044 | Approve pricing cards and pricing comparison as separate components | Approved | Plan orientation and detailed comparison are distinct tasks | Both may coexist; neither ships without approved pricing |
|
||||
| D-045 | Open mobile navigation from inline-start | Approved | Aligns navigation origin with document direction | Drawer opens left in LTR and right in RTL |
|
||||
| D-046 | Require overlay focus containment, inert background, Escape policy, and focus return | Approved | Visual layering without focus management is inaccessible | Drawer/modal implementations need behavioral tests |
|
||||
| D-047 | Place toasts at inline-end and keep critical errors persistent in context | Approved | Locale-aware placement and durable correction prevent missed failures | Error toasts cannot be the sole error record |
|
||||
| D-048 | Use the same component tree for LTR and RTL | Approved | Direction-specific implementations drift | Direction is resolved through document state, logical CSS, and selective mirroring |
|
||||
| D-049 | Respect reduced-motion preferences and keep motion non-essential | Approved | Motion should support continuity, not gate understanding | Shimmer and transforms are removed or minimized |
|
||||
| D-050 | Use English light/LTR only for the initial specimen while requiring full locale/theme tests | Approved | Gives a concrete first specimen without pretending it proves conformance | Acceptance still requires en/fr/ar and light/dark |
|
||||
| R-014 | Create separate LTR and RTL component implementations | Rejected | Guarantees drift and mismatched behavior | Use one semantic component tree |
|
||||
| R-015 | Use fixed-height cards, buttons, labels, or validation regions | Rejected | Breaks expansion, zoom, and Arabic metrics | Use content-driven block sizes |
|
||||
| R-016 | Publish placeholder metrics, logos, testimonials, certifications, or prices | Rejected | Converts missing evidence into deception | Omit until approval exists |
|
||||
| R-017 | Open navigation or critical content on hover only | Rejected | Excludes keyboard and touch users | Use explicit triggers and focus behavior |
|
||||
| R-018 | Use placeholder text instead of visible form labels | Rejected | Loses context after input and harms accessibility | Labels remain visible |
|
||||
| R-019 | Replace native select/date controls for visual consistency alone | Rejected | Adds keyboard, screen-reader, mobile, and localization risk | Native controls are baseline |
|
||||
| R-020 | Force marketing composites into the future product dashboard | Rejected | Shared appearance is not shared information architecture | Share primitives; specify product compositions later |
|
||||
| R-021 | Combine pricing cards and comparison into one shape-shifting component | Rejected | Merges distinct tasks and creates brittle responsive behavior | Maintain separate components |
|
||||
| R-022 | Auto-dismiss critical errors without a persistent correction path | Rejected | Users can miss failure and lose context | Keep errors in the owning form/page |
|
||||
| D-051 | Expose Light, Dark, and System as explicit appearance choices | Approved | Users need deterministic control while retaining platform preference support | Theme selector and persistence support all three values |
|
||||
| D-052 | Persist System as an explicit choice | Approved | System is a user preference, not merely missing state | OS changes update resolved theme while the selector remains System |
|
||||
| D-053 | Use `hpc-theme` cookie as canonical preference and local storage as a mirror | Approved | SSR needs a readable value; client tabs need immediate synchronization | Cookie wins disagreement; both are written together |
|
||||
| D-054 | Separate theme preference from resolved theme | Approved | System can resolve to Light or Dark without losing the user choice | Root uses `data-theme-preference` and `data-theme` |
|
||||
| D-055 | Apply theme before first paint with server state and a tiny inline bootstrap | Approved | Prevents incorrect-theme flash and hydration mismatch | Bootstrap runs before blocking CSS and receives a CSP nonce/hash |
|
||||
| D-056 | Use deep-navy dark surfaces rather than pure black or mechanical inversion | Approved | Preserves operational hierarchy and reduces glare without flattening the UI | Dark surface ladder uses canvas, surface, subtle, and raised roles |
|
||||
| D-057 | Use light blue with deep-navy text for routine primary actions in dark mode | Approved | Maintains brand recognition and high contrast without a luminous button wall | Dark primary action roles differ from light while preserving intent |
|
||||
| D-058 | Use orange-400 with dark text for conversion actions in dark mode | Approved | Meets contrast and preserves controlled conversion salience | Orange remains restricted to demo conversion and related emphasis |
|
||||
| D-059 | Use neutral-black dark shadows and prohibit colored glow elevation | Approved | Glow reduces precision and makes hierarchy noisy | Elevation uses borders, surface steps, and neutral shadows |
|
||||
| D-060 | Permit semantically equivalent light/dark hero artwork variants | Approved | Dark presentation may need art direction without changing the product claim | Same content/composition; only presentation treatment changes |
|
||||
| D-061 | Do not make product screenshots automatically follow website theme | Approved | A screenshot is evidence of an actual product state | Store capture theme metadata; never invert or recolor captures |
|
||||
| D-062 | Allow paired screenshot variants only for equivalent approved product states | Approved | Theme matching is useful only when evidence remains truthful | Content model records capture theme, locale, direction, and approval |
|
||||
| D-063 | Use theme-specific chart tokens plus redundant non-color encoding | Approved | Color-only data is inaccessible and fragile across themes | Charts use labels, markers, dashes, patterns, or data tables |
|
||||
| D-064 | Keep theme changes immediate and non-essential motion reduced | Approved | Global cross-fades create flash, noise, and intermediate contrast failures | No global theme transition; reduced motion removes shimmer/rotation |
|
||||
| D-065 | Update System mode live when the platform preference changes | Approved | System should continue following its stated source | Media-query changes update resolved theme only while preference is System |
|
||||
| D-066 | Set `color-scheme` from the resolved theme | Approved | Native controls and browser UI must match the rendered appearance | Selects, dates, autofill, scrollbars, and validation UI require browser testing |
|
||||
| R-023 | Create dark mode by inverting the light palette | Rejected | Numeric inversion destroys semantic hierarchy and brand intent | Map every role explicitly |
|
||||
| R-024 | Use pure black canvas and pure white text everywhere | Rejected | Produces harsh contrast and flattens hierarchy | Use deep navy and off-white/blue-gray text roles |
|
||||
| R-025 | Add blue/orange glows to cards, charts, buttons, or dialogs | Rejected | Turns operational software into decorative noise | Use neutral elevation and restrained emphasis |
|
||||
| R-026 | Store theme only in local storage and apply after hydration | Rejected | Causes incorrect-theme flash and prevents correct SSR | Use cookie, pre-paint bootstrap, and server state |
|
||||
| R-027 | Treat System as an unpersisted default | Rejected | Loses the user’s explicit preference | Persist System exactly like Light and Dark |
|
||||
| R-028 | Recolor, invert, or mirror screenshots for dark mode | Rejected | Misrepresents the product and can corrupt direction/data meaning | Use approved captures or a neutral frame |
|
||||
| R-029 | Encode chart series or status using color alone | Rejected | Fails accessibility and weakens print/forced-colors behavior | Add labels, markers, patterns, icons, or text |
|
||||
| D-067 | Use explicit bidi isolation for atomic mixed-direction values | Approved | Arabic labels frequently contain LTR operational identifiers whose punctuation otherwise reorders | Known technical values use `dir=ltr` and `bdi`/isolation at the smallest boundary |
|
||||
| D-068 | Use `dir=auto` only for genuinely unknown user-authored text | Approved | Known value classes have deterministic direction while free text may not | Translation resources and known identifiers never delegate direction to heuristics |
|
||||
| D-069 | Mirror icons by semantic metadata rather than global RTL CSS | Approved | Global mirroring corrupts object, status, brand, and evidence assets | Icon instances declare directional, fixed, or contextual behavior |
|
||||
| D-070 | Keep DOM order aligned with Arabic visual and focus order | Approved | CSS reversal creates inaccessible mismatch and brittle responsive behavior | `row-reverse`, `column-reverse`, CSS `order`, and positive tabindex are prohibited for RTL repair |
|
||||
| D-071 | Make Arabic mobile navigation enter from inline-start | Approved | Navigation origin should follow document direction | Drawer enters from the right in Arabic and retains full focus management |
|
||||
| D-072 | Let Arabic workflow progression run from right to left while preserving authored step order | Approved | Process reading should feel native without reversing data or source arrays | Natural RTL layout places step one on the right; mobile remains top-to-bottom |
|
||||
| D-073 | Keep chronological chart axes and trend meaning independent from RTL | Approved | Data chronology and increase/decrease meaning are domain semantics, not text direction | Charts are not automatically reversed and trend arrows are fixed |
|
||||
| D-074 | Preserve authoritative license-plate value and segment direction | Approved | Plate formats vary by country and may mix scripts | Latin, Arabic, and segmented plates use explicit direction; country templates remain market configuration |
|
||||
| D-075 | Require Arabic Light desktop and Arabic Dark mobile validation sheets | Approved | The riskiest direction/theme/responsive combinations need explicit evidence | 1440 Light desktop and 390 Dark mobile become release gates |
|
||||
| D-076 | Confirm Latin digits and Gregorian dates as the Phase 5 language-only baseline | Approved | Phase 2 already separated language from market formatting | Arabic-Indic digits or alternate calendars require explicit market research and configuration |
|
||||
| D-077 | Use visual arrow-key movement for horizontal RTL tabs and similar composites | Approved | Keyboard commands should match spatial expectation while preserving authored sequence | ArrowLeft moves visually left and ArrowRight visually right; implementation maps to logical index |
|
||||
| R-030 | Mirror every SVG or image under `dir=rtl` | Rejected | Reverses logos, vehicles, screenshots, status icons, plates, and evidence | Mirror only explicitly directional icons |
|
||||
| R-031 | Reverse arrays or use CSS row-reverse to make Arabic look correct | Rejected | Breaks DOM/focus order and causes responsive drift | Preserve authored order and rely on document direction/logical layout |
|
||||
| R-032 | Insert Unicode bidi override characters into translation strings | Rejected | Hidden controls are fragile, difficult to audit, and can create spoofing risk | Use semantic markup and isolation wrappers |
|
||||
| R-033 | Reverse chronological charts because the page is RTL | Rejected | Text direction does not redefine time or data chronology | Chart order follows the chart specification |
|
||||
| R-034 | Infer license-plate format, currency, timezone, or country from Arabic language | Rejected | Arabic spans many jurisdictions | Consume explicit business and market configuration |
|
||||
| R-035 | Leave technical fields RTL and compensate with right text alignment | Rejected | Caret, punctuation, selection, and copy behavior remain broken | Use explicit LTR direction and isolation for known technical values |
|
||||
| D-078 | Use content-pressure breakpoints rather than device-name breakpoints | Approved | Translated copy, controls, and product evidence fail at different widths | Shared ranges coordinate layout, while intrinsic sizing and container queries control components |
|
||||
| D-079 | Validate 1440, 1024, and 390 px frames plus a 320 px reflow gate | Approved | Reference designs alone do not prove intermediate or zoom behavior | Automated tests include intermediate widths and 400% zoom equivalent |
|
||||
| D-080 | Collapse full navigation below 1120 px | Approved | French and Arabic header pressure makes 1024 too narrow for stable full navigation | The 1024 frame uses compact navigation and an inline-start drawer |
|
||||
| D-081 | Keep the hero stacked below 1120 px | Approved | A forced 1024 split makes either copy or product preview less informative | Tablet uses claim, CTAs, then a full-width readable preview |
|
||||
| D-082 | Keep the demo action reachable at every width | Approved | Primary conversion cannot disappear because the header becomes compact | Below 480 px the demo CTA is the first drawer action; above it may remain in the header when it fits |
|
||||
| D-083 | Use art-directed product previews instead of shrinking desktop screenshots below legibility | Approved | Tiny desktop UI ceases to provide product evidence | Mobile uses a focused crop, mobile capture, or coded responsive state |
|
||||
| D-084 | Prohibit swipe-only carousels for core modules, trust, testimonials, and case studies | Approved | Hidden slides reduce discoverability and create gesture/accessibility dependence | Core content uses grids or vertical stacks; optional carousels require a later explicit exception |
|
||||
| D-085 | Prohibit page-level horizontal scrolling | Approved | Horizontal page scroll breaks reflow, focus visibility, and mobile use | Tables and atomic technical values may own bounded, labelled overflow |
|
||||
| D-086 | Preserve semantic pricing tables at constrained widths | Approved | Plan comparison is genuinely tabular and should not become an image or inaccessible card duplicate | Use internal horizontal scroll and a sticky inline-start feature column |
|
||||
| D-087 | Use a 45rem container threshold for two-column demo-form fields | Approved | The form may sit in sections of different widths at the same viewport | Field layout responds to its own available space |
|
||||
| D-088 | Require at least 10rem per step for a horizontal workflow | Approved | Breakpoint width alone does not protect French or Arabic step labels | Workflow stays vertical whenever actual content pressure fails the minimum |
|
||||
| D-089 | Use 18rem module cards, 20rem pricing cards, and 11rem metric cards as minimum readable widths | Approved | Fixed column counts create unreadable intermediate states | Grids use auto-fit/minmax and reduce column count before compressing content |
|
||||
| D-090 | Never truncate critical headings, labels, buttons, errors, or evidence for responsive fit | Approved | Truncation hides meaning and disproportionately harms French and Arabic | Components wrap, stack, or change structure before reducing readability |
|
||||
| D-091 | Keep 44 × 44 px minimum targets across all responsive states | Approved | Compact layouts increase, rather than reduce, touch accuracy needs | Headers, drawers, accordions, pagination, and form controls preserve target size |
|
||||
| D-092 | Remove evidence sections entirely when approved content is absent | Approved | Empty responsive slots invite placeholder or fabricated evidence | No reserved logo rails, metrics, or testimonial gaps are rendered |
|
||||
| R-036 | Use one global breakpoint set as the only adaptation mechanism | Rejected | Components fail at different content widths | Use shared coordination ranges plus intrinsic and container rules |
|
||||
| R-037 | Keep desktop navigation at 1024 by shrinking gaps and labels | Rejected | Creates a fragile header that fails translation and zoom | Use compact navigation below 1120 |
|
||||
| R-038 | Hide secondary actions on mobile | Rejected | Removes valid user paths and can trap users in one conversion route | Stack or move actions without deleting them |
|
||||
| R-039 | Scale a full desktop screenshot to fit mobile | Rejected | Makes product evidence unreadable | Use art-directed captures or coded responsive previews |
|
||||
| R-040 | Convert core module sections into swipe-only carousels | Rejected | Reduces discoverability and accessibility | Use visible grids/stacks |
|
||||
| R-041 | Allow body horizontal scrolling for comparison tables | Rejected | Breaks page navigation and reflow | Contain overflow inside the table region |
|
||||
| R-042 | Ellipsize French or Arabic headings and button labels | Rejected | Hides meaning and creates unequal localization quality | Use content-driven size and structural change |
|
||||
| R-043 | Use CSS order or DOM duplication to rearrange responsive content | Rejected | Creates focus, reading-order, maintenance, and analytics drift | Keep one semantic source order |
|
||||
| R-044 | Use fixed heights to maintain card and section alignment | Rejected | Translation, zoom, validation, and dynamic data overflow | Use content-driven block sizing |
|
||||
| D-093 | Use a workflow-first homepage narrative rather than a generic feature dump | Approved | Buyers need to understand the operating model before comparing modules | Hero, problem, workflow, roles, then modules establish the product story |
|
||||
| D-094 | Use the reservation-to-return promise as the five-second hero message | Approved | It states category, scope, and operating value without unsupported superlatives | Hero copy stays concrete across en/fr/ar |
|
||||
| D-095 | Render operational-coverage proof instead of unsupported customer-logo proof | Approved | Product scope can be shown without fabricating external trust | Lifecycle cards follow the hero; logos remain blocked |
|
||||
| D-096 | Show KPI definitions and measurement methodology instead of invented outcome numbers | Approved | Evaluation rigor is more credible than fictional percentages | Results section contains no claimed improvement values |
|
||||
| D-097 | Omit the case-study section until an approved customer evidence package exists | Approved | Empty or anonymous proof violates the evidence policy | Conditional insertion point remains after Security and before Pricing |
|
||||
| D-098 | Present integration categories without naming partners until connector availability is verified | Approved | Named logos imply working production support | Discovery confirms market, connector, and data ownership |
|
||||
| D-099 | Present security as an evidence-request checklist until certifications and reliability claims are approved | Approved | Badges without scope are decoration, not assurance | No certification, uptime, RTO, or SLA claim renders |
|
||||
| D-100 | Use quote-oriented pricing preview until the commercial model is approved | Approved | Invented tiers or prices create false expectations | Page explains scope factors and routes to pricing request |
|
||||
| D-101 | Use a coded, localized, explicitly illustrative product preview | Approved | It proves layout, theme, and RTL behavior without pretending to be production evidence | Replace or approve before launch |
|
||||
| D-102 | Repeat the demo conversion in header, hero, pricing, and final CTA while reserving orange for conversion | Approved | Repetition improves reach without flattening hierarchy | Routine navigation and product emphasis remain blue |
|
||||
| D-103 | Use a native dialog for the demo form baseline | Approved | Provides a testable accessible interaction with visible labels and error state | Production integration must complete focus, submission, consent, and analytics testing |
|
||||
| D-104 | Validate six named production frames plus a 320 px reflow gate | Approved | These frames cover the highest-risk locale, direction, theme, and responsive combinations | Screenshots and automated checks are package deliverables |
|
||||
| R-045 | Fill the trust strip with fictional customer logos | Rejected | Placeholder logos quickly become accidental production claims | Omit logos until approved |
|
||||
| R-046 | Publish “up to” performance percentages without methodology | Rejected | The qualifier does not rescue unsupported evidence | Publish only after baseline and source approval |
|
||||
| R-047 | Add anonymous testimonial quotes | Rejected | Anonymous praise is not verifiable customer evidence | Use an attributable approved case study or omit |
|
||||
| R-048 | Display common integration logos as visual shorthand | Rejected | A familiar logo still implies a connector exists | Show neutral categories until verified |
|
||||
| R-049 | Add security badges with “pending” labels to the public page | Rejected | Public users should not see internal evidence placeholders | Keep status in the internal asset and copy registers |
|
||||
| R-050 | Invent three pricing tiers to make the page look complete | Rejected | Visual completeness is not worth commercial misinformation | Use a quote-oriented preview |
|
||||
| R-051 | Hide unresolved evidence behind vague superlatives | Rejected | “Trusted,” “leading,” and “proven” are still claims | Use concrete scope, workflow, and evaluation language |
|
||||
| R-052 | Make the Arabic frame by mirroring the English screenshot | Rejected | It violates true RTL and corrupts product evidence | Render localized coded UI in document-level RTL |
|
||||
| ID | Decision | Status | Rationale | Consequence |
|
||||
| ----- | ---------------------------------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| D-001 | Use restrained operational blue as the main brand family | Approved | Supports trust, clarity, and product-led presentation | Blue carries navigation, links, selection, and routine primary actions |
|
||||
| D-002 | Reserve orange for demo conversion and tightly related emphasis | Approved | Preserves conversion salience and prevents visual noise | Orange is not a routine navigation or generic action color |
|
||||
| D-003 | Use Inter Variable for English and French | Approved | Screen-oriented, broad Latin support, strong UI and marketing utility | One Latin family across website and product-facing UI |
|
||||
| D-004 | Use Noto Sans Arabic for Arabic | Approved | Broad Arabic-script support and suitable sans-serif structure | Arabic receives separate metrics and locale loading |
|
||||
| D-005 | Use one token system with separate display and UI roles | Approved | Avoids two unrelated systems while respecting different density needs | Marketing headings and application labels do not share identical sizing |
|
||||
| D-006 | Adopt blue-600 `#1B5DD8` as brand anchor | Approved | Clear operational blue with useful lighter and darker neighbors | Accessible action background uses blue-700 |
|
||||
| D-007 | Adopt orange-600 `#EA580C` as accent anchor | Approved | Strong conversion accent without becoming the whole brand | White-text conversion CTA uses orange-700 |
|
||||
| D-008 | Ban orange-600 with white normal text | Approved restriction | 3.56:1 contrast fails normal-text AA | Use orange-700 in light theme or orange-400 with dark text in dark theme |
|
||||
| D-009 | Use a 4 px spacing base | Approved | Predictable, scalable, compatible with compact and spacious layouts | Exceptions require explicit optical rationale |
|
||||
| D-010 | Use 4/8/12 column grids for mobile/tablet/desktop | Approved | Distinct responsive composition without device-specific hacks | Reference frames: 390, 1024, 1440 px |
|
||||
| D-011 | Cap primary container at 1280 px | Approved | Protects readability while allowing strong product visuals | Wider screens gain outer breathing room, not stretched text |
|
||||
| D-012 | Use CSS logical properties | Approved | Required for real LTR/RTL parity | Physical left/right spacing tokens are rejected |
|
||||
| D-013 | Use Lucide as the base icon family | Approved | Consistent 24 px grid and 2 px outline language | Mixing icon families requires a documented exception |
|
||||
| D-014 | Prefer 44 × 44 px interactive targets | Approved | Exceeds the WCAG 2.2 minimum and improves usability | Compact exceptions must remain accessible |
|
||||
| D-015 | Require formal Modern Standard Arabic | Approved | Prevents inconsistent regional dialect and preserves professional tone | All Arabic product and marketing copy must be linguistically reviewed |
|
||||
| D-016 | Avoid automotive racing clichés | Approved | Product is operational software, not performance-car entertainment | No speedometers, racing stripes, carbon fiber, or aggressive neon styling |
|
||||
| R-001 | Use orange for every primary button | Rejected | Destroys conversion hierarchy | Routine primary actions remain blue |
|
||||
| R-002 | Force identical line-height across scripts | Rejected | Damages Arabic readability and vertical rhythm | Arabic uses script-specific line heights |
|
||||
| R-003 | Use fixed-width translated buttons | Rejected | Breaks under French expansion and Arabic content | Buttons are content-driven |
|
||||
| R-004 | Mirror all icons in RTL | Rejected | Many icons are semantically direction-neutral | Mirror only directional meaning |
|
||||
| R-005 | Create one type scale for every context | Rejected | Marketing and dense UI have different jobs | Shared foundations, separate role scales |
|
||||
| D-017 | Use English as source, default, and fallback locale | Approved | Provides one controlled authoring source while all public locales remain independently addressable | Missing translations fail CI; production fallback is logged |
|
||||
| D-018 | Use locale-prefixed public URLs for every indexable page | Approved | Makes language state shareable, crawlable, and deterministic | All navigation and SEO generation use a central route manifest |
|
||||
| D-019 | Limit automatic locale detection to root `/` | Approved | Respects explicit URLs and prevents redirect loops or crawler interference | Locale-prefixed pages are never overridden by browser language |
|
||||
| D-020 | Persist explicit language selection in a first-party cookie | Approved | Allows SSR to honor user choice without relying on client-only state | Manual choice outranks Accept-Language |
|
||||
| D-021 | Use localized public slugs backed by stable route IDs | Approved | Improves localized readability while preventing routing logic from depending on translated strings | Switchers, sitemaps, and canonicals map by route ID |
|
||||
| D-022 | Use English/French LTR and Arabic document-level RTL | Approved | Correct browser bidi and layout behavior requires root direction | Arabic pages render `html lang=ar dir=rtl` |
|
||||
| D-023 | Use formal Modern Standard Arabic only | Approved | Maintains professional, region-neutral baseline | Dialect and colloquial copy fail linguistic review |
|
||||
| D-024 | Use Latin digits and Gregorian dates in the language-only baseline | Approved | Keeps operational data and identifiers consistent before market-specific preferences exist | Revisit only after Arabic market research |
|
||||
| D-025 | Use `فحص المركبة` for vehicle inspection | Approved | Clear operational term with broad formal comprehension | `معاينة المركبة` remains a documented secondary term |
|
||||
| D-026 | Use `عقد الإيجار` for rental agreement | Approved | Concise and standard legal-operational label | Jurisdictional legal review remains required |
|
||||
| D-027 | Use `مبلغ الضمان` for security deposit | Approved | Avoids confusion between deposit and insurance | Arabic `تأمين` alone is rejected |
|
||||
| D-028 | Use `معدل استخدام الأسطول` for fleet utilization rate | Approved | Describes use rather than occupancy | Occupancy language is rejected |
|
||||
| D-029 | Keep currency, country, timezone, tax, and phone behavior independent from language | Approved | Language-only locales do not define business jurisdiction | Formatting consumes explicit data and context |
|
||||
| D-030 | Use semantic namespaced translation keys | Approved | Supports reuse, context, QA, and refactoring | Source sentences, positions, and visual styles are not keys |
|
||||
| D-031 | Fail missing translations in CI | Approved | Prevents silent partial localization | Production fallback is a logged safety net, not the workflow |
|
||||
| D-032 | Use self-canonical locale pages and reciprocal language-only hreflang | Approved | Prevents English from swallowing localized indexing | `x-default` points to `/en` until a neutral selector exists |
|
||||
| D-033 | Omit regional Open Graph locale until target markets are approved | Approved | Avoids inventing country-region assumptions | Localized OG copy still ships |
|
||||
| R-006 | Use country flags as language controls | Rejected | Languages are not equivalent to countries | Display language names in their own language |
|
||||
| R-007 | Redirect explicit locale URLs based on browser language | Rejected | Breaks shareability, intent, crawling, and testing | Detection is root-only |
|
||||
| R-008 | Use IP geolocation to choose language | Rejected | Country is not language and geolocation is unreliable | Use explicit preference and Accept-Language |
|
||||
| R-009 | Use English source sentences as translation keys | Rejected | Copy edits become code changes and context is lost | Use semantic namespaced keys |
|
||||
| R-010 | Simulate Arabic by right-aligning an LTR document | Rejected | Does not provide correct bidi or layout behavior | Set document-level RTL |
|
||||
| R-011 | Translate check-in/check-out like hotel terminology | Rejected | Misstates rental operational processes | Use pickup, handover, and return terms by perspective |
|
||||
| R-012 | Infer currency or legal jurisdiction from locale | Rejected | English, French, and Arabic span multiple markets | Use explicit business data |
|
||||
| R-013 | Publish placeholder logos, metrics, testimonials, or certifications | Rejected | Creates unsupported evidence | Hide evidence sections until approved sources exist |
|
||||
| D-034 | Organize the library into foundations, behavior primitives, and marketing composites | Approved | Prevents page-specific composition from masquerading as a reusable primitive | Component APIs and ownership follow explicit tiers |
|
||||
| D-035 | Share behavior primitives with the future product dashboard, not marketing composites by default | Approved | Marketing and operational interfaces have different density and interaction requirements | Dashboard reuse requires a later density specification |
|
||||
| D-036 | Use one Button component with primary-blue and conversion-orange intents | Approved | Preserves hierarchy and avoids duplicated behavior | Orange remains restricted to demo and related conversion |
|
||||
| D-037 | Require content-driven dimensions and prohibit truncation of important copy | Approved | French/Arabic expansion and zoom invalidate fixed text geometry | Components change structure before collision |
|
||||
| D-038 | Require visible labels for all form fields | Approved | Placeholders disappear and are unreliable accessible context | Placeholder-only fields fail review |
|
||||
| D-039 | Use native HTML semantics as the baseline for controls, tables, disclosures, and dialogs | Approved | Native behavior reduces accessibility defects | Custom widgets require separate interaction contracts |
|
||||
| D-040 | Use a native date input as the Phase 3 Gregorian baseline | Approved | Custom date pickers are complex and requirements are unresolved | Custom calendar work is deferred |
|
||||
| D-041 | Use one culturally neutral full-name field in the demo form | Approved | First/last assumptions fail across naming systems | CRM mapping preserves the complete name |
|
||||
| D-042 | Keep the demo form minimally qualified | Approved | Extra fields increase abandonment and privacy burden | Required: full name, work email, company, fleet-size range |
|
||||
| D-043 | Evidence-gate metrics, testimonials, case studies, trust marks, and pricing | Approved | A visual component must not make unsupported content publishable | Missing or expired approval suppresses rendering |
|
||||
| D-044 | Approve pricing cards and pricing comparison as separate components | Approved | Plan orientation and detailed comparison are distinct tasks | Both may coexist; neither ships without approved pricing |
|
||||
| D-045 | Open mobile navigation from inline-start | Approved | Aligns navigation origin with document direction | Drawer opens left in LTR and right in RTL |
|
||||
| D-046 | Require overlay focus containment, inert background, Escape policy, and focus return | Approved | Visual layering without focus management is inaccessible | Drawer/modal implementations need behavioral tests |
|
||||
| D-047 | Place toasts at inline-end and keep critical errors persistent in context | Approved | Locale-aware placement and durable correction prevent missed failures | Error toasts cannot be the sole error record |
|
||||
| D-048 | Use the same component tree for LTR and RTL | Approved | Direction-specific implementations drift | Direction is resolved through document state, logical CSS, and selective mirroring |
|
||||
| D-049 | Respect reduced-motion preferences and keep motion non-essential | Approved | Motion should support continuity, not gate understanding | Shimmer and transforms are removed or minimized |
|
||||
| D-050 | Use English light/LTR only for the initial specimen while requiring full locale/theme tests | Approved | Gives a concrete first specimen without pretending it proves conformance | Acceptance still requires en/fr/ar and light/dark |
|
||||
| R-014 | Create separate LTR and RTL component implementations | Rejected | Guarantees drift and mismatched behavior | Use one semantic component tree |
|
||||
| R-015 | Use fixed-height cards, buttons, labels, or validation regions | Rejected | Breaks expansion, zoom, and Arabic metrics | Use content-driven block sizes |
|
||||
| R-016 | Publish placeholder metrics, logos, testimonials, certifications, or prices | Rejected | Converts missing evidence into deception | Omit until approval exists |
|
||||
| R-017 | Open navigation or critical content on hover only | Rejected | Excludes keyboard and touch users | Use explicit triggers and focus behavior |
|
||||
| R-018 | Use placeholder text instead of visible form labels | Rejected | Loses context after input and harms accessibility | Labels remain visible |
|
||||
| R-019 | Replace native select/date controls for visual consistency alone | Rejected | Adds keyboard, screen-reader, mobile, and localization risk | Native controls are baseline |
|
||||
| R-020 | Force marketing composites into the future product dashboard | Rejected | Shared appearance is not shared information architecture | Share primitives; specify product compositions later |
|
||||
| R-021 | Combine pricing cards and comparison into one shape-shifting component | Rejected | Merges distinct tasks and creates brittle responsive behavior | Maintain separate components |
|
||||
| R-022 | Auto-dismiss critical errors without a persistent correction path | Rejected | Users can miss failure and lose context | Keep errors in the owning form/page |
|
||||
| D-051 | Expose Light, Dark, and System as explicit appearance choices | Approved | Users need deterministic control while retaining platform preference support | Theme selector and persistence support all three values |
|
||||
| D-052 | Persist System as an explicit choice | Approved | System is a user preference, not merely missing state | OS changes update resolved theme while the selector remains System |
|
||||
| D-053 | Use `hpc-theme` cookie as canonical preference and local storage as a mirror | Approved | SSR needs a readable value; client tabs need immediate synchronization | Cookie wins disagreement; both are written together |
|
||||
| D-054 | Separate theme preference from resolved theme | Approved | System can resolve to Light or Dark without losing the user choice | Root uses `data-theme-preference` and `data-theme` |
|
||||
| D-055 | Apply theme before first paint with server state and a tiny inline bootstrap | Approved | Prevents incorrect-theme flash and hydration mismatch | Bootstrap runs before blocking CSS and receives a CSP nonce/hash |
|
||||
| D-056 | Use deep-navy dark surfaces rather than pure black or mechanical inversion | Approved | Preserves operational hierarchy and reduces glare without flattening the UI | Dark surface ladder uses canvas, surface, subtle, and raised roles |
|
||||
| D-057 | Use light blue with deep-navy text for routine primary actions in dark mode | Approved | Maintains brand recognition and high contrast without a luminous button wall | Dark primary action roles differ from light while preserving intent |
|
||||
| D-058 | Use orange-400 with dark text for conversion actions in dark mode | Approved | Meets contrast and preserves controlled conversion salience | Orange remains restricted to demo conversion and related emphasis |
|
||||
| D-059 | Use neutral-black dark shadows and prohibit colored glow elevation | Approved | Glow reduces precision and makes hierarchy noisy | Elevation uses borders, surface steps, and neutral shadows |
|
||||
| D-060 | Permit semantically equivalent light/dark hero artwork variants | Approved | Dark presentation may need art direction without changing the product claim | Same content/composition; only presentation treatment changes |
|
||||
| D-061 | Do not make product screenshots automatically follow website theme | Approved | A screenshot is evidence of an actual product state | Store capture theme metadata; never invert or recolor captures |
|
||||
| D-062 | Allow paired screenshot variants only for equivalent approved product states | Approved | Theme matching is useful only when evidence remains truthful | Content model records capture theme, locale, direction, and approval |
|
||||
| D-063 | Use theme-specific chart tokens plus redundant non-color encoding | Approved | Color-only data is inaccessible and fragile across themes | Charts use labels, markers, dashes, patterns, or data tables |
|
||||
| D-064 | Keep theme changes immediate and non-essential motion reduced | Approved | Global cross-fades create flash, noise, and intermediate contrast failures | No global theme transition; reduced motion removes shimmer/rotation |
|
||||
| D-065 | Update System mode live when the platform preference changes | Approved | System should continue following its stated source | Media-query changes update resolved theme only while preference is System |
|
||||
| D-066 | Set `color-scheme` from the resolved theme | Approved | Native controls and browser UI must match the rendered appearance | Selects, dates, autofill, scrollbars, and validation UI require browser testing |
|
||||
| R-023 | Create dark mode by inverting the light palette | Rejected | Numeric inversion destroys semantic hierarchy and brand intent | Map every role explicitly |
|
||||
| R-024 | Use pure black canvas and pure white text everywhere | Rejected | Produces harsh contrast and flattens hierarchy | Use deep navy and off-white/blue-gray text roles |
|
||||
| R-025 | Add blue/orange glows to cards, charts, buttons, or dialogs | Rejected | Turns operational software into decorative noise | Use neutral elevation and restrained emphasis |
|
||||
| R-026 | Store theme only in local storage and apply after hydration | Rejected | Causes incorrect-theme flash and prevents correct SSR | Use cookie, pre-paint bootstrap, and server state |
|
||||
| R-027 | Treat System as an unpersisted default | Rejected | Loses the user’s explicit preference | Persist System exactly like Light and Dark |
|
||||
| R-028 | Recolor, invert, or mirror screenshots for dark mode | Rejected | Misrepresents the product and can corrupt direction/data meaning | Use approved captures or a neutral frame |
|
||||
| R-029 | Encode chart series or status using color alone | Rejected | Fails accessibility and weakens print/forced-colors behavior | Add labels, markers, patterns, icons, or text |
|
||||
| D-067 | Use explicit bidi isolation for atomic mixed-direction values | Approved | Arabic labels frequently contain LTR operational identifiers whose punctuation otherwise reorders | Known technical values use `dir=ltr` and `bdi`/isolation at the smallest boundary |
|
||||
| D-068 | Use `dir=auto` only for genuinely unknown user-authored text | Approved | Known value classes have deterministic direction while free text may not | Translation resources and known identifiers never delegate direction to heuristics |
|
||||
| D-069 | Mirror icons by semantic metadata rather than global RTL CSS | Approved | Global mirroring corrupts object, status, brand, and evidence assets | Icon instances declare directional, fixed, or contextual behavior |
|
||||
| D-070 | Keep DOM order aligned with Arabic visual and focus order | Approved | CSS reversal creates inaccessible mismatch and brittle responsive behavior | `row-reverse`, `column-reverse`, CSS `order`, and positive tabindex are prohibited for RTL repair |
|
||||
| D-071 | Make Arabic mobile navigation enter from inline-start | Approved | Navigation origin should follow document direction | Drawer enters from the right in Arabic and retains full focus management |
|
||||
| D-072 | Let Arabic workflow progression run from right to left while preserving authored step order | Approved | Process reading should feel native without reversing data or source arrays | Natural RTL layout places step one on the right; mobile remains top-to-bottom |
|
||||
| D-073 | Keep chronological chart axes and trend meaning independent from RTL | Approved | Data chronology and increase/decrease meaning are domain semantics, not text direction | Charts are not automatically reversed and trend arrows are fixed |
|
||||
| D-074 | Preserve authoritative license-plate value and segment direction | Approved | Plate formats vary by country and may mix scripts | Latin, Arabic, and segmented plates use explicit direction; country templates remain market configuration |
|
||||
| D-075 | Require Arabic Light desktop and Arabic Dark mobile validation sheets | Approved | The riskiest direction/theme/responsive combinations need explicit evidence | 1440 Light desktop and 390 Dark mobile become release gates |
|
||||
| D-076 | Confirm Latin digits and Gregorian dates as the Phase 5 language-only baseline | Approved | Phase 2 already separated language from market formatting | Arabic-Indic digits or alternate calendars require explicit market research and configuration |
|
||||
| D-077 | Use visual arrow-key movement for horizontal RTL tabs and similar composites | Approved | Keyboard commands should match spatial expectation while preserving authored sequence | ArrowLeft moves visually left and ArrowRight visually right; implementation maps to logical index |
|
||||
| R-030 | Mirror every SVG or image under `dir=rtl` | Rejected | Reverses logos, vehicles, screenshots, status icons, plates, and evidence | Mirror only explicitly directional icons |
|
||||
| R-031 | Reverse arrays or use CSS row-reverse to make Arabic look correct | Rejected | Breaks DOM/focus order and causes responsive drift | Preserve authored order and rely on document direction/logical layout |
|
||||
| R-032 | Insert Unicode bidi override characters into translation strings | Rejected | Hidden controls are fragile, difficult to audit, and can create spoofing risk | Use semantic markup and isolation wrappers |
|
||||
| R-033 | Reverse chronological charts because the page is RTL | Rejected | Text direction does not redefine time or data chronology | Chart order follows the chart specification |
|
||||
| R-034 | Infer license-plate format, currency, timezone, or country from Arabic language | Rejected | Arabic spans many jurisdictions | Consume explicit business and market configuration |
|
||||
| R-035 | Leave technical fields RTL and compensate with right text alignment | Rejected | Caret, punctuation, selection, and copy behavior remain broken | Use explicit LTR direction and isolation for known technical values |
|
||||
| D-078 | Use content-pressure breakpoints rather than device-name breakpoints | Approved | Translated copy, controls, and product evidence fail at different widths | Shared ranges coordinate layout, while intrinsic sizing and container queries control components |
|
||||
| D-079 | Validate 1440, 1024, and 390 px frames plus a 320 px reflow gate | Approved | Reference designs alone do not prove intermediate or zoom behavior | Automated tests include intermediate widths and 400% zoom equivalent |
|
||||
| D-080 | Collapse full navigation below 1120 px | Approved | French and Arabic header pressure makes 1024 too narrow for stable full navigation | The 1024 frame uses compact navigation and an inline-start drawer |
|
||||
| D-081 | Keep the hero stacked below 1120 px | Approved | A forced 1024 split makes either copy or product preview less informative | Tablet uses claim, CTAs, then a full-width readable preview |
|
||||
| D-082 | Keep the demo action reachable at every width | Approved | Primary conversion cannot disappear because the header becomes compact | Below 480 px the demo CTA is the first drawer action; above it may remain in the header when it fits |
|
||||
| D-083 | Use art-directed product previews instead of shrinking desktop screenshots below legibility | Approved | Tiny desktop UI ceases to provide product evidence | Mobile uses a focused crop, mobile capture, or coded responsive state |
|
||||
| D-084 | Prohibit swipe-only carousels for core modules, trust, testimonials, and case studies | Approved | Hidden slides reduce discoverability and create gesture/accessibility dependence | Core content uses grids or vertical stacks; optional carousels require a later explicit exception |
|
||||
| D-085 | Prohibit page-level horizontal scrolling | Approved | Horizontal page scroll breaks reflow, focus visibility, and mobile use | Tables and atomic technical values may own bounded, labelled overflow |
|
||||
| D-086 | Preserve semantic pricing tables at constrained widths | Approved | Plan comparison is genuinely tabular and should not become an image or inaccessible card duplicate | Use internal horizontal scroll and a sticky inline-start feature column |
|
||||
| D-087 | Use a 45rem container threshold for two-column demo-form fields | Approved | The form may sit in sections of different widths at the same viewport | Field layout responds to its own available space |
|
||||
| D-088 | Require at least 10rem per step for a horizontal workflow | Approved | Breakpoint width alone does not protect French or Arabic step labels | Workflow stays vertical whenever actual content pressure fails the minimum |
|
||||
| D-089 | Use 18rem module cards, 20rem pricing cards, and 11rem metric cards as minimum readable widths | Approved | Fixed column counts create unreadable intermediate states | Grids use auto-fit/minmax and reduce column count before compressing content |
|
||||
| D-090 | Never truncate critical headings, labels, buttons, errors, or evidence for responsive fit | Approved | Truncation hides meaning and disproportionately harms French and Arabic | Components wrap, stack, or change structure before reducing readability |
|
||||
| D-091 | Keep 44 × 44 px minimum targets across all responsive states | Approved | Compact layouts increase, rather than reduce, touch accuracy needs | Headers, drawers, accordions, pagination, and form controls preserve target size |
|
||||
| D-092 | Remove evidence sections entirely when approved content is absent | Approved | Empty responsive slots invite placeholder or fabricated evidence | No reserved logo rails, metrics, or testimonial gaps are rendered |
|
||||
| R-036 | Use one global breakpoint set as the only adaptation mechanism | Rejected | Components fail at different content widths | Use shared coordination ranges plus intrinsic and container rules |
|
||||
| R-037 | Keep desktop navigation at 1024 by shrinking gaps and labels | Rejected | Creates a fragile header that fails translation and zoom | Use compact navigation below 1120 |
|
||||
| R-038 | Hide secondary actions on mobile | Rejected | Removes valid user paths and can trap users in one conversion route | Stack or move actions without deleting them |
|
||||
| R-039 | Scale a full desktop screenshot to fit mobile | Rejected | Makes product evidence unreadable | Use art-directed captures or coded responsive previews |
|
||||
| R-040 | Convert core module sections into swipe-only carousels | Rejected | Reduces discoverability and accessibility | Use visible grids/stacks |
|
||||
| R-041 | Allow body horizontal scrolling for comparison tables | Rejected | Breaks page navigation and reflow | Contain overflow inside the table region |
|
||||
| R-042 | Ellipsize French or Arabic headings and button labels | Rejected | Hides meaning and creates unequal localization quality | Use content-driven size and structural change |
|
||||
| R-043 | Use CSS order or DOM duplication to rearrange responsive content | Rejected | Creates focus, reading-order, maintenance, and analytics drift | Keep one semantic source order |
|
||||
| R-044 | Use fixed heights to maintain card and section alignment | Rejected | Translation, zoom, validation, and dynamic data overflow | Use content-driven block sizing |
|
||||
| D-093 | Use a workflow-first homepage narrative rather than a generic feature dump | Approved | Buyers need to understand the operating model before comparing modules | Hero, problem, workflow, roles, then modules establish the product story |
|
||||
| D-094 | Use the reservation-to-return promise as the five-second hero message | Approved | It states category, scope, and operating value without unsupported superlatives | Hero copy stays concrete across en/fr/ar |
|
||||
| D-095 | Render operational-coverage proof instead of unsupported customer-logo proof | Approved | Product scope can be shown without fabricating external trust | Lifecycle cards follow the hero; logos remain blocked |
|
||||
| D-096 | Show KPI definitions and measurement methodology instead of invented outcome numbers | Approved | Evaluation rigor is more credible than fictional percentages | Results section contains no claimed improvement values |
|
||||
| D-097 | Omit the case-study section until an approved customer evidence package exists | Approved | Empty or anonymous proof violates the evidence policy | Conditional insertion point remains after Security and before Pricing |
|
||||
| D-098 | Present integration categories without naming partners until connector availability is verified | Approved | Named logos imply working production support | Discovery confirms market, connector, and data ownership |
|
||||
| D-099 | Present security as an evidence-request checklist until certifications and reliability claims are approved | Approved | Badges without scope are decoration, not assurance | No certification, uptime, RTO, or SLA claim renders |
|
||||
| D-100 | Use quote-oriented pricing preview until the commercial model is approved | Approved | Invented tiers or prices create false expectations | Page explains scope factors and routes to pricing request |
|
||||
| D-101 | Use a coded, localized, explicitly illustrative product preview | Approved | It proves layout, theme, and RTL behavior without pretending to be production evidence | Replace or approve before launch |
|
||||
| D-102 | Repeat the demo conversion in header, hero, pricing, and final CTA while reserving orange for conversion | Approved | Repetition improves reach without flattening hierarchy | Routine navigation and product emphasis remain blue |
|
||||
| D-103 | Use a native dialog for the demo form baseline | Approved | Provides a testable accessible interaction with visible labels and error state | Production integration must complete focus, submission, consent, and analytics testing |
|
||||
| D-104 | Validate six named production frames plus a 320 px reflow gate | Approved | These frames cover the highest-risk locale, direction, theme, and responsive combinations | Screenshots and automated checks are package deliverables |
|
||||
| R-045 | Fill the trust strip with fictional customer logos | Rejected | Placeholder logos quickly become accidental production claims | Omit logos until approved |
|
||||
| R-046 | Publish “up to” performance percentages without methodology | Rejected | The qualifier does not rescue unsupported evidence | Publish only after baseline and source approval |
|
||||
| R-047 | Add anonymous testimonial quotes | Rejected | Anonymous praise is not verifiable customer evidence | Use an attributable approved case study or omit |
|
||||
| R-048 | Display common integration logos as visual shorthand | Rejected | A familiar logo still implies a connector exists | Show neutral categories until verified |
|
||||
| R-049 | Add security badges with “pending” labels to the public page | Rejected | Public users should not see internal evidence placeholders | Keep status in the internal asset and copy registers |
|
||||
| R-050 | Invent three pricing tiers to make the page look complete | Rejected | Visual completeness is not worth commercial misinformation | Use a quote-oriented preview |
|
||||
| R-051 | Hide unresolved evidence behind vague superlatives | Rejected | “Trusted,” “leading,” and “proven” are still claims | Use concrete scope, workflow, and evaluation language |
|
||||
| R-052 | Make the Arabic frame by mirroring the English screenshot | Rejected | It violates true RTL and corrupts product evidence | Render localized coded UI in document-level RTL |
|
||||
|
||||
| D-105 | Treat Phase 8 as behavior validation, not aesthetic preference collection | Approved | Completion, comprehension, recovery, accessibility, and confidence are actionable; taste polling is not | Script uses task outcomes and neutral probes |
|
||||
| D-106 | Use a coded local prototype with deterministic query states | Approved | Reproducible states support moderation, accessibility checks, and automation | Demo, tour, locale, theme, navigation, failure, and research modes are addressable |
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "09_DEVELOPER_HANDOFF_v1.0.md",
|
||||
"bytes": 8091,
|
||||
"sha256": "e82d486f1e566d1bf0263f1baa7dd8035ae62da5c36bb6bfa417e61331ec8d51"
|
||||
"bytes": 8378,
|
||||
"sha256": "6cb5a76ee42e48caadd672e852520aeed0a974a9e4642be924c6b0b3176e8b1f"
|
||||
},
|
||||
{
|
||||
"path": "DECISION_LOG_v9.0.md",
|
||||
"bytes": 38775,
|
||||
"sha256": "85569a7418f8cad57d094ee9ec1d83b7727b5ab91a405cbda26de0271a8d76a1"
|
||||
"bytes": 63034,
|
||||
"sha256": "8e00d2cfa72b2bad65d855827a0fdfd09cce3e30e9885e46d8f8c6c7feb5f09f"
|
||||
},
|
||||
{
|
||||
"path": "DEFINITION_OF_DONE_v1.0.md",
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
{
|
||||
"path": "components/COMPONENT_CONTRACTS_v1.0.md",
|
||||
"bytes": 9800,
|
||||
"sha256": "84b455fe725bff11b6ab54bb13373b1aa52991b062a36fad07db47a940187a53"
|
||||
"bytes": 9698,
|
||||
"sha256": "c36fe8a5221d0f76f0b74894d7227135adb7bae45eca0a7554a31b9433218052"
|
||||
},
|
||||
{
|
||||
"path": "components/COMPONENT_STATE_MATRIX_v1.0.csv",
|
||||
@@ -138,23 +138,23 @@
|
||||
},
|
||||
{
|
||||
"path": "implementation/component-contracts_v1.0.ts",
|
||||
"bytes": 2844,
|
||||
"sha256": "177dd2420b804290cf1296e43f41a7e759c8859b3253073fbdc523a81f6f94c0"
|
||||
"bytes": 3041,
|
||||
"sha256": "45fb0222d99308b2f28014572316b13c2d7308bc16b1bd02f63b6e2257148399"
|
||||
},
|
||||
{
|
||||
"path": "implementation/hreflang-generator_v1.0.ts",
|
||||
"bytes": 751,
|
||||
"sha256": "ab919a36a5d08a28c38ae51b4ddd0d84cef5d3ec3bed13649ebd26c3fb80ac8a"
|
||||
"bytes": 799,
|
||||
"sha256": "aa9b5e2eb602b2f651b1195a2d0ff7c2a0e6157bf702d3b27ab25b5b86249ba8"
|
||||
},
|
||||
{
|
||||
"path": "implementation/localization-config_v1.0.ts",
|
||||
"bytes": 772,
|
||||
"sha256": "bd8ca09f121bfe516ece817948a81d58136e9205fbd2c05dad2ce87f2d7a1782"
|
||||
"bytes": 806,
|
||||
"sha256": "fd884ce4cd807ccdd196bd4c09e34128b3a26df73e0f267c120f270179c852d0"
|
||||
},
|
||||
{
|
||||
"path": "implementation/theme-config_v1.0.ts",
|
||||
"bytes": 725,
|
||||
"sha256": "f56d37da4129d5830ba9ebd77b18cd6084a144e8514bfd8a8152387ecf76607c"
|
||||
"bytes": 752,
|
||||
"sha256": "5ae9687c6ffa832ae12b70fa8272a61853960eea8a3cc18e3a68ef1f120046f8"
|
||||
},
|
||||
{
|
||||
"path": "interaction/ANALYTICS_EVENT_SPEC_v1.0.csv",
|
||||
@@ -168,28 +168,28 @@
|
||||
},
|
||||
{
|
||||
"path": "interaction/analytics-contract_v1.0.ts",
|
||||
"bytes": 832,
|
||||
"sha256": "2716eb69cc98032005e068ab7bdc265a4c896a8e1a1bed682c7fdd4f45368123"
|
||||
"bytes": 918,
|
||||
"sha256": "d79b32503a5f8346816c647b8a7f45a62468b96f4fc9525d6d5147b5d2f2ec45"
|
||||
},
|
||||
{
|
||||
"path": "interaction/demo-form-contract_v1.0.ts",
|
||||
"bytes": 632,
|
||||
"sha256": "194db77f7cfca417dbe35e6f2c2ea94cdd02806b83b1d0f1993fe509d8c10154"
|
||||
"bytes": 699,
|
||||
"sha256": "073cf70224f93312d177f77abd7987704205628209e30c44824ae45f929ee59e"
|
||||
},
|
||||
{
|
||||
"path": "interaction/theme-bootstrap_v1.0.js",
|
||||
"bytes": 753,
|
||||
"sha256": "7b89a849bdcd72e1049714ebf76f0c0decb39e44261480f0cb08bb7dfe734e4a"
|
||||
"bytes": 757,
|
||||
"sha256": "a90b31cc2b91fd085b8cd4442b85fce56127501602b9b7f90af75fd3c8f4c3b0"
|
||||
},
|
||||
{
|
||||
"path": "localization/LOCALIZATION_IMPLEMENTATION_v1.0.md",
|
||||
"bytes": 2062,
|
||||
"sha256": "87a494d7ef7ed9f5012ed1699edf90e38995aa89a0efc3800db854e1d57e1024"
|
||||
"bytes": 2069,
|
||||
"sha256": "63773e2f4777a6a322570d07d8badac8867dc4d6eef2d71bf05cbf81dfd2c85b"
|
||||
},
|
||||
{
|
||||
"path": "localization/ROUTE_MANIFEST_v1.0.json",
|
||||
"bytes": 1573,
|
||||
"sha256": "fa29b8d31da1f36f87d216a1228fd1bbe1cde337636950b40b0024a10efaf49a"
|
||||
"bytes": 1433,
|
||||
"sha256": "e08ac94ba1f3899bb02dfa934695f530d1089f9f03b0fadaa0feabd466ad8ec4"
|
||||
},
|
||||
{
|
||||
"path": "localization/TRANSLATION_KEY_INVENTORY_v1.0.csv",
|
||||
@@ -198,18 +198,18 @@
|
||||
},
|
||||
{
|
||||
"path": "localization/locales/ar/homepage.json",
|
||||
"bytes": 18615,
|
||||
"sha256": "fe08aabd60203dbdebc718117f5e497b52a453a8a6fb671fe0bca28651bf0d47"
|
||||
"bytes": 17419,
|
||||
"sha256": "19c1b9d0c90c017da1679b2cde4f123a98625ab6dae8002f58354839309f0474"
|
||||
},
|
||||
{
|
||||
"path": "localization/locales/en/homepage.json",
|
||||
"bytes": 13869,
|
||||
"sha256": "a5ed956c7f07fad016ca39b40b770c25e8beeae2799ff0fece82dbf087f18e51"
|
||||
"bytes": 12769,
|
||||
"sha256": "ba88daa108e3df63285327d0f7d7be6c0df3d971d800d48617e1e62c3414acfb"
|
||||
},
|
||||
{
|
||||
"path": "localization/locales/fr/homepage.json",
|
||||
"bytes": 15512,
|
||||
"sha256": "24079356bd47431ffeb66b7eb2981e108c03080205861b1bb42514b03b945a0a"
|
||||
"bytes": 14564,
|
||||
"sha256": "5a3dfe1dc43c99a30cfc275309b5084b5d9f583a33ea136d388babb0714de719"
|
||||
},
|
||||
{
|
||||
"path": "qa/ACCESSIBILITY_CHECKLIST_v1.0.csv",
|
||||
@@ -243,18 +243,18 @@
|
||||
},
|
||||
{
|
||||
"path": "qa/validate-phase9-package_v1.0.mjs",
|
||||
"bytes": 2534,
|
||||
"sha256": "631892318cf95c0530a605e5bf08def9b06c93af79b555205f3aeb8a2bcd87ed"
|
||||
"bytes": 2766,
|
||||
"sha256": "8bd42770f659b51e63d73b282d7d101d6d817650f8c9b8875cad1b0eb3eece4e"
|
||||
},
|
||||
{
|
||||
"path": "qa/visual-regression-scenarios_v1.0.json",
|
||||
"bytes": 1401,
|
||||
"sha256": "08a04f676cbb93e73b4c6bc112a55ebf287cb97be3dd3176ab80161e6de743c2"
|
||||
"bytes": 1209,
|
||||
"sha256": "27d961c33c32775485475f7037b8d3bbd13f634b65fe2f59d6eea898860edf5f"
|
||||
},
|
||||
{
|
||||
"path": "reference/FINDINGS_REPORT_v1.0.md",
|
||||
"bytes": 3062,
|
||||
"sha256": "ba34afd535856d0eaa8ed7e174c23a7b5f0eae87344c8fcee5daafe00c068ce4"
|
||||
"bytes": 4135,
|
||||
"sha256": "20eaba0c9fafd166220a64b2f0ede3c36b3bd9fc6f32982c1f6c7745f9311965"
|
||||
},
|
||||
{
|
||||
"path": "reference/PHASE_8_HANDOFF_v1.0.md",
|
||||
@@ -288,13 +288,13 @@
|
||||
},
|
||||
{
|
||||
"path": "tokens/semantic-tokens_v1.0.css",
|
||||
"bytes": 3553,
|
||||
"sha256": "99912762e078bfc78b4878fc46b8c7d03d90c073e9c236942a58d83f6585a1b9"
|
||||
"bytes": 3589,
|
||||
"sha256": "f28b2f03139a6aa89b85d00574f4fe56aa90d4ca1e03e80a1fb6eb8e5d9fa621"
|
||||
},
|
||||
{
|
||||
"path": "tokens/tokens_v1.0.ts",
|
||||
"bytes": 442,
|
||||
"sha256": "ec265270251f8dd01d9166aeca3fcec9be2b5e534d825c9058fb6ed3f51e3224"
|
||||
"sha256": "3a1a2bb1bd29e55bf024a468a7c95bf2ec29d3cdc479f580050e8503c19c40a7"
|
||||
},
|
||||
{
|
||||
"path": "validation/CONTENT_AND_STRUCTURE_VALIDATION_v1.0.json",
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
# Component Contracts
|
||||
|
||||
|
||||
|
||||
Every component below is implemented once for all locales, directions, and themes. Props are semantic; components do not fetch translations internally unless the architecture establishes a single typed content boundary.
|
||||
|
||||
|
||||
## SiteHeader
|
||||
|
||||
**Purpose:** Primary navigation, brand, locale/theme controls, login, demo CTA
|
||||
|
||||
**States:** `default,scrolled`
|
||||
**States:** `default,scrolled`
|
||||
|
||||
**Typed data:** `HeaderModel`
|
||||
**Typed data:** `HeaderModel`
|
||||
|
||||
**Copy namespace:** `header.*`
|
||||
|
||||
@@ -23,14 +20,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## MobileDrawer
|
||||
|
||||
**Purpose:** Compact/medium navigation overlay
|
||||
|
||||
**States:** `closed,open`
|
||||
**States:** `closed,open`
|
||||
|
||||
**Typed data:** `DrawerModel`
|
||||
**Typed data:** `DrawerModel`
|
||||
|
||||
**Copy namespace:** `header.*`
|
||||
|
||||
@@ -42,14 +38,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## LocaleSelector
|
||||
|
||||
**Purpose:** Switch locale by stable route ID
|
||||
|
||||
**States:** `closed,open,disabled`
|
||||
**States:** `closed,open,disabled`
|
||||
|
||||
**Typed data:** `LocaleSelectorProps`
|
||||
**Typed data:** `LocaleSelectorProps`
|
||||
|
||||
**Copy namespace:** `language names`
|
||||
|
||||
@@ -61,14 +56,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## ThemeSelector
|
||||
|
||||
**Purpose:** Set Light/Dark/System preference
|
||||
|
||||
**States:** `light,dark,system`
|
||||
**States:** `light,dark,system`
|
||||
|
||||
**Typed data:** `ThemeSelectorProps`
|
||||
**Typed data:** `ThemeSelectorProps`
|
||||
|
||||
**Copy namespace:** `header.light/dark/system`
|
||||
|
||||
@@ -80,14 +74,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## HeroSection
|
||||
|
||||
**Purpose:** Communicate category, promise, primary and secondary actions
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `HeroModel`
|
||||
**Typed data:** `HeroModel`
|
||||
|
||||
**Copy namespace:** `hero.*`
|
||||
|
||||
@@ -99,14 +92,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## ProductPreview
|
||||
|
||||
**Purpose:** Illustrative/evidence-governed product visual
|
||||
|
||||
**States:** `illustrative,approved-capture`
|
||||
**States:** `illustrative,approved-capture`
|
||||
|
||||
**Typed data:** `ProductPreviewModel`
|
||||
**Typed data:** `ProductPreviewModel`
|
||||
|
||||
**Copy namespace:** `preview.*`
|
||||
|
||||
@@ -118,14 +110,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## LifecycleProof
|
||||
|
||||
**Purpose:** Show operational coverage without customer logos
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `LifecycleItem[]`
|
||||
**Typed data:** `LifecycleItem[]`
|
||||
|
||||
**Copy namespace:** `trust.*`
|
||||
|
||||
@@ -137,14 +128,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## ComparisonSection
|
||||
|
||||
**Purpose:** Fragmented versus connected workflow
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `ComparisonModel`
|
||||
**Typed data:** `ComparisonModel`
|
||||
|
||||
**Copy namespace:** `comparison.*`
|
||||
|
||||
@@ -156,14 +146,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## WorkflowSteps
|
||||
|
||||
**Purpose:** Four-step rental flow and tour source
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `WorkflowStep[]`
|
||||
**Typed data:** `WorkflowStep[]`
|
||||
|
||||
**Copy namespace:** `workflow.*`
|
||||
|
||||
@@ -175,14 +164,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## RoleCards
|
||||
|
||||
**Purpose:** Explain value by operating role
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `RoleCard[]`
|
||||
**Typed data:** `RoleCard[]`
|
||||
|
||||
**Copy namespace:** `roles.*`
|
||||
|
||||
@@ -194,14 +182,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## ModuleGrid
|
||||
|
||||
**Purpose:** Describe approved product modules
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `ModuleCard[]`
|
||||
**Typed data:** `ModuleCard[]`
|
||||
|
||||
**Copy namespace:** `modules.*`
|
||||
|
||||
@@ -213,14 +200,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## MeasurementPlan
|
||||
|
||||
**Purpose:** Show KPI definitions without invented outcomes
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `MetricDefinition[]`
|
||||
**Typed data:** `MetricDefinition[]`
|
||||
|
||||
**Copy namespace:** `results.*`
|
||||
|
||||
@@ -232,14 +218,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## IntegrationCategories
|
||||
|
||||
**Purpose:** Neutral category list
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `IntegrationCategory[]`
|
||||
**Typed data:** `IntegrationCategory[]`
|
||||
|
||||
**Copy namespace:** `integrations.*`
|
||||
|
||||
@@ -251,14 +236,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## SecurityChecklist
|
||||
|
||||
**Purpose:** Evidence-request checklist
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `SecurityCheck[]`
|
||||
**Typed data:** `SecurityCheck[]`
|
||||
|
||||
**Copy namespace:** `security.*`
|
||||
|
||||
@@ -270,14 +254,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## PricingPreview
|
||||
|
||||
**Purpose:** Explain pricing factors and demo route
|
||||
|
||||
**States:** `quote-only,approved-pricing`
|
||||
**States:** `quote-only,approved-pricing`
|
||||
|
||||
**Typed data:** `PricingModel`
|
||||
**Typed data:** `PricingModel`
|
||||
|
||||
**Copy namespace:** `pricing.*`
|
||||
|
||||
@@ -289,14 +272,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## FAQAccordion
|
||||
|
||||
**Purpose:** Localized questions and answers
|
||||
|
||||
**States:** `closed,open`
|
||||
**States:** `closed,open`
|
||||
|
||||
**Typed data:** `FaqItem[]`
|
||||
**Typed data:** `FaqItem[]`
|
||||
|
||||
**Copy namespace:** `faq.*`
|
||||
|
||||
@@ -308,14 +290,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## FinalCTA
|
||||
|
||||
**Purpose:** Final conversion block and approved secondary contact
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `FinalCtaModel`
|
||||
**Typed data:** `FinalCtaModel`
|
||||
|
||||
**Copy namespace:** `finalCta.*`
|
||||
|
||||
@@ -327,14 +308,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## SiteFooter
|
||||
|
||||
**Purpose:** Localized navigation, legal, evidence status internal only
|
||||
|
||||
**States:** `default`
|
||||
**States:** `default`
|
||||
|
||||
**Typed data:** `FooterModel`
|
||||
**Typed data:** `FooterModel`
|
||||
|
||||
**Copy namespace:** `footer.*`
|
||||
|
||||
@@ -346,14 +326,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## DemoDialog
|
||||
|
||||
**Purpose:** Qualification form and submission states
|
||||
|
||||
**States:** `idle,invalid,submitting,success,recoverable-error,terminal-error`
|
||||
**States:** `idle,invalid,submitting,success,recoverable-error,terminal-error`
|
||||
|
||||
**Typed data:** `DemoFormProps`
|
||||
**Typed data:** `DemoFormProps`
|
||||
|
||||
**Copy namespace:** `form.*`
|
||||
|
||||
@@ -365,14 +344,13 @@ Every component below is implemented once for all locales, directions, and theme
|
||||
|
||||
**Content rule:** Block size is content-driven. Unsupported evidence suppresses the component or uses the explicitly approved neutral variant.
|
||||
|
||||
|
||||
## ProductTourDialog
|
||||
|
||||
**Purpose:** Four-step guided overview
|
||||
|
||||
**States:** `step-1,step-2,step-3,step-4,complete`
|
||||
**States:** `step-1,step-2,step-3,step-4,complete`
|
||||
|
||||
**Typed data:** `TourProps`
|
||||
**Typed data:** `TourProps`
|
||||
|
||||
**Copy namespace:** `tour.*`
|
||||
|
||||
|
||||
@@ -1,26 +1,136 @@
|
||||
export type Locale = "en" | "fr" | "ar";
|
||||
export type Direction = "ltr" | "rtl";
|
||||
export type EvidenceStatus = "approved" | "illustrative" | "pending" | "expired" | "rejected";
|
||||
export type ThemePreference = "light" | "dark" | "system";
|
||||
export type Locale = 'en' | 'fr' | 'ar';
|
||||
export type Direction = 'ltr' | 'rtl';
|
||||
export type EvidenceStatus = 'approved' | 'illustrative' | 'pending' | 'expired' | 'rejected';
|
||||
export type ThemePreference = 'light' | 'dark' | 'system';
|
||||
|
||||
export interface LinkModel { id: string; label: string; href: string; external?: boolean; }
|
||||
export interface SiteHeaderProps { locale: Locale; direction: Direction; links: LinkModel[]; login?: LinkModel; onDemo(): void; }
|
||||
export interface DrawerProps extends SiteHeaderProps { open: boolean; onOpenChange(open: boolean): void; returnFocusRef?: HTMLElement | null; }
|
||||
export interface LocaleSelectorProps { locale: Locale; routeId: string; hash?: string; onLocaleChange(locale: Locale): void; }
|
||||
export interface ThemeSelectorProps { preference: ThemePreference; onPreferenceChange(value: ThemePreference): void; }
|
||||
export interface HeroModel { eyebrow: string; title: string; body: string; primaryLabel: string; secondaryLabel: string; }
|
||||
export interface ProductPreviewModel { status: EvidenceStatus; caption: string; locale: Locale; direction: Direction; theme: "light"|"dark"; assetId?: string; }
|
||||
export interface LifecycleItem { id: string; title: string; description: string; icon: string; }
|
||||
export interface ComparisonModel { title: string; body: string; before: { title: string; items: string[] }; after: { title: string; items: string[] }; }
|
||||
export interface WorkflowStep { id: string; ordinal: string; title: string; description: string; }
|
||||
export interface RoleCard { id: string; title: string; description: string; }
|
||||
export interface ModuleCard { id: string; title: string; description: string; evidenceStatus: EvidenceStatus; }
|
||||
export interface MetricDefinition { id: string; title: string; definition: string; publishedValue?: never; }
|
||||
export interface IntegrationCategory { id: string; label: string; verifiedPartners?: never; }
|
||||
export interface SecurityCheck { id: string; title: string; description: string; certificationBadge?: never; }
|
||||
export interface PricingModel { mode: "quote-only" | "approved-pricing"; title: string; body: string; factors: string[]; note: string; }
|
||||
export interface FaqItem { id: string; question: string; answer: string; }
|
||||
export interface DemoFormValues { fullName: string; workEmail: string; company: string; fleetSize: string; market?: string; preferredLanguage?: Locale; message?: string; }
|
||||
export type DemoFormState = "idle"|"validating"|"submitting"|"success"|"recoverable_error"|"terminal_error";
|
||||
export interface DemoFormProps { state: DemoFormState; values: DemoFormValues; fieldErrors: Partial<Record<keyof DemoFormValues,string>>; formError?: string; onSubmit(values: DemoFormValues): Promise<void>; }
|
||||
export interface TourProps { steps: WorkflowStep[]; activeIndex: number; open: boolean; onOpenChange(open:boolean): void; onStepChange(index:number): void; }
|
||||
export interface LinkModel {
|
||||
id: string;
|
||||
label: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
}
|
||||
export interface SiteHeaderProps {
|
||||
locale: Locale;
|
||||
direction: Direction;
|
||||
links: LinkModel[];
|
||||
login?: LinkModel;
|
||||
onDemo(): void;
|
||||
}
|
||||
export interface DrawerProps extends SiteHeaderProps {
|
||||
open: boolean;
|
||||
onOpenChange(open: boolean): void;
|
||||
returnFocusRef?: HTMLElement | null;
|
||||
}
|
||||
export interface LocaleSelectorProps {
|
||||
locale: Locale;
|
||||
routeId: string;
|
||||
hash?: string;
|
||||
onLocaleChange(locale: Locale): void;
|
||||
}
|
||||
export interface ThemeSelectorProps {
|
||||
preference: ThemePreference;
|
||||
onPreferenceChange(value: ThemePreference): void;
|
||||
}
|
||||
export interface HeroModel {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
body: string;
|
||||
primaryLabel: string;
|
||||
secondaryLabel: string;
|
||||
}
|
||||
export interface ProductPreviewModel {
|
||||
status: EvidenceStatus;
|
||||
caption: string;
|
||||
locale: Locale;
|
||||
direction: Direction;
|
||||
theme: 'light' | 'dark';
|
||||
assetId?: string;
|
||||
}
|
||||
export interface LifecycleItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
}
|
||||
export interface ComparisonModel {
|
||||
title: string;
|
||||
body: string;
|
||||
before: { title: string; items: string[] };
|
||||
after: { title: string; items: string[] };
|
||||
}
|
||||
export interface WorkflowStep {
|
||||
id: string;
|
||||
ordinal: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
export interface RoleCard {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
export interface ModuleCard {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
evidenceStatus: EvidenceStatus;
|
||||
}
|
||||
export interface MetricDefinition {
|
||||
id: string;
|
||||
title: string;
|
||||
definition: string;
|
||||
publishedValue?: never;
|
||||
}
|
||||
export interface IntegrationCategory {
|
||||
id: string;
|
||||
label: string;
|
||||
verifiedPartners?: never;
|
||||
}
|
||||
export interface SecurityCheck {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
certificationBadge?: never;
|
||||
}
|
||||
export interface PricingModel {
|
||||
mode: 'quote-only' | 'approved-pricing';
|
||||
title: string;
|
||||
body: string;
|
||||
factors: string[];
|
||||
note: string;
|
||||
}
|
||||
export interface FaqItem {
|
||||
id: string;
|
||||
question: string;
|
||||
answer: string;
|
||||
}
|
||||
export interface DemoFormValues {
|
||||
fullName: string;
|
||||
workEmail: string;
|
||||
company: string;
|
||||
fleetSize: string;
|
||||
market?: string;
|
||||
preferredLanguage?: Locale;
|
||||
message?: string;
|
||||
}
|
||||
export type DemoFormState =
|
||||
| 'idle'
|
||||
| 'validating'
|
||||
| 'submitting'
|
||||
| 'success'
|
||||
| 'recoverable_error'
|
||||
| 'terminal_error';
|
||||
export interface DemoFormProps {
|
||||
state: DemoFormState;
|
||||
values: DemoFormValues;
|
||||
fieldErrors: Partial<Record<keyof DemoFormValues, string>>;
|
||||
formError?: string;
|
||||
onSubmit(values: DemoFormValues): Promise<void>;
|
||||
}
|
||||
export interface TourProps {
|
||||
steps: WorkflowStep[];
|
||||
activeIndex: number;
|
||||
open: boolean;
|
||||
onOpenChange(open: boolean): void;
|
||||
onStepChange(index: number): void;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import manifest from "../localization/ROUTE_MANIFEST_v1.0.json";
|
||||
type Locale = "en"|"fr"|"ar";
|
||||
export function localizedPath(routeId:string, locale:Locale):string {
|
||||
import manifest from '../localization/ROUTE_MANIFEST_v1.0.json';
|
||||
type Locale = 'en' | 'fr' | 'ar';
|
||||
export function localizedPath(routeId: string, locale: Locale): string {
|
||||
const route = manifest.routes.find((item) => item.id === routeId);
|
||||
if (!route) throw new Error(`Unknown route: ${routeId}`);
|
||||
const slug = route.slugs[locale];
|
||||
return slug ? `/${locale}/${slug}` : `/${locale}`;
|
||||
}
|
||||
export function hreflangLinks(origin:string, routeId:string) {
|
||||
const alternates = manifest.locales.map((locale) => ({ rel:"alternate", hreflang:locale, href:new URL(localizedPath(routeId, locale as Locale),origin).href }));
|
||||
alternates.push({ rel:"alternate", hreflang:"x-default", href:new URL(localizedPath(routeId,"en"),origin).href });
|
||||
export function hreflangLinks(origin: string, routeId: string) {
|
||||
const alternates = manifest.locales.map((locale) => ({
|
||||
rel: 'alternate',
|
||||
hreflang: locale,
|
||||
href: new URL(localizedPath(routeId, locale as Locale), origin).href,
|
||||
}));
|
||||
alternates.push({
|
||||
rel: 'alternate',
|
||||
hreflang: 'x-default',
|
||||
href: new URL(localizedPath(routeId, 'en'), origin).href,
|
||||
});
|
||||
return alternates;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
export const locales = ["en", "fr", "ar"] as const;
|
||||
export type Locale = typeof locales[number];
|
||||
export const defaultLocale: Locale = "en";
|
||||
export const directionByLocale: Record<Locale,"ltr"|"rtl"> = { en:"ltr", fr:"ltr", ar:"rtl" };
|
||||
export const localeCookie = "hpc-locale";
|
||||
export const localeStorageKey = "hpc.locale";
|
||||
export const campaignQueryAllowlist = ["utm_source","utm_medium","utm_campaign","utm_content","gclid"] as const;
|
||||
export const rootDetectionOrder = ["cookie","accept-language","default-en"] as const;
|
||||
export function isLocale(value: unknown): value is Locale { return typeof value === "string" && (locales as readonly string[]).includes(value); }
|
||||
export function isolateLtr(value: string): { dir:"ltr"; value:string } { return { dir:"ltr", value }; }
|
||||
export const locales = ['en', 'fr', 'ar'] as const;
|
||||
export type Locale = (typeof locales)[number];
|
||||
export const defaultLocale: Locale = 'en';
|
||||
export const directionByLocale: Record<Locale, 'ltr' | 'rtl'> = { en: 'ltr', fr: 'ltr', ar: 'rtl' };
|
||||
export const localeCookie = 'hpc-locale';
|
||||
export const localeStorageKey = 'hpc.locale';
|
||||
export const campaignQueryAllowlist = [
|
||||
'utm_source',
|
||||
'utm_medium',
|
||||
'utm_campaign',
|
||||
'utm_content',
|
||||
'gclid',
|
||||
] as const;
|
||||
export const rootDetectionOrder = ['cookie', 'accept-language', 'default-en'] as const;
|
||||
export function isLocale(value: unknown): value is Locale {
|
||||
return typeof value === 'string' && (locales as readonly string[]).includes(value);
|
||||
}
|
||||
export function isolateLtr(value: string): { dir: 'ltr'; value: string } {
|
||||
return { dir: 'ltr', value };
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
export const themeCookie = "hpc-theme";
|
||||
export const themeStorageKey = "hpc.theme.preference";
|
||||
export const themePreferences = ["light","dark","system"] as const;
|
||||
export type ThemePreference = typeof themePreferences[number];
|
||||
export type ResolvedTheme = "light"|"dark";
|
||||
export function isThemePreference(v:unknown): v is ThemePreference { return typeof v === "string" && (themePreferences as readonly string[]).includes(v); }
|
||||
export function resolveTheme(preference:ThemePreference, systemDark:boolean):ResolvedTheme { return preference === "system" ? (systemDark ? "dark" : "light") : preference; }
|
||||
export const themeCookieAttributes = { path:"/", sameSite:"lax" as const, maxAgeSeconds:31536000, secureInProduction:true };
|
||||
export const themeCookie = 'hpc-theme';
|
||||
export const themeStorageKey = 'hpc.theme.preference';
|
||||
export const themePreferences = ['light', 'dark', 'system'] as const;
|
||||
export type ThemePreference = (typeof themePreferences)[number];
|
||||
export type ResolvedTheme = 'light' | 'dark';
|
||||
export function isThemePreference(v: unknown): v is ThemePreference {
|
||||
return typeof v === 'string' && (themePreferences as readonly string[]).includes(v);
|
||||
}
|
||||
export function resolveTheme(preference: ThemePreference, systemDark: boolean): ResolvedTheme {
|
||||
return preference === 'system' ? (systemDark ? 'dark' : 'light') : preference;
|
||||
}
|
||||
export const themeCookieAttributes = {
|
||||
path: '/',
|
||||
sameSite: 'lax' as const,
|
||||
maxAgeSeconds: 31536000,
|
||||
secureInProduction: true,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,41 @@
|
||||
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;
|
||||
| '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;
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
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>; }
|
||||
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>;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -9,7 +9,7 @@ English is the authoring source. French and Arabic are independent production re
|
||||
Set document attributes on the server:
|
||||
|
||||
```html
|
||||
<html lang="ar" dir="rtl" data-theme-preference="system" data-theme="dark">
|
||||
<html lang="ar" dir="rtl" data-theme-preference="system" data-theme="dark"></html>
|
||||
```
|
||||
|
||||
Do not wait for hydration to set language, direction, or resolved theme.
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultLocale": "en",
|
||||
"locales": [
|
||||
"en",
|
||||
"fr",
|
||||
"ar"
|
||||
],
|
||||
"locales": ["en", "fr", "ar"],
|
||||
"rootDetection": true,
|
||||
"routes": [
|
||||
{
|
||||
@@ -16,15 +12,7 @@
|
||||
"ar": ""
|
||||
},
|
||||
"indexable": true,
|
||||
"hashes": [
|
||||
"top",
|
||||
"product",
|
||||
"workflow",
|
||||
"modules",
|
||||
"pricing",
|
||||
"faq",
|
||||
"contact"
|
||||
]
|
||||
"hashes": ["top", "product", "workflow", "modules", "pricing", "faq", "contact"]
|
||||
},
|
||||
{
|
||||
"id": "login",
|
||||
@@ -68,22 +56,12 @@
|
||||
}
|
||||
],
|
||||
"switching": {
|
||||
"preserveQueryAllowlist": [
|
||||
"utm_source",
|
||||
"utm_medium",
|
||||
"utm_campaign",
|
||||
"utm_content",
|
||||
"gclid"
|
||||
],
|
||||
"preserveQueryAllowlist": ["utm_source", "utm_medium", "utm_campaign", "utm_content", "gclid"],
|
||||
"preserveValidHash": true,
|
||||
"dropUnknownQuery": true
|
||||
},
|
||||
"hreflang": {
|
||||
"values": [
|
||||
"en",
|
||||
"fr",
|
||||
"ar"
|
||||
],
|
||||
"values": ["en", "fr", "ar"],
|
||||
"xDefaultLocale": "en",
|
||||
"selfCanonical": true
|
||||
}
|
||||
|
||||
@@ -7,26 +7,11 @@
|
||||
},
|
||||
"header": {
|
||||
"nav": [
|
||||
[
|
||||
"product",
|
||||
"المنتج"
|
||||
],
|
||||
[
|
||||
"workflow",
|
||||
"سير العمل"
|
||||
],
|
||||
[
|
||||
"modules",
|
||||
"الوحدات"
|
||||
],
|
||||
[
|
||||
"pricing",
|
||||
"التسعير"
|
||||
],
|
||||
[
|
||||
"faq",
|
||||
"الأسئلة الشائعة"
|
||||
]
|
||||
["product", "المنتج"],
|
||||
["workflow", "سير العمل"],
|
||||
["modules", "الوحدات"],
|
||||
["pricing", "التسعير"],
|
||||
["faq", "الأسئلة الشائعة"]
|
||||
],
|
||||
"login": "تسجيل الدخول",
|
||||
"demo": "احجز عرضًا توضيحيًا",
|
||||
@@ -50,22 +35,10 @@
|
||||
"title": "سير عمل واحد يغطي دورة التأجير كاملة.",
|
||||
"body": "حافظ على ترابط سياق العميل والمركبة والعقد والمهام مع تقدم عملية التأجير.",
|
||||
"items": [
|
||||
[
|
||||
"الحجز",
|
||||
"سجّل الطلب والبيانات المطلوبة."
|
||||
],
|
||||
[
|
||||
"المركبة",
|
||||
"تابع التوافر والجاهزية."
|
||||
],
|
||||
[
|
||||
"العقد",
|
||||
"اجمع مهام التسليم والسجلات في موضع واحد."
|
||||
],
|
||||
[
|
||||
"الإرجاع",
|
||||
"سجّل الحالة وأغلق أعمال المتابعة."
|
||||
]
|
||||
["الحجز", "سجّل الطلب والبيانات المطلوبة."],
|
||||
["المركبة", "تابع التوافر والجاهزية."],
|
||||
["العقد", "اجمع مهام التسليم والسجلات في موضع واحد."],
|
||||
["الإرجاع", "سجّل الحالة وأغلق أعمال المتابعة."]
|
||||
]
|
||||
},
|
||||
"comparison": {
|
||||
@@ -92,48 +65,20 @@
|
||||
"title": "حرّك عملية التأجير إلى الأمام من دون فقدان السياق.",
|
||||
"body": "توضح كل خطوة القرار والمسؤول والإجراء التالي بدل دفن التشغيل داخل قائمة عامة من الميزات.",
|
||||
"steps": [
|
||||
[
|
||||
"01",
|
||||
"تسجيل الحجز",
|
||||
"سجّل التواريخ وبيانات السائق والمتطلبات والمصدر."
|
||||
],
|
||||
[
|
||||
"02",
|
||||
"تخصيص مركبة جاهزة",
|
||||
"طابق التوافر مع الفئة والموقع وحالة الجاهزية."
|
||||
],
|
||||
[
|
||||
"03",
|
||||
"إكمال التسليم",
|
||||
"تحقق من المستندات والفحص والعقد ومهام تسليم المركبة."
|
||||
],
|
||||
[
|
||||
"04",
|
||||
"معالجة الإرجاع",
|
||||
"سجّل الحالة وأغلق الأعمال المفتوحة وأعد المركبة إلى التوافر."
|
||||
]
|
||||
["01", "تسجيل الحجز", "سجّل التواريخ وبيانات السائق والمتطلبات والمصدر."],
|
||||
["02", "تخصيص مركبة جاهزة", "طابق التوافر مع الفئة والموقع وحالة الجاهزية."],
|
||||
["03", "إكمال التسليم", "تحقق من المستندات والفحص والعقد ومهام تسليم المركبة."],
|
||||
["04", "معالجة الإرجاع", "سجّل الحالة وأغلق الأعمال المفتوحة وأعد المركبة إلى التوافر."]
|
||||
]
|
||||
},
|
||||
"roles": {
|
||||
"eyebrow": "مصمم لفريق التشغيل",
|
||||
"title": "امنح كل دور السياق الذي يحتاجه من دون إنشاء نسخ متعددة من الحقيقة.",
|
||||
"roles": [
|
||||
[
|
||||
"المالك أو المدير",
|
||||
"اطّلع على عبء العمل والاستثناءات والمؤشرات التشغيلية عبر النشاط."
|
||||
],
|
||||
[
|
||||
"فريق الحجوزات",
|
||||
"حوّل الطلبات إلى حجوزات مؤكدة مع عدد أقل من عمليات التسليم."
|
||||
],
|
||||
[
|
||||
"الأسطول والتشغيل",
|
||||
"اعرف المركبات المتاحة أو المحجوبة أو المستحقة أو التي تنتظر إجراءً."
|
||||
],
|
||||
[
|
||||
"مكتب الاستقبال",
|
||||
"حضّر مهام التسليم والإرجاع قبل انتظار العميل."
|
||||
]
|
||||
["المالك أو المدير", "اطّلع على عبء العمل والاستثناءات والمؤشرات التشغيلية عبر النشاط."],
|
||||
["فريق الحجوزات", "حوّل الطلبات إلى حجوزات مؤكدة مع عدد أقل من عمليات التسليم."],
|
||||
["الأسطول والتشغيل", "اعرف المركبات المتاحة أو المحجوبة أو المستحقة أو التي تنتظر إجراءً."],
|
||||
["مكتب الاستقبال", "حضّر مهام التسليم والإرجاع قبل انتظار العميل."]
|
||||
]
|
||||
},
|
||||
"modules": {
|
||||
@@ -141,30 +86,12 @@
|
||||
"title": "نواة مترابطة منظّمة حول أعمال التأجير الفعلية.",
|
||||
"body": "توضح حدود الوحدات المسؤوليات، بينما تمنع السجلات المشتركة فوضى النسخ واللصق المعتادة.",
|
||||
"items": [
|
||||
[
|
||||
"الحجوزات",
|
||||
"التوافر والتواريخ ومتطلبات العميل والحالة ضمن سير واحد."
|
||||
],
|
||||
[
|
||||
"جاهزية الأسطول",
|
||||
"توافر المركبات والحجوزات التشغيلية وحالة الخدمة وسياق الموقع."
|
||||
],
|
||||
[
|
||||
"عقود الإيجار",
|
||||
"متطلبات التسليم وحالة العقد والسجلات التشغيلية."
|
||||
],
|
||||
[
|
||||
"فحص المركبة",
|
||||
"فحوص منظمة عند التسليم والإرجاع مع مهام متابعة."
|
||||
],
|
||||
[
|
||||
"سجلات العملاء",
|
||||
"سجل التأجير وبيانات الاتصال المرتبطة بالعمل الجاري."
|
||||
],
|
||||
[
|
||||
"التقارير التشغيلية",
|
||||
"تعريفات وعروض تشغيلية يمكن التحقق منها مقابل النشاط المصدر."
|
||||
]
|
||||
["الحجوزات", "التوافر والتواريخ ومتطلبات العميل والحالة ضمن سير واحد."],
|
||||
["جاهزية الأسطول", "توافر المركبات والحجوزات التشغيلية وحالة الخدمة وسياق الموقع."],
|
||||
["عقود الإيجار", "متطلبات التسليم وحالة العقد والسجلات التشغيلية."],
|
||||
["فحص المركبة", "فحوص منظمة عند التسليم والإرجاع مع مهام متابعة."],
|
||||
["سجلات العملاء", "سجل التأجير وبيانات الاتصال المرتبطة بالعمل الجاري."],
|
||||
["التقارير التشغيلية", "تعريفات وعروض تشغيلية يمكن التحقق منها مقابل النشاط المصدر."]
|
||||
]
|
||||
},
|
||||
"results": {
|
||||
@@ -172,22 +99,10 @@
|
||||
"title": "قِس التغيير، لا الخطاب التسويقي.",
|
||||
"body": "لا ينبغي نشر أي نتيجة قبل اعتماد خط أساس حقيقي لعميل وفترة مقارنة وتعريف واضح للبيانات. لذلك تعرض هذه النسخة منهجية القياس بدل نسب مئوية مختلقة.",
|
||||
"metrics": [
|
||||
[
|
||||
"زمن معالجة الحجز",
|
||||
"من استلام طلب صالح للعمل حتى تأكيد الحجز."
|
||||
],
|
||||
[
|
||||
"زمن إعادة المركبة إلى الجاهزية",
|
||||
"من تسجيل الإرجاع حتى حالة جاهز للتأجير."
|
||||
],
|
||||
[
|
||||
"معدل استخدام الأسطول",
|
||||
"مقارنة أيام التوافر بأيام التأجير وفق تعريف واحد معتمد."
|
||||
],
|
||||
[
|
||||
"متابعة حالات التأخير",
|
||||
"عمليات التأجير المتأخرة المفتوحة مع مسؤول وإجراء تالٍ."
|
||||
]
|
||||
["زمن معالجة الحجز", "من استلام طلب صالح للعمل حتى تأكيد الحجز."],
|
||||
["زمن إعادة المركبة إلى الجاهزية", "من تسجيل الإرجاع حتى حالة جاهز للتأجير."],
|
||||
["معدل استخدام الأسطول", "مقارنة أيام التوافر بأيام التأجير وفق تعريف واحد معتمد."],
|
||||
["متابعة حالات التأخير", "عمليات التأجير المتأخرة المفتوحة مع مسؤول وإجراء تالٍ."]
|
||||
],
|
||||
"methodTitle": "المنهجية المطلوبة قبل نشر أي نتيجة",
|
||||
"method": [
|
||||
@@ -215,22 +130,10 @@
|
||||
"title": "قيّم المنصة بالأدلة، لا بالشعارات.",
|
||||
"body": "لا تُنشر شهادات أو وعود بمستوى الخدمة قبل وجود وثائق معتمدة. أثناء التقييم، اطلب إجابات محددة حول الضوابط المهمة لنشاطك.",
|
||||
"checks": [
|
||||
[
|
||||
"التحكم في الوصول",
|
||||
"الأدوار والصلاحيات وإجراءات المسؤول وإنهاء وصول المستخدم."
|
||||
],
|
||||
[
|
||||
"قابلية التدقيق",
|
||||
"التغييرات المسجلة والقابلة للبحث والتصدير."
|
||||
],
|
||||
[
|
||||
"معالجة البيانات",
|
||||
"الملكية والاحتفاظ والتصدير والحذف والجهات الفرعية."
|
||||
],
|
||||
[
|
||||
"استمرارية العمل",
|
||||
"النسخ الاحتياطي والاستعادة والتواصل عند الحوادث والدعم."
|
||||
]
|
||||
["التحكم في الوصول", "الأدوار والصلاحيات وإجراءات المسؤول وإنهاء وصول المستخدم."],
|
||||
["قابلية التدقيق", "التغييرات المسجلة والقابلة للبحث والتصدير."],
|
||||
["معالجة البيانات", "الملكية والاحتفاظ والتصدير والحذف والجهات الفرعية."],
|
||||
["استمرارية العمل", "النسخ الاحتياطي والاستعادة والتواصل عند الحوادث والدعم."]
|
||||
]
|
||||
},
|
||||
"pricing": {
|
||||
@@ -289,65 +192,32 @@
|
||||
[
|
||||
"المنتج",
|
||||
[
|
||||
[
|
||||
"سير العمل",
|
||||
"#workflow"
|
||||
],
|
||||
[
|
||||
"الوحدات",
|
||||
"#modules"
|
||||
],
|
||||
[
|
||||
"التسعير",
|
||||
"#pricing"
|
||||
]
|
||||
["سير العمل", "#workflow"],
|
||||
["الوحدات", "#modules"],
|
||||
["التسعير", "#pricing"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"الشركة",
|
||||
[
|
||||
[
|
||||
"تواصل مع المبيعات",
|
||||
"#contact"
|
||||
],
|
||||
[
|
||||
"احجز عرضًا توضيحيًا",
|
||||
"#demo"
|
||||
]
|
||||
["تواصل مع المبيعات", "#contact"],
|
||||
["احجز عرضًا توضيحيًا", "#demo"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"الحساب",
|
||||
[
|
||||
[
|
||||
"تسجيل الدخول",
|
||||
"#login"
|
||||
],
|
||||
[
|
||||
"اللغة",
|
||||
"#language"
|
||||
],
|
||||
[
|
||||
"المظهر",
|
||||
"#theme"
|
||||
]
|
||||
["تسجيل الدخول", "#login"],
|
||||
["اللغة", "#language"],
|
||||
["المظهر", "#theme"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"قانوني",
|
||||
[
|
||||
[
|
||||
"الخصوصية",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"الشروط",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"إمكانية الوصول",
|
||||
"#!"
|
||||
]
|
||||
["الخصوصية", "#!"],
|
||||
["الشروط", "#!"],
|
||||
["إمكانية الوصول", "#!"]
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -377,11 +247,7 @@
|
||||
"أكثر من 500 مركبة",
|
||||
"غير متأكد بعد"
|
||||
],
|
||||
"langOptions": [
|
||||
"English",
|
||||
"Français",
|
||||
"العربية"
|
||||
],
|
||||
"langOptions": ["English", "Français", "العربية"],
|
||||
"successTitle": "تم تسجيل طلب العرض",
|
||||
"successBody": "اكتمل مسار الحجز في هذا النموذج الأولي. لم يُرسل أي طلب إلى نظام مبيعات فعلي.",
|
||||
"submitAnother": "إرسال طلب آخر",
|
||||
|
||||
@@ -7,26 +7,11 @@
|
||||
},
|
||||
"header": {
|
||||
"nav": [
|
||||
[
|
||||
"product",
|
||||
"Product"
|
||||
],
|
||||
[
|
||||
"workflow",
|
||||
"Workflow"
|
||||
],
|
||||
[
|
||||
"modules",
|
||||
"Modules"
|
||||
],
|
||||
[
|
||||
"pricing",
|
||||
"Pricing"
|
||||
],
|
||||
[
|
||||
"faq",
|
||||
"FAQ"
|
||||
]
|
||||
["product", "Product"],
|
||||
["workflow", "Workflow"],
|
||||
["modules", "Modules"],
|
||||
["pricing", "Pricing"],
|
||||
["faq", "FAQ"]
|
||||
],
|
||||
"login": "Log in",
|
||||
"demo": "Book a demo",
|
||||
@@ -50,22 +35,10 @@
|
||||
"title": "One workflow across the rental lifecycle.",
|
||||
"body": "Keep the customer, vehicle, agreement, and task context connected as the rental moves forward.",
|
||||
"items": [
|
||||
[
|
||||
"Reservation",
|
||||
"Capture the request and required details."
|
||||
],
|
||||
[
|
||||
"Vehicle",
|
||||
"Track availability and readiness."
|
||||
],
|
||||
[
|
||||
"Agreement",
|
||||
"Keep pickup tasks and records together."
|
||||
],
|
||||
[
|
||||
"Return",
|
||||
"Record condition and close follow-up work."
|
||||
]
|
||||
["Reservation", "Capture the request and required details."],
|
||||
["Vehicle", "Track availability and readiness."],
|
||||
["Agreement", "Keep pickup tasks and records together."],
|
||||
["Return", "Record condition and close follow-up work."]
|
||||
]
|
||||
},
|
||||
"comparison": {
|
||||
@@ -92,48 +65,23 @@
|
||||
"title": "Move the rental forward without losing the thread.",
|
||||
"body": "Each step exposes the decision, owner, and next action instead of burying operations inside a generic feature list.",
|
||||
"steps": [
|
||||
[
|
||||
"01",
|
||||
"Capture the reservation",
|
||||
"Record dates, driver details, requirements, and source."
|
||||
],
|
||||
[
|
||||
"02",
|
||||
"Assign a ready vehicle",
|
||||
"Match availability with class, location, and readiness."
|
||||
],
|
||||
[
|
||||
"03",
|
||||
"Complete pickup",
|
||||
"Confirm documents, inspection, agreement, and handover tasks."
|
||||
],
|
||||
[
|
||||
"04",
|
||||
"Process the return",
|
||||
"Record condition, close open work, and release the vehicle."
|
||||
]
|
||||
["01", "Capture the reservation", "Record dates, driver details, requirements, and source."],
|
||||
["02", "Assign a ready vehicle", "Match availability with class, location, and readiness."],
|
||||
["03", "Complete pickup", "Confirm documents, inspection, agreement, and handover tasks."],
|
||||
["04", "Process the return", "Record condition, close open work, and release the vehicle."]
|
||||
]
|
||||
},
|
||||
"roles": {
|
||||
"eyebrow": "Built for the operating team",
|
||||
"title": "Give each role the context it needs, without creating separate versions of the truth.",
|
||||
"roles": [
|
||||
[
|
||||
"Owner or manager",
|
||||
"See workload, exceptions, and operating signals across the business."
|
||||
],
|
||||
[
|
||||
"Reservations team",
|
||||
"Move requests from inquiry to confirmed rental with fewer handoffs."
|
||||
],
|
||||
["Owner or manager", "See workload, exceptions, and operating signals across the business."],
|
||||
["Reservations team", "Move requests from inquiry to confirmed rental with fewer handoffs."],
|
||||
[
|
||||
"Fleet and operations",
|
||||
"Know which vehicles are available, blocked, due, or awaiting action."
|
||||
],
|
||||
[
|
||||
"Front desk",
|
||||
"Prepare pickup and return tasks before the customer is waiting."
|
||||
]
|
||||
["Front desk", "Prepare pickup and return tasks before the customer is waiting."]
|
||||
]
|
||||
},
|
||||
"modules": {
|
||||
@@ -141,26 +89,14 @@
|
||||
"title": "A connected core, organized around real rental work.",
|
||||
"body": "Module boundaries clarify responsibility while shared records prevent the usual copy-and-paste archaeology.",
|
||||
"items": [
|
||||
[
|
||||
"Reservations",
|
||||
"Availability, dates, customer requirements, and status in one flow."
|
||||
],
|
||||
[
|
||||
"Fleet readiness",
|
||||
"Vehicle availability, blocks, service state, and location context."
|
||||
],
|
||||
[
|
||||
"Rental agreements",
|
||||
"Pickup requirements, agreement status, and operational records."
|
||||
],
|
||||
["Reservations", "Availability, dates, customer requirements, and status in one flow."],
|
||||
["Fleet readiness", "Vehicle availability, blocks, service state, and location context."],
|
||||
["Rental agreements", "Pickup requirements, agreement status, and operational records."],
|
||||
[
|
||||
"Vehicle inspections",
|
||||
"Structured pickup and return condition checks with follow-up tasks."
|
||||
],
|
||||
[
|
||||
"Customer records",
|
||||
"Rental history and contact context attached to active work."
|
||||
],
|
||||
["Customer records", "Rental history and contact context attached to active work."],
|
||||
[
|
||||
"Operations reporting",
|
||||
"Definitions and operational views that can be validated against source activity."
|
||||
@@ -172,22 +108,13 @@
|
||||
"title": "Measure the change, not the marketing.",
|
||||
"body": "No outcome claim should appear until a real customer baseline, comparison period, and data definition are approved. The production page therefore shows the measurement plan instead of fictional percentages.",
|
||||
"metrics": [
|
||||
[
|
||||
"Reservation handling time",
|
||||
"From usable request to confirmed reservation."
|
||||
],
|
||||
[
|
||||
"Vehicle turnaround time",
|
||||
"From return recorded to ready-for-rental status."
|
||||
],
|
||||
["Reservation handling time", "From usable request to confirmed reservation."],
|
||||
["Vehicle turnaround time", "From return recorded to ready-for-rental status."],
|
||||
[
|
||||
"Fleet utilization",
|
||||
"Available rental days compared with rented days, using one approved definition."
|
||||
],
|
||||
[
|
||||
"Overdue follow-up",
|
||||
"Open overdue rentals with an assigned owner and next action."
|
||||
]
|
||||
["Overdue follow-up", "Open overdue rentals with an assigned owner and next action."]
|
||||
],
|
||||
"methodTitle": "Methodology required before publishing results",
|
||||
"method": [
|
||||
@@ -215,22 +142,10 @@
|
||||
"title": "Evaluate the platform with evidence, not badges.",
|
||||
"body": "Certifications and service-level claims remain unpublished until approved documentation exists. During evaluation, request concrete answers for the controls that matter to your operation.",
|
||||
"checks": [
|
||||
[
|
||||
"Access control",
|
||||
"Roles, permissions, administrator actions, and offboarding."
|
||||
],
|
||||
[
|
||||
"Auditability",
|
||||
"Which changes are recorded, searchable, and exportable."
|
||||
],
|
||||
[
|
||||
"Data handling",
|
||||
"Ownership, retention, export, deletion, and subprocessors."
|
||||
],
|
||||
[
|
||||
"Continuity",
|
||||
"Backup, recovery, incident communication, and support process."
|
||||
]
|
||||
["Access control", "Roles, permissions, administrator actions, and offboarding."],
|
||||
["Auditability", "Which changes are recorded, searchable, and exportable."],
|
||||
["Data handling", "Ownership, retention, export, deletion, and subprocessors."],
|
||||
["Continuity", "Backup, recovery, incident communication, and support process."]
|
||||
]
|
||||
},
|
||||
"pricing": {
|
||||
@@ -289,65 +204,32 @@
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
[
|
||||
"Workflow",
|
||||
"#workflow"
|
||||
],
|
||||
[
|
||||
"Modules",
|
||||
"#modules"
|
||||
],
|
||||
[
|
||||
"Pricing",
|
||||
"#pricing"
|
||||
]
|
||||
["Workflow", "#workflow"],
|
||||
["Modules", "#modules"],
|
||||
["Pricing", "#pricing"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
[
|
||||
[
|
||||
"Contact sales",
|
||||
"#contact"
|
||||
],
|
||||
[
|
||||
"Book a demo",
|
||||
"#demo"
|
||||
]
|
||||
["Contact sales", "#contact"],
|
||||
["Book a demo", "#demo"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Account",
|
||||
[
|
||||
[
|
||||
"Log in",
|
||||
"#login"
|
||||
],
|
||||
[
|
||||
"Language",
|
||||
"#language"
|
||||
],
|
||||
[
|
||||
"Theme",
|
||||
"#theme"
|
||||
]
|
||||
["Log in", "#login"],
|
||||
["Language", "#language"],
|
||||
["Theme", "#theme"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Legal",
|
||||
[
|
||||
[
|
||||
"Privacy",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"Terms",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"Accessibility",
|
||||
"#!"
|
||||
]
|
||||
["Privacy", "#!"],
|
||||
["Terms", "#!"],
|
||||
["Accessibility", "#!"]
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -377,11 +259,7 @@
|
||||
"More than 500 vehicles",
|
||||
"Not sure yet"
|
||||
],
|
||||
"langOptions": [
|
||||
"English",
|
||||
"Français",
|
||||
"العربية"
|
||||
],
|
||||
"langOptions": ["English", "Français", "العربية"],
|
||||
"successTitle": "Demo request recorded",
|
||||
"successBody": "This prototype has completed the booking flow. No request was sent to a real sales system.",
|
||||
"submitAnother": "Submit another request",
|
||||
|
||||
@@ -7,26 +7,11 @@
|
||||
},
|
||||
"header": {
|
||||
"nav": [
|
||||
[
|
||||
"product",
|
||||
"Produit"
|
||||
],
|
||||
[
|
||||
"workflow",
|
||||
"Parcours"
|
||||
],
|
||||
[
|
||||
"modules",
|
||||
"Modules"
|
||||
],
|
||||
[
|
||||
"pricing",
|
||||
"Tarifs"
|
||||
],
|
||||
[
|
||||
"faq",
|
||||
"FAQ"
|
||||
]
|
||||
["product", "Produit"],
|
||||
["workflow", "Parcours"],
|
||||
["modules", "Modules"],
|
||||
["pricing", "Tarifs"],
|
||||
["faq", "FAQ"]
|
||||
],
|
||||
"login": "Se connecter",
|
||||
"demo": "Réserver une démo",
|
||||
@@ -50,22 +35,10 @@
|
||||
"title": "Un même parcours pour tout le cycle de location.",
|
||||
"body": "Conservez le contexte du client, du véhicule, du contrat et des tâches au fil de la location.",
|
||||
"items": [
|
||||
[
|
||||
"Réservation",
|
||||
"Saisir la demande et les informations nécessaires."
|
||||
],
|
||||
[
|
||||
"Véhicule",
|
||||
"Suivre la disponibilité et l’état de préparation."
|
||||
],
|
||||
[
|
||||
"Contrat",
|
||||
"Regrouper les tâches de départ et les documents."
|
||||
],
|
||||
[
|
||||
"Retour",
|
||||
"Enregistrer l’état et clôturer les actions de suivi."
|
||||
]
|
||||
["Réservation", "Saisir la demande et les informations nécessaires."],
|
||||
["Véhicule", "Suivre la disponibilité et l’état de préparation."],
|
||||
["Contrat", "Regrouper les tâches de départ et les documents."],
|
||||
["Retour", "Enregistrer l’état et clôturer les actions de suivi."]
|
||||
]
|
||||
},
|
||||
"comparison": {
|
||||
@@ -118,10 +91,7 @@
|
||||
"eyebrow": "Conçu pour l’équipe opérationnelle",
|
||||
"title": "Donnez à chaque rôle le contexte utile, sans multiplier les versions de la vérité.",
|
||||
"roles": [
|
||||
[
|
||||
"Direction ou responsable",
|
||||
"Voir la charge, les exceptions et les signaux d’exploitation."
|
||||
],
|
||||
["Direction ou responsable", "Voir la charge, les exceptions et les signaux d’exploitation."],
|
||||
[
|
||||
"Équipe réservations",
|
||||
"Faire passer les demandes vers une location confirmée avec moins de transmissions."
|
||||
@@ -130,10 +100,7 @@
|
||||
"Parc et opérations",
|
||||
"Savoir quels véhicules sont disponibles, bloqués, attendus ou en attente d’action."
|
||||
],
|
||||
[
|
||||
"Comptoir",
|
||||
"Préparer les tâches de départ et de retour avant que le client n’attende."
|
||||
]
|
||||
["Comptoir", "Préparer les tâches de départ et de retour avant que le client n’attende."]
|
||||
]
|
||||
},
|
||||
"modules": {
|
||||
@@ -141,30 +108,15 @@
|
||||
"title": "Un socle connecté, organisé autour du travail réel de location.",
|
||||
"body": "Les modules clarifient les responsabilités, tandis que les dossiers partagés évitent l’archéologie du copier-coller.",
|
||||
"items": [
|
||||
[
|
||||
"Réservations",
|
||||
"Disponibilités, dates, besoins du client et statut dans un même flux."
|
||||
],
|
||||
[
|
||||
"Préparation du parc",
|
||||
"Disponibilité, blocages, état de service et contexte d’agence."
|
||||
],
|
||||
[
|
||||
"Contrats de location",
|
||||
"Exigences de départ, état du contrat et dossiers opérationnels."
|
||||
],
|
||||
["Réservations", "Disponibilités, dates, besoins du client et statut dans un même flux."],
|
||||
["Préparation du parc", "Disponibilité, blocages, état de service et contexte d’agence."],
|
||||
["Contrats de location", "Exigences de départ, état du contrat et dossiers opérationnels."],
|
||||
[
|
||||
"Inspections des véhicules",
|
||||
"Contrôles structurés au départ et au retour avec actions de suivi."
|
||||
],
|
||||
[
|
||||
"Dossiers clients",
|
||||
"Historique de location et coordonnées rattachés au travail en cours."
|
||||
],
|
||||
[
|
||||
"Rapports opérationnels",
|
||||
"Définitions et vues vérifiables par rapport à l’activité source."
|
||||
]
|
||||
["Dossiers clients", "Historique de location et coordonnées rattachés au travail en cours."],
|
||||
["Rapports opérationnels", "Définitions et vues vérifiables par rapport à l’activité source."]
|
||||
]
|
||||
},
|
||||
"results": {
|
||||
@@ -176,18 +128,12 @@
|
||||
"Temps de traitement des réservations",
|
||||
"De la demande exploitable à la réservation confirmée."
|
||||
],
|
||||
[
|
||||
"Temps de remise en service",
|
||||
"Du retour enregistré au statut prêt à louer."
|
||||
],
|
||||
["Temps de remise en service", "Du retour enregistré au statut prêt à louer."],
|
||||
[
|
||||
"Taux d’utilisation du parc",
|
||||
"Jours louables comparés aux jours loués, selon une définition approuvée."
|
||||
],
|
||||
[
|
||||
"Suivi des retards",
|
||||
"Locations en retard avec responsable et prochaine action."
|
||||
]
|
||||
["Suivi des retards", "Locations en retard avec responsable et prochaine action."]
|
||||
],
|
||||
"methodTitle": "Méthode requise avant toute publication",
|
||||
"method": [
|
||||
@@ -215,22 +161,10 @@
|
||||
"title": "Évaluez la plateforme avec des preuves, pas avec des badges.",
|
||||
"body": "Aucune certification ni promesse de niveau de service n’est publiée sans documentation approuvée. Pendant l’évaluation, demandez des réponses concrètes sur les contrôles utiles à votre activité.",
|
||||
"checks": [
|
||||
[
|
||||
"Contrôle des accès",
|
||||
"Rôles, autorisations, actions administrateur et départs."
|
||||
],
|
||||
[
|
||||
"Traçabilité",
|
||||
"Modifications enregistrées, recherchables et exportables."
|
||||
],
|
||||
[
|
||||
"Gestion des données",
|
||||
"Propriété, conservation, export, suppression et sous-traitants."
|
||||
],
|
||||
[
|
||||
"Continuité",
|
||||
"Sauvegarde, reprise, communication d’incident et support."
|
||||
]
|
||||
["Contrôle des accès", "Rôles, autorisations, actions administrateur et départs."],
|
||||
["Traçabilité", "Modifications enregistrées, recherchables et exportables."],
|
||||
["Gestion des données", "Propriété, conservation, export, suppression et sous-traitants."],
|
||||
["Continuité", "Sauvegarde, reprise, communication d’incident et support."]
|
||||
]
|
||||
},
|
||||
"pricing": {
|
||||
@@ -289,65 +223,32 @@
|
||||
[
|
||||
"Produit",
|
||||
[
|
||||
[
|
||||
"Parcours",
|
||||
"#workflow"
|
||||
],
|
||||
[
|
||||
"Modules",
|
||||
"#modules"
|
||||
],
|
||||
[
|
||||
"Tarifs",
|
||||
"#pricing"
|
||||
]
|
||||
["Parcours", "#workflow"],
|
||||
["Modules", "#modules"],
|
||||
["Tarifs", "#pricing"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Entreprise",
|
||||
[
|
||||
[
|
||||
"Contacter le service commercial",
|
||||
"#contact"
|
||||
],
|
||||
[
|
||||
"Réserver une démo",
|
||||
"#demo"
|
||||
]
|
||||
["Contacter le service commercial", "#contact"],
|
||||
["Réserver une démo", "#demo"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Compte",
|
||||
[
|
||||
[
|
||||
"Se connecter",
|
||||
"#login"
|
||||
],
|
||||
[
|
||||
"Langue",
|
||||
"#language"
|
||||
],
|
||||
[
|
||||
"Thème",
|
||||
"#theme"
|
||||
]
|
||||
["Se connecter", "#login"],
|
||||
["Langue", "#language"],
|
||||
["Thème", "#theme"]
|
||||
]
|
||||
],
|
||||
[
|
||||
"Mentions légales",
|
||||
[
|
||||
[
|
||||
"Confidentialité",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"Conditions",
|
||||
"#!"
|
||||
],
|
||||
[
|
||||
"Accessibilité",
|
||||
"#!"
|
||||
]
|
||||
["Confidentialité", "#!"],
|
||||
["Conditions", "#!"],
|
||||
["Accessibilité", "#!"]
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -377,11 +278,7 @@
|
||||
"Plus de 500 véhicules",
|
||||
"Je ne sais pas encore"
|
||||
],
|
||||
"langOptions": [
|
||||
"English",
|
||||
"Français",
|
||||
"العربية"
|
||||
],
|
||||
"langOptions": ["English", "Français", "العربية"],
|
||||
"successTitle": "Demande de démonstration enregistrée",
|
||||
"successBody": "Ce prototype a terminé le parcours de réservation. Aucune demande n’a été envoyée à un véritable système commercial.",
|
||||
"submitAnother": "Envoyer une autre demande",
|
||||
|
||||
@@ -3,22 +3,77 @@ import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
|
||||
const required = [
|
||||
'09_DEVELOPER_HANDOFF_v1.0.md','DECISION_LOG_v9.0.md','DEFINITION_OF_DONE_v1.0.md','OPEN_ISSUE_REGISTER_v1.0.csv',
|
||||
'tokens/design-tokens_v1.0.json','tokens/semantic-tokens_v1.0.css','components/COMPONENT_CONTRACTS_v1.0.md',
|
||||
'localization/ROUTE_MANIFEST_v1.0.json','localization/locales/en/homepage.json','localization/locales/fr/homepage.json','localization/locales/ar/homepage.json',
|
||||
'interaction/theme-bootstrap_v1.0.js','qa/QA_MATRIX_v1.0.csv','qa/ACCESSIBILITY_CHECKLIST_v1.0.csv','qa/PERFORMANCE_BUDGETS_v1.0.json'
|
||||
'09_DEVELOPER_HANDOFF_v1.0.md',
|
||||
'DECISION_LOG_v9.0.md',
|
||||
'DEFINITION_OF_DONE_v1.0.md',
|
||||
'OPEN_ISSUE_REGISTER_v1.0.csv',
|
||||
'tokens/design-tokens_v1.0.json',
|
||||
'tokens/semantic-tokens_v1.0.css',
|
||||
'components/COMPONENT_CONTRACTS_v1.0.md',
|
||||
'localization/ROUTE_MANIFEST_v1.0.json',
|
||||
'localization/locales/en/homepage.json',
|
||||
'localization/locales/fr/homepage.json',
|
||||
'localization/locales/ar/homepage.json',
|
||||
'interaction/theme-bootstrap_v1.0.js',
|
||||
'qa/QA_MATRIX_v1.0.csv',
|
||||
'qa/ACCESSIBILITY_CHECKLIST_v1.0.csv',
|
||||
'qa/PERFORMANCE_BUDGETS_v1.0.json',
|
||||
];
|
||||
const errors=[]; let checks=0;
|
||||
for (const file of required){ checks++; if(!fs.existsSync(path.join(root,file))) errors.push(`Missing ${file}`); }
|
||||
const readJson=(f)=>JSON.parse(fs.readFileSync(path.join(root,f),'utf8'));
|
||||
for (const file of ['tokens/design-tokens_v1.0.json','localization/ROUTE_MANIFEST_v1.0.json','qa/PERFORMANCE_BUDGETS_v1.0.json','qa/visual-regression-scenarios_v1.0.json']) { try{readJson(file);checks++;}catch(e){errors.push(`${file}: ${e.message}`);} }
|
||||
function shape(value){ if(Array.isArray(value)) return value.map(shape); if(value && typeof value==='object') return Object.fromEntries(Object.keys(value).sort().map(k=>[k,shape(value[k])])); return typeof value; }
|
||||
const locales=['en','fr','ar'].map(l=>readJson(`localization/locales/${l}/homepage.json`));
|
||||
const base=JSON.stringify(shape(locales[0]));
|
||||
for (let i=1;i<locales.length;i++){ checks++; if(JSON.stringify(shape(locales[i]))!==base) errors.push(`Locale shape mismatch: ${['en','fr','ar'][i]}`); }
|
||||
const routes=readJson('localization/ROUTE_MANIFEST_v1.0.json');
|
||||
for (const route of routes.routes){ for (const locale of routes.locales){ checks++; if(!(locale in route.slugs)) errors.push(`Route ${route.id} missing ${locale}`); } }
|
||||
const issueCsv=fs.readFileSync(path.join(root,'OPEN_ISSUE_REGISTER_v1.0.csv'),'utf8'); checks++; if(/,(Critical|High),[^\n]*,,/.test(issueCsv)) errors.push('Critical/High issue missing ownership data');
|
||||
const css=fs.readFileSync(path.join(root,'tokens/semantic-tokens_v1.0.css'),'utf8'); checks++; if(!css.includes('--color-action-conversion')) errors.push('Conversion semantic token missing');
|
||||
if(errors.length){ console.error(JSON.stringify({status:'FAILED',checks,errors},null,2)); process.exit(1); }
|
||||
console.log(JSON.stringify({status:'PASSED',checks,files:fs.readdirSync(root).length},null,2));
|
||||
const errors = [];
|
||||
let checks = 0;
|
||||
for (const file of required) {
|
||||
checks++;
|
||||
if (!fs.existsSync(path.join(root, file))) errors.push(`Missing ${file}`);
|
||||
}
|
||||
const readJson = (f) => JSON.parse(fs.readFileSync(path.join(root, f), 'utf8'));
|
||||
for (const file of [
|
||||
'tokens/design-tokens_v1.0.json',
|
||||
'localization/ROUTE_MANIFEST_v1.0.json',
|
||||
'qa/PERFORMANCE_BUDGETS_v1.0.json',
|
||||
'qa/visual-regression-scenarios_v1.0.json',
|
||||
]) {
|
||||
try {
|
||||
readJson(file);
|
||||
checks++;
|
||||
} catch (e) {
|
||||
errors.push(`${file}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
function shape(value) {
|
||||
if (Array.isArray(value)) return value.map(shape);
|
||||
if (value && typeof value === 'object')
|
||||
return Object.fromEntries(
|
||||
Object.keys(value)
|
||||
.sort()
|
||||
.map((k) => [k, shape(value[k])]),
|
||||
);
|
||||
return typeof value;
|
||||
}
|
||||
const locales = ['en', 'fr', 'ar'].map((l) => readJson(`localization/locales/${l}/homepage.json`));
|
||||
const base = JSON.stringify(shape(locales[0]));
|
||||
for (let i = 1; i < locales.length; i++) {
|
||||
checks++;
|
||||
if (JSON.stringify(shape(locales[i])) !== base)
|
||||
errors.push(`Locale shape mismatch: ${['en', 'fr', 'ar'][i]}`);
|
||||
}
|
||||
const routes = readJson('localization/ROUTE_MANIFEST_v1.0.json');
|
||||
for (const route of routes.routes) {
|
||||
for (const locale of routes.locales) {
|
||||
checks++;
|
||||
if (!(locale in route.slugs)) errors.push(`Route ${route.id} missing ${locale}`);
|
||||
}
|
||||
}
|
||||
const issueCsv = fs.readFileSync(path.join(root, 'OPEN_ISSUE_REGISTER_v1.0.csv'), 'utf8');
|
||||
checks++;
|
||||
if (/,(Critical|High),[^\n]*,,/.test(issueCsv))
|
||||
errors.push('Critical/High issue missing ownership data');
|
||||
const css = fs.readFileSync(path.join(root, 'tokens/semantic-tokens_v1.0.css'), 'utf8');
|
||||
checks++;
|
||||
if (!css.includes('--color-action-conversion')) errors.push('Conversion semantic token missing');
|
||||
if (errors.length) {
|
||||
console.error(JSON.stringify({ status: 'FAILED', checks, errors }, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
JSON.stringify({ status: 'PASSED', checks, files: fs.readdirSync(root).length }, null, 2),
|
||||
);
|
||||
|
||||
@@ -5,80 +5,56 @@
|
||||
{
|
||||
"id": "VR-001",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
1440,
|
||||
1000
|
||||
],
|
||||
"viewport": [1440, 1000],
|
||||
"theme": "light",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-002",
|
||||
"route": "/fr",
|
||||
"viewport": [
|
||||
1024,
|
||||
900
|
||||
],
|
||||
"viewport": [1024, 900],
|
||||
"theme": "dark",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-003",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"viewport": [390, 844],
|
||||
"theme": "light",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-004",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"viewport": [390, 844],
|
||||
"theme": "dark",
|
||||
"state": "drawer-open"
|
||||
},
|
||||
{
|
||||
"id": "VR-005",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"viewport": [390, 844],
|
||||
"theme": "light",
|
||||
"state": "demo-invalid"
|
||||
},
|
||||
{
|
||||
"id": "VR-006",
|
||||
"route": "/fr",
|
||||
"viewport": [
|
||||
768,
|
||||
1024
|
||||
],
|
||||
"viewport": [768, 1024],
|
||||
"theme": "dark",
|
||||
"state": "tour-step-3"
|
||||
},
|
||||
{
|
||||
"id": "VR-007",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
320,
|
||||
800
|
||||
],
|
||||
"viewport": [320, 800],
|
||||
"theme": "dark",
|
||||
"state": "reflow-gate"
|
||||
},
|
||||
{
|
||||
"id": "VR-008",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
1440,
|
||||
1000
|
||||
],
|
||||
"viewport": [1440, 1000],
|
||||
"theme": "dark",
|
||||
"state": "pricing-and-final-cta"
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ No claim is made that users understand the product, find the CTA, or complete th
|
||||
|
||||
## Pre-test findings
|
||||
|
||||
| ID | Evidence | Finding | Severity | Status |
|
||||
|---|---|---|---|---|
|
||||
| P8-001 | Expert + code review | Demo submission in Phase 7 prevented default and produced no success, failure, or recovery state. | S0/C | Fixed in prototype |
|
||||
| P8-002 | Expert + code review | “Watch product tour” navigated to a workflow section rather than opening a tour, creating an expectation mismatch. | S1/C | Fixed with four-step tour |
|
||||
| P8-003 | Expert + code review | Mobile drawer moved focus initially but did not contain focus or make background content inert. | S0/C | Fixed in prototype |
|
||||
| P8-004 | Expert + code review | Theme existed in URL state but did not persist reliably when revisiting without the query parameter. | S1/C | Fixed with local persistence |
|
||||
| P8-005 | Expert review | Multi-location support is implied by branch/location copy but is not an approved capability claim. Users may over-infer. | S2/C | Test explicitly; content approval pending |
|
||||
| P8-006 | Expert review | Final legal consent, alternate failure contact, CRM routing, and spam controls are unresolved. | S1/C for production | Prototype limitation; open |
|
||||
| P8-007 | Automated browser test | EN/FR/AR direction, tour completion, form validation/success, locale hash preservation, theme persistence, and mobile focus return pass the scripted browser checks. | S4/A | Passed |
|
||||
| ID | Evidence | Finding | Severity | Status |
|
||||
| ------ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----------------------------------------- |
|
||||
| P8-001 | Expert + code review | Demo submission in Phase 7 prevented default and produced no success, failure, or recovery state. | S0/C | Fixed in prototype |
|
||||
| P8-002 | Expert + code review | “Watch product tour” navigated to a workflow section rather than opening a tour, creating an expectation mismatch. | S1/C | Fixed with four-step tour |
|
||||
| P8-003 | Expert + code review | Mobile drawer moved focus initially but did not contain focus or make background content inert. | S0/C | Fixed in prototype |
|
||||
| P8-004 | Expert + code review | Theme existed in URL state but did not persist reliably when revisiting without the query parameter. | S1/C | Fixed with local persistence |
|
||||
| P8-005 | Expert review | Multi-location support is implied by branch/location copy but is not an approved capability claim. Users may over-infer. | S2/C | Test explicitly; content approval pending |
|
||||
| P8-006 | Expert review | Final legal consent, alternate failure contact, CRM routing, and spam controls are unresolved. | S1/C for production | Prototype limitation; open |
|
||||
| P8-007 | Automated browser test | EN/FR/AR direction, tour completion, form validation/success, locale hash preservation, theme persistence, and mobile focus return pass the scripted browser checks. | S4/A | Passed |
|
||||
|
||||
## Hypotheses requiring participant evidence
|
||||
|
||||
|
||||
@@ -66,14 +66,14 @@
|
||||
--duration-standard: 160ms;
|
||||
--duration-overlay: 220ms;
|
||||
--easing-standard: ease;
|
||||
--easing-productive: cubic-bezier(.2,0,0,1);
|
||||
--easing-productive: cubic-bezier(0.2, 0, 0, 1);
|
||||
--font-latin: Inter Variable, Inter, Arial, sans-serif;
|
||||
--font-arabic: Noto Sans Arabic, Arial, sans-serif;
|
||||
--breakpoint-medium: 768px;
|
||||
--breakpoint-wide: 1120px;
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
html[data-theme='light'] {
|
||||
--color-canvas: var(--color-gray-25);
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-raised: #ffffff;
|
||||
@@ -91,10 +91,10 @@ html[data-theme="light"] {
|
||||
--color-on-inverse: #f8fafc;
|
||||
--color-soft-blue: #eef5ff;
|
||||
--color-soft-orange: #fff7ed;
|
||||
--shadow-color: rgba(8,20,38,.12);
|
||||
--shadow-color: rgba(8, 20, 38, 0.12);
|
||||
color-scheme: light;
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
html[data-theme='dark'] {
|
||||
--color-canvas: var(--color-navy-950);
|
||||
--color-surface: var(--color-navy-900);
|
||||
--color-surface-raised: var(--color-navy-850);
|
||||
@@ -112,11 +112,19 @@ html[data-theme="dark"] {
|
||||
--color-on-inverse: var(--color-navy-950);
|
||||
--color-soft-blue: #10284a;
|
||||
--color-soft-orange: #3a2015;
|
||||
--shadow-color: rgba(0,0,0,.36);
|
||||
--shadow-color: rgba(0, 0, 0, 0.36);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const breakpoints = { medium: 768, wide: 1120 } as const;
|
||||
export const themePreferences = ["light", "dark", "system"] as const;
|
||||
export const themePreferences = ['light', 'dark', 'system'] as const;
|
||||
export type ThemePreference = (typeof themePreferences)[number];
|
||||
export type ResolvedTheme = Exclude<ThemePreference, "system">;
|
||||
export type ResolvedTheme = Exclude<ThemePreference, 'system'>;
|
||||
export const layout = { containerMaxPx: 1280, touchTargetMinPx: 44, spacingBasePx: 4 } as const;
|
||||
export const motion = { fastMs: 120, standardMs: 160, overlayMs: 220 } as const;
|
||||
|
||||
Reference in New Issue
Block a user