/* ============================================================
   Photoblog — clintmcmahon.com
   Nocturne — a quiet, compact dark interface: a near-neutral blue-grey ground,
   Inter at medium weight, a blurple accent used as a line and a glow rather
   than a flood. Adapted from the Nocturne design system's tokens onto this
   site's existing .pb-* class architecture — same real markup and JS
   (lightbox, lazy-load, pager), retinted.
   ============================================================ */

/* ---- Typeface ---------------------------------------------
   Inter, self-hosted, for both chrome and captions — Nocturne runs one
   typeface throughout rather than pairing a serif for body copy. The
   variable file carries both a weight axis and an optical-size axis, so
   headings get slightly tighter, more display-like letterforms than
   captions do — that is what font-optical-sizing: auto below is for.
   ------------------------------------------------------------ */

@font-face {
  font-family: "Inter";
  src: url("/fonts/Inter-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: normal;
  font-style: normal;
  font-display: swap;
}

:root {
  --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont,
               "Segoe UI", Helvetica, Arial, sans-serif;

  /* Type scale. Six steps that are visibly different from each other, rather
     than the fourteen near-identical ones this file used to carry. */
  --fs-xs:    0.75rem;    /* 12px — counts, pill numbers */
  --fs-sm:    0.8125rem;  /* 13px — nav, footer, meta, labels */
  --fs-base:  1rem;       /* 16px — captions, comments: anything you read */
  --fs-lg:    1.125rem;   /* 18px — the about page */
  --fs-md:    1.25rem;    /* 20px — section headings */
  --fs-title: 1.75rem;    /* 28px — a photo's title */
  --fs-h1:    2rem;       /* 32px — archive and tag page titles */

  /* Nocturne ground and ink — a near-neutral blue-grey, not a true black, so
     photographs read as prints on a dark wall rather than punched through
     a void. */
  --bg:       #161826;
  --bg-sunk:  #1e2030;   /* the well a photo sits in before it loads */
  --bg-surface: #232532; /* raised panels: inputs, list-row hover, comment items */
  --bg-blur:  rgba(22, 24, 38, 0.94);  /* sticky bars */
  --ink:      #e9e9ed;
  --ink-2:    #cfd3e5;
  --muted:    #9397ab;
  --rule:     rgba(233, 233, 237, 0.14);
  --rule-2:   #595d6c;

  /* The one accent — a blurple, used as a line and a glow, never a flood.
     --accent-dim is the deep ramp step for paragraph-size accent text (the
     accent itself only clears ~3:1 against this ground, enough for icons
     and chrome, not body copy). */
  --accent:      #9184d9;
  --accent-dim:  #d2cefd;
  --accent-tint: rgba(145, 132, 217, 0.14);

  /* Contact form result banner. Broken out as tokens (rather than hardcoded in the
     .pb-flash rules) so the light theme below can swap them for values that still
     clear contrast on white. */
  --flash-success-bg:  rgba(94, 196, 133, 0.12);
  --flash-success-ink: #9fd8b3;
  --flash-error-bg:    rgba(224, 90, 90, 0.12);
  --flash-error-ink:   #e3a3a3;

  /* Hints native form controls (date pickers, scrollbars) to draw themselves dark. */
  color-scheme: dark;

  /* Spacing scale — the vertical rhythm was previously ad hoc (0.6/1.4/3/4rem). */
  --space-1: 0.5rem;
  --space-2: 0.875rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --space-6: 6rem;

  /* The page shell — header, hero, grid and list sections all cap and center to this
     so their edges line up instead of each picking its own width. */
  --shell: 1180px;

  /* The photograph's frame. Wider than the old fixed 1000px so a big display shows a
     big picture, but held back from the edges so it reads as a print on a wall rather
     than a background. Height does the real work — see .pb-image. */
  --stage: min(94vw, 1180px);

  /* How tall a photograph may stand. Used twice: to cap the image, and to work out
     how wide its frame should be — see --ar below. */
  --frame-h: min(78vh, 1000px);

  /* How narrow the grid of supporting photos may get while following the opening frame.
     Below roughly this, two columns stop being photographs and start being thumbnails. */
  --grid-min: 620px;
}

/* ---- Light theme -------------------------------------------
   Same system, same layout, same sans typography — only the palette flips. The accent
   moves to a deeper step of its own ramp (accent-600/700) rather than the dark theme's
   accent/accent-300, per the design system's own rule: light steps read against a dark
   ground, deep steps read against a light one. Applied via [data-theme], set before first
   paint by the inline script at the top of every page's <head> — see that script for the
   light-by-day/dark-by-night default and the manual override in _SiteHeader's toggle. */
:root[data-theme="light"] {
  --bg:         #ffffff;
  --bg-sunk:    #f0f0f2;
  --bg-surface: #f5f5f7;
  --bg-blur:    rgba(255, 255, 255, 0.94);
  --ink:        #16161a;
  --ink-2:      #43434a;
  --muted:      #74747c;
  --rule:       #e6e6ea;
  --rule-2:     #c7c7cf;

  --accent:      #796cbf;
  --accent-dim:  #5d5294;
  --accent-tint: rgba(121, 108, 191, 0.1);

  --flash-success-bg:  rgba(46, 155, 92, 0.1);
  --flash-success-ink: #2f7a4e;
  --flash-error-bg:    rgba(200, 60, 60, 0.1);
  --flash-error-ink:   #b23a3a;

  color-scheme: light;
}

*, *::before, *::after {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-optical-sizing: auto;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- Fixed header -----------------------------------------
   Two groups, space-between — not the old three-column grid. Left: a short accent dash,
   the name (plain case, not a tracked-uppercase wordmark), and the tagline sitting right
   beside it. Right: section nav. This is 3a's actual header shape. */
.pb-header {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 1.05rem 1.4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--rule);
}

.pb-header-brandline {
  display: flex;
  align-items: center;
  /* 0.7rem (11.2px), not var(--space-4) — this site's own --space-4 (2.5rem) predates the
     reskin and names a completely different value than the mockup's own --space-4. */
  gap: 0.7rem;
}

/* Weather sits directly above the accent mark, the two stacked as one unit to the left
   of the site name — not inline with either of them. */
.pb-header-mark-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}

.pb-header-mark {
  display: inline-block;
  width: 1.4rem;
  height: 1px;
  background: var(--accent);
  flex: none;
}

.pb-site-name {
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  transition: color 0.2s;
}
.pb-site-name:hover { color: var(--accent-dim); }

.pb-header-byline {
  font-size: 0.65625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

/* Sits in the brandline's own slot between the accent dash and the site name — sized
   down to fit there, spaced by .pb-header-left's gap like every other sibling in that
   row rather than its own margin. */
.weather-widget {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.weather-icon {
  font-size: var(--fs-sm);
}

.weather-temp {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* ---- Main layout ------------------------------------------ */

.pb-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Almost no padding on top: the photograph is the first thing on the page. */
  padding: var(--space-1) var(--space-4) var(--space-5);
}

/* ---- Image rows ------------------------------------------- */

/* The opening frame is the one thing that shrinks: it takes the photograph's own aspect
   ratio, so when the height cap bites, the frame narrows with the picture instead of
   leaving it letterboxed inside a wider box. Text, pager, responses and footer all stay
   pinned at the full 1180.

   The narrowing lives on the row rather than on this container. It used to sit here, which
   meant the grid of supporting photos below inherited it: a vertical opening frame dragged
   the whole grid in with it, and an extreme vertical (1:3) squeezed four photographs into
   a 211px column as 104x69 thumbnails inside an otherwise empty 1180px stage. */
.pb-rows {
  width: 100%;
  max-width: var(--stage);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

/* Each row is a horizontal strip — one or more images side by side. This is what narrows
   to the picture's shape; --ar is written per post by the view from the stored dimensions,
   and the 99 fallback means "do not narrow" for posts whose dimensions aren't recorded. */
.pb-row {
  display: flex;
  gap: 4px;
  width: 100%;
  max-width: min(100%, calc(var(--frame-h) * var(--ar, 99)));
  /* Anchors the hover arrows to the frame rather than to the stage, so they sit on the
     photograph's own edges however narrow a vertical makes it. */
  position: relative;
}

/* ---- Hover arrows over the frame --------------------------- */

/* Overlay that does not itself take clicks — only the arrows do, and only once the frame
   is hovered. Clicking anywhere else on the photograph still opens the lightbox. */
.pb-frame-nav {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.pb-frame-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  /* A scrim, because a white chevron is invisible on a bright photograph and a dark one
     is invisible on a shadow. This works on both. */
  background: rgba(0, 0, 0, 0.4);
  border-radius: 50%;
  backdrop-filter: blur(2px);
  opacity: 0;
  /* Not clickable until visible: an invisible hit area over the frame's edge would
     navigate when someone meant to open the lightbox. */
  pointer-events: none;
  transition: opacity 0.18s ease, background-color 0.18s ease;
}

.pb-frame-arrow svg {
  width: 1.4rem;
  height: 1.4rem;
}

.pb-frame-arrow--prev { left: 0.85rem; }
.pb-frame-arrow--next { right: 0.85rem; }

.pb-frame-arrow:hover { background: rgba(0, 0, 0, 0.65); }

/* Pointer devices only. A phone has no hover state, and the swipe gesture already covers
   this — arrows permanently stuck over the photograph would just be in the way. */
@media (hover: hover) and (pointer: fine) {
  .pb-row:hover .pb-frame-arrow {
    opacity: 1;
    pointer-events: auto;
  }
}

@media (hover: none), (pointer: coarse) {
  .pb-frame-nav { display: none; }
}

/* The lightbox has arrows of its own; two sets at once would be confusing. */
body.pb-lightbox-open .pb-frame-nav { display: none; }

@media (prefers-reduced-motion: reduce) {
  .pb-frame-arrow { transition: none; }
}

.pb-image {
  flex: 1;
  min-width: 0;
  display: block;
  width: 0;        /* forces equal flex distribution regardless of natural size */
  height: auto;
  /* A vertical frame filling a 1000px column stands 1250px tall (4:5) or 1500px (2:3),
     which is taller than the window — you land on a wall of photo with no caption in
     sight. Capping the height and letting object-fit letterbox inside the flex slot
     shrinks tall frames to fit while leaving everything wider than 5:4 untouched,
     including the older small photos that rely on width:0 to fill the column. */
  max-height: var(--frame-h);
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.55s ease;
}
.pb-image.is-loaded { opacity: 1; }

/* The grid follows the opening frame's width, so the two align — a height-capped landscape
   frame is about 950px on a laptop, and a grid running the full 1080 would overhang it.
   It stops following at --grid-min: a vertical frame is narrow enough to squeeze four
   photographs into 104x69 thumbnails, which is what this floor exists to prevent.
   .pb-rows is a centring flex column, so the explicit width is what stops this collapsing
   to its content.

   Two explicit flex columns, not CSS multi-column: `columns: 2; column-fill: balance` left
   Blink and WebKit disagreeing on how to split the same images, and WebKit's balance could
   leave one column visibly shorter than the other. The view now decides which image goes in
   which column (Views/Photos/Detail.cshtml), so the split is identical everywhere. */
.pb-masonry {
  width: 100%;
  max-width: max(min(100%, var(--grid-min)), calc(var(--frame-h) * var(--ar, 99)));
  display: flex;
  gap: 4px;
  margin-top: 4px;
}

.pb-masonry-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* No height cap and no object-fit here. A capped box keeps its full column width
   while contain shrinks the picture inside it, so every image taller than the cap
   floated in a pool of empty space instead of filling its column. Grid images fill
   their column and take whatever height their aspect ratio asks for — that is what
   makes a masonry grid read as tight. */
.pb-masonry-img {
  width: 100%;
  height: auto;
  display: block;
  opacity: 0;
  transition: opacity 0.55s ease;
}

.pb-masonry-img.is-loaded { opacity: 1; }

.pb-date {
  color: var(--muted);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}

/* ---- Metadata row below image ----------------------------- */

/* The words sit under the photograph and share its left edge. Pinned at the full stage width
   they started 300px to the left of a vertical frame, which read as two unrelated columns
   rather than a caption belonging to a picture. The floor keeps an extreme vertical from
   squeezing the caption into a ribbon. */
/* The "← Index" link above the frame — 3a's only wayfinding on this page besides the
   header nav; the old separate Archive/Random pager links live in the header now. */
.pb-back-link {
  display: inline-block;
  width: 100%;
  max-width: var(--stage);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  margin-bottom: var(--space-2);
  transition: color 0.15s;
}
.pb-back-link:hover { color: var(--accent-dim); }

/* Caption and Prev/Next share one row, the way 3a draws it — not a caption block
   followed by a separate bordered pager section. */
.pb-meta {
  width: 100%;
  max-width: var(--stage);
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Mirrors .pb-hero-meta on the home page: words left, camera line right. */
.pb-detail-head {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  grid-template-areas:
    "text exif"
    "body body";
  column-gap: var(--space-4);
  align-items: start;
}
.pb-detail-head > .pb-caption-area { grid-area: text; }
.pb-detail-head > .pb-hero-exif { grid-area: exif; }
.pb-detail-head > .pb-detail-body { grid-area: body; width: 100%; min-width: 0; }
@media (max-width: 760px) {
  .pb-detail-head {
    grid-template-columns: 1fr;
    grid-template-areas:
      "text"
      "body"
      "exif";
    row-gap: 0.75rem;
  }
}

.pb-caption-area {
  flex: 1;
  min-width: 0;
}

.pb-title {
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: 0 0 0.7rem;
  /* Wraps as a unit on a phone rather than stranding the date on its own line. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5rem;
}

.pb-title-sep {
  color: var(--rule-2);
  font-weight: 300;
}

/* Full column width, matching the photo above it — not capped to a reading measure,
   which left the text ending halfway across the frame. Sans, at Nocturne's body size
   and weight — no serif pairing in this direction. */
.pb-caption {
  font-size: 0.9375rem;
  color: var(--ink-2);
  line-height: 1.6;
  margin: 0;
}
.pb-caption p { margin: 0 0 0.5em; }
.pb-caption p:last-child { margin-bottom: 0; }

/* Camera, focal length, shutter, aperture, ISO — the line every photoblog of this kind runs
   under the picture. Tabular figures so the numbers do not shimmer between photographs. */
.pb-exif {
  margin: 0.7rem 0 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Plain words, separated by thin dots, rather than bordered pills. A pill is a control, and
   controls beside a photograph make the page read as an interface instead of a print. */
.pb-detail-tags {
  margin: 0.7rem 0 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--muted);
}

.pb-tag {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.15s;
}
.pb-tag:hover { color: var(--accent-dim); }

/* Separator drawn rather than typed, so it is never selected or read aloud. */
.pb-detail-tags a + a::before {
  content: "·";
  margin: 0 0.45rem;
  color: var(--rule-2);
}

/* ---- Pager --------------------------------------------------
   Prev / count / Next, sitting at the right end of the caption row — not a separate
   bordered section. rel=prev/next on the anchors still carries the SEO/a11y signal
   the old thumbnail pager did; this is a visual simplification, not a functional one.
   ------------------------------------------------------------ */

.pb-pager {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
}

.pb-pager-count {
  font-size: var(--fs-sm);
  color: var(--muted);
  min-width: 3.5rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ---- Focus ------------------------------------------------
   Keyboard focus is the accent ring throughout, per Nocturne — never the
   browser default, and never plain --ink either.
   ------------------------------------------------------------ */

a:focus-visible,
button:focus-visible,
img:focus-visible,
input:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 1px;
}

::selection { background: var(--accent-tint); }

/* Webmentions and Mastodon replies share the one container width. */
.pb-responses {
  width: 100%;
  max-width: var(--stage);
}

/* ---- Comments --------------------------------------------- */

/* Comments are switched off by a stub partial, which still left the container's
   margin and rule painting an empty band between the pager and the webmentions.
   :has() lets it collapse when nothing renders inside, and come back if it does. */
.pb-comments:not(:has(*)) {
  display: none;
}

.pb-comments {
  width: 100%;
  max-width: var(--stage);
  margin-top: 4rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
}

.pb-comments-heading {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 1.75rem;
}

.pb-comment-list {
  list-style: none;
  margin: 0 0 2.5rem;
  padding: 0;
}

.pb-comment-item {
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--rule);
}

.pb-comment-meta {
  font-size: var(--fs-sm);
  margin-bottom: 0.3rem;
}

.pb-comment-author {
  font-weight: 500;
  color: var(--ink);
}

.pb-comment-time {
  color: var(--muted);
  margin-left: 0.5rem;
}

.pb-comment-body {
  font-size: var(--fs-base);
  color: var(--ink-2);
  line-height: 1.65;
}

.pb-no-comments {
  font-size: var(--fs-base);
  color: var(--muted);
  margin: 0 0 2rem;
}

/* ---- Webmentions / Mastodon engagement -----------------------
   Moved out of _Webmentions.cshtml's own inline <style> (hardcoded light
   hex values that clashed with the dark ground) so both partials — they
   share these classes and always render on the same page — draw from the
   theme's tokens instead. */

.wm-notice {
  font-size: var(--fs-xs);
  color: var(--muted);
  margin-bottom: 1.25rem;
  border-top: 1px solid var(--rule);
  padding-top: 1.5rem;
}
.wm-notice a { color: var(--accent-dim); }

/* The link to the actual Mastodon post, sitting between the webmentions notice and any
   likes/boosts/replies pulled from it — context for the engagement below, not a tag. */
.pb-syndication-link {
  font-size: var(--fs-xs);
  margin-bottom: 1.25rem;
}
.pb-syndication-link a {
  color: var(--accent-dim);
  text-decoration: none;
}
.pb-syndication-link a:hover { text-decoration: underline; }

.wm-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.6rem;
}

.wm-facepile .wm-faces { display: flex; flex-wrap: wrap; gap: 6px; }
.wm-face img {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--bg);
  box-shadow: 0 0 0 1px var(--rule-2);
}

.wm-reply { padding: 0.75rem 0; border-bottom: 1px solid var(--rule); }
.wm-reply:last-child { border-bottom: none; }
.wm-reply-author { display: flex; align-items: center; gap: 8px; margin-bottom: 0.4rem; }
.wm-reply-author img { width: 28px; height: 28px; border-radius: 50%; }
.wm-reply-author-name {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}
.wm-reply-author-name:hover { color: var(--accent-dim); }
.wm-reply-date { font-size: var(--fs-xs); color: var(--muted); margin-left: auto; }
.wm-reply-content { font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6; }

/* ---- Form fields --------------------------------------------
   Shared by the (currently disabled) comment form and the Contact page —
   underlined fields on the dark ground rather than boxed inputs, matching
   Nocturne's outlined-not-filled instinct for controls. */

.pb-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 420px;
}

.pb-field-label {
  display: block;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.4rem;
}

.pb-input,
.pb-textarea {
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--rule);
  padding: 0.45rem 0;
  font-size: var(--fs-base);
  font-family: inherit;
  color: var(--ink);
  outline: none;
  resize: none;
  transition: border-color 0.2s;
}
.pb-input:focus,
.pb-textarea:focus {
  border-color: var(--accent);
}
.pb-input::placeholder,
.pb-textarea::placeholder {
  color: var(--rule-2);
}

.pb-textarea {
  min-height: 80px;
}

.pb-submit {
  align-self: flex-start;
  background: none;
  border: 1px solid var(--accent);
  padding: 0.7rem 1.4rem;
  font-size: var(--fs-sm);
  font-family: inherit;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-dim);
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 0.2s;
}
.pb-submit:hover {
  background: var(--accent-tint);
}
.pb-submit:disabled {
  opacity: 0.55;
  cursor: default;
  background: none;
}

/* ---- Buttons --------------------------------------------------
   Three variants, matching the design system: primary is an accent outline (never a
   fill), secondary a neutral outline, ghost no border at all. Used for the "Older
   frames" / archive-pagination links, the About page's CTAs, and the frame pager's
   Prev/Next. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-decoration: none;
  cursor: pointer;
  border-radius: 4px;
  /* 0.7rem block padding clears a real 44px tap target at this font/line-height — measured
     40px before, under the touch-target minimum. */
  padding: 0.7rem 1.2rem;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}
.btn-primary {
  background: none;
  border: 1px solid var(--accent);
  color: var(--accent-dim);
}
.btn-primary:hover { background: var(--accent-tint); }

.btn-secondary {
  background: none;
  border: 1px solid var(--rule-2);
  color: var(--ink-2);
}
.btn-secondary:hover { background: var(--bg-surface); border-color: var(--muted); }

.btn-ghost {
  background: none;
  border: 1px solid transparent;
  color: var(--accent-dim);
  padding-inline: 0.4rem;
}
.btn-ghost:hover { background: var(--accent-tint); }

/* Honeypot field — real inputs, off-screen rather than display:none, since some spam
   bots skip fields hidden that way but still fill in anything present in the DOM. */
.pb-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Post-redirect-get result banner — success or error, on the Contact page. */
.pb-flash {
  max-width: 420px;
  padding: 0.7rem 1rem;
  margin-bottom: 1.5rem;
  border-radius: 6px;
  font-size: var(--fs-sm);
  line-height: 1.5;
}
.pb-flash--success {
  background: var(--flash-success-bg);
  color: var(--flash-success-ink);
}
.pb-flash--error {
  background: var(--flash-error-bg);
  color: var(--flash-error-ink);
}

/* ---- Footer ------------------------------------------------
   One row, space-between — copyright left, Instagram/RSS right. Matches the design
   exactly. The auto side margins matter: the footer partial sits inside .pb-main on
   some pages and directly in <body> on others, and only .pb-main centers its children. */
.pb-footer {
  width: 100%;
  max-width: var(--stage);
  margin: 3rem auto 0;
  padding: 1.5rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.9rem;
  border-top: 1px solid var(--rule);
}

.pb-footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.pb-footer-link {
  font-size: 0.65625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  transition: color 0.18s;
}
.pb-footer-link:hover { color: var(--accent-dim); }

.pb-footer-colophon {
  margin: 0;
  font-size: 0.65625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- Header nav link ---------------------------------------- */

/* Plain gaps, no dividers — the mockup spaces its nav with flex gap alone, not the
   pipe-separated convention the old light theme used. 1.5rem, not var(--space-6): this
   site's own spacing scale (--space-6 = 6rem) predates the reskin and names a completely
   different value than the Nocturne mockup's own --space-6 (16.8px) — reusing it here
   blew the nav out to six items with 96px gaps, wide enough to force it onto its own row. */
.pb-header-nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  /* Real mobile testing (Playwright at 375px) showed this row running 413px wide against a
     375px viewport with no wrap of its own — .pb-header wrapping the whole nav group onto
     its own row was not enough by itself, since that row still had to fit six items plus
     the search box and the toggle. Without this, the theme toggle rendered fully off-screen. */
  gap: 1.05rem;
  row-gap: 0.6rem;
  justify-content: flex-end;
}

.pb-header-link {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--muted);
  letter-spacing: normal;
  text-decoration: none;
  transition: color 0.2s;
  /* Keeps the row from reflowing when the current page turns the label darker. */
  white-space: nowrap;
  /* Padding out and pulling back in with a negative margin widens the tap target to a real
     44px without moving the link visually or affecting the row's gap-based spacing — the
     12px line of text alone measured 19px tall on a real mobile pass, well under Apple's/
     Google's ~44px touch-target minimum. */
  padding: 0.75rem 0.3rem;
  margin: -0.75rem -0.3rem;
  display: inline-block;
}

.pb-header-link:hover { color: var(--accent-dim); }

/* Color only, matching the design exactly — no underline. aria-current in the markup
   carries the signal for assistive tech; this was the old pre-reskin theme's convention
   (color + underline together) and never belonged to this design. */
.pb-header-link.is-current {
  color: var(--accent-dim);
}

/* ---- Header search --------------------------------------------
   Same bordered-pill look the mockup drew as decoration, now a real GET form to /search.
   The input is borderless/transparent so the pill itself still reads as one plain shape,
   not an input-inside-a-box. */
.pb-header-search {
  display: flex;
  align-items: center;
  /* 0.35rem (5.6px), not var(--space-2) — same token-name collision as elsewhere in this
     file; this site's own --space-2 (0.875rem) isn't the mockup's (5.6px). */
  gap: 0.35rem;
  border: 1px solid var(--rule-2);
  border-radius: 4px;
  padding: 0.4rem 0.85rem;
  margin-left: 0.6rem;
  transition: border-color 0.15s;
}
.pb-header-search:focus-within { border-color: var(--accent); }

.pb-header-search-dot {
  width: 9px;
  height: 9px;
  border: 1.5px solid var(--muted);
  border-radius: 50%;
  flex: none;
}

.pb-header-search-input {
  border: none;
  background: none;
  outline: none;
  padding: 0;
  width: 6.5rem;
  font-family: inherit;
  font-size: 0.6875rem;
  color: var(--ink);
}
.pb-header-search-input::placeholder { color: var(--muted); }
/* Safari/Chrome draw a default cancel/clear glyph on type=search that doesn't match the
   theme at all — the pill is small enough that it just crowds the text. */
.pb-header-search-input::-webkit-search-cancel-button { display: none; }

/* ---- Nav toggle (hamburger) ----------------------------------
   Hidden above the collapse breakpoint, where .pb-header-nav is a plain row. Below it,
   this is what replaces that row's visibility — see the media query for the panel itself. */
.pb-nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.5rem -0.375rem -0.5rem auto;
  padding: 0;
  border: none;
  background: none;
  color: var(--ink-2);
  cursor: pointer;
}
.pb-nav-toggle:hover { color: var(--ink); }

.pb-nav-toggle-icon { width: 1.4rem; height: 1.4rem; }

/* ---- Theme toggle ------------------------------------------- */

/* 2.75rem (44px) box around a visually unchanged icon — same reasoning as
   .pb-header-link: a real touch target without changing how it looks. */
.pb-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.5rem -0.375rem -0.5rem 0.35rem;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s;
}
.pb-theme-toggle:hover { color: var(--accent-dim); }

.pb-theme-icon { width: 1.05rem; height: 1.05rem; display: block; }

/* Sun shows while dark is active (click it to go light); moon shows while light is
   active (click it to go dark) — the icon names the theme a click will switch to. */
.pb-theme-icon--moon { display: none; }
:root[data-theme="light"] .pb-theme-icon--sun { display: none; }
:root[data-theme="light"] .pb-theme-icon--moon { display: block; }

@media (max-width: 400px) {
  .pb-theme-toggle { margin-left: 0.45rem; }
}

/* ---- About page ------------------------------------------- */

.pb-about {
  width: 100%;
  max-width: var(--stage);
  padding-top: 0.5rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--space-4);
  align-items: start;
}

.pb-about-col {
  max-width: 560px;
}

.pb-about-heading {
  font-size: var(--fs-h1);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0 0 2.5rem;
}

.pb-about-body {
  font-size: var(--fs-lg);
  line-height: 1.8;
  color: var(--ink-2);
}

.pb-about-body p {
  margin: 0 0 1.4em;
}

.pb-about-body a {
  color: var(--accent-dim);
  text-decoration-color: var(--rule-2);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.2s;
}
.pb-about-body a:hover { text-decoration-color: var(--accent-dim); }

.pb-about-photo {
  width: 100%;
  height: 340px;
  object-fit: cover;
  display: block;
  border-radius: 2px;
}

.pb-about-photo-caption {
  margin-top: 0.6rem;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

@media (max-width: 860px) {
  .pb-about { grid-template-columns: 1fr; }
}

/* ---- Lightbox ----------------------------------------------
   Click any photo to view it large; step through the set with
   arrows, swipe, or the keyboard.
   ------------------------------------------------------------ */

.pb-image,
.pb-masonry-img {
  cursor: zoom-in;
}

body.pb-lightbox-open {
  overflow: hidden;
}

.pb-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 4, 8, 0);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, background 0.3s ease, visibility 0s linear 0.3s;
  touch-action: none;
}

.pb-lightbox.is-open {
  opacity: 1;
  visibility: visible;
  background: rgba(4, 4, 8, 0.96);
  transition: opacity 0.3s ease, background 0.3s ease;
}

.pb-lightbox-stage {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4.5rem 5.5rem;
}

.pb-lightbox-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.98);
  transition: opacity 0.3s ease, transform 0.3s ease;
  user-select: none;
  -webkit-user-drag: none;
}
.pb-lightbox-img.is-visible {
  opacity: 1;
  transform: scale(1);
}

.pb-lightbox-close,
.pb-lightbox-prev,
.pb-lightbox-next {
  position: absolute;
  z-index: 1;
  background: none;
  border: none;
  padding: 0;
  color: rgba(255, 255, 255, 0.65);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.18s ease, opacity 0.18s ease;
}
.pb-lightbox-close:hover,
.pb-lightbox-prev:hover,
.pb-lightbox-next:hover {
  color: #fff;
}
.pb-lightbox-close svg,
.pb-lightbox-prev svg,
.pb-lightbox-next svg {
  width: 100%;
  height: 100%;
}

.pb-lightbox-close {
  top: 1.25rem;
  right: 1.5rem;
  width: 1.4rem;
  height: 1.4rem;
}

.pb-lightbox-prev,
.pb-lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  width: 1.6rem;
  height: 1.6rem;
}
.pb-lightbox-prev { left: 1.25rem; }
.pb-lightbox-next { right: 1.25rem; }

.pb-lightbox--single .pb-lightbox-prev,
.pb-lightbox--single .pb-lightbox-next {
  display: none;
}

.pb-lightbox-info {
  position: absolute;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  max-width: 80%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  text-align: center;
  pointer-events: none;
}

.pb-lightbox-caption {
  margin: 0;
  font-size: var(--fs-base);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.85);
}

.pb-lightbox-counter {
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.4);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
  .pb-lightbox-stage { padding: 3.5rem 1rem; }
  .pb-lightbox-close {
    top: 0.9rem;
    right: 0.9rem;
  }
  .pb-lightbox-prev { left: 0.6rem; }
  .pb-lightbox-next { right: 0.6rem; }
  .pb-lightbox-info { bottom: 1rem; max-width: 90%; }
}

/* ---- Keyboard hint ---------------------------------------- */

.pb-keys {
  position: fixed;
  bottom: 1.75rem;
  right: 2.5rem;
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: var(--rule-2);
  pointer-events: none;
  user-select: none;
}

/* ---- Responsive ------------------------------------------- */

@media (max-width: 640px) {
  .pb-header {
    position: relative;
    padding: 1.1rem 1.25rem;
    gap: 0.75rem;
  }
  .pb-header-byline { display: none; }

  .pb-nav-toggle { display: inline-flex; }

  /* The row becomes a dropdown panel anchored under the header, closed until the
     hamburger opens it. Full header width, not the brandline's — a panel narrower than
     the header it drops from would look like a mistake, not a menu. */
  .pb-header-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    flex-wrap: nowrap;
    gap: 0.25rem;
    padding: 0.75rem 1.25rem 1.25rem;
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
    z-index: 50;
  }
  .pb-header-nav.is-open { display: flex; }

  .pb-header-link { padding: 0.6rem 0; margin: 0; width: 100%; }

  .pb-header-search {
    margin: 0.4rem 0 0;
    width: 100%;
  }
  .pb-header-search-input { width: 100%; }

  .pb-theme-toggle { margin: 0.25rem -0.375rem 0 -0.375rem; }

  /* Caption and pager stack instead of sharing a row. Side by side, .pb-pager's fixed
     content width (Prev/count/Next, flex: 0 0 auto) left whatever remained for the
     caption — as little as 46% of the row on a Pro Max-class phone (414-430px), well
     above the 400px this used to kick in at, since only sub-400px phones got the
     column layout that avoided the squeeze. */
  .pb-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
  }
  .pb-pager { align-self: flex-start; }
}

/* Narrowest phones. The weather is a flourish and the nav is the way out of the page, so
   when only one of them fits it is not the weather. */
@media (max-width: 400px) {
  .pb-header .weather-widget { display: none; }
  .pb-main {
    padding: 0.5rem 1.25rem 3rem;
  }

  .pb-keys { display: none; }

  /* The wordmark stays plain-case on a phone; only the size backs off. */
  .pb-site-name {
    font-size: var(--fs-sm);
    white-space: nowrap;
  }

  .pb-footer {
    gap: 0.75rem;
    padding: 1.5rem 0 2.5rem;
  }
  .pb-footer-nav { gap: 1.1rem; }
}

/* ================================================================
   Home, archive grid, and tag pages
   ================================================================ */

/* ---- Sticky archive/tag nav --------------------------------- */

.pb-archive-nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg-blur);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
}

/* ---- Tag strip ------------------------------------------------
   Wraps instead of scrolling sideways — a scrolling row hid most of the tags behind a drag
   gesture with no visible affordance that there was more. Wrapping means every tag is on
   screen and scannable at once, at the cost of the strip taking more vertical space. */

.pb-tag-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 0.5rem;
  column-gap: 0.9rem;
  padding: 0.65rem 2.5rem;
  border-top: 1px solid var(--rule);
}

.pb-tag-strip--standalone {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg-blur);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
  border-top: none;
}

/* Names the row as a control rather than leaving it to read as a stray line of tags. */
.pb-tag-strip-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  margin-right: 0.15rem;
}

/* Words, not capsules. The border and the pill shape were doing the work of separating one
   tag from the next; letter-spacing and a little air do it without drawing twelve rounded
   rectangles across the top of a page whose subject is photographs. */
.pb-tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.22rem 0.15rem;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.pb-tag-pill:hover { color: var(--accent-dim); border-bottom-color: var(--rule-2); }
.pb-tag-pill.is-active { color: var(--accent-dim); border-bottom-color: var(--accent); }

.pb-tag-pill-count {
  font-size: var(--fs-xs);
  color: var(--muted);
  transition: color 0.15s;
}
.pb-tag-pill.is-active .pb-tag-pill-count,
.pb-tag-pill:hover .pb-tag-pill-count { color: var(--muted); }

/* ---- Archive main ----------------------------------------- */

/* This is what actually wraps .pb-grid on the index, full archive, and tag pages — the
   .pb-section-head + .pb-grid rule below it never matched anything, since .pb-grid is this
   element's child, not .pb-section-head's adjacent sibling. Constraining it here is what
   makes the contact sheet line up with the hero and the archive list above it. */
.pb-archive-main {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 1.5rem 5rem;
}

/* ---- Photo grid — the contact sheet -------------------------
   Five columns with real air between them — the mockup's contact sheet is a grid of
   separate prints, not the seamless mosaic the old light theme used. */

.pb-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-3) var(--space-2);
}

/* A caption line under each frame, always visible — not a hover overlay. That was the
   old light theme's pattern; 3a's actual contact sheet prints a metadata line under
   every thumbnail the way a real contact sheet does. */
.pb-grid-item {
  display: block;
  text-decoration: none;
  cursor: pointer;
}

.pb-grid-frame {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--bg-sunk);
}

.pb-grid-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  transition: opacity 0.35s ease, transform 0.45s ease;
  will-change: transform, opacity;
}
.pb-grid-img.is-loaded { opacity: 1; }
.pb-grid-item:hover .pb-grid-img { transform: scale(1.05); }

.pb-grid-item--error .pb-grid-frame { background: var(--bg-surface); }

.pb-grid-caption {
  margin-top: 0.6rem;
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.15s;
}
.pb-grid-item:hover .pb-grid-caption { color: var(--accent-dim); }

/* ---- Tag page header -------------------------------------- */

.pb-tag-page-header {
  padding: 1.75rem 2.5rem 0.25rem;
}

.pb-tag-page-back {
  font-size: var(--fs-xs);
  color: var(--muted);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.6rem;
  transition: color 0.15s;
}
.pb-tag-page-back:hover { color: var(--accent-dim); }

/* The archive is a wall of photographs; the heading only has to name it. A 32px bold title
   above a grid of pictures is the page shouting over its own content. */
.pb-tag-page-title {
  font-size: var(--fs-md);
  font-weight: 550;
  color: var(--ink);
  margin: 0 0 0.2rem;
  letter-spacing: -0.01em;
  line-height: 1.3;
}

.pb-tag-page-count {
  font-size: var(--fs-sm);
  color: var(--muted);
  margin: 0 0 0.5rem;
}

/* ---- Archive pagination ------------------------------------- */

.pb-archive-pager {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 2rem 1.5rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Paired with .btn.btn-secondary in markup — this just adds the tracked-caps treatment
   the rest of the nav uses. */
.pb-archive-pager-link {
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.pb-archive-pager-link[aria-disabled="true"] {
  visibility: hidden;
}

.pb-archive-pager-status {
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ================================================================
   Homepage — hero, contact sheet, recent list
   ================================================================ */

.pb-index-header {
  padding: 1.75rem 2.5rem 0.25rem;
}

.pb-hero {
  display: block;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 1.5rem;
  text-decoration: none;
}

/* Width follows the photograph's own shape, the same rule as .pb-row on the detail page:
   --ar is written by the view, and a vertical stops at --frame-h tall instead of filling
   the column. The 99 fallback means "do not narrow" when dimensions aren't recorded. */
.pb-hero-img {
  width: 100%;
  max-width: calc(var(--frame-h) * var(--ar, 99));
  margin: 0 auto;
  object-fit: contain;
  background: var(--bg-sunk);
  display: block;
}

.pb-hero-meta {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--space-3) 1.5rem var(--space-4);
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  grid-template-areas:
    "text exif"
    "body body";
  column-gap: var(--space-4);
  align-items: start;
}
.pb-hero-meta > .pb-hero-text { grid-area: text; min-width: 0; }
.pb-hero-meta > .pb-hero-exif { grid-area: exif; }
.pb-hero-meta > .pb-hero-text-body { grid-area: body; width: 100%; min-width: 0; }

.pb-hero-body {
  margin-top: 0.75rem;
}

.pb-hero-kicker {
  font-size: 0.65625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-dim);
}

/* Now an <h1> (see Index.cshtml) rather than a div, for the homepage's sole real heading —
   reset to a plain weight and zero side/bottom margin so it still reads exactly as it did
   as a div, not as a browser-default bold heading. */
.pb-hero-caption {
  margin: 0.5rem 0 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 620px;
}

.pb-hero-exif {
  font-size: 0.65625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: right;
  line-height: 1.8;
}

@media (max-width: 760px) {
  .pb-hero-meta {
    grid-template-columns: 1fr;
    grid-template-areas:
      "text"
      "body"
      "exif";
    row-gap: 0.75rem;
  }
  .pb-hero-exif { text-align: left; }
}

.pb-section-head {
  max-width: var(--shell);
  margin: 0 auto var(--space-3);
  padding: var(--space-3) 1.5rem 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.pb-section-label {
  font-size: 0.65625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- Recent list — a thumbnail and a line per row -------------- */

.pb-row-list {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 1.5rem;
  display: flex;
  flex-direction: column;
}

/* A thumbnail on every row, not text alone — this is a photoblog, so even the link-list
   rows carry an image. */
.pb-row-list-item {
  display: grid;
  grid-template-columns: 48px 90px minmax(0, 1fr) 200px 44px;
  gap: var(--space-3);
  align-items: center;
  padding: 0.6rem 0.4rem;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  text-decoration: none;
  transition: background-color 0.15s, color 0.15s;
}
.pb-row-list-item:hover {
  background: var(--accent-tint);
  color: var(--accent-dim);
}

.pb-row-list-thumb {
  width: 48px;
  height: 48px;
  object-fit: cover;
  background: var(--bg-sunk);
  display: block;
}

.pb-row-list-date,
.pb-row-list-place {
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.pb-row-list-count {
  text-align: right;
  color: var(--rule-2);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 760px) {
  .pb-row-list-item { grid-template-columns: 40px 70px minmax(0, 1fr) 32px; }
  .pb-row-list-thumb { width: 40px; height: 40px; }
  .pb-row-list-place { display: none; }
}

/* Wraps the "Older frames" button at the shell's own width/padding so its left edge
   lines up with everything above it, rather than the raw viewport edge. */
.pb-older-wrap {
  max-width: var(--shell);
  margin: var(--space-4) auto 0;
  padding: 0 1.5rem;
}

/* Paired with .btn.btn-secondary in markup — "Older frames" is a secondary action,
   not the primary one on this page (the contact sheet above it is). */
.pb-older-link {
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---- Grid/list responsive ------------------------------------ */

@media (max-width: 960px) {
  .pb-grid { grid-template-columns: repeat(3, 1fr); }
  /* The homepage sheet holds 20 frames, which fills 5 columns. Drop the last two so the
     3- and 2-column layouts (18 frames) don't end on a short row either. */
  .pb-grid--home .pb-grid-item:nth-child(n+19) { display: none; }
}

@media (max-width: 640px) {
  .pb-tag-strip { padding: 0.6rem 1.25rem; }
  .pb-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-2); }
  .pb-tag-page-header { padding: 1.25rem 1.25rem 0; }
  .pb-tag-page-title { font-size: var(--fs-title); }
  .pb-hero,
  .pb-hero-meta,
  .pb-section-head,
  .pb-row-list,
  .pb-older-wrap,
  .pb-archive-main,
  .pb-archive-pager { padding-left: 1.25rem; padding-right: 1.25rem; }
}
