~/changelog · currently v68

changelog

Commits to this site, newest first. Versions auto-increment per push. Co-authored with claude.

  1. v68 2026-05-19 683cea5
    backfill: webp + lqip on all 95 existing images
  2. v67 2026-05-19 e51a781
    webp + lqip pipeline, broken-image checker, link checker, dev watch
    details
    images:
    - process-staging.mjs now renders 6 keys per upload: jpeg + webp for
      full/med/thumb. metadata.yaml entries get s3_webp/s3_med_webp/
      s3_thumb_webp/lqip in addition to the existing jpeg urls.
    - makeLQIP renders a 24px-wide blurred jpeg and embeds it as base64
      (~600 bytes per image) in metadata as `lqip`.
    - new helper `pictureFor(im, size, alt, extraAttrs)` in build.mjs:
      emits  with WebP source + JPEG fallback + inline lqip
      background-image when available.
    - photo page main image, gallery feed view, and post-inline images
      all use pictureFor (or its inline equivalent in the markdown body
      rewriter). older entries without webp variants fall through to a
      plain .
    
    new scripts:
    - scripts/backfill-webp-lqip.mjs: walks all metadata + post
      frontmatter, downloads each S3 jpeg that's missing webp, renders
      + uploads variants, writes lqip base64, patches the yaml/md.
      idempotent.
    - scripts/check-images.mjs (`npm run check:images`): HEADs every
      S3 url referenced from content. exit 1 if any are unreachable.
    - scripts/check-links.mjs (`npm run check:links`): crawls _site
      html and HEADs every external href. opt-in, run after build.
    - scripts/dev.mjs (`npm run dev`): watches content/templates/
      design/copy.yaml + scripts/build.mjs; rebuilds on change;
      serves _site on :4321.
  3. v66 2026-05-19 74bf4a2
    perf + a11y + feeds + new pages: 8 enhancements
    details
    performance:
    - preconnect to s3 bucket in 
    - width/height attrs on every  (uses exif dims already in
      metadata) — eliminates layout shift as photos load
    - fetchpriority="high" on the photo-page hero
    
    accessibility:
    - prefers-reduced-motion: kills hover-grow transforms + cuts
      transition durations to 0.01ms for users who set the preference
    - :focus-visible: 2px accent outline + accent-3 link bg
    - skip-to-content link (visible on focus)
    
    feeds + favicon:
    - /atom.xml alongside /rss.xml (some readers prefer atom)
    -  for both feeds in head
    - design/favicon.svg: simple ~/red monogram, wired in head + copied
      to _site at build
    
    new pages:
    - /musings/tags index — alphabetical list of all tags + counts
    - /musings/YYYY year archives — group posts by year, with cross-
      links to other years
    - related-posts section on each post page (up to 3 by shared-tag
      score, then recency)
    
    random-post link:
    - "take me somewhere" link in the footer; clicking picks a random
      musing url client-side. all post urls are emitted as a json
      attribute on the link.
  4. v65 2026-05-19 3466754
    gallery view modes: folders / grid / feed
    details
    each gallery page that has sub-albums now renders a small toggle
    (top-right of the gallery) with three views:
    
    - folders (default): sub-albums above, then loose photos in this
      folder below
    - grid: every image in this subtree (this album + every descendant)
      in a single tight VSCO grid
    - feed: same set, but as a vertical scrolling stream — large centered
      images with caption + album link below each
    
    implementation:
    - build.mjs computes `allDescendantImages` (deduped by src) per node
    - template renders all three modes; CSS toggles which is visible via
      :checked sibling-selector — no JS required for the toggle
    - a tiny JS hook persists the chosen mode in localStorage so it
      survives page navigation
    - empty galleries (no sub-albums, no photos) fall through to the
      empty-state message, and single-folder galleries skip the toggle.
  5. v64 2026-05-19 25f7569
    license page + footer copyright + changelog redesign + mona/outback fixes
    details
    - /license page: full CC BY-NC 4.0 reuse terms with body + bullets +
      commercial section, all driven by copy.license in copy.yaml.
    - footer adds a 'license' link, an inline copyright line
      '© George Main · CC BY-NC 4.0', drops the 'est ' line.
    - changelog: cleaner ol-based row layout, version+date+hash inline at
      top (hash is a clickable github.com/.../commit/ link), subject
      on its own line, optional body collapsed in a 
    summary. - outback restructure: misc is no longer a sub-album. All 19 misc photos moved (S3 mv) into /media/australia/outback/ as loose photos. content/media/australia/outback/misc/ deleted. - new content/media/australia/tasmania/metadata.yaml: cover = russell-falls (better than the default first-descendant). - mona cover = fat-car (more striking than cement-truck for the album thumbnail). - mona alts: noodles-on-pedestal renamed to rubber-bands-on-pedestal (s3 mv + alt); 'red text piece by an artist commenting on artists' → 'Red text piece, MONA.'; bit.fall flipped to 'looking down at'; 'Textural close-up resembling stalactites' → 'Wall of rock inside MONA.'
  6. v63 2026-05-19 8153f77
    album cover overrides + corrections + photo-arrow layout
    details
    - build.mjs: new optional `cover:` field on a gallery's metadata.yaml
      selects which image becomes the album cover (instead of images[0]).
      cover lookup recurses into descendant galleries so virtual parents
      (e.g. /media/australia, /media/australia/outback) can pick a cover
      from any nested album.
    - new content/media/australia/metadata.yaml + .../outback/metadata.yaml
      (parent nodes that were previously virtual-only) — both pick
      uluru-sunset as cover.
    - misc covered by northern-territory-sign; kings-canyon by
      lost-city-overview.
    - s3 renames + alt fixes for the misc outback gallery: memorial-bell
      → centre-of-australia (it's the Centre of Australia cast-iron
      monument); red-dirt-campground → yulara-coach-campground;
      desert-camp → yulara-camp-overview; glamping-tents-overview →
      yulara-eco-tents-overview; camp-from-above → yulara-camp-from-above;
      glamping-tents → yulara-eco-tents.
    - kings-canyon: overhang → rim-walk-squeeze (alt: rim walk squeeze
      between two rocks); person-on-rim → canyon-rim-trail (no person
      in shot); shelby-on-rim deleted entirely.
    - uluru: uluru-from-viewpoint deleted.
    - photo page < / > arrows moved beside the image (flex row) instead
      of overlaid on top of it.
  7. v62 2026-05-19 a907fa0
    uluru: 'red-dirt-tire-tracks' was actually shoe prints in sand — rename slug + s3 keys + alt
  8. v61 2026-05-19 03bc11a
    revert palette to v2 cream — fully back to v2 design
  9. v60 2026-05-19 0ee197c
    homepage shuffle + perf + universal hover-grow
    details
    - drop the musings index subtitle ("Long-form notes...") — not needed.
      copy.musings.index.intro removed from copy.yaml and template.
    - fix random-photo shuffle: allImages was including the same image
      twice when it appeared in both a real and a synthesized-from-post
      gallery. dedupe by src URL so consecutive shuffles always land on
      a different image.
    - preload all shuffled photos via new Image() at homepage load so reroll
      swaps are instant from the browser cache.
    - hover-grow added to: album cards (.sp-album), homepage random-photo
      widget (#photo-img), photo-page main image. previously only the vsco
      grid cells and post figures had it.
  10. v59 2026-05-19 f75b129
    revert v3 design refresh — keep paper palette only
    details
    user feedback: v3 paper-card design felt worse (abrupt bottom edge,
    broke the stats grid boxes). preferred the v2 chrome but liked the
    new paper color. so revert tokens.css + site.css + templates/ to
    commit 6ce9249 (v2-final), then patch in only the cool paper palette
    (#f2f1ec bg, #b21f1a accent) from v3.
    
    what stays from v3: just the color palette.
    what's restored from v2: section markers, paper-card-free shell,
    stats-grid boxes, all template structure.
  11. v58 2026-05-19 d525491
    v3 design refresh: paper palette, less power-user chrome
    details
    design bundle update from claude design tool:
    - new tokens.css: cool off-white paper (#f2f1ec), no beige. deeper
      burgundy red accent (#b21f1a). subtle SVG noise texture. gutter 40px.
    - new site.css: content sits on a paper card (.site-shell now has
      background + border-left/right). section markers are now a centered
      '· · ·' ornament + small italic '— a random musing —' kicker, not
      '// 01 · words' mono captions. headings are serif weight 600, not
      mono. tags are serif italic. .sp-tree replaced by nested ul. new
      .sp-photo-frame + .sp-photo-meta + .sp-photo-counter classes.
    - templates:
      - drop mono '~/musings · 67 posts' breadcrumbs everywhere.
      - musing/category/musings-index now share the .musings-layout with
        a tree sidebar (was: index didn't have the tree). class renamed
        musings-main -> musings-body.
      - serif italic crumbs with › separators (Musings › Travel › Japan).
      - written-out dates (May 11, 2026) instead of 2026·05·11.
      - drop file-path lines from random widgets — title is the title.
      - photo page switches table → 
    + n of total counter + keyboard hint. - build.mjs: fmtLongDate helper, exposed to templates. shuffled musings/ photos + recent + stats all carry dateLong now. - footer: drop "est " line; version
  12. gets class="version".
  13. v57 2026-05-19 6ce9249
    photo page: floating < / > arrows on either side of the image
  14. v56 2026-05-19 4bf652b
    upload mt field national park gallery (russell, horseshoe, lady barron falls)
    details
    5 photos from sony rx100 vii, ~20mb of originals on s3, ~0 bytes in repo.
    metadata.yaml at content/media/australia/tasmania/mt-field/ with full
    camera/lens/settings + dimensions captured from sharp.
    
    also:
    - alt text is now optional with a warning, not a hard build failure
      (build.mjs + process-staging.mjs)
    - build auto-creates virtual parent gallery nodes for any folder depth,
      so /media/australia and /media/australia/tasmania resolve as routes
      even without metadata.yaml at those levels
  15. v55 2026-05-19 5612e1a
    backfill image dimensions; capture w/h via sharp on every ingest going forward
  16. v54 2026-05-19 a6bc841
    photo page: add size, dimensions + megapixels rows
  17. v53 2026-05-19 72b7a27
    cap image heights so portrait shots fit the viewport without forcing scroll
    details
    - homepage random photo: max-height 60vh
    - photo page: max-height 82vh, width auto
    - post figures full-width: max-height 75vh with object-fit contain
    - inline-floated images keep natural sizing
  18. v52 2026-05-19 930e53c
    fix: featured-image entry now picks up s3 urls from frontmatter.images map
  19. v51 2026-05-19 bad8ddf
    all images on s3 — zero image bytes in repo (migration of existing posts done)
  20. v50 2026-05-19 4b8bc4e
    stats: pictures-size pulls recorded full-size bytes (s3 original) when available
  21. v49 2026-05-19 dcba78f
    drop accidentally-committed AWSCLIV2.pkg installer
  22. v48 2026-05-19 39ff258
    s3 originals + camera EXIF metadata
    details
    scripts/process-staging.mjs:
    - reads full EXIF (camera, lens, ISO, aperture, shutter, focal length, dimensions)
      in addition to date/GPS
    - scrubs the original (strips all EXIF including GPS via sharp's default behavior)
    - uploads scrubbed full-size original to s3://georgemain-com-media//
    - writes a smaller 1400px local reference into content/media/ for build-time
      thumb/med generation (keeps repo lean)
    - metadata.yaml entries now carry { file, s3, alt, title, date, location, exif }
    
    templates/media-photo.eta:
    - camera/lens/settings/dimensions table when exif present
    - "view full size ↗" link to the S3 original when s3 is set,
      falls back to local file for older entries
  23. v47 2026-05-19 76dff3a
    homepage: by-the-numbers stats grid (musings/words/pictures/size/dates)
  24. v46 2026-05-19 59a0edc
    japan: fix year, 2023 not 2024
  25. v45 2026-05-19 33d3007
    category page: replace .sp-tree (white-space: pre eats template whitespace) with compact .cat-list
  26. v44 2026-05-19 de6aa62
    japan: add Kiyomizu-dera title image
  27. v43 2026-05-19 e519ce4
    japan: replace vending machine photo with higher-res original
  28. v42 2026-05-19 8ec7fd5
    dates: budapest 2025-11-01, japan 2024-09-01
  29. v41 2026-05-19 11496dd
    cleanup: wire home button copy keys, strip orphan token-viewer css, better build error messages
    details
    - templates/home.eta: random/next/read/open buttons now read their labels
      from copy.yaml's home.random_musing.* and home.random_photo.*. previously
      hardcoded.
    - design/site.css: dropped the .tg-* class block (~64 lines). these were
      styles for the token-viewer page in the design canvas, never used in
      production.
    - scripts/build.mjs: error messages now lead with the file path and name
      the exact missing field — e.g. ": image 'foo.jpg' is missing
      required 'alt' text" instead of "Image alt missing in ...".
  30. v40 2026-05-19 cdbe826
    footer: simpler creed, drop em-dash
  31. v39 2026-05-19 07b7323
    GBR post: hero caption + boats-my-whole-life note
  32. v38 2026-05-19 96ff541
    per-photo pages with prev/next nav (vsco-style)
    details
    clicking a gallery cell now opens a dedicated photo page at
    /media/// instead of the raw image file. each page:
    
    - big image with caption (alt text)
    - metadata table: title, date, where, album, original file link
    - prev / next nav (wraps around at the ends)
    - counter "3 / 12"
    - left/right arrow keys also navigate
    - ↑ back to  link
    
    works for both real galleries and synthesized-from-musing galleries.
  33. v37 2026-05-19 e2f424c
    auto-derive media gallery path for any musing with images
    details
    - before: synthesis was opt-in via gallery.path
    - now: any post with images auto-gets a gallery. default path = post
      category minus first segment + slug. override with gallery.path,
      disable with gallery: false.
    - trim verbose alt text on three images
    - make nunobiki falls inline (small) instead of full-width
    - add CLAUDE.md
  34. v36 2026-05-19 6d037ae
    tree sidebar: nested ul (wraps long names) + mobile-collapsed by default
    details
    - renderTreeHtml outputs nested 
      so long names like "great-barrier-reef" wrap naturally instead of being clipped by the fixed sidebar column. - depth indent via padding-left + dashed left-border guide (cleaner than ASCII glyphs that depend on white-space: pre). - mobile (<=900px): tree is hidden behind a "browse ▾" toggle (pure CSS checkbox hack). desktop unaffected — tree always visible. - previous spaces+pre approach is gone; no more clipping or weird wrap. also fixed earlier issue: vsco gallery captions now appear BELOW the cell on hover, not overlaid on the photo.
  35. v35 2026-05-19 173850e
    fix: stripe pattern bleeding through over real photos in gallery cells
  36. v34 2026-05-19 207888d
    synthesize media galleries from musings' frontmatter
    details
    opt-in via gallery: { path, title, subtitle, location, date } on a musing.
    build walks ancestor segments to create virtual parent gallery nodes so
    breadcrumbs and album-tree work. images stay single-source in the post's
    media/ folder (no duplication). synthesized gallery + each card show a
    "↩ from the musing: " link back to the source post.
    
    skip the page-route check for image/static asset urls.</pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v33</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/95e4eef" title="view commit on github">95e4eef</a>
              </div>
              <div class="cl-subject">fix: don't double-escape alt/caption; trim hero caption</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v32</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/c4f338d" title="view commit on github">c4f338d</a>
              </div>
              <div class="cl-subject">post figures: scale only when hovering the image itself, not the caption</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v31</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/d8417fe" title="view commit on github">d8417fe</a>
              </div>
              <div class="cl-subject">post figures: subtle hover-enlarge to match media grid</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v30</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/4c01161" title="view commit on github">4c01161</a>
              </div>
              <div class="cl-subject">fix: marked v14 image renderer signature — hero figures were rendering with undefined href</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v29</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/efd8993" title="view commit on github">efd8993</a>
              </div>
              <div class="cl-subject">constrain post figure images to figure width</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v28</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/9ef4aff" title="view commit on github">9ef4aff</a>
              </div>
              <div class="cl-subject">fix: dedupe featured_image key in great barrier reef post</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v27</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/1e35208" title="view commit on github">1e35208</a>
              </div>
              <div class="cl-subject">great barrier reef: add hero photo + inline deck shot</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v26</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/ad3c2ce" title="view commit on github">ad3c2ce</a>
              </div>
              <div class="cl-subject">decode html entities when stripping markdown for previews</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v25</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/c1c4611" title="view commit on github">c1c4611</a>
              </div>
              <div class="cl-subject">add folder-tree sidebar to musing posts + tighten category page</div>
              <details class="cl-detail"><summary>details</summary><pre>- build.mjs: walks musings into a {folder|post} tree, renders to HTML
      with active/in-active classes. Passed to musing.eta + musings-category.eta.
    - musing.eta + musings-category.eta: 2-col grid (200px tree + reading column).
    - site.css: .musings-layout grid, sticky tree sidebar, :has() override widens
      the site-shell to 1080px on musing pages so the tree fits without squishing
      the reading column. Mobile collapses to single column under 900px.
    - category page restructured: no more 3 stacked sp-sections each with 64px
      top margin + horizontal rule. Now subfolders + posts are tight sequential
      blocks with just a thin rule + // caption between them.</pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v24</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/4e2ecb2" title="view commit on github">4e2ecb2</a>
              </div>
              <div class="cl-subject">drop top gap+rule above the first section on a page</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v23</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/8f61de6" title="view commit on github">8f61de6</a>
              </div>
              <div class="cl-subject">fix: random/next buttons — eta 3 doesn't recognize <%- so JSON payload was empty</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v22</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/e23c49d" title="view commit on github">e23c49d</a>
              </div>
              <div class="cl-subject">apply v2 design refresh — toned-down retro</div>
              <details class="cl-detail"><summary>details</summary><pre>design bundle update from claude design tool:
    - flat background (drop grid texture). content column narrowed 1240→820.
    - new tokens.css: red-only, ibm plex only, fs ramp adjusted, lh-body=1.65,
      reading=680px / content=760px / max=820px / gutter=32px.
    - new site.css: section markers are now thin top rule + "// 01 · words"
      caption (was the boxy [§ ...] rail). random-musing + random-photo blocks
      drop the panel chrome entirely (sp-panel → sp-block). tags become plain
      #tag text (no border/chip). lighter buttons, no offset shadow. new
      sp-link-btn ([text] inline link-style button). albums grid now 2-col.
    - templates updated to match: home is content-first with sp-block + sp-link-btn,
      musings index uses inline browse strip (folder list + tag chips) instead
      of right sidebar, footer uses sp-footer-mark class and inlines version + est.</pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v21</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/add0fa0" title="view commit on github">add0fa0</a>
              </div>
              <div class="cl-subject">changelog: render from git log instead of manual yaml entries</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v20</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/84e328c" title="view commit on github">84e328c</a>
              </div>
              <div class="cl-subject">footer: keep version label, drop attribution</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v19</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/5580e81" title="view commit on github">5580e81</a>
              </div>
              <div class="cl-subject">remove footer attribution line entirely</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v18</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/8342f10" title="view commit on github">8342f10</a>
              </div>
              <div class="cl-subject">fix html/body sizing — use 100vh on body, drop 100% on html</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v17</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/f8489f5" title="view commit on github">f8489f5</a>
              </div>
              <div class="cl-subject">remove stale hero comment from copy.yaml</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v16</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/55e1bc3" title="view commit on github">55e1bc3</a>
              </div>
              <div class="cl-subject">remove homepage hero — straight into the random-musing widget</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v15</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/8d8a934" title="view commit on github">8d8a934</a>
              </div>
              <div class="cl-subject">fix scroll clipping on long pages + relabel footer attribution</div>
              <details class="cl-detail"><summary>details</summary><pre>- design/site.css: remove overflow:hidden from .site-page. it was
      there for design-canvas artboards but clips body scroll on the
      real site. long posts now scroll.
    - copy.yaml: footer.built_by_hand "built by hand" -> "built with claude".
      truthful and matches the changelog tone (prose hand-written, finished
      with AI).</pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v14</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/2738965" title="view commit on github">2738965</a>
              </div>
              <div class="cl-subject">add staging/ media ingest workflow + EXIF GPS check in pre-commit</div>
              <details class="cl-detail"><summary>details</summary><pre>- scripts/process-staging.mjs: interactive CLI (npm run media)
      - reads EXIF date/GPS via exifr
      - groups bursts (same filename prefix + capture time within 30 min)
      - asks gallery / batch date / batch location once per burst
      - per-image: title (optional) + alt (required)
      - strips ALL metadata on output (sharp default behavior)
      - moves file out of staging/ into content/media/<gallery>/
      - appends entry to metadata.yaml
      - offers img_NNN renaming
    - scripts/check-pii.mjs now also scans staged image EXIF and fails
      on GPS coords, so missed scrubs are caught at commit time.
    - .claude/skills/process-staging/SKILL.md as the chat-friendly entry point.
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com></pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v13</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/05325b9" title="view commit on github">05325b9</a>
              </div>
              <div class="cl-subject">add CI deploy, PII pre-commit hook, claude code skills</div>
              <details class="cl-detail"><summary>details</summary><pre>- .github/workflows/deploy.yml: build + deploy to GitHub Pages on push to main
    - .githooks/pre-commit + scripts/check-pii.mjs: regex scan of staged diff for
      emails, phones, SSNs, CC#s, GPS coords, precise timestamps, US addresses.
      optional AI second-pass when ANTHROPIC_API_KEY is set.
      .pii-allowlist.yaml lets known-public strings through.
    - npm run setup wires git hooks (one-time per clone).
    - .claude/skills/: add-musing, add-images-to-musing, add-media-gallery,
      add-images-to-gallery, add-tab, seo-audit, add-static-site, pii-audit.
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com></pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v12</span>
                <span class="cl-date">2026-05-19</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/8bae61c" title="view commit on github">8bae61c</a>
              </div>
              <div class="cl-subject">add static site generator + copy.yaml + initial musings</div>
              <details class="cl-detail"><summary>details</summary><pre>build pipeline: node + eta templates + sharp for images. content lives in
    content/musings (markdown + frontmatter) and content/media (yaml + jpegs).
    all human-facing copy is in copy.yaml so it can be edited in one place.
    version auto-increments from git commit count. light/dark toggle persists
    to localStorage. existing flight pages moved to content/sites/ and pass
    through to /sites/* unchanged.
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com></pre></details>        </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v11</span>
                <span class="cl-date">2026-03-12</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/f91203c" title="view commit on github">f91203c</a>
              </div>
              <div class="cl-subject">update timing and add pilot stats</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v10</span>
                <span class="cl-date">2026-03-12</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/b67dadb" title="view commit on github">b67dadb</a>
              </div>
              <div class="cl-subject">update for kml</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v9</span>
                <span class="cl-date">2026-02-10</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/9cc0070" title="view commit on github">9cc0070</a>
              </div>
              <div class="cl-subject">add cache</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v8</span>
                <span class="cl-date">2026-02-10</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/387e56b" title="view commit on github">387e56b</a>
              </div>
              <div class="cl-subject">add flight viewer</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v7</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/088e60f" title="view commit on github">088e60f</a>
              </div>
              <div class="cl-subject">add desktop version</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v6</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/7c990c0" title="view commit on github">7c990c0</a>
              </div>
              <div class="cl-subject">mobile</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v5</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/c3a80ef" title="view commit on github">c3a80ef</a>
              </div>
              <div class="cl-subject">add first-flight</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v4</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/d35930a" title="view commit on github">d35930a</a>
              </div>
              <div class="cl-subject">Create CNAME</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v3</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/c5518ef" title="view commit on github">c5518ef</a>
              </div>
              <div class="cl-subject">Delete CNAME</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v2</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/db73e37" title="view commit on github">db73e37</a>
              </div>
              <div class="cl-subject">Create CNAME</div>
                      </li>
                  <li class="cl-row">
              <div class="cl-line">
                <span class="cl-ver">v1</span>
                <span class="cl-date">2026-01-31</span>
                <a class="cl-hash" href="https://github.com/gemivnet/gemivnet.github.io/commit/847b4e5" title="view commit on github">847b4e5</a>
              </div>
              <div class="cl-subject">Initial commit</div>
                      </li>
              </ol>
      </section>
    
    <style>
      .changelog { list-style: none; padding: 0; margin: 0; }
      .cl-row {
        padding: var(--sp-3) 0;
        border-top: 1px solid var(--rule);
      }
      .cl-row:last-child { border-bottom: 1px solid var(--rule); }
      .cl-line {
        font-family: var(--font-mono);
        font-size: var(--fs-tiny);
        color: var(--ink-3);
        display: flex;
        gap: var(--sp-4);
        align-items: baseline;
        margin-bottom: 4px;
      }
      .cl-line .cl-ver { color: var(--accent); font-weight: 600; font-size: var(--fs-small); min-width: 4em; }
      .cl-line .cl-hash {
        margin-left: auto;
        color: var(--ink-3);
        opacity: 0.7;
        text-decoration: none;
      }
      .cl-line .cl-hash:hover { color: var(--accent); opacity: 1; }
      .cl-subject { color: var(--ink); font-size: var(--fs-body); line-height: 1.45; }
      .cl-detail { margin-top: var(--sp-2); font-family: var(--font-mono); font-size: var(--fs-small); }
      .cl-detail summary { cursor: pointer; color: var(--ink-3); }
      .cl-detail summary:hover { color: var(--accent); }
      .cl-detail pre {
        white-space: pre-wrap;
        margin: var(--sp-2) 0 0;
        padding-left: var(--sp-4);
        border-left: 2px solid var(--rule);
        color: var(--ink-2);
      }
      @media (max-width: 560px) {
        .cl-line { flex-wrap: wrap; }
        .cl-line .cl-hash { margin-left: 0; }
      }
    </style>
      </main>
    
      <footer class="sp-footer">
        <div class="sp-footer-inner">
          <div>
            <div class="sp-footer-mark"><span class="tilde">~</span>/george.main</div>
            <p class="creed">a personal knowledge page, made for fun. no ads, no tracking, no warranty.</p>      </div>
          <div>
            <h5>elsewhere</h5>
            <ul>
                          <li><a href="https://github.com/gemivnet">github.com/gemivnet</a></li>
                          <li><a href="mailto:george.main@gemiv.net">george.main@gemiv.net</a></li>
                      </ul>
          </div>
          <div>
            <h5>this site</h5>
            <ul>
                          <li><a href="/rss.xml">/rss.xml</a>  <span style="color: var(--accent);">●</span></li>
                          <li><a href="/changelog">/changelog</a></li>
                                    <li><a href="#" id="random-musing-link" data-urls='["/musings/travel/australia/great-barrier-reef","/musings/travel/budapest/suggestions","/musings/travel/new-zealand/suggestions","/musings/travel/japan/suggestions"]'>take me somewhere</a></li>
                        <li><a href="/license">license</a></li>
              <li style="margin-top: var(--sp-3); color: var(--ink-3);">© George Main · CC BY-NC 4.0</li>
              <li class="version" style="color: var(--ink-3);">v68</li>
            </ul>
          </div>
        </div>
      </footer>
    
      <script>
        (function () {
          var page = document.body;
          var toggle = document.getElementById('theme-toggle');
          function setTheme(t) {
            if (t === 'dark') { page.classList.remove('light'); toggle.innerHTML = '☽ dark'; }
            else { page.classList.add('light'); toggle.innerHTML = '☀ light'; }
            try { localStorage.setItem('gm-theme', t); } catch (_) {}
          }
          try { var s = localStorage.getItem('gm-theme'); if (s) setTheme(s); } catch (_) {}
          toggle && toggle.addEventListener('click', function () {
            setTheme(page.classList.contains('light') ? 'dark' : 'light');
          });
    
          var rnd = document.getElementById('random-musing-link');
          if (rnd) {
            rnd.addEventListener('click', function (e) {
              e.preventDefault();
              try {
                var urls = JSON.parse(rnd.getAttribute('data-urls') || '[]');
                if (urls.length) window.location.href = urls[Math.floor(Math.random() * urls.length)];
              } catch (_) {}
            });
          }
        })();
      </script>
    </body>
    </html>