···116116 ```
117117 (Overlay still appears on hover, just instantly with reduced motion)
118118119119+**Tailwind JIT and Dynamic Classes:**
120120+- Tailwind's JIT compiler scans source code at build time to find class names
121121+- **NEVER use template literals with variables for Tailwind arbitrary values:**
122122+ ```tsx
123123+ // BAD - Tailwind can't see this at build time, CSS won't be generated
124124+ className={`aspect-[${CARD_ASPECT_RATIO}] rounded-[${CARD_BORDER_RADIUS}]`}
125125+126126+ // GOOD - Use inline styles for dynamic values
127127+ style={{ aspectRatio: CARD_ASPECT_RATIO, borderRadius: CARD_BORDER_RADIUS }}
128128+129129+ // GOOD - Hardcoded arbitrary values work fine (with comment explaining magic numbers)
130130+ className="aspect-[672/936] rounded-[4.75%/3.5%]"
131131+ ```
132132+- This only affects arbitrary value syntax like `aspect-[...]`, `max-w-[...]`, `rounded-[...]`
133133+- Regular class composition with variables is fine: `className={`p-4 ${isActive ? 'bg-blue-500' : ''}`}`
134134+- Card aspect ratio and border radius use `CARD_STYLES` from `@/components/CardImage` for inline styles
135135+119136### Development Tooling
120137121138- **Nix**: flake.nix provides Node.js 22, TypeSpec, and language servers
···117117118118### Lower Priority
119119120120+#### CSS variable for card aspect ratio
121121+- **Location**: `src/routes/card/$id.tsx`, various places with `672/936` hardcoded
122122+- **Issue**: Card aspect ratio (672/936) is hardcoded in Tailwind arbitrary values because JIT can't see template literals
123123+- **Fix**: Define `--card-aspect-ratio: 672/936` in styles.css, use `calc(50vh*var(--card-aspect-ratio))` in Tailwind
124124+- **Effort**: Trivial (15 min)
125125+120126#### Extract meta tag builder in card route
121127- **Location**: `src/routes/card/$id.tsx:62-113`
122128- **Issue**: 51 lines of nested object literals for OG/Twitter meta tags