1. 15 Aug, 2026 2 commits
    • fix(cosmicfest): link the lineup out, and stop the RSVP line going stale · efc02813
      Two gaps between the live page and the canonical invite text.
      
      1. The lineup named the three acts but linked nowhere. PLN's invite carries a
         destination for each, so the page now sends people to the work:
           ParVagues  -> soundcloud.com/parvagues
           Nesta Flex -> soundcloud.com/antoine-renaudet
           shipow     -> instagram.com/shipow
         The anchor takes `color: inherit` so the existing odd/even cyan/magenta
         :hover rules on .lineupName stay in charge; the underline is a 30% mix that
         solidifies on hover/focus-visible rather than a permanent blue link.
      
      2. The RSVP paragraph read "une réponse avant le 1er août, ce serait top."
         True when written, silently wrong since 2 August, and actively confusing
         today — 15 August, with arrivals open and jour J six days out. A page whose
         copy expires is the same failure as a countdown to a date that has passed.
      
         So it reads the clock instead of a frozen sentence. Four phases off the
         existing JOUR_J plus three new date constants: before the deadline, after it
         but pre-arrivals, during the 15-23 August window, and after — which hands
         over to the v3.42Hz teaser from the invite.
      
         Rendered null on the server and filled in an effect, because the phase
         depends on the viewer's clock and a server/client disagreement is a
         hydration mismatch. The invariant "pas de billetterie / Riders, come to the
         storm" line stays outside the component, so the paragraph still reads
         correctly if JS never arrives.
      
      Every other fact on the page was checked against the canonical invite and
      already matched: 20-23 August, arrivals from the 15th, Labenne-Océan, jour J
      the 22nd, 10 indoor places, tents, van, 16 capacity, +1 welcome, both stations,
      the 15-minute Bayonne->Labenne train, the taxis, and the packing list.
      
      Verified: clean build, all three hrefs present in the prerendered HTML, and
      zero occurrences of "1er août" in the server output.
      PLN (Algolia) authored
    • merge(cosmicfest): port the v2.67Hz invite onto the App Router · 3e2d2cd3
      origin/main carried PR #7 — the entanglement-themed CosmicFest rebuild, merged
      2026-06-29 and LIVE on me.nech.pl/cosmicfest ever since. Meanwhile this branch
      hoisted the whole app out of next/ and deleted the Pages Router. The two sides
      had zero overlapping paths, so a naive `git diff --name-only` check reported no
      conflict — and was wrong in the most expensive way available: one side had
      deleted the entire tree the other side was editing.
      
      Resolving "ours" would have silently deleted the invite from a page guests are
      reading right now, six days before the festival. Resolving "theirs" would have
      resurrected the Pages Router. Both merge without complaint. So: a port.
      
      Git's rename detection did most of it — Countdown, the stylesheet and
      EntanglementField all landed at their App Router homes automatically. Five
      content hunks in the page body needed hand-resolution, all "theirs, adapted":
      
      - next/pages/cosmicfest/index.js  ->  app/cosmicfest/CosmicFestClient.js
      - <Head> block                    ->  page.js `metadata` + `viewport` exports
        (App Router has no <Head>; theme-color moves to `viewport` per Next 15)
      - kept 'use client' and the @/ path aliases over the ../../ relatives
      - dropped the v0 June-21st countdown; JOUR_J is now 2026-08-22T18:00:00+02:00
      
      Also fixes a build break that predates this merge and had nothing to do with it:
      #161 regenerated the OPAL tracks.json from the parsers, renaming `ingredients`
      to `samples` AND changing its shape — rich {type,code,description} objects
      became bare name strings. LiveEvent.js still called track.ingredients.map(),
      so /parvagues/live/opal-festival-2026 threw at prerender and failed the whole
      build. Now renders whichever generation is present, guarded with Array.isArray
      so a third shape can't crash the build either.
      
      Verified: yarn build clean, 86/86 static pages. /cosmicfest 6.04 kB vs the
      6.3 kB the original PR measured. Built HTML carries intrication, v2.67Hz,
      Labenne, the full lineup, <title>cosmicfest_v2.67Hz</title>, theme-color
      #1a1330, and zero occurrences of "juin". The 2026-08-22 target is present in
      the shipped chunk. next/ stays deleted — 0 files.
      PLN (Algolia) authored
  2. 14 Aug, 2026 1 commit
    • fix(lives): OPAL tracks.json carried the old boundaries, off by up to 50 s · 62cc2b0f
      These timecodes were generated before #148 — back when track boundaries came
      from Pulsar tab-focus events rather than from the audio. Tab focus records when
      PLN LOOKED at a file, not when the music changed, so every cut was wrong by
      however long he sat on a tab before evaluating it.
      
      Regenerated from the measured boundaries (segments_v3.json) via
      Tidal/tools/gig-metadata.py, which now reads that shape directly and matches
      segments to the setlist by title rather than by position.
      
        #  track                        was        now      drift
        5  You My Sunshine              0:25:41    0:25:58    +18s
        7  Piment Bresilien             0:36:51    0:37:07    +17s
        9  Perfect                      0:48:24    0:49:14    +50s
       10  Gimme Acid                   0:53:39    0:54:12    +33s
       11  Ghosts in the Toilets        0:58:48    0:59:20    +32s
       13  Vague de CRIME               1:04:33    1:04:49    +16s
       14  Desire                       1:09:12    1:09:40    +28s
      
      totalDuration_s 4809 -> 4769.1. Tracks 1, 12 and 15 were already correct.
      
      Only timing changed: names, order, bpmRange, styleDistribution and all 60
      samplePacks diff byte-identical against the previous file, and venue/stage are
      carried through verbatim. This matters beyond the website — Slopmotion reads
      this file to cut visuals to the set, so shipping it stale would have put the
      cuts half a minute off the music on a third of the album.
      PLN (Algolia) authored
  3. 12 Aug, 2026 1 commit
    • feat(lives): OPAL Festival 2026 — "Sunset Forest", Scène Cabane · 423d0e9f
      Third Opal in a row and the first on the main stage. Saturday 8 Aug 2026,
      sunset slot, 1h20, 15 tracks of live-coded TidalCycles.
      
      tracks.json is GENERATED, not typed — Sound/Tidal/tools/gig-metadata.py reads
      membership/order/name/bpm/section from backlog.md via the setlist parser, the
      sample packs from each .tidal via setlist_samples, and the timecodes from the
      Ardour take. Nothing here was copied by hand, which is the point: the last time
      gig strings were hand-carried between files they were wrong.
      
      The per-track timecodes are new for a lives entry and deliberate — they let
      Shipow cut Slopmotion visuals to the set without re-deriving boundaries by ear.
      They were measured by aligning the 1 Hz gig log to the audio via
      cross-correlation (r>=0.99 over 20 probes, 0.00 ms spread).
      
      Note for whoever touches lives content next: the live tree is content/lives/,
      NOT content/parvagues/lives/ (legacy, last moved by the June hoist) and not
      next/content/lives/ (the path Sound/Tidal/CLAUDE.md still documents — stale
      since the app moved out of next/).
      PLN (Algolia) authored
  4. 29 Jun, 2026 1 commit
    • feat(cosmicfest): rebuild /cosmicfest as v2.67Hz, entanglement-themed invite (#7) · 32a2a5bc
      Full redesign of the standalone invite page for the 2026 edition (20-23 August,
      Labenne-Ocean, jour J samedi 22). Concept: intrication (entanglement) - two
      states, one system, communicating across distance. Dark OKLCH design system with
      a paired cyan/magenta accent language, an EntanglementField canvas (starfield +
      mirrored particle pairs + 67Hz interference waveform), Archivo (wdth) + Martian
      Mono, replacing the v0 light theme.
      
      - new EntanglementField signature canvas (DPR-capped, mobile-scaled, full
        prefers-reduced-motion static path)
      - sections: hero, concept, jour-J lineup, sejour, hebergement, transport,
        packing, v0 souvenirs (lifted audio embeds + accessible photo lightbox), RSVP
      - countdown retargeted to 2026-08-22, hydration-safe placeholder
      - lightbox renders full-size via plain img with managed focus
      - add PRODUCT.md / DESIGN.md design context at repo root
      
      Verified: clean SSG build (6.3 kB), WCAG AA contrast, zero console errors,
      correct SSR + reduced-motion paths.
      Paul-Louis NECH authored
  5. 23 Jun, 2026 1 commit
    • fix(parvagues): Montreuil tracklist — Aria→Mafia Sans Serif + repair dangling .tidal ref · aff33d43
      The canonical tracklist carried the old track name "Aria Sans Serif" and, worse,
      a `file` pointing at live/collab/raph/aria_sans_serif.tidal — a path that no
      longer exists. The track was renamed to Mafia Sans Serif in the score long ago
      (real file: mafia_sans_serif.tidal); only the published metadata lagged. Since
      the track identity in the armada catalog pipeline IS its .tidal path, the
      dangling ref meant the catalog keyed this track on a non-existent score.
      
      Fixed both the display name and the file ref to the real score. This is the
      canonical root: the armada catalog regenerates from here (tide.py build).
      PLN (Algolia) authored
  6. 07 Jun, 2026 9 commits
    • docs: update Content model for the unified lib/content loader · 1d7ee222
      Reflect Step 5: single lib/content/index.ts (two collection shapes),
      wrappers re-export from @/lib/content, plain string[] ids +
      .map((id) => ({ id })), and the parvagues-presence.ts seam.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • refactor(content): unify the content loaders + add a presence seam · 249b1b9c
      Phase 1 Step 5. Fold lib/utils.js (flat Markdown collections) and
      lib/livesData.js (year-foldered ParVagues gigs) into one module,
      lib/content/index.ts — two collection shapes in one entry, not one
      forced shape. Thin section wrappers (posts/hydras/poems/talks) now
      re-export from @/lib/content; the two ParVagues pages import the lives
      API from there too. Old lib/utils.js and lib/livesData.js deleted.
      
      - strip the per-file console.log noise that was in lib/utils.js;
      - getAllContentIds now returns plain string[]; the three dynamic pages
        drop the vestigial Pages-Router `.map(({params}) => params)` adapter
        for `.map((id) => ({ id }))`;
      - add lib/content/parvagues-presence.ts: a single thin seam holding the
        already-in-repo platform links, so a future tooling-emitted
        presence.json swaps in without touching components. No schema, no
        invented links, components not yet rewired (deliberate).
      
      Verified: prod build green (14 routes, all dynamic params intact);
      fresh-dev smoke 14/14.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • refactor(styles): extract token layer, kill the global neon leak · 9a2498a9
      Phase 1 Step 4. Move the palette out of globals.css :root into a new
      styles/tokens.css: a neutral/professional default on :root (landing +
      content) and the ParVagues neon/dark palette scoped to
      [data-theme="parvagues"], set on the app/parvagues/layout.tsx wrapper.
      The ~90 var(--…) consumers resolve against that ancestor scope, so
      theming is one attribute — no per-component edits.
      
      - globals.css: drop the :root palette + hardcoded dark html/body bg;
        body now reads var(--surface)/var(--text-primary).
      - parvagues.module.css: remove the dead duplicate .colorRoot/.colorContainer.
      - main.css: replace the body:not(:has(.parvagues-root)) element-style hack
        with a positive .site-shell scope on the content Layout container; links
        use the new --link token.
      - delete dead styles/dunbar.module.css.
      
      Verified: prod build green (14 routes, ParVagues ISR intact); fresh-dev
      smoke all 200; data-theme scoping confirmed (landing neutral, parvagues neon).
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • feat(app-router): migrate CosmicFest, remove Pages Router, update docs · 3dfa3a52
      Step 3 (group 3 of 3) — completes the App Router migration. The Pages
      Router is now fully gone (no more pages/ or _app.js).
      
      - /cosmicfest: server metadata wrapper + CosmicFestClient ('use client')
        for the modal/keyboard state. Swapped the deprecated next/image
        layout="intrinsic"/objectFit props for modern width/height + style.
      - Deleted the dead legacy shells components/ParVaguesHeader.js and
        components/ParVaguesFooter.js (only ever referenced by commented-out
        imports in CosmicFest).
      - tests/jest.setup.js: mock next/navigation (App Router hooks) instead of
        next/router.
      - CLAUDE.md: Pages Router -> App Router throughout (global CSS from
        app/layout.tsx, server-components-by-default + 'use client' leaves,
        generateStaticParams, the root-shell + ParVagues-sub-layout split,
        next/navigation in tests).
      
      Full App Router build green; route-smoke 14/14.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • feat(app-router): migrate ParVagues onto the shared shell · 8206199a
      Step 3 (group 2 of 3). Moves /parvagues, /parvagues/fiche,
      /parvagues/live and /parvagues/live/[slug] to the App Router under a
      single segment layout, retiring the old per-page components/parvagues/Layout.js.
      
      - app/parvagues/layout.tsx: the ParVagues sub-shell (Syne via next/font,
        parvagues-root/noise-overlay wrapper, footer) inheriting the root shell.
        Header scroll-state and the `.reveal` IntersectionObserver are split into
        client leaves (ParvaguesHeader, RevealController); the latter re-arms on
        client-side navigation via usePathname so reveals keep working between pages.
      - Pages become thin server components: data fetch + metadata + segment config
        (revalidate=60 on /parvagues and /parvagues/live/[slug] preserved;
        getStaticPaths -> generateStaticParams; per-page <title> -> metadata).
      - /parvagues/live redirect: client useRouter.replace -> server redirect().
      - /parvagues/live/[slug]: the interactive body (Prism, loupe, countdown,
        gallery) extracted to a client component (LiveEvent); the server page does
        the fs/data fetch.
      - /parvagues/fiche: window.print + <style jsx global> body extracted to a
        client component (FicheContent) behind a server metadata wrapper.
      - Marked the page-rendered feature components 'use client'
        (TourTimeline, MusicSection, VideoSection, BookingForm); Hero/BioSection/
        AsSeenAt stay server.
      - Dropped og:type="music.musician" (not in Next's typed Metadata enum; it
        threw at prerender) — inherits the root "website" default; see note for Phase 2.
      
      Build green; route-smoke 14/14. Only /cosmicfest remains on the Pages Router.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • feat(app-router): migrate landing + content routes to App Router · e1595147
      Step 3 (group 1 of 3) of the foundation redesign. Stands up the root
      App-Router shell and moves the low-risk static/content routes onto it,
      leaving ParVagues + CosmicFest on the Pages Router for now (no route URL
      changes — the smoke net stays green across the split).
      
      - app/layout.tsx: the one root shell (<html>/<body> + global CSS), with
        the shared <head> (OG/twitter/icons/title template) consolidated from
        components/layout.js into the Metadata API.
      - Migrated: / , /talks, /starry-nights, /hydra, /hydra/[id],
        /post/[id], /poesie/[id], /poesie, /fleurs. getStaticProps -> inline
        server fetch; getStaticPaths -> generateStaticParams; <Head> -> metadata.
      - components/layout.js: now a client component using next/navigation
        (router.back) instead of next/router; <Head> removed.
      - Client boundaries split out where needed: HydraClient (ssr:false dynamic),
        PoemsBrowser (filter/sort state), FleursClient (p5 + styled-jsx).
      - Deprecation sweep: dropped next/link legacyBehavior (talks, starry-nights;
        they wrapped invalid nested anchors) and the render-blocking Google Fonts
        <link> on /fleurs (now next/font Cormorant Garamond). Removed dead
        ReactPlayer import on /talks.
      - tsconfig moduleResolution -> bundler; added .yarn/sdks (PnP TS SDK for
        editors). next.config: skip Next's build-time type-check — its generated
        route validators deep-import a Next-internal type that stock (non-PnP)
        tsc can't resolve in Yarn PnP zips; SWC compile + lint + smoke are the gates.
      
      Build green (App + Pages split); route-smoke 14/14.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • test(redesign): Phase 1 Step 2 — add route-smoke safety net · fe205e02
      Re-establish a baseline of coverage (Phase 0 removed all tests with Dunbar)
      and guard the upcoming App Router migration: the route URLs are stable across
      that migration, so this spec must stay green before and after.
      
      - tests/e2e/smoke.spec.ts: visits all 14 public routes, asserts the document
        responds < 400, renders non-empty content, and throws no genuine uncaught
        exception. Tolerates dev-only React hydration warnings and third-party embed
        noise (would otherwise be flaky); caps the settle wait so embed-heavy pages
        don't hang.
      - Fix a pre-existing Playwright version split: @playwright/test (1.57.0 via
        ^1.55.0) and playwright (1.58.2) resolved to two copies, breaking the runner
        ("test() not expected here"). Pin both to 1.60.0.
      
      All 14 routes green (~55s, Node 20). Surfaced for later: a real /cosmicfest
      dev-mode hydration mismatch (task #7), a legacyBehavior next/link deprecation,
      and a next/head stylesheet warning — all to address in the App Router pass.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • refactor(redesign): Phase 1 Step 1 — hoist app from next/ to repo root · 6e51fffd
      Flatten the nested next/ app directory up to the repository root, removing
      the `cd next` friction that the old .cursor/rules existed largely to manage.
      
      - Move all app files (pages, components, lib, styles, content, public,
        tests, scripts, configs) and Yarn Zero-Install state (.pnp.cjs, .yarn/)
        from next/ to repo root via git mv (history preserved, 316 renames).
      - Merge next/.gitignore into the root .gitignore; note .pnp.cjs/.yarn are
        committed (Zero-Install) and must not be ignored.
      - Pin outputFileTracingRoot to the project in next.config.js: a stray
        ~/package-lock.json was making Next infer the wrong workspace root.
        Also makes standalone output tracing correct for self-hosting later.
      - Update CLAUDE.md: commands run from repo root; @/ alias, content/,
        public/, .next/ paths corrected; Vercel root dir must become '.'.
      
      Build green from root (yarn build, Node 20, all 14 route patterns, zero
      warnings). Content loaders (process.cwd()-based) resolve correctly since
      content/ and public/ now sit at the root.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
    • chore(redesign): Phase 0 — remove Dunbar, fix prod bugs, prune deps · 50e87a15
      Demolition step ahead of the site rationalization (App Router + shared
      design system foundation to follow).
      
      - Remove Dunbar entirely (pages, components, lib, tests); it's folding
        into a separate project. UX learnings preserved at
        docs/dunbar-design-learnings.md.
      - Prune 6 now-unused deps: d3-force, d3-zoom, minisearch,
        react-force-graph(-2d), snowball-stemmers, stopword.
      - Fix 3 production bugs in layout.js: drop the http://localhost:8097
        React DevTools script, replace the create-next-app template OG image
        with the site profile image, and compute the footer year dynamically.
      - Delete dead files: pages/parvagues.js.backup, the unused HeroVariants.
      - Add CLAUDE.md documenting the codebase and the redesign state.
      
      Build green (yarn build, 40 routes). Note: this removes all test
      coverage (it was all Dunbar's) — to be re-established in Phase 2.
      Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
      PLN (Algolia) authored
  7. 24 May, 2026 3 commits
    • fix(parvagues): clamp loupe width to viewport on narrow screens · 5fd1374e
      Shrink the loupe popup to (viewport_width - 32px) when 480px would
      overflow, keeping the 16:9 aspect ratio. Centering math uses the
      computed width so the clamp keeps the popup fully on-screen.
      PLN (Algolia) authored
    • fix(parvagues): mobile-friendly loupe — tap-to-zoom then tap-to-open · 40891851
      On touch devices the loupe overlay is now revealed by the first tap on a
      track thumbnail (the link's default action is prevented). A second tap on
      the same thumbnail navigates to the Instagram album. Tap-outside or 4s
      timeout auto-closes the loupe. Desktop hover behavior unchanged — gated
      on (hover: hover) media query.
      PLN (Algolia) authored
    • feat(parvagues): add Montreuil Algorave gig page · 11d7a44d
      15-track collapsible list driven by metadata extracted from the .tidal
      sources via the Pulsar HUD parser. Each row: name, BPM, style, expandable
      ingredients (per-orbit sample/role/CC bindings) + git.nech.pl src link.
      Six tracks have IG story loops (5s, 16:9 cropped from 9:16 originals,
      ~150 KB each) with a fixed-position hover loupe (480×270) that escapes
      the row's clipping box; click opens the highlight album. Page-level SC
      embed + Instagram album link in MediaSection. Full-length story MP4s
      gitignored — only the loop previews + jpg thumbs ship.
      PLN (Algolia) authored
  8. 01 May, 2026 1 commit
  9. 16 Mar, 2026 5 commits
  10. 15 Mar, 2026 6 commits
  11. 14 Mar, 2026 3 commits
    • fix(tailwind): align PostCSS config with Tailwind v4 docs · 706b2d03
      Root cause: Vercel wasn't processing Tailwind through @tailwindcss/postcss,
      serving raw CSS with zero utility classes (23KB theme-only vs 53KB with
      utilities). The source() directive was output literally as @media source().
      
      Fixes per official Tailwind v4 + Next.js setup:
      - Switch postcss.config.js (CJS) → postcss.config.mjs (ESM)
      - Remove autoprefixer (built into Tailwind v4)
      - Use plain @import "tailwindcss" (auto source detection)
      - Move tailwindcss + @tailwindcss/postcss to dependencies
      PLN (Algolia) authored
    • refactor: remove Bootstrap, fix Tailwind v4 in production · e903cb57
      Bootstrap's CSS was overriding all Tailwind utility classes in production,
      making parvagues render as raw unstyled HTML. Root cause: _app.js imported
      bootstrap.css globally before globals.css (Tailwind v4).
      
      - Remove bootstrap dependency and import from _app.js
      - Remove stale tailwind.config.js (v3 format, ignored by v4)
      - Remove @tailwindcss/aspect-ratio (v3 plugin, built-in in v4)
      - Replace 3 Bootstrap `container` usages with Tailwind equivalents
      - Scope main.css legacy element styles with :not(:has(.parvagues-root))
        to prevent link colors, section margins, footer styles from bleeding
        into parvagues pages
      - Add .parvagues-root class to parvagues Layout for scoping
      PLN (Algolia) authored
  12. 13 Mar, 2026 3 commits
  13. 12 Mar, 2026 1 commit
    • feat(fleurs): generative garden invitation — La Fête des Fleurs · 15299120
      p5.js canvas with impressionist top-down flower garden: organic bloom
      lifecycle (bud → bloom → wilt → respawn), rare mutations (queen,
      hypersaturated, variegated, concentric, firecracker), wind-driven
      grass, falling petals, ambient day/night light cycle, dawn reveal.
      
      DOM overlay with staggered text entrance, RSVP modal with decorative
      floral ornaments and entrance animation. Dark theme, mobile-first.
      PLN (Algolia) authored
  14. 10 Mar, 2026 1 commit
    • fix(parvagues): poster overflow, dark mode, reveal visibility · 61919d6a
      - Use inline opacity (0.15) on poster image — Tailwind opacity-20 not
        applying in production build
      - Add color-scheme: dark meta tag for browser/reader mode compatibility
      - Set html/body background to #0a0a0a to prevent white bleed
      - Fix reveal animation: sections visible by default, animate only after
        JS hydration via .reveal-ready class on documentElement
      - Bump screenshots script to domcontentloaded + 3s render wait
      PLN (Algolia) authored
  15. 09 Mar, 2026 2 commits