/* ============================================================
   Blog: "Too Good to Share"
   Loaded only by /blog/ pages, on top of css/site.css.
   Everything is expressed in site.css custom properties so light
   and dark mode come for free. --sun is a warmer sibling of
   --orange, used for dates and other quiet accents.
   ============================================================ */
:root {
  --sun: #FF9B54;
}

.blog-page {
  max-width: 860px;
  padding-top: 32px;
}
.blog-page .back-link {
  display: inline-block;
  font-size: 0.82rem;
  color: var(--muted);
  text-decoration: none;
  letter-spacing: 0.04em;
  margin-bottom: 26px;
}
.blog-page .back-link:hover { color: var(--orange); }

/* ---- Site-wide banner (blog_banner setting) ---- */
/* 4:1 by intent - the same crop the homepage blog card uses. */
.blog-hero {
  margin: 0 0 28px;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  line-height: 0;
}
.blog-hero img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 1;
  object-fit: cover;
}

/* ---- Masthead ---- */
.blog-masthead {
  margin-bottom: 40px;
  padding-bottom: 28px;
  border-bottom: 1px solid var(--border);
}
.blog-eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  font-weight: 600;
  margin: 0 0 10px;
}
.blog-masthead h1 {
  font-size: 2.4rem;
  margin: 0 0 14px;
  letter-spacing: -0.025em;
  font-weight: 700;
  line-height: 1.08;
}
.blog-tagline {
  margin: 0;
  font-size: 1.08rem;
  color: var(--text);
  opacity: 0.8;
  line-height: 1.55;
}

/* ---- Index feed ----
   Two columns on desktop, one on narrow screens (auto-fit, same shape as the
   homepage project grid). Cards stretch to the tallest in their row and each is
   a flex column, so a short description leaves its slack inside the card body
   and the footer link still lands on the bottom edge. The banner is a fixed 2:1
   strip on top: never a side column, so it cannot be squeezed into a vertical
   sliver by long copy. */
.blog-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  align-items: stretch;
  gap: 22px;
}

.blog-item {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.blog-item:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--orange);
}

.blog-item-thumb {
  display: block;
  flex: none;
  aspect-ratio: 2 / 1;
  background: linear-gradient(135deg, var(--bg) 0%, var(--border) 100%);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
.blog-item-thumb picture { display: block; width: 100%; height: 100%; }
.blog-item-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The wordmark stand-in is not a banner crop - letterbox it instead. */
.blog-item-thumb .blog-item-thumb-fallback {
  object-fit: contain;
  padding: 12px 0;
}

/* The body takes the card's leftover height, so `margin-top: auto` on the footer
   link below pins it to the bottom edge however short the description is. */
.blog-item-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 22px 24px 20px;
}
.blog-item-date {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sun);
  font-weight: 600;
  margin-bottom: 8px;
}
.blog-item-title {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin: 0 0 10px;
}
.blog-item-title a { color: var(--text); text-decoration: none; }
.blog-item-title a:hover { color: var(--orange); }
/* Descriptions keep the line breaks their author typed: pre-line preserves a
   newline, collapses runs of spaces, and still wraps normally. The min-height
   holds room for about four lines, so a short card and a clamped one start out
   the same height. */
.blog-item-excerpt {
  position: relative;
  margin: 0 0 14px;
  color: var(--muted);
  line-height: 1.6;
  white-space: pre-line;
  min-height: 6.4em;
}
/* Both state classes are only ever set by the feed script, so a browser without
   JS gets the whole description rather than four lines and no way to reach the
   rest.

   A max-height clamp, deliberately NOT `-webkit-line-clamp`: that one draws its
   own literal "..." on the last line, which then reads as part of the toggle's
   label. The tail is faded out instead (::after), and the zero-width ::before is
   a float the height of the first three lines - the toggle clears past it and so
   lands on the fourth, where it reserves the space the text wraps short of. */
.blog-item-excerpt.is-clamped {
  max-height: 6.4em;
  overflow: hidden;
}
.blog-item-excerpt.is-clamped::before {
  content: "";
  float: right;
  width: 0;
  height: 4.8em;
}
.blog-item-excerpt.is-clamped::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.6em;
  background: linear-gradient(to right, transparent 0%, transparent 42%, var(--surface) 76%);
  pointer-events: none;
}
/* Expanded: the toggle drops to its own line, right-aligned under the full text.
   The padding is the room it sits in. */
.blog-item-excerpt.is-expanded { padding-bottom: 1.9em; }

.blog-item-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 1.6rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--orange);
  cursor: pointer;
}
.blog-item-toggle[hidden] { display: none; }
/* Floated right and cleared past the ::before spacer: the last visible line of
   the blurb ends short of it, never underneath it. The left padding is that gap,
   and `z-index` keeps the control above the fade that runs behind it. */
.blog-item-excerpt.is-clamped .blog-item-toggle {
  float: right;
  clear: right;
  position: relative;
  z-index: 1;
  padding-left: 20px;
}
.blog-item-excerpt.is-expanded .blog-item-toggle {
  position: absolute;
  right: 0;
  bottom: 0;
}
.blog-item-toggle-chev {
  font-size: 0.9em;
  line-height: 1;
}
/* The button is a flex box, so underline the label rather than the container. */
.blog-item-toggle:hover .blog-item-toggle-label { text-decoration: underline; }
.blog-item-toggle:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Series landing lede: paragraphs are split on blank lines by the layout, so
   pre-line is what keeps the single newlines inside one of them. */
.writeup-lede.blog-series-lede { white-space: pre-line; }
/* A distinct control from the expander above it: `margin-top: auto` pushes it to
   the bottom of the flex body, so it never reads as a continuation of the blurb. */
.blog-item-more {
  margin-top: auto;
  align-self: flex-start;
  padding-top: 4px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--orange);
  text-decoration: none;
  letter-spacing: 0.01em;
}
.blog-item-more:hover { text-decoration: underline; }

/* ---- Empty state ---- */
.blog-empty {
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: 12px;
  padding: 48px 28px;
  text-align: center;
  color: var(--muted);
}
.blog-empty-title { font-size: 1.15rem; font-weight: 600; color: var(--text); margin: 0 0 6px; }
.blog-empty p:last-child { margin: 0; }

/* ---- Single post ---- */
.blog-post-header {
  margin-bottom: 30px;
  padding-bottom: 28px;
  border-bottom: 1px solid var(--border);
}
.blog-post-date {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sun);
  font-weight: 600;
  margin-bottom: 10px;
}
.blog-post-header h1 {
  font-size: 2.4rem;
  margin: 0 0 16px;
  letter-spacing: -0.025em;
  font-weight: 700;
  line-height: 1.08;
}
.blog-post-lede {
  margin: 0;
  font-size: 1.12rem;
  color: var(--text);
  opacity: 0.85;
  line-height: 1.55;
}

.blog-post-hero { margin: 0 0 34px; }
.blog-post-hero img {
  display: block;
  width: 100%;
  border-radius: 12px;
  border: 1px solid var(--border);
}

.blog-post-body { font-size: 1.05rem; }
.blog-post-body p { margin: 0 0 1.15em; line-height: 1.7; }
.blog-post-body h2 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin: 1.9em 0 0.7em;
  padding-top: 1.4em;
  border-top: 1px solid var(--border);
}
.blog-post-body h2:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.blog-post-body h3 {
  font-size: 1.12rem;
  font-weight: 600;
  color: var(--orange);
  margin: 1.9em 0 0.5em;
}
.blog-post-body ul, .blog-post-body ol { margin: 0 0 1.15em; padding-left: 1.3em; }
.blog-post-body li { margin: 6px 0; line-height: 1.6; }
.blog-post-body img { max-width: 100%; border-radius: 8px; }
.blog-post-body hr { border: none; border-top: 1px solid var(--border); margin: 2.4em 0; }
.blog-post-body blockquote {
  margin: 0 0 1.3em;
  padding: 14px 20px;
  border-left: 3px solid var(--orange);
  background: var(--surface);
  border-radius: 0 8px 8px 0;
  color: var(--muted);
}
.blog-post-body blockquote p:last-child { margin-bottom: 0; }
.blog-post-body code {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 0.88em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.blog-post-body pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  overflow-x: auto;
  margin: 0 0 1.3em;
}
.blog-post-body pre code { background: none; border: none; padding: 0; font-size: 0.86rem; }

/* ---- Prism syntax highlighting override layer ----
   Vendored Prism (css/vendor/prism-theme.min.css, the Tomorrow dark theme) is
   loaded on public post pages BEFORE this file. Left alone it paints code as
   #ccc on a fixed #2d2d2d box - a grey box that clashes with the site's own
   surface and would be unreadable in light mode. So we pull the panel back onto
   the site's own surface/border/radius (identical to a plain no-language block,
   so highlighted and plain fences read as one family) and remap the token hues
   to a mode-aware palette. Token colours never use the brand orange
   (var(--orange)) - that stays reserved for brand. Prism only ever targets
   <pre><code class="language-*">, so inline `code` (no class) is untouched. */
.blog-post-body {
  /* Light-mode token palette: saturated enough to read on the light surface.
     --code-num replaces Tomorrow's #f08d49 orange, which would clash the brand. */
  --code-comment: var(--muted);
  --code-punct:   var(--text);
  --code-tag:     #c92a2a; /* tag / attr-name / deleted */
  --code-fn-name: #1864ab; /* function-name */
  --code-num:     #1971c2; /* number / boolean / function (was orange) */
  --code-name:    #0b7285; /* class-name / constant / property / symbol */
  --code-kw:      #6741d9; /* keyword / atrule / builtin / selector */
  --code-str:     #2b8a3e; /* string / attr-value / char / regex / variable */
  --code-op:      #495057; /* operator / url / entity */
}
@media (prefers-color-scheme: dark) {
  .blog-post-body {
    /* Dark-mode palette: brighter, on the navy surface. */
    --code-tag:     #e2777a;
    --code-fn-name: #6196cc;
    --code-num:     #66b2ff; /* replaces Tomorrow's #f08d49 orange */
    --code-name:    #f0d478;
    --code-kw:      #cc99cd;
    --code-str:     #7ec699;
    --code-op:      #67cdcc;
  }
}
.blog-post-body pre[class*="language-"],
.blog-post-body code[class*="language-"] {
  color: var(--text);
  background: none;
  text-shadow: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.86rem;
  tab-size: 2;
}
.blog-post-body pre[class*="language-"] {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  margin: 0 0 1.3em;
  overflow-x: auto;
}
.blog-post-body pre[class*="language-"] code { font-size: inherit; }
.blog-post-body .token.comment,
.blog-post-body .token.block-comment,
.blog-post-body .token.prolog,
.blog-post-body .token.doctype,
.blog-post-body .token.cdata      { color: var(--code-comment); font-style: italic; }
.blog-post-body .token.punctuation { color: var(--code-punct); opacity: 0.8; }
.blog-post-body .token.tag,
.blog-post-body .token.attr-name,
.blog-post-body .token.namespace,
.blog-post-body .token.deleted     { color: var(--code-tag); }
.blog-post-body .token.function-name { color: var(--code-fn-name); }
.blog-post-body .token.boolean,
.blog-post-body .token.number,
.blog-post-body .token.function    { color: var(--code-num); }
.blog-post-body .token.class-name,
.blog-post-body .token.constant,
.blog-post-body .token.property,
.blog-post-body .token.symbol      { color: var(--code-name); }
.blog-post-body .token.atrule,
.blog-post-body .token.builtin,
.blog-post-body .token.keyword,
.blog-post-body .token.selector,
.blog-post-body .token.important   { color: var(--code-kw); }
.blog-post-body .token.attr-value,
.blog-post-body .token.char,
.blog-post-body .token.regex,
.blog-post-body .token.string,
.blog-post-body .token.variable    { color: var(--code-str); }
.blog-post-body .token.entity,
.blog-post-body .token.operator,
.blog-post-body .token.url         { color: var(--code-op); }
.blog-post-body .token.inserted    { color: var(--code-str); }
.blog-post-body .token.bold, .blog-post-body .token.important { font-weight: 700; }
.blog-post-body .token.italic { font-style: italic; }
.blog-post-body table { width: 100%; border-collapse: collapse; margin: 0 0 1.3em; }
.blog-post-body th, .blog-post-body td {
  border: 1px solid var(--border);
  padding: 8px 12px;
  text-align: left;
}
.blog-post-body th { background: var(--surface); font-weight: 600; }

.blog-post-foot {
  margin-top: 44px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

/* ---- Easter-egg admin door: a faint dot under the footer ---- */
.blog-admin-dot {
  display: block;
  text-align: center;
  padding: 0 0 18px;
  color: var(--border);
  font-size: 1.4rem;
  line-height: 1;
  text-decoration: none;
  opacity: 0.5;
  transition: color 0.2s ease, opacity 0.2s ease;
}
.blog-admin-dot:hover { color: var(--orange); opacity: 1; }

/* ============================================================
   Admin
   ============================================================ */
.blog-admin-masthead .blog-btn { margin-top: 18px; }

.blog-flash {
  background: var(--surface);
  border: 1px solid var(--green);
  border-left-width: 3px;
  border-radius: 0 8px 8px 0;
  padding: 10px 16px;
  margin: 0 0 24px;
  font-size: 0.92rem;
}
.blog-errors {
  background: var(--surface);
  border: 1px solid var(--orange);
  border-left-width: 3px;
  border-radius: 0 8px 8px 0;
  padding: 10px 16px;
  margin: 0 0 24px;
}
.blog-errors p { margin: 4px 0; font-size: 0.92rem; }

.blog-btn {
  display: inline-block;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: 8px;
  padding: 9px 16px;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease;
}
.blog-btn:hover { border-color: var(--orange); color: var(--orange); }
.blog-btn-primary {
  background: var(--orange);
  border-color: var(--orange);
  color: #FFFFFF;
}
.blog-btn-primary:hover { background: var(--orange-hover); border-color: var(--orange-hover); color: #FFFFFF; }
.blog-btn-small { padding: 5px 11px; font-size: 0.78rem; }
.blog-btn-danger:hover { border-color: #C0392B; color: #C0392B; }

.blog-admin-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.blog-admin-table th {
  text-align: left;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 0 12px 10px 0;
  border-bottom: 1px solid var(--border);
}
.blog-admin-table td { padding: 14px 12px 14px 0; border-bottom: 1px solid var(--border); vertical-align: middle; }
.blog-admin-title { display: block; font-weight: 600; color: var(--text); text-decoration: none; }
.blog-admin-title:hover { color: var(--orange); }
.blog-admin-slug { display: block; font-size: 0.78rem; color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.blog-admin-date { color: var(--muted); white-space: nowrap; }
.blog-admin-actions-col { width: 1%; }
.blog-admin-actions { white-space: nowrap; }
.blog-admin-actions form { display: inline; }
.blog-admin-actions > * { margin-left: 6px; }
.blog-admin-actions > *:first-child { margin-left: 0; }

.blog-pill {
  display: inline-block;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
}
.blog-pill-published { border-color: var(--green); color: var(--green); }
.blog-pill-draft { border-color: var(--sun); color: var(--sun); }

/* ---- Admin form ---- */
.blog-form { display: flex; flex-direction: column; gap: 20px; }
.blog-field { display: block; }
.blog-label {
  display: block;
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 7px;
}
.blog-label em {
  font-style: normal;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  opacity: 0.75;
}
.blog-field input[type="text"],
.blog-field textarea,
.blog-field select {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  padding: 10px 12px;
}
.blog-field input:focus, .blog-field textarea:focus, .blog-field select:focus {
  outline: none;
  border-color: var(--orange);
}
.blog-body-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.88rem;
  line-height: 1.6;
  resize: vertical;
}

/* Toast UI editor on the Body field. The textarea is still the form control -
   the editor writes markdown back into it on submit - so it is only hidden once
   the script has taken over. The editor's dark theme rides on
   prefers-color-scheme, the same switch the rest of the site uses; these rules
   just pull its chrome back to DL's radius and accent. */
.blog-body-input[hidden] { display: none; }
.blog-body-editor .toastui-editor-defaultUI { border-radius: 8px; border-color: var(--border); }
.blog-body-editor .toastui-editor-defaultUI-toolbar { border-radius: 8px 8px 0 0; }
.blog-body-editor .toastui-editor-mode-switch { border-radius: 0 0 8px 8px; }
.blog-body-editor .toastui-editor-mode-switch .tab-item.active { color: var(--orange); }

/* ---- Insert-image popup: drag-and-drop zone ----
   Injected by blog_admin_editor.php into Toast UI's "Insert image" File tab (via
   a MutationObserver, the same DOM-hook pattern as the code-block language
   control). It replaces the bland default file input with an obvious, labelled
   drop target that highlights on dragover. Everything rides on the site.css
   custom properties, so it matches whichever theme the popup is in (near-black
   surface, orange accent in dark mode). The drop still uploads through
   addImageBlobHook -> upload.php with the CSRF token. */
.dl-img-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 0 12px;
  padding: 20px 16px;
  text-align: center;
  cursor: pointer;
  color: var(--muted);
  background: var(--surface);
  border: 2px dashed var(--border);
  border-radius: 8px;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.dl-img-dropzone:hover { border-color: var(--orange); color: var(--text); }
.dl-img-dropzone:focus-visible { outline: none; border-color: var(--orange); color: var(--text); }
.dl-img-dropzone.is-dragover {
  border-color: var(--orange);
  color: var(--text);
  background: rgba(242, 106, 31, 0.12);
}
/* Children ignore pointer events so drag events target the zone, not a child,
   which keeps the dragover highlight from flickering. */
.dl-img-dropzone-icon { width: 26px; height: 26px; opacity: 0.85; pointer-events: none; }
.dl-img-dropzone.is-dragover .dl-img-dropzone-icon { opacity: 1; }
.dl-img-dropzone-text { font-size: 0.9rem; font-weight: 600; pointer-events: none; }
.dl-img-dropzone-hint {
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  opacity: 0.75;
  pointer-events: none;
}

.blog-field-inline select { width: auto; min-width: 160px; }
.blog-form-actions { display: flex; gap: 12px; align-items: center; margin-top: 4px; }

/* ---- Admin card-image picker ---- */
.blog-image-picker { display: flex; gap: 16px; align-items: flex-start; }
.blog-image-thumb {
  flex: 0 0 auto;
  width: 132px;
  height: 92px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.blog-image-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.blog-image-empty {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 8px;
  text-align: center;
}
.blog-image-controls { flex: 1 1 auto; min-width: 0; }
.blog-dropzone {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 10px;
  padding: 12px 14px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: var(--surface);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.blog-dropzone.is-over { border-color: var(--orange); background: rgba(242, 106, 31, 0.07); }
.blog-dropzone.is-busy { opacity: 0.6; pointer-events: none; }
.blog-dropzone-hint { font-size: 0.82rem; color: var(--muted); }
.blog-upload-msg { margin: 8px 0 0; font-size: 0.82rem; color: var(--muted); min-height: 1.2em; }
.blog-upload-msg.is-error { color: #C0392B; }

@media (max-width: 640px) {
  .blog-masthead h1, .blog-post-header h1 { font-size: 1.9rem; }
  .blog-admin-table thead { display: none; }
  .blog-admin-table td { display: block; border-bottom: none; padding: 4px 0; }
  .blog-admin-table tr { display: block; padding: 14px 0; border-bottom: 1px solid var(--border); }
  .blog-admin-actions > * { margin: 6px 6px 0 0; }
  .blog-image-picker { flex-direction: column; }
  .blog-image-thumb { width: 100%; height: 140px; }
}

/* ---- Series ----
   The landing and member pages reuse the writeup shell, sticky sidebar and
   prev/next from site.css (.writeup-*). Only the blog-specific extras live
   here: the wider page, the feed tag, and the contents list. */
.blog-series-page {
  /* The sidebar needs the room; .blog-page's 860px is for a single column. */
  max-width: 1120px;
}

.blog-item-meta {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 8px;
}
.blog-item-meta .blog-item-date { margin-bottom: 0; }

.blog-series-tag {
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--orange);
  border: 1px solid var(--orange);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}

.blog-series-contents {
  margin: 0;
  padding-left: 1.3em;
}
.blog-series-contents li { margin-bottom: 14px; }
.blog-series-contents li::marker {
  color: var(--sun);
  font-weight: 700;
}
.blog-series-contents a {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}
.blog-series-contents a:hover { color: var(--orange); }
.blog-series-contents-excerpt {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.45;
}

/* A member post keeps its own date and hero under the section heading. */
.blog-series-page .writeup-section .blog-post-date { margin-bottom: 20px; }
.blog-series-page .writeup-section .blog-post-hero { margin: 0 0 26px; }

/* ---- Admin: series ---- */
.blog-admin-series {
  display: block;
  margin-top: 3px;
  font-size: 0.75rem;
  color: var(--orange);
  font-weight: 600;
}

/* Series picker and its order box, side by side. */
.blog-field-row {
  display: flex;
  gap: 18px;
  align-items: flex-start;
}
.blog-subfield {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
}
.blog-subfield-narrow { flex: 0 0 160px; }
.blog-subfield select, .blog-subfield input { width: 100%; box-sizing: border-box; }

.blog-admin-members {
  margin-top: 52px;
  padding-top: 30px;
  border-top: 1px solid var(--border);
}
.blog-admin-members h2 { margin: 0 0 6px; font-size: 1.3rem; }
.blog-admin-hint {
  margin: 0 0 20px;
  color: var(--muted);
  font-size: 0.9rem;
}
.blog-admin-order-col { width: 84px; }
.blog-admin-order { width: 68px; }

/* The series publish control. A series is the publishable unit, so this box sits
   above the edit form rather than inside it - it is its own POST. */
.blog-admin-publish {
  margin: 0 0 30px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 10px;
}
.blog-admin-publish-state {
  margin: 0 0 8px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-weight: 600;
}
.blog-admin-publish-date { color: var(--muted); font-size: 0.85rem; font-weight: 400; }
.blog-admin-publish .blog-admin-hint { margin-bottom: 14px; }

@media (max-width: 640px) {
  .blog-field-row { flex-direction: column; gap: 0; }
  .blog-subfield-narrow { flex-basis: auto; width: 100%; }
}

/* ---- Admin home: one merged list of standalone posts and series groups ---- */
/* A "run" is however many standalone posts happen to land next to each other in
   the merged order. They share one table so the columns line up across the run;
   the margin matches .blog-admin-group so a run and a series sit evenly apart. */
.blog-admin-run { margin-bottom: 12px; }
.blog-admin-run tr:last-child td { border-bottom: none; }

.blog-admin-section { margin-top: 44px; }
.blog-admin-section:first-of-type { margin-top: 0; }
.blog-admin-section-title {
  margin: 0 0 14px;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}

.blog-admin-group {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  margin-bottom: 12px;
}
.blog-admin-group-summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
}
.blog-admin-group-summary::-webkit-details-marker { display: none; }
/* Disclosure triangle, drawn rather than typed so it stays ASCII in the HTML. */
.blog-admin-group-summary::before {
  content: "";
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-left: 6px solid var(--muted);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform 0.16s ease;
}
.blog-admin-group[open] > .blog-admin-group-summary::before { transform: rotate(90deg); }
.blog-admin-group[open] > .blog-admin-group-summary { border-bottom: 1px solid var(--border); }
.blog-admin-group-summary:hover .blog-admin-group-title { color: var(--orange); }
.blog-admin-group-title { font-weight: 600; }
.blog-admin-group-count { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.blog-admin-group-actions { margin-left: auto; white-space: nowrap; }
/* Same three actions as the series screen, so borrow its inline-form rules. */
.blog-admin-group-actions form { display: inline; }
.blog-admin-group-actions > * { margin-left: 6px; }
.blog-admin-group-actions > *:first-child { margin-left: 0; }

/* The indent plus the left rule is what says "these belong to the series above". */
.blog-admin-group-body {
  margin: 4px 16px 10px 30px;
  padding-left: 16px;
  border-left: 2px solid var(--border);
}
.blog-admin-group-body .blog-admin-table tr:last-child td { border-bottom: none; }
.blog-admin-group-empty { color: var(--muted); font-size: 0.9rem; margin: 10px 0; }

/* ---- Drag-handle reordering of series members ----
   Only a member row carries a grip; a standalone post has no series to be
   ordered within. The grip is Sortable's `handle`, so the rest of the row - the
   title link, Edit/View/Publish/Delete - keeps behaving as an ordinary row. */
.blog-admin-drag { width: 1%; padding-right: 10px; }
.blog-admin-grip {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  width: 18px;
  padding: 4px 0;
  cursor: grab;
  color: var(--muted);
  touch-action: none;
}
.blog-admin-grip span {
  display: block;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  opacity: 0.55;
}
.blog-admin-grip:hover { color: var(--orange); }
.blog-admin-grip:hover span { opacity: 1; }
.blog-admin-grip:active { cursor: grabbing; }

.blog-admin-row-ghost td { background: rgba(242, 106, 31, 0.10); }
.blog-admin-row-chosen td { background: rgba(242, 106, 31, 0.06); }
/* While the drop is in flight the rows must not be dragged or clicked again. */
.blog-admin-sortable tbody.is-saving { opacity: 0.6; pointer-events: none; }

.blog-admin-reorder-error {
  margin: 10px 0;
  padding: 8px 12px;
  border-left: 3px solid #c0392b;
  background: rgba(192, 57, 43, 0.08);
  font-size: 0.85rem;
}

/* ---- Post revisions ----
   The version-history screen: a list of prior states, and one of them shown in
   full. Reuses .blog-admin-table / .blog-pill; only the reason chip, the preview
   cell and the read-only body block are new. */
.blog-admin-revision-post { margin: 16px 0 0; }
/* The one-revision detail table is a label/value grid, so its th is a row head. */
.blog-admin-table tbody th {
  width: 1%;
  white-space: nowrap;
  padding: 14px 18px 14px 0;
  text-align: left;
  vertical-align: middle;
  color: var(--muted);
  font-weight: 500;
  border-bottom: 1px solid var(--border);
}
.blog-admin-empty-cell { color: var(--muted); }
.blog-admin-reason {
  display: inline-block;
  margin-top: 4px;
  padding: 1px 8px;
  border: 1px solid var(--orange);
  border-radius: 999px;
  color: var(--orange);
  font-size: 0.72rem;
  white-space: nowrap;
}
.blog-admin-revision-preview {
  display: block;
  color: var(--muted);
  font-size: 0.85rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* A 240-character preview would otherwise set the row height on its own. */
  max-height: 3.4em;
  overflow: hidden;
}
.blog-admin-revision-current td { background: rgba(242, 106, 31, 0.06); }
.blog-admin-revision-subhead { margin: 26px 0 8px; font-size: 1.05rem; }
.blog-admin-revision-body {
  margin: 0;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.6;
  /* The stored source, verbatim: wrap it rather than scroll it sideways. */
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 60vh;
  overflow-y: auto;
}

@media (max-width: 640px) {
  .blog-admin-group-summary { flex-wrap: wrap; }
  .blog-admin-group-actions { margin-left: 0; width: 100%; }
  .blog-admin-group-body { margin-left: 12px; padding-left: 10px; }
  .blog-admin-table tbody th { display: block; border-bottom: none; padding: 4px 0; }
}
