Source: data.js

/**
 * @file Content and i18n data for the A. Morvan photographer portfolio (demo).
 * Holds the FR/EN translation trees, the gallery photo metadata and the theme
 * accent color. No React here — pure data, unit-tested in `data.test.js`.
 * @module data
 */

/**
 * A pair of strings for the two supported locales.
 * @typedef {Object} LocalizedText
 * @property {string} fr French text.
 * @property {string} en English text.
 */

/**
 * A project category shown on the Projects page.
 * @typedef {Object} Category
 * @property {string} number Two-digit index, e.g. `"01"`.
 * @property {string} title Category name.
 * @property {string} subtitle Short description.
 * @property {string} quote Editorial pull-quote card text.
 */

/**
 * A practice area listed on the "Œil et l'Intention" page.
 * @typedef {Object} Terrain
 * @property {string} title Area name.
 * @property {string} desc One-line description.
 */

/**
 * All translated UI strings for a single locale.
 * @typedef {Object} Locale
 * @property {string} brandRole Role shown under the logo.
 * @property {{projets: string, oeil: string, contact: string}} nav Nav labels.
 * @property {Object} hero Hero section strings.
 * @property {Category[]} categories The three project categories.
 * @property {Object} oeil Approach page strings (incl. `terrains: Terrain[]`).
 * @property {Object} contact Contact page strings.
 * @property {string} footer Footer text (may contain newlines).
 * @property {string} lightboxClose Accessible label for the lightbox close button.
 */

/**
 * Translation trees keyed by locale code.
 * @type {{fr: Locale, en: Locale}}
 */
export const TRANSLATIONS = {
  fr: {
    brandRole: 'Photographe',
    nav: {
      projets: 'Projets',
      oeil: "L'Œil et l'Intention",
      contact: 'Contact',
    },
    hero: {
      eyebrow: 'Portfolio',
      title1: "Capturer l'instant,",
      title2: "révéler l'intention.",
      subtitle:
        'Photographe basé entre scènes vivantes, architectures urbaines et instants volés — une exploration sensible de la lumière et du silence.',
      cue: 'Découvrir',
    },
    categories: [
      {
        number: '01',
        title: 'Live & Scène',
        subtitle: "L'énergie brute des concerts, capturée dans la lumière et l'obscurité.",
        quote: "Le bruit a une lumière qu'on ne voit qu'une fois.",
      },
      {
        number: '02',
        title: 'Géométrie Urbaine',
        subtitle: 'Lignes, ombres et structures : la ville comme partition graphique.',
        quote: "La ville dessine ce que l'œil n'ose pas cadrer.",
      },
      {
        number: '03',
        title: 'Instants Volés',
        subtitle: 'Des scènes de vie saisies sur le vif, entre pudeur et vérité.',
        quote: "Un regard volé dit parfois plus qu'un sourire posé.",
      },
    ],
    oeil: {
      eyebrow: 'Démarche',
      title: "L'Œil et l'Intention.",
      portraitAlt: 'Portrait sombre du photographe',
      aiNote: "Portrait d'illustration (photo libre de droits) utilisé à titre de maquette.",
      p1: 'Je ne cherche pas à photographier ce qui se voit, mais ce qui se joue entre les gens, dans la seconde qui précède le geste.',
      p2: 'Mon regard navigue entre trois territoires : la scène et son électricité collective, la ville et sa géométrie silencieuse, la rue et ses visages fugaces. Dans chacun, je traque la même chose — une vérité qui échappe à la pose.',
      p3: "Le noir et blanc, le grain, le contraste : des outils pour révéler l'intention derrière l'image, jamais pour la maquiller.",
      terrains: [
        {
          title: 'Scènes & Concerts',
          desc: "L'instant électrique d'un show live, entre ombre et flash.",
        },
        {
          title: 'Architectures Urbaines',
          desc: 'Les villes comme grammaire de lignes et de perspectives.',
        },
        {
          title: 'Instants Volés',
          desc: "Les visages croisés, saisis avant qu'ils ne se dérobent.",
        },
      ],
    },
    contact: {
      eyebrow: 'Contact',
      title: 'Donnons vie à vos projets visuels.',
      emailNote: "(adresse fictive — cette boîte mail n'existe pas)",
      demoTitle: 'Site de démonstration',
      demoText:
        "Ceci est une maquette : merci de ne pas envoyer de message à l'adresse ci-dessus, elle n'est pas surveillée.",
      github: 'Voir le code',
      docs: 'Voir la doc',
    },
    footer:
      'Site de démonstration — photographies libres de droits (Unsplash),\npersonnes et lieux non affiliés à ce projet.\n© 2026 A. Morvan.',
    lightboxClose: 'Fermer',
  },
  en: {
    brandRole: 'Photographer',
    nav: {
      projets: 'Projects',
      oeil: 'The Eye and the Intent',
      contact: 'Contact',
    },
    hero: {
      eyebrow: 'Portfolio',
      title1: 'Capture the moment,',
      title2: 'reveal the intent.',
      subtitle:
        'A photographer moving between live stages, urban architecture and stolen moments — a sensitive exploration of light and silence.',
      cue: 'Discover',
    },
    categories: [
      {
        number: '01',
        title: 'Live & Stage',
        subtitle: 'The raw energy of concerts, captured in light and darkness.',
        quote: 'Noise has a light you only see once.',
      },
      {
        number: '02',
        title: 'Urban Geometry',
        subtitle: 'Lines, shadows and structure: the city as a graphic score.',
        quote: 'The city draws what the eye dares not frame.',
      },
      {
        number: '03',
        title: 'Candid Moments',
        subtitle: 'Scenes of life caught on the fly, between restraint and truth.',
        quote: 'A stolen glance sometimes says more than a posed smile.',
      },
    ],
    oeil: {
      eyebrow: 'Approach',
      title: 'The Eye and the Intent.',
      portraitAlt: 'Dark artistic portrait of the photographer',
      aiNote: 'Illustrative portrait (royalty-free photo) used as a mockup.',
      p1: "I don't try to photograph what is seen, but what unfolds between people, in the second before the gesture.",
      p2: 'My eye moves across three territories: the stage and its collective electricity, the city and its silent geometry, the street and its fleeting faces. In each, I chase the same thing — a truth that escapes the pose.',
      p3: 'Black and white, grain, contrast: tools to reveal the intent behind the image, never to disguise it.',
      terrains: [
        {
          title: 'Stages & Concerts',
          desc: 'The electric instant of a live show, between shadow and flash.',
        },
        {
          title: 'Urban Architecture',
          desc: 'Cities as a grammar of lines and perspective.',
        },
        {
          title: 'Candid Moments',
          desc: 'Faces crossed in passing, caught before they slip away.',
        },
      ],
    },
    contact: {
      eyebrow: 'Contact',
      title: "Let's bring your visual projects to life.",
      emailNote: "(fictional address — this mailbox doesn't exist)",
      demoTitle: 'Demo website',
      demoText: "This is a demo mockup — please don't send messages to the address above, it isn't monitored.",
      github: 'View the code',
      docs: 'View the docs',
    },
    footer:
      'Demo website — royalty-free photographs (Unsplash),\npeople and places not affiliated with this project.\n© 2026 A. Morvan.',
    lightboxClose: 'Close',
  },
}

/**
 * Builds a public asset URL prefixed with Vite's `BASE_URL`, so images resolve
 * both locally (served at `/`) and under a GitHub Pages project subpath
 * (e.g. `/demo-website/`).
 * @param {string} name File name inside `public/images/`, e.g. `"Urban-01.jpg"`.
 * @returns {string} The resolved URL, e.g. `"/images/Urban-01.jpg"`.
 */
const asset = (name) => `${import.meta.env.BASE_URL}images/${name}`

/**
 * A gallery photo entry.
 * @typedef {Object} Photo
 * @property {string} url Resolved image URL (see {@link asset}).
 * @property {string} aspect CSS aspect ratio, e.g. `"4/5"`.
 * @property {LocalizedText} alt Bilingual alternative text.
 */

/**
 * Gallery photos grouped by category. Order matters: it drives display order
 * and the flat index used by the lightbox prev/next navigation.
 * @type {{live: Photo[], urban: Photo[], portraits: Photo[]}}
 */
export const PHOTO_DATA = {
  live: [
    {
      url: asset('LiveStage-01.jpg'),
      aspect: '4/5',
      alt: {
        fr: 'Foule au concert sous les lumières de scène',
        en: 'Crowd at a concert under stage lights',
      },
    },
    {
      url: asset('LiveStage-02.jpg'),
      aspect: '4/3',
      alt: {
        fr: 'Scène de concert éclairée dans la nuit',
        en: 'Concert stage lit up at night',
      },
    },
    {
      url: asset('LiveStage-03.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Silhouettes du public en concert',
        en: 'Silhouettes of the crowd at a concert',
      },
    },
    {
      url: asset('LiveStage-04.jpg'),
      aspect: '1/1',
      alt: {
        fr: 'DJ sous projecteurs colorés',
        en: 'DJ under colorful spotlights',
      },
    },
    {
      url: asset('LiveStage-05.jpg'),
      aspect: '3/4',
      alt: {
        fr: 'Mains levées face aux lumières de scène',
        en: 'Raised hands facing the stage lights',
      },
    },
    {
      url: asset('LiveStage-06.jpg'),
      aspect: '4/3',
      alt: {
        fr: 'Public devant la scène, lumières vives',
        en: 'Crowd in front of the stage, bright lights',
      },
    },
    {
      url: asset('LiveStage-07.jpg'),
      aspect: '1/1',
      alt: {
        fr: 'Faisceaux laser au-dessus de la foule',
        en: 'Laser beams sweeping over the crowd',
      },
    },
    {
      url: asset('LiveStage-08.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Silhouettes dans un stade, contre-jour',
        en: 'Silhouettes inside a stadium, backlit',
      },
    },
  ],
  urban: [
    {
      url: asset('Urban-01.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Architecture moderne en contre-plongée',
        en: 'Modern architecture from a low angle',
      },
    },
    {
      url: asset('Urban-02.jpg'),
      aspect: '1/1',
      alt: {
        fr: 'Lignes géométriques de la ville',
        en: 'Geometric lines of the city',
      },
    },
    {
      url: asset('Urban-03.jpg'),
      aspect: '4/5',
      alt: {
        fr: 'Façade architecturale contemporaine',
        en: 'Contemporary architectural façade',
      },
    },
    {
      url: asset('Urban-04.jpg'),
      aspect: '4/3',
      alt: {
        fr: 'Immeuble de verre et ombres portées',
        en: 'Glass building and cast shadows',
      },
    },
    {
      url: asset('Urban-05.jpg'),
      aspect: '3/4',
      alt: {
        fr: 'Tour moderne aux lignes épurées',
        en: 'Modern tower with clean lines',
      },
    },
    {
      url: asset('Urban-06.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Bâtiment gris en contre-plongée',
        en: 'Gray building from a low angle',
      },
    },
    {
      url: asset('Urban-07.jpg'),
      aspect: '4/3',
      alt: {
        fr: "Lignes d'un immeuble vues d'en bas",
        en: 'Building lines seen from below',
      },
    },
    {
      url: asset('Urban-08.jpg'),
      aspect: '1/1',
      alt: {
        fr: 'Façade de verre en niveaux de gris',
        en: 'Grayscale glass façade',
      },
    },
  ],
  portraits: [
    {
      url: asset('CandidMoment-01.jpg'),
      aspect: '4/5',
      alt: {
        fr: 'Homme attendant un train, saisi sur le vif',
        en: 'Man waiting for a train, caught candidly',
      },
    },
    {
      url: asset('CandidMoment-02.jpg'),
      aspect: '1/1',
      alt: {
        fr: 'Silhouette dans un bus, ambiance argentique',
        en: 'Silhouette on a bus, film-like mood',
      },
    },
    {
      url: asset('CandidMoment-03.jpg'),
      aspect: '4/3',
      alt: {
        fr: 'Silhouette en marche dans la rue',
        en: 'A figure walking down the street',
      },
    },
    {
      url: asset('CandidMoment-04.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Silhouette descendant un escalier',
        en: 'Silhouette walking down a stairway',
      },
    },
    {
      url: asset('CandidMoment-05.jpg'),
      aspect: '3/4',
      alt: {
        fr: 'Silhouette féminine sur la route',
        en: 'A figure walking along the road',
      },
    },
    {
      url: asset('CandidMoment-06.jpg'),
      aspect: '4/3',
      alt: {
        fr: 'Passage piéton vu de haut, en niveaux de gris',
        en: 'Pedestrian crossing seen from above, grayscale',
      },
    },
    {
      url: asset('CandidMoment-07.jpg'),
      aspect: '2/3',
      alt: {
        fr: 'Silhouette sous la pluie, nuit urbaine',
        en: 'Silhouette in the rain, urban night',
      },
    },
  ],
}

/**
 * Warm-gold accent color used across the UI (nav underline, language pill,
 * links, quote marks, logo).
 * @type {string}
 */
export const ACCENT = '#c9a869'