After the wrapper's -π/2 yaw, applying positive rotation.x to the
planeHolder rotates the plane around world X with the nose dropping,
not lifting (visible in the user-supplied screenshot). Flipping to
-0.18 (≈10°) puts the nose where the trajectory says it should go.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The +0.20 (≈11°) climb angle from last commit looked cartoonishly
steep. Real airliners climb at maybe 3–5° once they've cleaned up
after takeoff. Cut targetPitch to 0.06 + p·0.02 (3.5° → 4.6°).
Roll/yaw untouched.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
py was descending +7→+2, now ascending -1→+12 so the plane gains
altitude across the traversal. px stretched 18→-22 (was 16→-16) so
both endpoints sit clearly outside the visible frustum (camera at
z=22 + 40° vfov gives ~28 world-units of visible width on a 16:9
viewport). Pitch flipped from negative to +0.20 + p·0.10 to read
as nose-up for the climb. Roll/yaw unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Poly by Google airplane's default orientation in the GLB is the
opposite of what I assumed: at wrapper.rotation.y = +π/2 the nose
ended up pointing into the direction the plane was moving away from
(it looked like it was flying tail-first). Switch to -π/2 so the
nose actually leads the trajectory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two adjustments to the intro plane:
1. Autonomous cruise speed. Adds BASE_SPEED (1/28 per second of
wallclock) to targetProgress on every frame, so the plane crosses
the screen on its own in ~28 s without any input. Mouse motion
still adds a boost (one full traversal per ~4500 px of cursor
travel), which feels like the plane "speeding up" when the user
interacts. Time delta clamped to 0.1 s so the plane doesn't jump
forward after the tab returns from background.
2. Reverse direction. Plane now enters from upper-right (x = +16),
traverses to upper-left (x = -16), nose pointing -X. Wrapper rot.y
flipped to +π/2; px formula flipped; banking angles inverted so
the plane still rolls "into the turn" along its new direction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Old behaviour: plane.x mapped 1:1 to cursor.x — moving the mouse left
made the plane reverse. New behaviour: every pixel of cursor travel
(any direction) increments a progress counter from 0 to 1, the plane
position is derived from progress, and progress saturates at 1 — so
once the plane has exited stage right, it stays gone.
FULL_DISTANCE = 3500 px of cursor travel for a full traversal. The
existing lerp (0.06) still smooths the rendered position. Background
parallax still uses raw cursor X/Y (independent of plane progress).
deviceorientation handler updated symmetrically — gamma+beta deltas
push progress forward on mobile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Background was inadvertently the wrong Gemini export (an unrelated
airliner-over-mountains photo) — sorted out and replaced with the
intended aerial illustration of Antananarivo (Lake Anosy + Rova
hill + city, 487 KB, 1920px wide).
Also lifted the plane's trajectory: y goes from +7 (offscreen
upper-left) to +2 (still upper half, exiting right) instead of
+5..-2. The plane now stays clearly in the upper third of the
viewport, leaving room for the centered CTA button and the city
detail at the bottom.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User-supplied aerial illustration of Antananarivo (Lake Anosy + Rova
hill + surrounding city, 1920px wide JPG, 241 KB) replaces the
parachute-drop video as the static intro backdrop. The video files
are deleted from /videos.
The plane no longer orbits a scroll timeline. Now:
- Page is a single viewport, no scroll, no act labels, no scroll hint.
- Mouse X (0..1) drives plane.position.x from -16 (offscreen left) to
+16 (offscreen right), with plane.position.y descending from +5 to
-2 — so the plane enters from the upper-left and exits lower-right.
- Pitch/roll/yaw lerp toward small targets that depend on mouse X, so
the plane banks naturally as it crosses.
- Background image gets a softer mouse parallax (-16/-10px) via the
existing --mx/--my CSS vars, now updated from intro-scene.js.
- Three.js cloud spheres are gone; the photo is the entire backdrop.
- ScrollTrigger + the GSAP timeline are removed; the page no longer
needs gsap at all (the script tag stayed for now in case it comes
back, but the dependency could be dropped on a future pass).
- CTA button is back to plain visible/centered, no reveal animation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drops the cylinder+box airplane built last commit in favor of a CC-BY
3D commercial airliner from Poly by Google (188 KB GLB, 11.3k tris,
hosted in assets/airplane.glb). Loaded at runtime via three/addons
GLTFLoader; importmap extended to expose the addons subpath.
Bug worth noting: a naive setFromObject + position.sub(center) +
scale.setScalar pipeline leaves the model offset by -center after
scaling because position is in pre-scale units. Fix is to wrap the
model in a Group, apply the centering offset to the inner model,
then scale the outer Group — the whole transform stays consistent.
Attribution added in two places per CC-BY 3.0:
- HTML header comment with creator + source URL + license link
- JS file header in intro-scene.js
Tone-mapping bumped to ACES filmic for a slightly nicer render.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure the page so the first 4 viewports of scroll drive a
Three.js scene composited on top of the Antananarivo parachute video.
What's there:
- Three.js (ESM, r158 via importmap) renders a low-poly cargo airliner
built from primitives: cylinder fuselage, cone nose, sphere cockpit
(dark glass + emissive), box wings/tail/fin, cylinder engines with
torus intakes, gold trim band, navy fin with gold logo box. No
external model file.
- Hemisphere + directional + ambient lights tuned for golden-hour fill.
- 14 cloud spheres scattered around the plane, slowly rotating.
- GSAP + ScrollTrigger drive a single progress value scrubbed against
scroll position. Inside the rAF loop, the camera arcs from rear-left
(-0.6 rad) to front-right (+1.1 rad), radius dipping mid-flight, and
the plane rolls slightly with scroll.
- Three act labels (Paris CDG / Vol cargo / Antananarivo) cross-fade at
20%/40%-60%/72% scroll positions via a chained gsap timeline.
- Gold CTA button stays opacity:0 + pointer-events:none until the last
~10% of scroll, then fades and scales in. Hover transform rebuilt
without the old mouse-parallax tilt (fights the scroll animation).
- Scroll hint pill (chevron + "Faites défiler") at the bottom of the
first viewport, fades out on first scroll event.
- prefers-reduced-motion shortcut: scroll stage hidden, CTA visible,
no animation. Page reverts to a static screen with the video bg.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The new index.html is a 3-act scroll-driven storytelling intro:
Act 1 (Tarmac at sunset)
Sunset gradient sky, mountain silhouettes, hangar, runway with
centerline lights. Inline SVG cargo plane (MVA-branded gold tail)
sits on the ground while box emojis cycle up a loading ramp.
Act 2 (Take-off at dusk)
Dusk sky, two parallax cloud layers and distant mountains. The
plane translates diagonally up-and-right with a slight tilt and
a glowing contrail, sized down progressively.
Act 3 (Arrival at night)
Deep navy night sky with twinkling stars, a glowing moon, and
the Madagascar coastline silhouette. A small plane fades in
descending toward the island, then a centered CTA block reveals:
"Bienvenue à bord" with the gold "Accéder au site" button that
routes to accueil.html (the real homepage).
Implementation:
- Pure CSS layered scenes; no library
- Scroll progress driven by a single CSS custom property --scroll
(0→1) updated via rAF, layers transform off it
- Mouse-move parallax on layers via --mx/--my (skipped on touch)
- Inline SVG plane reused across the 3 scenes
- All text is i18n-driven with a new `intro` section in FR/EN/MG
Architecture changes:
- index.html → new parallax intro
- accueil.html → former index.html content (full homepage)
- All nav/logo/footer links updated index.html → accueil.html across
the 8 existing pages
- Mobile-nav on accueil.html now also includes Service Commande
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Footer copyright bumped to 2026 across all 8 HTML pages and in the
three translation strings (FR/EN/MG) in translations.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Dépasser les dimensions autorisées" now has an asterisk pointing to
a small italic note below the dos/donts grid: "Nous contacter pour
tout colis volumineux." (FR/EN/MG).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New page service-commande.html explaining the order-on-behalf service:
- 10% commission on total order amount
- 50% deposit + 50% on order validation
- Right to request 100% upfront for large orders
- Standard 70,000 Ar/kg shipping fee on top, paid on receipt
- 5-question FAQ
- CTA to contact / Messenger
Trilingual content (FR / EN / MG) added to translations.js with full
serviceCommande section + nav.serviceCommande key.
Menu link inserted right after "Tarifs" in main nav, mobile nav and
footer links across all pages: index, about, tarifs, contact,
guide-envoi, application, cgv, service-commande.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a client tries to re-register with an existing email:
- The form still shows the on-screen "already registered" message
- HubSpot data is still untouched (no duplicate, ref number preserved)
- NEW: a welcome-back email is now sent via EmailJS reminding them
of their existing client reference number
- The internal MVA notification (Formspree) is preserved
Requires creating a new EmailJS template (id: template_welcome_back)
with variables: firstname, email, reference_client.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace the cramped map-container layout with a polished map-card
- Centered section header (h2 + gold-line + subtitle) above the card
- Navy gradient header inside the card with gold location icon
- Address displayed in the header alongside an "open in Google Maps" CTA
- Increased map height to 420px (320px on mobile)
- Soft shadow and gold-tinted border for premium feel
- Added mapSubtitle translation key (FR/EN/MG)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajout de data-i18n sur tous les éléments non traduits (tarifs, contact, guide,
application, accueil) et ajout des clés correspondantes en FR/EN/MG dans
translations.js : détails tarifaires, cartes livraison, stats accueil, sections
CTA, récapitulatif contact, footer CGV.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Sarany" est le terme correct pour désigner les tarifs/prix dans ce contexte.
Corrige : heroTitle tarifs, heroSubtitle tarifs, nav pricing, heroCtaSecondary,
et value3Desc (about).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Fandefasana" est la forme correcte (nominalisée) en malgache pour désigner
le transport/fret aérien. Corrige le titre hero et 5 autres occurrences dans
les textes descriptifs, sous-titres et footer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mise à jour de toutes les mentions du délai de livraison sur l'ensemble du site
(index, tarifs, contact) et dans les 3 langues (FR/EN/MG) pour indiquer clairement
que les 2 semaines sont comptées à compter de l'arrivée du colis au dépôt de Paris.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajout du SDK EmailJS (CDN) dans contact.html
- Initialisation EmailJS avec la clé publique dans form-handler.js
- Nouvelle fonction sendWelcomeEmail() : envoie au client son prénom,
numéro de référence et l'adresse de dépôt Paris (AEIC Forwarding, Orly)
- Appel automatique dans showSuccess() après inscription réussie
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Active WORKER_PROXY_URL pointant vers mva-hubspot-proxy.mvaglobalfret.workers.dev.
Le Worker interroge l'API CRM HubSpot côté serveur (contourne le CORS) et
retourne les données du contact si l'email est déjà enregistré.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Supprime HUBSPOT_SERVICE_KEY du code JS public (évite l'exposition du token)
- Remplace l'appel CORS-bloqué vers api.hubapi.com par un appel au proxy
Cloudflare Worker (WORKER_PROXY_URL, vide par défaut = sans blocage)
- Ajoute cloudflare-worker/hubspot-proxy.js : code complet du Worker à
déployer gratuitement depuis dash.cloudflare.com (sans CLI ni Node.js)
- Quand WORKER_PROXY_URL est renseigné, la détection doublon est active :
un client existant voit son message "déjà inscrit" sans re-soumission
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- generateRefNumber() basé sur timestamp : références jamais identiques
- checkExistingContact() : lecture HubSpot via clé de service (read-only)
- Si email déjà connu : affiche message 'déjà client' + référence existante,
AUCUNE soumission envoyée (référence existante jamais modifiée)
- Notification interne Formspree si tentative double inscription
- Traductions FR/EN/MG pour les nouveaux messages