/* ============================================================
   DANIEL GRÜNFELD — client galleries
   Same look as the portfolio: Schibsted Grotesk, white paper,
   hairlines, classic link blue, red for whatever is "on". One
   family throughout; the hierarchy comes from weight and size.
   Nothing is loaded from another server.
   ============================================================ */

/* The font is taken from this folder if it is there, otherwise from the
   portfolio next door. So the app works inside the site and, with the four
   .woff2 files copied into galleries/assets/fonts/, also on its own.
   Variable fonts: each file holds every weight from 400 to 700. */
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/schibsted-grotesk-latin.woff2") format("woff2"),
       url("../../../assets/fonts/schibsted-grotesk-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/schibsted-grotesk-latin-ext.woff2") format("woff2"),
       url("../../../assets/fonts/schibsted-grotesk-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/schibsted-grotesk-italic-latin.woff2") format("woff2"),
       url("../../../assets/fonts/schibsted-grotesk-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/schibsted-grotesk-italic-latin-ext.woff2") format("woff2"),
       url("../../../assets/fonts/schibsted-grotesk-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --ink: #111111;
  --paper: #ffffff;
  --blue: #0000EE;
  --red: #e02b20;
  --mute: #555555;
  --hair: #d9d9d9;
  --ui: "Schibsted Grotesk", "Helvetica Neue", Arial, sans-serif;
  /* The name is from the typewriter years; it is kept so the many rules
     that use it need not change. It is the same family as everything else. */
  --mono: var(--ui);
  --code: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  --gap: 14px;
  /* height a row of photos aims for; the rows are justified around it */
  --row-h: 300px;
  /* what lies over what, lowest first */
  --z-sticky: 5;
  --z-lightbox: 10;
  --z-dialog: 20;
  /* room the sticky bar takes, so a jump to #p-… or #sec-… does not land under it */
  --bar-room: 72px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

body {
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--blue); color: #fff; }

a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { background: var(--blue); color: #fff; text-decoration: none; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}

img { max-width: 100%; height: auto; }

.mono { font-family: var(--ui); font-size: 14px; }
.small { font-size: 14px; }

/* ---------- Frame ---------- */

.wrap {
  max-width: 1480px;
  margin: 0 auto;
  padding: 30px 40px 120px;
}

.top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 48px;
}

.brand a, .brand span:first-child {
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
}
.brand a:hover { background: none; color: var(--blue); }
.brand .role {
  display: block;
  font-size: 13px;
  color: var(--mute);
  margin-top: 2px;
}

.tools { color: var(--mute); }
.tools a { white-space: nowrap; }

.topline {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  font-size: 13px;
  color: var(--mute);
  border-bottom: 1px solid var(--hair);
  padding-bottom: 14px;
  margin: 0 0 48px;
}
.topline span:first-child { color: var(--ink); font-weight: 500; }

main { min-width: 0; }

.notice { font-size: 17px; color: var(--mute); margin: 40px 0; }
.notice.small { font-size: 14px; margin-top: 56px; }

/* ---------- Front page: one cover per gallery ---------- */

/* A grid, every cover cropped to the same 3:2 shape, so the rows and
   captions line up whether the picture is landscape or portrait.
   "cover-focus:" in info.txt moves the crop (object-position). */
.covers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 56px var(--gap);
}
.cover {
  display: block;
  text-decoration: none;
  color: var(--ink);
}
.covers img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  object-position: 50% 50%;
  background: #ececec;
  outline: 2px solid transparent;
  outline-offset: 3px;
  transition: outline-color 0.15s ease-out;
}
.cover:hover { background: none; }
.cover:hover img { outline-color: var(--blue); }
.cover .cap { display: block; margin-top: 14px; }
/* the gallery's name is what a client looks for, so it is the largest line */
.cover .cap .t {
  display: block;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--ink);
  text-wrap: balance;
}
.cover:hover .cap .t { color: var(--blue); }
.cover .cap .s { display: block; font-size: 14px; color: var(--mute); margin-top: 4px; font-variant-numeric: tabular-nums; }
.cover .nocover {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 3 / 2;
  border: 1px solid var(--hair);
  color: var(--mute);
  font-size: 14px;
}

/* ---------- Head of a gallery: title page with the cover ---------- */

.ghead {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 56px;
  align-items: center;
  margin: 8px 0 64px;
  padding-bottom: 40px;
  border-bottom: 1px solid var(--ink);
}
.ghead.no-cover { grid-template-columns: minmax(0, 1fr); }
.ghead .facts {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mute);
  margin-bottom: 18px;
  font-variant-numeric: tabular-nums;
}
.ghead h1 {
  font-weight: 600;
  font-size: clamp(40px, 5.4vw, 80px);
  line-height: 0.98;
  letter-spacing: -0.04em;
  text-wrap: balance;
  overflow-wrap: break-word;
}
.ghead .note { margin-top: 26px; max-width: 46ch; }
.ghead .note p { font-size: 18px; line-height: 1.55; color: #2a2a2a; margin-bottom: 0.8em; }
.ghead .note p:last-child { margin-bottom: 0; }
.ghead .gcover { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: #ececec; }

.download {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 32px;
}
.download .meta { color: var(--mute); }

.favs { color: var(--mute); margin-top: 18px; }
.favs a { color: var(--blue); }

/* ---------- Buttons ---------- */

.button {
  display: inline-block;
  font-family: var(--ui);
  font-size: 15px;
  font-weight: 500;
  color: #fff;
  background: var(--blue);
  border: 1px solid var(--blue);
  padding: 12px 22px;
  text-decoration: none;
  cursor: pointer;
}
.button:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.button[disabled] { color: var(--mute); background: none; border-color: var(--hair); cursor: default; }
.button[disabled]:hover { background: none; color: var(--mute); border-color: var(--hair); }

.plain {
  font-family: var(--ui);
  font-size: 14px;
  background: none;
  border: none;
  color: var(--blue);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  padding: 4px 6px;
  margin-left: 8px;
}
.plain:hover { background: var(--blue); color: #fff; text-decoration: none; }

/* ---------- Tabs (a gallery with several picture folders) ---------- */

/* With JavaScript the tabs switch between the parts. Without it they are
   plain links down to the parts, which are then all visible at once. */
.tabs {
  display: flex;
  gap: 4px 28px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 8px;
  margin-bottom: 44px;
}

.tab {
  font-size: 15px;
  font-weight: 500;
  color: var(--mute);
  text-decoration: none;
  padding: 6px 2px;               /* a finger-sized target, not just the word */
}
.tab .n { color: var(--mute); margin-left: 8px; font-size: 13px; font-weight: 400; font-variant-numeric: tabular-nums; }
.tab:hover { background: none; color: var(--blue); }
.tab:hover .n { background: none; color: var(--blue); }
/* The open tab: dark and underlined, so it reads as "you are here" and not
   as one more link. */
.tabs-on .tab.is-on { color: var(--ink); box-shadow: inset 0 -2px 0 var(--ink); }
.tabs-on .tab.is-on:hover { color: var(--ink); }
/* "only the marked ones" is showing: no single tab is open */
.show-marked .tab.is-on { color: var(--mute); box-shadow: none; }

/* The bar that stays at the top while scrolling: the parts of a gallery
   and the marked filter, or the dashboard's tabs. On a gallery of 400
   pictures the way to the other part should not be 400 pictures up. */
.bar-sticky {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--paper);
  padding-top: 12px;
}
.gbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 4px 24px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 10px;
  margin-bottom: 28px;
}
.gbar .tabs { border: none; padding: 0; margin: 0; }
.gbar .tabs:only-child { flex: 1; }

/* "marked only 12": shows only the pictures the client has marked. Red,
   like everything that has to do with marking. */
.only-marked {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding: 6px 2px;
  font-family: var(--ui);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--red);
  background: none;
  border: none;
  cursor: pointer;
}
.only-marked[hidden] { display: none; }
.only-marked .h { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.only-marked .n { color: var(--mute); font-weight: 400; font-variant-numeric: tabular-nums; }
.only-marked:hover { color: var(--ink); }
.only-marked[aria-pressed="true"] { box-shadow: inset 0 -2px 0 var(--red); }
.only-marked[aria-pressed="true"] .h { fill: currentColor; }
.only-marked[aria-pressed="true"] .n { color: var(--red); }
.only-marked[disabled] { color: var(--mute); cursor: default; }
.only-marked[disabled]:hover { color: var(--mute); }

/* With the filter on, every part is open and only the marked pictures in
   it are left; a part with none marked disappears with its name. */
.show-marked .shots[hidden] { display: block; }
.show-marked .shot-wrap:not(.is-liked) { display: none; }
.show-marked .shots:not(:has(.shot-wrap.is-liked)) { display: none; }
.show-marked.tabs-on .sec-name { display: block; }
body.show-marked .shots + .shots { margin-top: 56px; }

.shots, .shot-wrap { scroll-margin-top: var(--bar-room); }
.shots[hidden] { display: none; }
.shots + .shots { margin-top: 80px; }
.tabs-on .shots + .shots { margin-top: 0; }   /* one part at a time, no gap above it */

/* the name of a part: shown without JavaScript, and with "marked only" */
.sec-name {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 18px;
}
.tabs-on .sec-name { display: none; }

/* ---------- Photos: justified rows, in reading order ----------

   Like a contact sheet: the pictures run left to right, row by row, and
   every picture keeps its own shape. Each one grows in proportion to its
   width-to-height ratio (--r, written by gallery.php from the file's own
   size), so a row ends up the same height all along and fills the width.
   The empty ::after soaks up the last row, which would otherwise be blown
   up to fill it. Until v 0.9 this was CSS columns, which run top to bottom:
   picture 2 was under picture 1, not beside it. */
.photos {
  display: flex;
  flex-wrap: wrap;
  gap: 6px var(--gap);
}
.photos::after { content: ""; flex-grow: 999; }
.photos > .shot-wrap {
  flex: var(--r, 1.5) 1 calc(var(--r, 1.5) * var(--row-h));
  min-width: 0;
}
.photos img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--r, 1.5);
  object-fit: cover;
  background: #ececec;
  outline: 2px solid transparent;
  outline-offset: 3px;
  transition: outline-color 0.15s ease-out;
}
.shot { display: block; line-height: 0; cursor: zoom-in; }
.shot:hover { background: none; }
.shot:hover img { outline-color: var(--blue); }

/* under every picture: its number in the gallery, and the mark button */
.shot-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  min-height: 40px;
}
.shot-foot .no {
  font-size: 13px;
  color: var(--mute);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

/* ---------- Favourites ---------- */

.shot-wrap { position: relative; }
.shot-wrap.is-liked .shot img { outline-color: var(--red); }

.like { line-height: 0; }
/* The one thing a client does on this page, so it is sized for a thumb:
   the box is 40px high even though the heart and word are small. */
.like-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 40px;
  padding: 6px 0 6px 10px;
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.4;
  color: var(--mute);
  background: none;
  border: none;
  cursor: pointer;
}
.like-btn .h {
  width: 18px;
  height: 18px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}
.like-btn:hover { color: var(--blue); }
.like-btn.is-liked { color: var(--red); }
.like-btn.is-liked .h { fill: currentColor; }

/* The box that asks once for the address */
.ask-mail {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(255, 255, 255, 0.93);
}
.ask-mail[hidden] { display: none; }
.ask-mail form {
  width: 360px;
  max-width: 100%;
  padding: 28px;
  background: var(--paper);
  border: 1px solid var(--hair);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
}
.ask-mail .ask-title { font-size: 22px; font-weight: 600; letter-spacing: -0.015em; margin-bottom: 18px; }
.ask-mail label { display: block; font-weight: 500; color: var(--ink); margin-bottom: 8px; }
.ask-mail .hint { color: var(--mute); margin-top: 20px; line-height: 1.6; }
.ask-mail .error { color: var(--red); margin-top: 14px; }

/* ---------- Password gate ---------- */

.gate {
  display: flex;
  gap: 56px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.gate-cover { width: 480px; max-width: 100%; }
.gate-form { max-width: 340px; flex: 1 1 260px; }
.gate-form label { display: block; font-size: 15px; font-weight: 500; color: var(--ink); margin-bottom: 8px; }
.gate-form input[type="password"],
.gate-form input[type="email"],
.ask-mail input[type="email"] {
  display: block;
  width: 100%;
  font-family: var(--ui);
  font-size: 16px;
  color: var(--ink);
  background: none;
  border: 1px solid #c9c9c9;
  padding: 11px 12px;
  margin-bottom: 16px;
}
.gate-form input:focus, .ask-mail input:focus { border-color: var(--blue); }
.gate-form .error { color: var(--red); margin-top: 16px; }
.gate-form .hint { color: var(--mute); margin-top: 24px; line-height: 1.6; }

/* ---------- Dashboard ---------- */

.dash-section { margin-bottom: 64px; max-width: 900px; }
.dash-section[hidden] { display: none; }

.dash-section h2 {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.15;
  color: var(--ink);
  border-top: 1px solid var(--hair);
  padding-top: 16px;
  margin-bottom: 28px;
}
.dash-section h2.second { margin-top: 56px; }

/* With the tabs switched on, each one already names the section above it. */
.tabs-on .dash-section > h2:first-child {
  border-top: none;
  padding-top: 0;
  margin-top: 8px;
}

/* The label is what the eye runs down when looking for a setting, so it is
   the darkest line of a field; the hint under it is the quiet one. */
.field { margin-bottom: 26px; max-width: 560px; }
.field > label {
  display: block;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 6px;
}
.field input[type="text"],
.field input[type="password"],
.field input[type="number"],
.field input[type="search"],
.field select,
.field textarea {
  display: block;
  width: 100%;
  font-family: var(--ui);
  font-size: 15px;
  color: var(--ink);
  background: none;
  border: 1px solid #c9c9c9;
  padding: 10px 12px;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--blue); }
.field input::placeholder, .fav-filter input::placeholder { color: var(--mute); opacity: 1; }
.field textarea { resize: vertical; }
.field .hint { margin-top: 6px; font-size: 14px; color: var(--mute); line-height: 1.55; }

.field .switch { display: flex; align-items: center; gap: 10px; min-height: 32px; }
.field .switch input { width: 18px; height: 18px; accent-color: var(--blue); cursor: pointer; }
.field .switch-word { font-size: 15px; color: var(--ink); cursor: pointer; }

.cron {
  max-width: 620px;
  margin: 32px 0 8px;
  padding-top: 24px;
  border-top: 1px solid var(--hair);
}
.cron h3 {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 12px;
}
.cron .hint { margin: 12px 0; }
.cron .codeblock { margin: 10px 0 14px; }

.dash-save { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 26px; }
.dash-save .hint { color: var(--mute); }
/* Under the settings the Save bar stays at the foot of the window, so the
   E-mail tab with its cron block does not have to be scrolled to the end
   to save one field at its top. */
.dash-section > .dash-save {
  position: sticky;
  bottom: 0;
  z-index: var(--z-sticky);
  background: var(--paper);
  border-top: 1px solid var(--hair);
  padding: 14px 0;
}
.dash-save .dirty { color: var(--red); font-weight: 500; }
.dash-save .dirty[hidden] { display: none; }
.dash-tools { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }

/* Tools: one row per button, the button on the left and what it does on the
   right. The buttons share a column width so they read as a list of things
   to press rather than a paragraph with a button in it. Outlined, not
   filled: six solid blue blocks in a column would shout. */
.tool-list { margin-bottom: 22px; }
.tool {
  display: grid;
  /* wide enough that the longest label, "Look for new galleries", stays on
     one line, so every button in the column is the same height */
  grid-template-columns: 240px 1fr;
  gap: 6px 24px;
  align-items: start;
  padding: 16px 0;
  border-top: 1px solid var(--hair);
}
.tool:last-child { border-bottom: 1px solid var(--hair); }
.tool form { margin: 0; }
.tool .button { width: 100%; text-align: center; color: var(--blue); background: none; }
.tool .button:hover { color: #fff; background: var(--blue); border-color: var(--blue); }
.tool .what {
  margin: 0;
  color: var(--mute);
  line-height: 1.6;
  max-width: 62ch;
}
@media (max-width: 640px) {
  .tool { grid-template-columns: 1fr; gap: 10px; }
  .tool .button { width: auto; }
}

/* the cache figures on the Tools tab */
.cache-facts { border-collapse: collapse; margin-bottom: 18px; }
.cache-facts th,
.cache-facts td { text-align: left; padding: 6px 22px 6px 0; vertical-align: baseline; }
.cache-facts th { font-weight: 400; color: var(--mute); white-space: nowrap; }
.cache-facts td.note { color: var(--mute); }
.dash-section .hint { color: var(--mute); line-height: 1.6; max-width: 620px; }

.flash {
  border: 1px solid var(--blue);
  color: var(--blue);
  padding: 12px 16px;
  margin-bottom: 28px;
  max-width: 900px;
  font-size: 15px;
  line-height: 1.55;
}
.flash.is-bad  { border-color: var(--red); color: var(--red); }
.flash.is-warn { border-color: var(--hair); color: var(--mute); }

/* what a Tools button answered: plain text with aligned columns, so code */
.result {
  font-family: var(--code);
  font-size: 13px;
  white-space: pre-wrap;
  border: 1px solid var(--hair);
  padding: 12px 14px;
  margin-bottom: 28px;
  max-width: 900px;
  line-height: 1.6;
}

.dash-intro { max-width: 560px; }
.dash-intro code, .field code, .hint code {
  font-family: var(--code);
  font-size: 0.9em;
  background: #f4f4f4;
  padding: 1px 4px;
}

/* the list of what clients marked */
.fav-filter { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }
.fav-filter label { color: var(--ink); font-weight: 500; }
.fav-filter input {
  font-family: var(--ui);
  font-size: 15px;
  color: var(--ink);
  background: none;
  border: 1px solid #c9c9c9;
  padding: 9px 12px;
  min-width: 280px;
  flex: 0 1 340px;
}
.fav-filter input:focus { border-color: var(--blue); }

.fav { margin-bottom: 28px; }
.fav[hidden] { display: none; }
.fav-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.fav-head strong { font-weight: 600; }
.fav-head .fav-meta { color: var(--mute); }

/* A list of marked pictures, shown the way code is shown */
.codeblock {
  position: relative;
  border: 1px solid var(--hair);
  background: #fafafa;
}
.codeblock pre {
  margin: 0;
  padding: 12px 84px 12px 14px;      /* room for the button on the right */
  overflow-x: auto;
  font-family: var(--code);
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
}
.codeblock code { font-family: inherit; }

.codeblock .copy {
  position: absolute;
  top: 6px;
  right: 6px;
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--blue);
  background: var(--paper);
  border: 1px solid var(--hair);
  padding: 5px 10px;
  cursor: pointer;
}
.codeblock .copy:hover { background: var(--blue); color: #fff; border-color: var(--blue); }
.codeblock .copy.is-done { color: var(--red); border-color: var(--red); }
.codeblock .copy.is-done:hover { background: var(--red); color: #fff; }

/* ---------- Footer ---------- */

.section {
  margin-top: 140px;
  border-top: 1px solid var(--hair);
  padding-top: 16px;
}
.footer-line { color: var(--mute); font-size: 13px; }

/* The version stamp, dashboard only. Floated rather than made a flex item:
   the footer is an inline run of links and "·" separators, and turning it
   into a flex row would make every one of those a box of its own. On a
   narrow screen it drops onto its own line instead. */
.footer-line .version { float: right; opacity: 0.75; }

/* ---------- Lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  background: var(--paper);
  z-index: var(--z-lightbox);
  display: none;
  flex-direction: column;
  padding: 20px;
}
.lightbox.open { display: flex; }

/* Centred, not baseline-aligned: the mark button is a flex box of its own,
   so its "baseline" would be taken from the heart and sit too high. */
.lightbox .bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.lightbox .bar > span { font-size: 15px; font-weight: 500; font-variant-numeric: tabular-nums; }
.lightbox .nav-btns { display: flex; gap: 4px 8px; align-items: center; flex-wrap: wrap; }
.lightbox button, .lightbox .nav-btns a {
  font-family: var(--ui);
  font-size: 15px;
  font-weight: 500;
  /* Set on purpose, and it has to stay. A <button> does not inherit
     line-height from the page (the browser gives it "normal"), so its
     height came from whatever font each glyph in it landed in. "prev" and
     "next" carry ← and →, which the site's font files do not hold, so
     those two fall back to a system face; on Android that face is taller
     and only the two buttons with an arrow grew, which put "close (Esc)"
     on a different baseline once the row wrapped. A number here makes the
     line box the same height whatever font a stray glyph pulls in. */
  line-height: 1.5;
  background: none;
  border: none;
  color: var(--blue);
  text-decoration: none;
  cursor: pointer;
  padding: 9px 10px;             /* 40px high: these are pressed with a thumb */
}
.lightbox button:hover, .lightbox .nav-btns a:hover {
  background: var(--blue);
  color: #fff;
}

.lightbox .lb-like {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  line-height: 1.5;          /* the same box height as the plain buttons */
}
.lightbox .lb-like .h {
  width: 18px;
  height: 18px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}
.lightbox .lb-like { color: var(--mute); }
.lightbox .lb-like:hover { background: none; color: var(--blue); }
.lightbox .lb-like.is-liked { color: var(--red); }
.lightbox .lb-like.is-liked .h { fill: currentColor; }
.lightbox .lb-like.is-liked:hover { background: none; color: var(--red); }

.lightbox .stage {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 0;
}
.lightbox .stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  display: block;
  cursor: e-resize;              /* the right half goes on … */
}
.lightbox .stage img.to-prev { cursor: w-resize; }   /* … the left half goes back */

.lightbox .caption {
  font-size: 14px;
  color: var(--mute);
  text-align: center;
  min-height: 1.5em;
}

/* ---------- Smaller screens ---------- */

@media (max-width: 1000px) {
  .ghead { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .ghead .gcover { order: -1; }       /* on a narrow screen the picture comes first */
}

@media (max-width: 720px) {
  :root { --row-h: 200px; }
  .wrap { padding: 18px 20px 80px; }
  .top { margin-bottom: 32px; }
  .covers { grid-template-columns: minmax(0, 1fr); gap: 44px; }
  .ghead { margin-bottom: 40px; padding-bottom: 32px; }
  .ghead .note p { font-size: 17px; }
  .tabs { gap: 4px 20px; }
  .footer-line .version { float: none; display: block; margin-top: 8px; }
}
