/* Public pages: photostream, albums, album, photo, tag. Admin pages keep
   Bootstrap (layouts/main.njk); nothing here touches them. */

:root {
  --bg: #ffffff;
  --fg: #212124;
  --muted: #6b6b70;
  --line: #e4e4e7;
  --link: #0a66c2;
  --chip: #f0f0f2;
  --bar: #121214;
  --bar-fg: #f4f4f5;
  --bar-muted: #a1a1aa;
  --stage: #0d0d0e;
  --tile-bg: #e9e9ec;
  --row-h: 240px;
  --gap: 4px;
  --gutter: 16px;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #18181b;
    --fg: #ececee;
    --muted: #a1a1aa;
    --line: #2e2e33;
    --link: #6cb2ff;
    --chip: #27272a;
    --tile-bg: #26262a;
  }
}

@media (max-width: 900px) { :root { --row-h: 180px; } }
@media (max-width: 560px) { :root { --row-h: 130px; --gap: 2px; } }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 15px/1.5 var(--font);
}
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2 { line-height: 1.2; }

/* ---------------------------- top bar ---------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bar);
  color: var(--bar-fg);
}
.topbar-inner {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 52px;
  padding: 0 var(--gutter);
}
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  color: var(--bar-fg);
  font-weight: 650;
  font-size: 17px;
  white-space: nowrap;
}
.brand-mark { font-size: 20px; font-weight: 700; letter-spacing: 0.02em; }
/* The mark never gives up its room to the tabs on a narrow screen */
.brand-mark, .brand-logo { flex: none; }
.brand-logo { display: block; height: 32px; width: auto; }
.brand-title { overflow: hidden; text-overflow: ellipsis; }
.brand:hover { text-decoration: none; }
.tabs { display: flex; gap: 4px; height: 100%; }
.tabs a {
  display: flex;
  align-items: center;
  padding: 0 12px;
  color: var(--bar-muted);
  border-bottom: 3px solid transparent;
  border-top: 3px solid transparent;
}
.tabs a:hover { color: var(--bar-fg); text-decoration: none; }
.tabs a.active { color: var(--bar-fg); border-bottom-color: var(--bar-fg); }
.spacer { flex: 1; }
.login { color: var(--bar-fg); white-space: nowrap; }
.bar-link { color: var(--bar-fg); white-space: nowrap; padding: 6px 10px; border-radius: 6px; }
.bar-link:hover { background: #2a2a2e; text-decoration: none; }

.menu { position: relative; }
.menu summary {
  list-style: none;
  cursor: pointer;
  color: var(--bar-fg);
  padding: 6px 10px;
  border-radius: 6px;
  white-space: nowrap;
}
.menu summary::-webkit-details-marker { display: none; }
.menu summary::after { content: " \25BE"; color: var(--bar-muted); }
.menu[open] summary, .menu summary:hover { background: #2a2a2e; }
.menu-list {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 200px;
  padding: 6px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.menu-list a, .menu-list button {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--fg);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.menu-list a:hover, .menu-list button:hover { background: var(--chip); text-decoration: none; }
.menu-list hr { border: 0; border-top: 1px solid var(--line); margin: 6px 0; }
.menu-list form { margin: 0; }

@media (max-width: 560px) {
  .topbar-inner { gap: 10px; }
  .tabs a { padding: 0 8px; }
  .brand { font-size: 15px; }
}

/* ---------------------------- headings ---------------------------- */

.profile { padding: 28px var(--gutter) 18px; }
.profile h1 { margin: 0 0 4px; font-size: 30px; font-weight: 700; }
.counts { margin: 0; color: var(--muted); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.counts .dot { color: var(--muted); }
.eyebrow { margin: 0 0 6px; color: var(--muted); font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; }
.eyebrow a { color: inherit; }
.empty { padding: 40px var(--gutter); color: var(--muted); }
.notfound p { color: var(--muted); }

.button {
  display: inline-block;
  padding: 5px 14px;
  border: 1px solid currentColor;
  border-radius: 999px;
  color: inherit;
  font-size: 14px;
}
.button:hover { text-decoration: none; background: rgba(127, 127, 127, 0.15); }
button.button { background: none; font: inherit; font-size: 14px; cursor: pointer; }

/* ---------------------------- justified grid ----------------------------
   Each tile's flex-basis is its width at the row height, and it grows by an
   amount proportional to its aspect ratio, so every tile in a row gains the
   same height. The <i> holds the tile's shape; the image fills it. The
   ::after filler soaks up the last row's space, so it isn't stretched. */

.jgrid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
  padding: 0 var(--gutter);
}
.jgrid::after { content: ""; flex-grow: 999999999; }
.tile {
  position: relative;
  display: block;
  flex-grow: calc(var(--ar) * 100);
  flex-basis: calc(var(--ar) * var(--row-h));
  /* Never wider than the screen, and not blown up past twice the row height */
  max-width: 100%;
  max-height: calc(var(--row-h) * 2);
  overflow: hidden;
  background: var(--tile-bg);
}
.tile > i { display: block; }
.tile > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Pick stars, in a tile's upper left. Gold when picked; an unpicked star
   (only a customer picking sees those) is a faint outline until hovered. */
.pick {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 2;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  color: rgba(255, 255, 255, 0.85);
  cursor: default;
}
.pick svg, .pick-glyph svg { display: block; flex: none; width: 22px; height: 22px; fill: currentColor; }
.pick-glyph svg { width: 18px; height: 18px; }
.pick-glyph { display: inline-flex; vertical-align: -3px; }
.pick[role="button"] { cursor: pointer; opacity: 0.55; transition: opacity 0.15s, transform 0.1s; }
.tile:hover .pick[role="button"], .pick[role="button"]:focus-visible { opacity: 1; }
.pick[role="button"]:active { transform: scale(0.9); }
.pick.on { color: #ffc531; opacity: 1; background: rgba(0, 0, 0, 0.5); }
@media (hover: none) { .pick[role="button"] { opacity: 0.85; } }

.pick-button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 14px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  cursor: pointer;
}
.pick-button:hover { background: var(--chip); }
.pick-button.on { border-color: #e0a800; }
.pick-glyph { color: var(--muted); }
.pick-button.on .pick-glyph, .picked-by .pick-glyph { color: #e0a800; }
.picked-by { margin: 0 0 12px; color: var(--muted); }

.tile-title {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 24px 10px 8px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
  color: #fff;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transition: opacity 0.15s;
}
.tile:hover .tile-title, .tile:focus-visible .tile-title { opacity: 1; }
.tile:hover { text-decoration: none; }
/* A video's tile: a play mark and its length, top right */
.tile-video {
  position: absolute;
  top: 6px;
  right: 6px;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px 7px 3px 4px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 12px;
  line-height: 1;
  pointer-events: none;
}
.tile-video svg { width: 14px; height: 14px; fill: currentColor; }
@media (hover: none) { .tile-title { display: none; } }

.pager {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 520px;
  margin: 28px auto 40px;
  padding: 0 var(--gutter);
}
.pager-pos { color: var(--muted); }

/* ---------------------------- albums ---------------------------- */

/* Albums: the same justified rows as the photostream, so every cover keeps
   its own shape, with the title and count over it and wide gutters */
.album-grid {
  --album-h: 220px;
  display: flex;
  flex-wrap: wrap;
  gap: 32px 24px;
  padding: 8px var(--gutter) 48px;
}
.album-grid::after { content: ""; flex-grow: 999999999; }
.album-card {
  display: flex;
  flex-direction: column;
  flex-grow: calc(var(--ar) * 100);
  flex-basis: calc(var(--ar) * var(--album-h));
  max-width: 100%;
  min-width: 0;
  color: #fff;
}
.album-card:hover { text-decoration: none; }
.album-card-img {
  position: relative;
  display: block;
  /* Not stretched past half again the row height on a short last row */
  max-height: calc(var(--album-h) * 1.5);
  overflow: hidden;
  border-radius: 4px;
  background: var(--tile-bg);
}
.album-card-img > i { display: block; }
.album-card-img > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s;
}
.album-card:hover img { transform: scale(1.03); }
.album-card-text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  padding: 36px 12px 10px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.72));
  color: #fff;
}
/* Text over a photo: a black shadow keeps it readable on a light picture */
.album-card-text, .album-hero:not(.no-cover) .album-hero-text, .profile-banner h1 {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9), 0 0 6px rgba(0, 0, 0, 0.6);
}
.album-card-title {
  font-weight: 650;
  font-size: 16px;
  line-height: 1.25;
  /* At most two lines, ending "…"; the card's hover text has the whole title.
     A name with no spaces (20090119-air_and_space_museum) breaks anywhere
     rather than running off the side. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.album-card-count { margin-top: 2px; font-size: 13px; opacity: 0.85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 900px) { .album-grid { --album-h: 170px; gap: 28px 18px; } }
@media (max-width: 560px) { .album-grid { --album-h: 120px; gap: 24px 12px; } }

.album-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 300px;
  margin-bottom: 16px;
  overflow: hidden;
  background: #2a2a2e;
  color: #fff;
}
.album-hero.no-cover { min-height: 0; background: none; color: var(--fg); }
.album-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.album-hero:not(.no-cover)::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Flickr's banner gradient: clear through the top fifth, so the photo
     keeps its contrast, then easing dark behind the title */
  background: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 0.03) 8%, rgba(0, 0, 0, 0.11) 21%,
    rgba(0, 0, 0, 0.61) 78%, rgba(0, 0, 0, 0.7) 95%, rgba(0, 0, 0, 0.7));
}
.album-hero-text {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 900px;
  padding: 28px var(--gutter) 22px;
}
.album-hero h1 { margin: 0 0 6px; font-size: 34px; }
.album-hero .eyebrow, .album-hero:not(.no-cover) .counts { color: rgba(255, 255, 255, 0.85); }
.album-hero:not(.no-cover) a { color: #fff; }
.album-desc { margin: 0 0 10px; max-width: 70ch; }
/* The profile banner on the photostream and Albums pages. The gradient is
   Flickr's: clear at the top, easing to 70% black at the bottom. The subject
   sits left of center, so keep it in frame when a narrow screen crops. */
.profile-banner {
  position: relative;
  display: flex;
  align-items: flex-end;
  height: 280px;
  overflow: hidden;
  background: #000;
  color: #fff;
}
.profile-banner img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 30% 50%;
}
.profile-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 0.03) 8%, rgba(0, 0, 0, 0.11) 21%,
    rgba(0, 0, 0, 0.61) 78%, rgba(0, 0, 0, 0.7) 95%, rgba(0, 0, 0, 0.7));
}
.profile-banner h1 {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0 var(--gutter) 20px;
  font-size: 34px;
  font-weight: 700;
}
.banner-counts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 20px;
  padding: 14px var(--gutter) 18px;
}

/* An on/off switch that is really a link: the page reloads filtered */
.switch { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 14px; }
.switch:hover { text-decoration: none; color: var(--fg); }
.switch-track {
  position: relative;
  width: 34px;
  height: 20px;
  border-radius: 10px;
  background: rgba(127, 127, 127, 0.4);
  transition: background 0.15s;
}
.switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  transition: left 0.15s;
}
.switch.on { color: var(--fg); }
.switch.on .switch-track { background: var(--link); }
.switch.on .switch-knob { left: 16px; }
@media (max-width: 560px) {
  .profile-banner { height: 180px; }
  .profile-banner h1 { font-size: 26px; }
}
@media (max-width: 560px) {
  .album-hero { min-height: 220px; }
  .album-hero h1 { font-size: 26px; }
}

/* ---------------------------- photo page ---------------------------- */

.stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: calc(100vh - 52px);
  min-height: 320px;
  max-height: 1400px;
  background: var(--stage);
  overflow: hidden;
}
/* While the photo loads: the grid's smaller copy, blurred, which the browser
   usually has already, under a spinner (js/site.js sets .loading) */
.stage::before {
  content: "";
  position: absolute;
  inset: -20px;
  background: var(--preview) center / contain no-repeat;
  filter: blur(14px);
  opacity: 0;
  transition: opacity 0.3s;
}
.stage.loading::before { opacity: 0.55; }
.stage-spinner { position: absolute; top: 50%; left: 50%; margin: -22px 0 0 -22px; display: none; }
.stage.loading .stage-spinner { display: block; }
.stage-failed { position: absolute; z-index: 2; color: #eee; }
.stage-failed a { color: #fff; text-decoration: underline; }
.stage-img {
  position: relative;
  display: block;
  max-width: 100%;
  max-height: calc(100vh - 120px);
  width: auto;
  height: auto;
  object-fit: contain;
}
.stage-video { background: #000; max-width: 100%; }
.stage-note {
  position: absolute;
  bottom: 16px;
  left: 0;
  right: 0;
  z-index: 2;
  margin: 0;
  text-align: center;
  color: #eee;
  text-shadow: 0 1px 3px #000;
}
.stage-back, .stage-pos {
  position: absolute;
  top: 14px;
  z-index: 2;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.45);
  color: #e4e4e7;
  font-size: 14px;
}
.stage-back { left: 14px; }
.stage-back:hover { color: #fff; text-decoration: none; background: rgba(0, 0, 0, 0.7); }
.stage-pos { right: 14px; }
.stage-nav {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  width: 18%;
  max-width: 160px;
  padding: 0 16px;
  color: #fff;
  opacity: 0;
  transition: opacity 0.15s;
}
.stage:hover .stage-nav, .stage-nav:focus-visible { opacity: 0.85; }
.stage-nav:hover { opacity: 1; text-decoration: none; }
.stage-nav.prev { left: 0; justify-content: flex-start; }
.stage-nav.next { right: 0; justify-content: flex-end; }
.stage-nav span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  font-size: 34px;
  line-height: 1;
}
@media (hover: none) { .stage-nav { opacity: 0.8; } }
@media (max-width: 560px) {
  .stage { height: auto; min-height: 0; padding: 48px 0 12px; }
  .stage-img { max-height: 75vh; }
  .stage-nav { width: 22%; padding: 0 6px; }
  .stage-nav span { width: 36px; height: 36px; font-size: 26px; }
}

.photo-info {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 40px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 24px var(--gutter) 60px;
}
@media (max-width: 800px) { .photo-info { grid-template-columns: 1fr; gap: 16px; } }
.photo-main h1 { margin: 0 0 10px; font-size: 26px; overflow-wrap: anywhere; }
.photo-main h2, .photo-side h2 {
  margin: 24px 0 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.photo-desc { overflow-wrap: anywhere; }

.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 18px 0 0; padding: 0; list-style: none; }
.tags a {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--chip);
  color: var(--fg);
  font-size: 13px;
}
.tags a:hover { text-decoration: none; filter: brightness(0.95); }
.in-albums, .sizes { margin: 0; padding: 0; list-style: none; }
.in-albums li, .sizes li { padding: 3px 0; }

.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 16px;
  margin: 0;
}
.facts dt { color: var(--muted); }
.facts dd { margin: 0; }

/* ---------------------------- footer and text pages ---------------------------- */

.site-footer {
  display: flex;
  gap: 18px;
  justify-content: center;
  padding: 28px var(--gutter) 36px;
  font-size: 13px;
}
.site-footer a { color: var(--muted); }
.doc { max-width: 70ch; margin: 0 auto; padding: 28px var(--gutter) 8px; line-height: 1.55; }
.doc h1 { margin: 0 0 4px; font-size: 30px; }
.doc h2 { margin: 28px 0 6px; font-size: 19px; }
.about-img { display: block; max-width: 100%; height: auto; margin: 4px 0; }
.doc p { margin: 0 0 12px; }
.doc-date { color: var(--muted); font-size: 14px; }

/* ---------------------------- comments ---------------------------- */

.comments { margin-top: 24px; }
.comment { padding: 12px 0; border-top: 1px solid var(--line); }
.comment header { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.comment-when { color: var(--muted); font-size: 13px; }
.comment-body { margin-top: 4px; overflow-wrap: anywhere; }
.comment-actions { display: flex; gap: 14px; align-items: flex-start; margin-top: 6px; font-size: 13px; }
.comment-actions summary { cursor: pointer; color: var(--link); }
.comment-actions form { margin: 6px 0 0; }
.comment-delete { margin: 0 !important; }
.comment-actions button, .comment-delete button {
  padding: 0;
  border: 0;
  background: none;
  color: var(--link);
  font: inherit;
  cursor: pointer;
}
.comment-form { display: flex; flex-direction: column; gap: 8px; padding-top: 14px; border-top: 1px solid var(--line); }
.comment-form label { font-weight: 600; }
.comments textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  resize: vertical;
}
.comment-form button, .comment-actions details button {
  align-self: flex-start;
  padding: 6px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--chip);
  color: var(--fg);
  font: inherit;
  cursor: pointer;
}
.comment-error { color: #c0392b; }

/* ---------------------------- installed app ---------------------------- */

/* The installed app draws under the phone's status bar and around the
   notch, so the bar and the page keep clear of them */
.topbar { padding-top: env(safe-area-inset-top); }
.topbar-inner, .site-footer {
  padding-left: max(var(--gutter), env(safe-area-inset-left));
  padding-right: max(var(--gutter), env(safe-area-inset-right));
}
.site-footer { padding-bottom: max(36px, env(safe-area-inset-bottom)); }

/* ---------------------------- notifications page ---------------------------- */

.notify-page section { margin-top: 24px; }
.notify-card { padding: 14px 16px; border: 1px solid var(--line); border-radius: 10px; }
.notify-card h2 { margin-top: 0; }
.notify-prefs fieldset { margin: 0 0 16px; padding: 0; border: 0; }
.notify-prefs legend { margin-bottom: 6px; font-weight: 650; }
.notify-prefs label { display: flex; gap: 8px; align-items: baseline; margin: 6px 0; }
.notify-albums { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0 16px; }
.notify-help, .notify-status { color: var(--muted); font-size: 14px; }
.notify-saved { color: #2e7d32; }
.button-solid, .button-plain {
  margin: 4px 8px 4px 0;
  padding: 8px 18px;
  border-radius: 999px;
  font: inherit;
  cursor: pointer;
}
.button-solid { border: 0; background: var(--link); color: #fff; }
.button-plain { border: 1px solid var(--line); background: var(--chip); color: var(--fg); }

/* ---------------------------- tags ---------------------------- */

/* The cloud, as the photoblog had it: A to Z, sized by how often a tag is used */
.tag-cloud { max-width: 70ch; padding: 0 var(--gutter) 40px; line-height: 1.9; }
.tag-cloud a { display: inline-block; margin: 0 0.6em 0 0; white-space: nowrap; }
.tag-count { color: var(--muted); font-size: 0.75em; }
.tag-list { columns: 16em; padding: 0 var(--gutter) 40px calc(var(--gutter) + 1.5em); }
.tag-list li { margin: 2px 0; break-inside: avoid; }
.tag-related { margin: 10px 0 0; color: var(--muted); font-size: 14px; }

/* ---------------------------- loading ---------------------------- */

/* A spinner for a picture on its way: on the photo page, and over a grid
   tile or album cover until its image arrives (js/site.js) */
.spinner {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.25);
  border-top-color: rgba(255, 255, 255, 0.9);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.tile.loading::after, .album-card-img.loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  border: 3px solid rgba(127, 127, 127, 0.25);
  border-top-color: rgba(127, 127, 127, 0.8);
  animation: spin 0.9s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .spinner, .tile.loading::after, .album-card-img.loading::after { animation-duration: 3s; }
}

/* A customer's own album, seen by the owner from the library: whose it is */
.album-card-owner {
  align-self: flex-start;
  margin-top: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  background: #e0a800;
  color: #000;
  font-size: 12px;
  font-weight: 650;
  text-shadow: none;
}
.album-owner {
  display: inline-block;
  margin: 0 0 8px;
  padding: 2px 10px;
  border-radius: 999px;
  background: #e0a800;
  color: #000;
  font-size: 14px;
  font-weight: 600;
  text-shadow: none;
}
.album-owner a { color: #000 !important; text-decoration: underline; }

/* ---------------------------- editing in place ---------------------------- */

.edit-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 12px var(--gutter);
  border-bottom: 1px solid var(--line);
}
.edit-add { cursor: pointer; }
.edit-arrange-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: 100%; }
.edit-arrange-tools[hidden] { display: none; }
.edit-uploads { width: 100%; margin: 0; padding: 0; list-style: none; font-size: 14px; }
.edit-uploads li { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; border-top: 1px solid var(--line); }
.edit-uploads li:first-child { border-top: 0; }
.edit-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.edit-upload-state { color: var(--muted); white-space: nowrap; }
.edit-uploads li.done .edit-upload-state { color: #2e7d32; }
.edit-uploads li.failed .edit-upload-state { color: #c0392b; white-space: normal; }
.edit-upload-summary { justify-content: flex-start !important; font-weight: 600; }
.edit-details { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 40em; }
.edit-details[hidden] { display: none; }
.edit-details label, .edit-photo label { display: flex; flex-direction: column; gap: 4px; font-weight: 600; }
.edit-details label.edit-check { flex-direction: row; align-items: center; gap: 8px; font-weight: 400; }
.edit-details input, .edit-details textarea, .edit-photo input, .edit-photo textarea, .edit-photo select {
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-weight: 400;
}
.edit-help, .edit-status { margin: 0; color: var(--muted); font-size: 13px; }

/* Arranging: tiles are selected by a tap, and carry move arrows */
body.arranging .tile { outline: 3px solid transparent; outline-offset: -3px; }
body.arranging .tile.selected { outline-color: var(--link); }
body.arranging .tile.selected::before {
  content: "\2713";
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 3;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--link);
  color: #fff;
  font-weight: 700;
  line-height: 26px;
  text-align: center;
}
body.arranging .tile.busy { opacity: 0.5; }
.edit-move { display: none; }
body.arranging .edit-move {
  position: absolute;
  bottom: 6px;
  left: 50%;
  z-index: 3;
  display: flex;
  gap: 8px;
  transform: translateX(-50%);
}
.edit-move button {
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

/* The photo page's edit panel */
.edit-photo { margin: 0 0 18px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; }
.edit-photo summary { cursor: pointer; font-weight: 650; }
.edit-photo form { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.edit-buttons { display: flex; flex-wrap: wrap; gap: 4px; }
.edit-danger { color: #c0392b; }

/* ---------------- slideshow (js/slideshow.js) ---------------- */

body.slideshow-open { overflow: hidden; }
.slideshow {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #000;
  color: #f4f4f5;
  user-select: none;
  -webkit-user-select: none;
}
.slideshow-stage { position: absolute; inset: 0; }
.slideshow-layer {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.slideshow-layer.on { opacity: 1; }
.slideshow-layer img { max-width: 100%; max-height: 100%; object-fit: contain; }
.slideshow-card, .slideshow-start {
  max-width: 34em;
  padding: 24px;
  text-align: center;
}
.slideshow-start {
  position: absolute;
  inset: 0;
  max-width: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #000;
  z-index: 2;
}
.slideshow-card h2, .slideshow-start h2 { margin: 0 0 12px; font-size: clamp(28px, 6vw, 56px); font-weight: 300; letter-spacing: 0.02em; }
.slideshow-desc { margin: 6px 0; color: #d4d4d8; font-size: clamp(16px, 2.4vw, 22px); }
.slideshow-by { margin: 18px 0 6px; color: #a1a1aa; font-size: clamp(14px, 2vw, 18px); }
.slideshow-go { margin-top: 24px; font-size: 18px; padding: 10px 34px; }
.slideshow-caption {
  position: absolute;
  left: 16px;
  bottom: 64px;
  margin: 0;
  max-width: 70%;
  font-size: 14px;
  color: #e4e4e7;
  text-shadow: 0 1px 3px #000;
  transition: opacity 0.4s;
}
.slideshow-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
  transition: opacity 0.4s;
}
.slideshow.idle .slideshow-bar, .slideshow.idle .slideshow-caption { opacity: 0; }
.slideshow.idle { cursor: none; }
.slideshow-btn {
  min-width: 44px;
  height: 44px;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font: inherit;
  font-size: 22px;
  cursor: pointer;
}
.slideshow-btn:hover { background: rgba(255, 255, 255, 0.24); }
.slideshow-play { font-size: 15px; }
.slideshow-count { flex: 1; padding-left: 8px; color: #a1a1aa; font-size: 13px; }
.slideshow-close { margin-left: auto; }

/* Prints: the old jorj.org player's look. A white border (5px on a 600px
   print there, so a little under 1% of the short side here), a solid black
   drop shadow down and to the right (10px offset and blur on a 600px print
   there, so about 1.7%), on dark gray. The tilt is set per slide. */
.slideshow-prints, .slideshow-prints .slideshow-start { background: #3a3a3a; }
.slideshow-print {
  padding: clamp(5px, 0.9vmin, 12px);
  background: #fff;
  box-shadow: clamp(8px, 1.4vmin, 24px) clamp(8px, 1.4vmin, 24px) clamp(8px, 1.4vmin, 24px) #000;
  line-height: 0;
}
.slideshow-print img {
  display: block;
  max-width: calc(100vw - 72px);
  max-height: calc(100vh - 150px);
  max-height: calc(100dvh - 150px);
}
.slideshow-print .slideshow-card {
  box-sizing: border-box;
  width: min(calc(100vw - 72px), calc(100dvh - 150px), 640px);
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.3;
  color: #111;
  font-family: Didot, "Bodoni 72", "Bodoni MT", Georgia, serif;
}
.slideshow-print .slideshow-card h2 { font-weight: 400; }
.slideshow-print .slideshow-desc, .slideshow-print .slideshow-by { color: #333; }
.slideshow-prints .slideshow-start h2 { font-family: Didot, "Bodoni 72", "Bodoni MT", Georgia, serif; font-weight: 400; }
.slideshow-progress {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top));
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  color: #fff;
  font-size: 14px;
  transition: opacity 0.4s;
}
.slideshow-prints .slideshow-caption { color: #d4d4d8; }

/* Title and credits over the first photo (titleOnPhoto), laid out like the
   old jorj.org cards. Sizes are in cqw, a share of the photo's width, so the
   text keeps its place on the photo at any size. Dark text with a light
   halo, for the light backgrounds those cards had. */
/* The box has to be given its width (a container's width can't come from
   its contents), so it's worked out from the photo's shape, --aspect, set by
   js/slideshow.js: as wide as fits the same space a photo slide gets. */
.slideshow-titled { position: relative; container-type: inline-size; line-height: normal; width: min(100vw, 100dvh * var(--aspect, 1)); }
.slideshow-print .slideshow-titled { width: min(100vw - 72px, (100dvh - 150px) * var(--aspect, 1)); }
.slideshow-titled img, .slideshow-print .slideshow-titled img { display: block; width: 100%; height: auto; max-width: none; max-height: none; }
.slideshow-overlay {
  position: absolute;
  inset: 0;
  padding: 5cqw 6cqw;
  color: #111;
  font-family: Didot, "Bodoni 72", "Bodoni MT", Georgia, serif;
  text-align: center;
  text-shadow: 0 0 0.35em rgba(255, 255, 255, 0.85);
}
.slideshow-overlay-title { margin: 0; font-size: 11cqw; font-weight: 400; line-height: 1.1; }
.slideshow-overlay-sub { margin: 2cqw 0 0; font-size: 4.6cqw; }
.slideshow-overlay-foot {
  position: absolute;
  left: 8cqw;
  bottom: 9cqw;
  max-width: 60cqw;
  text-align: center;
  font-size: 3.8cqw;
}
.slideshow-overlay-foot p { margin: 0 0 1cqw; }

/* ---------------- a blog-style album (views/blog.njk, views/post.njk) ----------------
   After the old WordPress photoblog's theme (Polite): Prata for the masthead
   and titles, Mulish (it was called Muli then) for everything else, gray
   text, red tags, light gray dates, white throughout. Both fonts are served
   from here (static/fonts, with their licenses), not from Google. */

@font-face {
  font-family: "Prata";
  src: url("/static/fonts/prata-latin.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Mulish";
  src: url("/static/fonts/mulish-latin.woff2") format("woff2");
  font-weight: 200 900;
  font-display: swap;
}

body.blog {
  --blog-text: #4c4f53;
  --blog-tag: #d42929;
  --blog-date: #989ea6;
  --blog-panel: #f9f9f9;
  --blog-line: #ececec;
  background: #fff;
  color: var(--blog-text);
  font-family: "Mulish", var(--font);
}
/* The top bar goes quiet: the sections are in the masthead */
body.blog .topbar { background: #fff; color: #222; border-bottom: 1px solid var(--blog-line); }
body.blog .topbar .tabs { display: none; }
body.blog .topbar a, body.blog .topbar summary { color: #222; }
body.blog .site-footer { border-top: 1px solid var(--blog-line); }
body.blog a { color: inherit; }

.masthead { padding: 48px var(--gutter) 40px; text-align: center; border-bottom: 1px solid var(--blog-line); }
.masthead-title { font-family: "Prata", Georgia, serif; font-size: clamp(26px, 4vw, 32px); color: #000; }
.masthead-title:hover { text-decoration: none; }
.masthead-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 40px;
  padding: 18px var(--gutter);
  border-bottom: 1px solid var(--blog-line);
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.masthead-nav a { color: #222; }

.blog-page { max-width: 1140px; margin: 0 auto; padding: 40px var(--gutter) 24px; }
.post-tags { margin: 0 0 10px; font-size: 14px; color: var(--blog-tag); }
.tag-heading { margin: 0 0 32px; font-family: "Prata", Georgia, serif; font-weight: 400; font-size: 28px; color: #000; }
.tag-heading span { color: var(--blog-tag); }
/* The tags page and About, in the blog's column and type */
body.blog .profile, body.blog .tag-cloud, body.blog .tag-list { max-width: 1140px; margin-left: auto; margin-right: auto; box-sizing: border-box; }
body.blog .profile { padding-top: 40px; }
body.blog .profile h1, body.blog .doc h1 { font-family: "Prata", Georgia, serif; font-weight: 400; color: #000; }
body.blog .counts { color: var(--blog-date); }
body.blog .tag-cloud a { color: var(--blog-text); }
body.blog .doc { font-size: 16px; line-height: 1.75; }
body.blog .post-tags a { color: var(--blog-tag); }
.post-title { margin: 0 0 14px; font-family: "Prata", Georgia, serif; font-weight: 400; font-size: 24px; line-height: 1.6; color: #000; }
.post-title a { color: #000; }
.post-date { margin: 0 0 14px; font-size: 12px; color: var(--blog-date); }
.post-excerpt { margin: 0 0 14px; font-size: 16px; line-height: 1.7; }

/* The newest post: its picture beside a panel with its words */
.feature { display: grid; grid-template-columns: 3fr 2fr; margin-bottom: 48px; background: var(--blog-panel); }
.feature-img { display: block; background: #eee; }
.feature-img img { display: block; width: 100%; height: 100%; max-height: 520px; object-fit: cover; }
.feature-text { align-self: center; padding: 40px 48px; }
.feature .post-title { font-size: 28px; }
.read-more { display: inline-flex; align-items: center; gap: 12px; text-decoration: underline; color: #000; }
.read-more::before { content: ""; width: 40px; border-top: 1px solid currentColor; }

/* The rest: cards in three columns, each as tall as it needs (masonry) */
.cards { columns: 3 300px; column-gap: 30px; }
.card { break-inside: avoid; margin: 0 0 40px; }
.card-img { position: relative; display: block; background: #f1f1f1; aspect-ratio: var(--ar, 1.5); }
.card-img img { display: block; width: 100%; height: 100%; object-fit: cover; }
.card-text { padding: 22px 15px 0; }

/* A post */
.post { max-width: 800px; margin: 0 auto; padding: 24px var(--gutter) 32px; }
.crumbs { margin: 0 0 20px; font-size: 15px; color: var(--blog-text); }
.crumbs a { color: var(--blog-text); }
.post-photo { margin: 0 0 24px; }
.post-photo img, .post-photo video { display: block; width: 100%; height: auto; }
.post-photo video { background: #000; }
.post .post-title { font-size: 30px; line-height: 1.4; }
.post-body { font-size: 16px; line-height: 1.75; }
.post-body p { margin: 0 0 1.1em; }
.post-picture { margin: 1.6em 0; }
.post-picture img { display: block; width: 100%; height: auto; }
.post-nav { display: flex; margin: 32px 0; padding: 16px 0; border-top: 1px solid var(--blog-line); border-bottom: 1px solid var(--blog-line); }
.post-nav .spacer { flex: 1; }
.post .comments { margin-top: 32px; }

@media (max-width: 760px) {
  .feature { grid-template-columns: 1fr; }
  .feature-text { padding: 24px 20px; }
  .masthead { padding: 32px var(--gutter) 28px; }
}
