# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

Static marketing site + MVP tyre shop for **Gumirom**, a tyre shop (vulcanizare) in Gherla, Romania. Plain HTML5 + CSS3 + vanilla JS — no framework, no package.json, no build or lint step. UI copy and code comments are in Romanian.

- `app/` is the deploy unit: it is uploaded **as-is via FTP**. Anything that must not be public (tests, docs, tooling, this file) lives outside `app/`.
- `app/index.html` — landing page structured on the "Sell Like Crazy" 17-step sales spine + Hormozi offer mechanics (see `docs/audit-sell-like-crazy-hormozi.md`).
- `app/magazin.html` — tyre shop (new + second-hand): catalog → filters → cart → order sent as a prefilled WhatsApp message. No online payment, no backend.
- `docs/` — audit, design spec and implementation plan (`docs/superpowers/`), owner hand-off guide (`GHID-PROPRIETAR.md`). `_backup-2026-10-06/` is the pre-rewrite version. Not a git repo.

## Commands

The site is served by XAMPP Apache at `http://localhost:8082/projects/gumirom/v3/app/` (document root `E:/xampp/htdocs`, Apache listens on 8082).

Run from `v3/` (Node 22+; no dependencies to install):

```bash
node --test "tests/*.test.js"                                      # all unit tests (a directory arg does NOT work on Node 22)
node --test --test-name-pattern="season" tests/core.test.js        # single test / subset
node tests/browser/run.js probe index index.html                   # browser probe for the landing page
node tests/browser/run.js probe magazin magazin.html               # browser probe for the shop
node tests/browser/run.js errors index.html magazin.html           # JS/console errors at 1280px and 375px
node tests/browser/run.js shot out.png index.html 375 667 "#oferta" ["js to run first"]   # real device-emulated screenshot
node --check app/js/shop.js                                        # syntax check (no linter exists)
```

`tests/browser/run.js` launches headless Chrome (`C:/Program Files/Google/Chrome/Application/chrome.exe`, override with `CHROME`) and drives it over the DevTools Protocol with Node's built-in `WebSocket`, cache disabled. Prefer it over the Chrome extension tools: the user's Chrome window is often minimized (viewport 0×0), which breaks extension screenshots and layout measurements, and plain `chrome --headless --screenshot` clamps the window to ~500px so it cannot show real mobile layouts.

## Architecture

Script load order on both pages is `core.js` → `stoc.js` → `main.js` → `quiz.js` (index only) → `shop.js`, all `defer`.

- **`js/core.js`** — `window.Gumirom` with `CONFIG` (schedule, tariffs, seasons, guarantees, company data) and DOM-free pure helpers: `openState`, `isClosedDate`, `season`/`seasonInfo`/`isWinterish`/`nextSeasonStart`, `quote` (calculator pricing), `rimClass`, `waUrl` (single `encodeURIComponent` of the whole text), `formatLei`, `icsEvent`, `track` (pushes `gumirom_*` events to `dataLayer` if present), `storage` (try/catch localStorage).
- **`js/quiz.js`, `js/shop.js`** — each file is pure logic attached to `Gumirom.quiz` / `Gumirom.shop`, followed by a DOM section that starts with `if (typeof document === 'undefined') return;`. Keep that split: the logic half is what the unit tests load.
- **`js/main.js`** — page interactions shared by both pages (preloader, split-text, reveals, header/menu, hero canvas, calculator, booking form, sticky CTA, season/offer/guarantee binding). Every `init*()` exits silently when its elements are missing, so the same file runs on `index.html` and `magazin.html`.
- **`js/stoc.js`** — `window.GUMIROM_STOC = { demo, actualizat, produse: [...] }`, edited by hand by the owner. `demo: true` shows "catalog demonstrativ" banners. `Gumirom.shop.normalize(list, warn)` drops invalid products one by one with a console warning (it never guesses a season; SH tyres require `profilMm` and `dot`); `sezon`/`stare` are matched case- and diacritic-insensitively.
- Every file guards `if (!window.Gumirom)` — if `core.js` fails to load, `main.js` removes the `js` class so the page still renders.

Cross-cutting mechanisms that span HTML, CSS and JS:

- **No-JS safety:** an inline script in `<head>` adds `.js` to `<html>`; CSS hides the preloader and `[data-reveal]` content only under `.js`. `main.js` adds `.js-ready`; a CSS fallback reveals everything after 3s if `.js-ready` never appears. New content that animates in must follow this pattern.
- **Season-driven copy:** elements with `data-season-name|message|cta|warning` are filled from `CONFIG.seasons` (iarna 1 Oct–15 Dec, vara 15 Mar–15 May, else drum). Urgency comes only from the calendar or real stock counts — never countdowns or invented scarcity (EU unfair-practice rules).
- **Guarantees:** blocks marked `data-guarantee="<id>"` are shown only when `CONFIG.offer.guarantees[].confirmed === true`. «Fără vibrații» is an unconfirmed proposal and stays hidden.
- **Cart:** stored in localStorage under `gumirom.cart.v1` (mounting toggle under `gumirom.montaj.v1`) and always passed through `cartReconcile` against the current stock on load. Lookup maps use `Object.create(null)`.
- **Filters ↔ URL:** `Gumirom.shop.toQuery`/`fromQuery` (whitelisted values) keep shop filters in the query string; the index links into the shop with them.
- Catalog and user data are inserted only with `textContent` / `setAttribute` — never `innerHTML`.
- Booking-form inputs deliberately have no `name` attributes (JS reads them by `id`), so a no-JS submit does not put personal data in the URL.

## Tests

- `tests/helpers/load.js` runs app scripts inside a `vm` context (no `window`, so files attach to `globalThis`). Objects returned from the sandbox come from another realm: wrap them with `plain()` before `assert.deepEqual`, or the comparison fails even when the values match.
- Browser probes are ES modules (`tests/browser/probe-index.js`, `probe-magazin.js`) that load the real pages in fixed-size iframes (cache-busted) and return pass/fail lines; `harness.html` hosts them and `run.js` collects the result. The magazin probe uses and cleans up the localStorage keys above.

## Content and code rules for this project

- Never invent facts on the pages. The only verified business facts are: 4,4★ from 125 Google reviews, the prices in `CONFIG.prices`, the listed services, hours, address (Str. Liviu Rebreanu 56, Gherla) and phones 0744 398 932 / 0740 619 306. Unknown facts (years in business, testimonials, payment methods, delivery) must not be made up; new promises must be gated in `CONFIG` until the owner confirms them.
- Phone numbers are also hard-coded in both HTML files (`tel:` links must work without JS) — change them there as well as in `CONFIG`.
- Match the existing style: ES5-style `var`/`function` in IIFEs, `'use strict'`, short Romanian comments, correct Romanian diacritics (ș, ț with comma below). Animate only transform/opacity and respect `prefers-reduced-motion`. Layout must not overflow at 320px, and the hero's primary CTA must stay above the fold at 375×667.
- On this Windows machine, Bash heredocs containing Romanian quotes („ ”) break the shell — write multi-line scripts to a file first. Run Python with `python -I -X utf8` (the console code page is cp1252).
