FFmpeg inside a find|while pipe was consuming the remaining filenames
from stdin as interactive commands, causing parse errors. Adding
< /dev/null isolates ffmpeg from the pipe so find can feed all paths
to the while loop uninterrupted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hugo has no video processing, so FFmpeg runs in the builder stage
before hugo builds, compressing every content/**/*.mp4 in-place.
scripts/compress-videos.sh:
- H.264 CRF 28 + preset slow (good web compression)
- faststart for progressive streaming
- AAC 64k audio
- Only replaces source if output is actually smaller, so
already-lean videos are skipped
Dockerfile:
- apk adds ffmpeg alongside hugo in the builder stage
- RUN compress-videos.sh runs after COPY . . before pnpm build
(compressed files land in public/ via Hugo's copy of page bundles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: media container was position:absolute inset-0 but without
z-index, while buttons had no z-index either — in fill mode the image
painted over the buttons making close/fill-toggle unclickable.
Fixes:
- Media div is now absolute inset-0 (explicit), chrome bars are z-10
- Top bar is a single flex row (h-14): left spacer | counter | FILL+✕
so counter, fill toggle and close are vertically aligned at the same
height instead of scattered absolute elements
- Bottom bar is a flex row (h-14) centred over dots
- Both bars have a linear-gradient scrim so chrome stays legible over
fill-mode images without a solid background
- Prev/Next arrows get z-10 so they always sit above the media
- Media padding uses pt-14 pb-14 in fit mode to clear the chrome bars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add fill state (default false) to the Alpine lightbox data:
- FILL button (top-right, left of ✕): switches media to object-cover,
filling the entire overlay edge-to-edge; button turns heat pink
- FIT restores object-contain with padding; button returns to fog
- Keyboard shortcut F toggles fill while lightbox is open
- close() resets fill to false for the next open
- transition-all duration-300 on container padding and media for
a smooth morph between modes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hugo pre-computes a JSON array of {img, video} entries per gallery item
(WebP 1200w src + optional mp4 src). Alpine reads the array and manages
lightbox state (open/idx/prev/next/close).
Features:
- Click any card → opens fullscreen dark overlay (bg-void/95 + backdrop-blur)
- Videos: controls, autoplay, x-effect reloads src on navigate
- Images: full-res WebP centred with object-contain
- Keyboard: Escape closes, ←/→ navigate
- Dot indicators: active dot expands (heat colour)
- Prev/next arrow buttons (hidden when only 1 item)
- Grid cards: cursor-zoom-in, pointer-events-none on media so click
always hits the <figure>
- Teleported to <body> via x-teleport to avoid stacking context issues
- Gradient-line accent top and bottom of overlay
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lighthouse reported is-crawlable FAIL because the server was sending
X-Robots-Tag: none,noarchive,... (likely injected by Traefik/Coolify
security-headers middleware). Explicitly declare the correct value here;
override via Traefik label also documented below.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add layouts/partials/img.html helper that resizes and converts any Hugo
page-resource image to WebP, emitting a responsive srcset at multiple
widths. Wire it up to every image rendering site:
- post-card.html: thumbnail (600w/1000w) + avatar (64px WebP)
- post-card-large.html: featured card background (800w/1200w)
- posts/single.html: banner (1200w/1800w, eager) + gallery (800w/1200w)
- author-card.html: avatar (96px WebP, 2× retina)
Result: banner.png 7.9 MB → 496 KB WebP at 1800w (−94 %).
Hugo caches processed images in resources/_gen/ across builds.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- nav: fix label-content-name-mismatch — aria-label now uses logoText
("PIVOINE") to match the visible link text (WCAG 2.5.3)
- index: improve link-text — "Learn more" → "About the magazine"
so the /about/ CTA has a descriptive accessible name
- head: load Google Fonts async via media=print/onload pattern to
eliminate render-blocking resource penalty (~480ms est. savings)
Remaining items needing server-side action:
- is-crawlable: x-robots-tag: none,... header must be removed in the
web server / CDN config (not controllable from this repo)
- image-delivery: large banner PNGs on live site need manual WebP
conversion or migration to Hugo page bundles for auto-processing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds translateY lift and smoother easing on hover for desktop nav,
footer nav, and social links. Active underline gets expo easing.
No markup changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Desktop nav: underline uses scaleX transform (origin-left) instead of
width, now shows on hover for inactive links too
- Mobile nav: link text slides translateX on hover for directional feel
- Footer nav: matching scaleX underline on hover
- Footer social: external arrow icon nudges diagonally on hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>