The thing that clicked today: the surface got a new look and a header that finally behaves at every width, while underneath, most of the real thinking went into data plumbing, how my edited photos should come down, why six of them weren't matching up, and where my project notes should live.
Built / shipped
A "liquid glass" redesign, shipped dark. I had a new visual treatment for the whole app: glassy chrome, a warm backlit light-table for the photo grids, cream-mounted slides. Rather than rip out the existing "darkroom" look, the whole thing sits as a dormant layer that only switches on when a single class is set on the page. Look on, look off, instant A/B, no risk. I gave the marketing site the same treatment but left it committed-and-not-deployed: a public page has no hidden A/B toggle, so going live there is a separate, deliberate call.
The header, finally responsive at every width. This ate a lot of the day. The persistent top header (brand on the left, nav centered, a little stats box on the right) needed to hold one clean row when wide and degrade gracefully as the window narrows. It landed as three layout bands: wide keeps the rigid one-row layout, a middle "fold zone" drops the stats into a flat strip under the nav, and mobile becomes a centered wrapping stack. Verified the whole thing in a scripted browser across a 720 to 1920 pixel sweep.
Polish on the single-photo view. Moved the viewing controls out into the photo corners so the bottom bar holds only the cull decisions, and quieted a score badge that had been loudly wearing the wrong styling.
Moved my project notes vault to live alongside the app instead of the pipeline. The trick was doing it without triggering a full re-sync to my other machine: pause the sync, do a same-disk move that preserves the folder's identity, repoint it through the API, unpause. It re-indexed in place instead of re-downloading. Took a full backup first, of course.
Compacted the decision and learning logs. They'd grown past the point where reading them whole was wasteful, so I put a snapshot-first compaction routine in place and ran it: collapsed ~30 sprawling entries into 10 tidy ones, dropped what was superseded, and promoted the durable principles up top. Snapshots taken and verified before anything got rewritten.
Problems & fixes
The stats box kept getting crushed at the in-between widths. Wide was fine, narrow was fine, but somewhere in the middle the box collapsed below its own text and the numbers spilled past the border. The fix was to give it a hard floor (never shrink below its widest stat) and a safety valve to drop to its own row instead of clipping. What I learned about how the layout works: an item in a flexible row decides whether to wrap based on its natural size, not its squeezed size. So you can't get "shrink a bit, then wrap." It either shrinks or it wraps. Pick one and tune the threshold.
My browser tests were lying to me for about ten iterations. The cache-busting tag on the CSS file is computed once when the app boots, so after editing the stylesheet without redeploying, the browser kept serving the old cached copy. I was debugging a layout that no longer existed. Once I made the test browser ignore its cache, the real behavior showed up immediately.
A score badge that wasn't unstyled. It was wrongly styled. A tiny generic class name was reused for two unrelated things, so the badge silently inherited a cream pill meant for something else entirely. And it was printing "23th" instead of "23rd" because the helper that makes ordinals was not reachable from that part of the code. Both small, both the kind of thing that nags.
The mobile brand was shoved off the right edge. One root cause behind two symptoms: a desktop no-wrap setting was leaking into the mobile nav. Let the nav wrap and both the clipping and the off-center brand fixed themselves.
Decisions
- The faithful-library principle. My photo library on disk is originals only. Every edit I've ever made (crops, trims, portrait blur) still lives only in the cloud, so the app currently shows me pre-edit frames. Going forward the rule is: a faithful pull keeps both the original and the edited version, mirroring exactly what's stored upstream. Originals-only is allowed only as an explicit, labeled choice, never the silent default.
- Augment, don't re-pull. To get those edits, I'll pull only the edited renders and pair each to its existing original, not re-download the whole ~2TB library. Cheaper, and safer: my cull decisions are keyed to file paths, so leaving the originals exactly where they are keeps every keep/cut/vault decision intact. Hard rule: the augment must not rename or move anything that already exists.
- The download rides my Mac's already-signed-in session. Worked out the mechanism for pulling photos down through a Mac that already has the library, with no extra credential handling on my side. Validated it on a small test album and learned the hard way that one tempting shortcut silently wrote tiny broken stub files for videos while reporting success. A size check caught it.
Learned
Apple stores an edit as a whole second rendered file, not a recipe. The little sidecar is just the edit instructions; the actual edited image is a complete separate asset living alongside the untouched original. The default download tool only grabs originals, which is exactly why my library came down edit-free.
Six photos weren't pairing, and the cause was a filename suffix, not what I assumed. I went in convinced I needed to add date-awareness to the matcher to split recycled filenames. Wrong on two counts: the matcher already uses the date, and the six were just videos renamed on disk with a -<filesize> suffix to dodge a name collision. The clean filename never matched the suffixed one. Strip the suffix (the digits are the size) and five of six resolve instantly. The sixth is a genuine duplicate import (same bytes added twice) that no key can split, and I'm choosing to accept that.
You can measure a layout for real without installing anything: drive the browser you already have through its built-in debugging interface and read each element's position and size directly, far more reliable than screenshotting one width at a time.
Still open / next
The glass redesign is an unmerged A/B branch: whether it replaces the darkroom look or stays a toggle is a call for after I live with it in a real browser for a bit. The header needs my in-browser pass on an actual phone; the scripted browser says it's centered but I want to see it. The photo-pairing fix is diagnosed but not yet built: strip the suffix, rebuild the bridge, re-run the cross-check, and only then wire up the edited-render augmentation itself. And there's a thread I ran a security review on and tightened up. Connecting to a personal photo library is exactly the kind of thing where I'd rather be quiet about the internals than clever about them in public, so that's all I'll say. It's only ever reachable on my home network, and I'm treating the whole area with extra care.