/* ============================================================
   Blog Page — Layout, Sidebars, Prose
   ============================================================ */

/* Widget styles consume --term-* tokens from style.css (same visual values) */

.page__inner--article { border-top: 0; }

/* ── Terminal widget command-line header — shared chrome ────────────────────
   Five components (TOC widget, meta widget, post-nav, related-posts, link-card)
   share the same terminal-prompt header look. This rule captures the common
   properties; each component keeps its own padding, letter-spacing, and layout.
   For ::before pseudo-elements the class cannot be added in HTML, so we list the
   selectors directly in this rule instead.                                       */
.toc-widget::before,
.meta-widget::before,
.post-nav__hdr,
.related-posts__header,
.lc-wrap .link-card__hdr {
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--green);
    opacity: 0.85;
    border-bottom: 1px solid var(--term-border-dim);
}

/* ============================================================
   tmux-style reading progress status bar (directly below navbar)
   ============================================================ */
.read-progress {
    height: 14px;
    background-color: var(--term-bg);
    border-bottom: 1px solid var(--term-border-dim);
    /* position is relative — parent .sticky-bar handles sticking */
    position: relative;
    display: flex;
    align-items: center;
    overflow: hidden;
    transition: background-color 0.15s;
}

.read-progress__fill {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 0%;
    background: var(--highlight-bg);
    border-right: 2px solid var(--green);
    transition: width 0.15s linear;
}

.read-progress__mode {
    position: absolute;
    left: 12px;
    font-size: 10px;
    color: var(--secondary);
    font-family: 'JetBrains Mono', monospace;
    letter-spacing: 2px;
    z-index: 1;
    opacity: 0.6;
}

.read-progress__pct {
    position: absolute;
    right: 12px;
    font-size: 10px;
    color: var(--green);
    font-family: 'JetBrains Mono', monospace;
    letter-spacing: 1px;
    z-index: 1;
}

/* ============================================================
   Blog 3-column grid: left meta | content | right TOC
   ============================================================ */
.blog-layout {
    display: grid;
    /* Left meta sidebar (180px) | content (1fr) | right TOC (320px).
       minmax(0, 1fr) prevents grid item inflation from min-width:auto. */
    grid-template-columns: 180px minmax(0, 1fr) 320px;
    gap: 0;
    padding: var(--sp-10) var(--content-pad);
    position: relative;
    flex: 1;
    align-content: start;
}

/* No right TOC sidebar: left meta | content only */
.blog-layout:not(:has(.blog-sidebar--right)) {
    grid-template-columns: 180px minmax(0, 1fr);
}

.prose strong {
    color: var(--green);
}

@media (max-width: 768px) {
    .blog-layout { padding: var(--sp-6) 0 min(3vh, 2rem); }
    .blog-content { padding: 0 !important; }
    /* Slightly smaller body text on mobile — JetBrains Mono reads large at 16px on narrow screens */
    /* Left-align on mobile: justify creates excessive word gaps on narrow viewports with monospace font */
    .prose { font-size: var(--fs-sm) !important; text-align: left !important; }
    /* Mobile title: larger range for clear hierarchy over body text (1rem) */
    .blog-content__title { font-size: clamp(1.4rem, 6vw, 2rem) !important; margin-bottom: 1.2rem !important; }
    .blog-content__tags  { margin-bottom: var(--sp-6) !important; }

    /* Fluid prose heading scale for mobile — always subordinate to .blog-content__title
       which is clamped to max 1.5rem (24px) at ≤768px.
       Max values are capped so no in-body heading can exceed the article title.
       Minimum values are strictly ordered (h1 min > h2 min > h3 min > h4) so
       hierarchy never inverts even on the narrowest viewports (320px–373px range).
       hyphens:auto (Turkish dictionary in browser, lang="tr" set on <html>) adds
       correct syllable breaks for long words — the industry-standard approach.
       overflow-wrap:break-word is a hard fallback for words that resist hyphenation.
       !important is required: the base .prose h1 { font-size: var(--fs-3xl) } rule
       appears later in the file (same selector specificity) and would otherwise
       override these mobile clamps regardless of viewport width. */
    .prose h1 { font-size: clamp(1.2rem, 5vw, 1.4rem) !important; }
    .prose h2 { font-size: clamp(1.1rem, 4.5vw, 1.3rem) !important; }
    .prose h3 { font-size: clamp(1.05rem, 4vw, 1.15rem) !important; }
    .prose h4 { font-size: var(--fs-base) !important; }
    .prose h1, .prose h2, .prose h3, .prose h4 {
        hyphens: auto;
        overflow-wrap: break-word;
    }

    /* Meta date row: allow items to wrap on narrow screens instead of
       overflowing right (no flex-wrap on base rule) */
    .blog-content__meta {
        flex-wrap: wrap;
        row-gap: 2px;
        justify-content: center;
    }

    /* Share bar: full-width on mobile, centred when label+btns wrap */
    .share-bar {
        width: 100%;
        box-sizing: border-box;
        margin-left: 0;
        margin-right: 0;
        flex-wrap: wrap;
        gap: 0.5rem;
        justify-content: center;
    }

    /* Post-nav: tighter padding on small screens */
    .post-nav__link { padding: 0.55rem 0.75rem; }

    /* Related item meta: allow text to wrap so it doesn't push title off */
    .related-item__meta { white-space: normal; }

}

/* 769px–1024px: landscape phones and small tablets.
   Uses fixed rem values instead of vw-based clamp to prevent the orientation
   flip problem: when a phone rotates to landscape, vw becomes the long dimension
   (~850px) and heading sizes jump unpredictably relative to the fixed body text.
   Fixed rem values are stable regardless of orientation — hierarchy is always
   h1 > h2 > h3 > h4 > body text, both in portrait and landscape.
   Values are a clean midpoint between mobile maxes (1.4/1.3/1.15/1rem) and
   desktop vars (--fs-3xl/2xl/xl/lg).
   !important reason: same as the ≤768px block above — the base .prose h*
   rules appear later in the file at equal specificity and would otherwise win. */
@media (min-width: 769px) and (max-width: 1024px) {
    .prose h1 { font-size: 1.6rem !important; }
    .prose h2 { font-size: 1.45rem !important; }
    .prose h3 { font-size: 1.25rem !important; }
    .prose h4 { font-size: 1.1rem !important; }
}

/* ≤1280px: hide both sidebars, single-column content.
   NOTE: .blog-layout:not(:has(.blog-sidebar--right)) has specificity 0-2-0,
   which is higher than a plain .blog-layout rule (0-1-0) in a media query.
   We repeat the :not(:has()) selector here so both forms are overridden at
   the same specificity level, and the later-in-file rule wins.
   NOTE: .blog-content { grid-column: 2 } is defined later in the file so it
   overrides this media query at equal specificity — !important is needed here
   to ensure the single-column override wins and content doesn't float right. */
@media (max-width: 1280px) {
    .blog-sidebar--left  { display: none !important; }
    .blog-sidebar--right { display: none !important; }
    .blog-layout,
    .blog-layout:not(:has(.blog-sidebar--right)) { grid-template-columns: minmax(0, 1fr); }
    .blog-content { grid-column: 1 !important; }
}

/* >1280px: both sidebars visible — hide duplicate meta + tags from article header.
   On ≤1280px sidebars are hidden, so these elements reappear for mobile/tablet. */
@media (min-width: 1281px) {
    .blog-content__header { display: none !important; }
    .blog-content__tags   { display: none !important; }
    /* Restore spacing between title and prose now that tags are gone */
    .blog-content .prose  { margin-top: 1.5rem; }
}

/* ============================================================
   Article Content
   ============================================================ */
.blog-content {
    grid-column: 2;
    grid-row: 1;
    color: var(--text);
    text-align: left;
    padding: 0 3%;
    min-width: 0; /* grid item safety: prevent min-content from expanding the track */
    overflow-x: hidden; /* belt-and-suspenders: clip any residual overflow */
}

.blog-content__header { max-width: 80ch; margin: 0 auto 1.2rem; }

/* Tags live below the title — centered, just above prose */
.blog-content__tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 6px;
    max-width: 80ch;
    margin: 0 auto var(--sp-10);
}
.blog-content__tags .tag-link { text-decoration: none; }

.blog-content__meta {
    display: flex;
    align-items: center;
    gap: 5px;
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    letter-spacing: 0.2px;
}

.blog-content__sep {
    color: var(--secondary);
    opacity: 0.35;
    user-select: none;
}

.blog-content__date    { color: var(--secondary); }
.blog-content__readtime { color: var(--fg-accent); }

.blog-content__title {
    color: var(--text);
    font-weight: 800;
    font-size: clamp(1.6rem, 5vw, var(--fs-3xl));
    text-align: center;
    max-width: 80ch;
    margin: 0 auto 2.5rem;
    line-height: 1.2;
    letter-spacing: -0.02em;
}

/* Featured badge on blog post reading page */
.featured-badge--post {
    display: block;
    text-align: center;
    font-size: 11px;
    color: var(--green);
    border: 1px solid var(--green);
    width: fit-content;
    margin: 0.2rem auto 0.75rem;
    padding: 0 0.55em 0.1em;
    opacity: 0.8;
    letter-spacing: 0.04em;
}

/* Strikethrough inside blog post main title and navigation titles */
.blog-content__title del,
.related-item__title del,
.post-nav__link del { text-decoration: line-through; opacity: 0.6; }

/* Inline code inside blog post main title */
.blog-content__title code {
    color: var(--accent-text);
    background-color: var(--code-bg);
    padding: 2px 8px;
    border-radius: 2px;
    font-weight: 700;
}

/* Inline code inside related posts and prev/next navigation */
.related-item__title code,
.post-nav__link code {
    color: inherit;
    background-color: var(--code-bg);
    padding: 1px 4px;
    border-radius: 2px;
    font-size: 0.92em;
}

/* ============================================================
   Right Sidebar — TOC only
   ============================================================ */
.blog-sidebar--right {
    grid-column: 3;
    grid-row: 1;
    position: sticky;
    top: 4rem; /* clears sticky-bar: navbar 48px + progress bar 14px = 62px ≈ 3.875rem → 4rem with buffer */
    height: max-content;
    line-height: calc(1rem + 1.2vw);
    text-align: right;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-left: 16px;
}

/* ============================================================
   TOC Widget — Linux directory tree
   ============================================================ */
.toc-widget {
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    box-shadow: 0 0 10px 2px color-mix(in srgb, var(--accent) 5%, transparent);
    padding: 12px 14px;
    text-align: left; /* tree chars are on the left — must NOT inherit text-align:right from sidebar */
    transition: background-color 0.15s, border-color 0.15s;
    /* Flex layout: ::before header stays pinned, .toc list scrolls inside */
    display: flex;
    flex-direction: column;
}

.toc-widget::before {
    content: '$ toc --list';
    display: block;
    /* font-family/font-size/color/opacity/border-bottom shared via rule above */
    /* .blog-sidebar--right sets line-height: calc(1rem + 1.2vw) ≈ 33px at 1440px.
       Without an explicit override, ::before inherits this huge value — its line
       box becomes 33px for a 12px font, adding ~10px of ghost space above AND
       below the text. line-height: 1 collapses the line box to just the font size. */
    line-height: 1;
    letter-spacing: 1px;
    padding-bottom: 8px;
    margin-bottom: 10px;
}

.toc-widget .toc {
    margin: 0;
    padding: 0;
    /* Cap scroll region to viewport; accounts for sticky-bar (~4rem) + page
       padding/borders (~7rem) + toc header (~2rem) so it never clips at load. */
    max-height: calc(100vh - 20rem);
    /* Vertical scroll when TOC overflows — flex child takes remaining height */
    overflow-y: auto;
    flex: 1;
    min-height: 2rem; /* always show at least one line */
    /* Scrollbar — always dark; toc-widget sits on --term-bg in both light and dark themes.
       Use --term-bg (not --bg) for the track so it stays dark in light mode too. */
    scrollbar-width: thin;
    scrollbar-color: var(--border-accent) var(--term-bg);
}
/* WebKit scrollbar — uses --term-bg so track stays dark in light mode */
.toc-widget .toc::-webkit-scrollbar       { width: 3px; }
.toc-widget .toc::-webkit-scrollbar-track  { background: var(--term-bg); }
.toc-widget .toc::-webkit-scrollbar-thumb  { background: var(--border-accent); border-radius: 0; }
.toc-widget .toc::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
.toc-widget ul  { list-style: none; padding: 0; margin: 0; }
.toc-widget li  { margin: 0; }

.toc-widget a {
    text-decoration: none;
    display: block;   /* JS builds .toc-entry-line rows — <a> is no longer a flex container */
    padding: 2px 0;
    line-height: 1.5;
    transition: color 0.15s;
    color: var(--term-text); /* dark bg in both themes — must NOT use --text/--secondary */
}
/* JS-generated row: [.toc-prefix][.toc-text] — one per visual line */
.toc-entry-line {
    display: flex;
    align-items: flex-start;
    line-height: inherit;
}
/* Tree-prefix span: "├── ", "│   ├── ", continuation "│   │   ", etc. */
.toc-prefix {
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    color: var(--border-strong);
    white-space: pre;
    flex-shrink: 0;
    line-height: inherit;
}
/* Heading text — safety net for a single word longer than the column */
.toc-text {
    min-width: 0;
    flex: 1;
    word-break: break-word;
    overflow-wrap: break-word;
}
/* Inline code inside TOC entries — inherit font so JS canvas measurement stays accurate */
.toc-text code {
    font: inherit;
    background: var(--code-bg);
    padding: 1px 3px;
    border-radius: 2px;
}
.toc-widget a:hover { color: var(--green); }
/* specificity (0,3,1) — beats the level-specific rules below */
.toc-widget .toc a.toc-active {
    color: var(--green);
    opacity: 1;
    font-weight: 600;
}
/* Prefix chars inherit green when the link is active */
.toc-widget .toc a.toc-active .toc-prefix { color: inherit; }


/* CSS ::before prefix replaced by JS-generated .toc-prefix spans */
.toc-widget a::before { content: none !important; }

/* ── Level 1 (h2) ── */
.toc-widget .toc > ul > li > a
                                { font-size: var(--fs-xs); font-weight: 500; color: var(--term-text); }

/* ── Level 2 (h3) ── */
.toc-widget .toc > ul > li > ul > li > a
                                { font-size: var(--fs-xs); color: color-mix(in srgb, var(--term-text) 75%, transparent); }

/* ── Level 3 (h4) ── */
.toc-widget .toc > ul > li > ul > li > ul > li > a
                                { font-size: 11px; color: color-mix(in srgb, var(--term-text) 60%, transparent); }

/* ============================================================
   Left Sidebar — Sticky metadata panel ($ stat post.md)
   ============================================================ */
.blog-sidebar--left {
    grid-column: 1;
    grid-row: 1;
    position: sticky;
    top: 4rem; /* clears sticky-bar (navbar 48px + progress 14px ≈ 62px → 4rem with buffer) */
    height: max-content;
    display: flex;
    flex-direction: column;
    padding-right: 16px;
}

.meta-widget {
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    box-shadow: 0 0 10px 2px color-mix(in srgb, var(--accent) 5%, transparent);
    padding: 12px 14px;
    transition: background-color 0.15s, border-color 0.15s;
}

/* Terminal command header — reads "$ stat post.md" from data-cmd attribute
   so the string lives in strings.py and can be changed from one place. */
.meta-widget::before {
    content: attr(data-cmd);
    display: block;
    /* font-family/font-size/color/opacity/border-bottom shared via rule above */
    letter-spacing: 1px;
    margin-bottom: 10px;
    padding-bottom: 8px;
}

.meta-widget__row {
    margin-bottom: 8px;
}

.meta-widget__row:last-child { margin-bottom: 0; }

.meta-widget__key {
    display: block;
    font-family: 'JetBrains Mono', monospace;
    font-size: 9px;
    color: var(--green);
    opacity: 0.6;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    margin-bottom: 2px;
}

.meta-widget__val {
    display: block;
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--term-text);
    line-height: 1.4;
    word-break: break-word;
}

.meta-widget__sep {
    border: none;
    border-top: 1px solid var(--term-border-dim);
    margin: 10px 0;
}

.meta-widget__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* Scale down the shared .tag elements inside the meta sidebar */
.meta-widget__tags .tag {
    font-size: 10px;
    padding: 1px 5px;
}

/* Meta widget always sits on a dark terminal background (--term-bg stays dark
   in both themes). Override the light-mode tag rules so tags keep their
   dark-theme appearance regardless of the active theme. */
[data-theme="light"] .meta-widget__tags .tag {
    background-color: color-mix(in srgb, var(--tag-color, #7a9878) 10%, transparent);
    border-color:     color-mix(in srgb, var(--tag-color, #7a9878) 55%, transparent);
    color:            var(--tag-color, #7a9878);
    filter: none;
}
[data-theme="light"] .meta-widget__tags .tag-link:hover .tag {
    background-color: color-mix(in srgb, var(--tag-color, #7a9878) 22%, transparent);
    border-color:     var(--tag-color, #7a9878);
}

/* ============================================================
   Prev/Next Post Navigation — terminal style
   ============================================================ */
.post-nav {
    max-width: 90ch;
    margin: var(--sp-5) auto 0;
    border: 1px solid var(--term-border-dim);
    background-color: var(--term-bg);
}

.post-nav__hdr {
    /* font-family/font-size/color/opacity/border-bottom shared via rule above */
    padding: 0.4rem 1rem;
    letter-spacing: 0.5px;
}

.post-nav__links { display: flex; }

.post-nav__link {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 0.7rem 1rem;
    text-decoration: none;
    transition: background-color 0.15s;
    flex: 1;
    min-width: 0;
    overflow: hidden; /* clip white-space:nowrap title — prevents link from inflating its flex row */
}

.post-nav__link:hover { background-color: var(--hover-bg); }

.post-nav__link--featured { background-color: var(--hover-bg); }
.post-nav__link--featured:hover { background-color: var(--code-bg); }

.post-nav__star {
    color: var(--green);
    opacity: 0.85;
    font-size: 0.9em;
    display: inline-block;
    line-height: 1;
    overflow: hidden;
    vertical-align: middle;
    margin-right: 0.3em;
}
/* .post-nav__empty is removed from the template — no longer needed */

/* When next is the only link (no prev), remove the divider border */
.post-nav__next:first-child { border-left: none; }

.post-nav__dir {
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--green);
    opacity: 0.65;
}

.post-nav__title {
    font-size: var(--fs-sm);
    line-height: 1.4;
    color: var(--term-text);
    transition: color 0.15s;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.post-nav__link:hover .post-nav__title { color: var(--green); }

.post-nav__next {
    text-align: right;
    border-left: 1px solid var(--term-border-dim);
}


/* ============================================================
   Prose — Markdown rendered content
   ============================================================ */
.prose { font-size: var(--fs-base); line-height: var(--lh-body); color: var(--text); max-width: 80ch; margin: 0 auto; transition: color 0.15s; }

/* Opt .prose children back into colour transitions during theme switch.
   .theme-transition *:not(.prose *) intentionally excludes them to avoid
   a flash-of-wrong-colour on first paint; this rule reinstates the
   transition so headings, ::before prefixes (# / ## / ###), links, and
   all other prose elements cross-fade in sync with the surrounding page. */
.theme-transition .prose * {
    transition: color 0.15s, background-color 0.15s, border-color 0.15s, scrollbar-color 0.15s !important;
}
.theme-transition .prose *::before,
.theme-transition .prose *::after {
    transition: color 0.15s, border-color 0.15s !important;
}

/* Justified text + auto-hyphenation for monospace body copy.
   With fixed-width characters word gaps are uniform, so justify looks
   clean and eliminates the ragged-right edge. hyphens:auto uses the
   document lang="tr" dictionary for correct Turkish word breaks. */
.prose p,
.prose li {
    text-align: left;
    text-wrap: pretty; /* fallback orphan fix */
}

/* Editor alignment blocks */
.prose .align-left    { text-align: left; }
.prose .align-center  { text-align: center; }
.prose .align-right   { text-align: right; }
.prose .align-justify { text-align: justify; }
.prose .align-left p,    .prose .align-left li,
.prose .align-center p,  .prose .align-center li,
.prose .align-right p,   .prose .align-right li,
.prose .align-justify p, .prose .align-justify li {
    text-align: inherit;
    hyphens: none;
    -webkit-hyphens: none;
}

/* Never justify headings or short inline elements */
.prose h1, .prose h2, .prose h3,
.prose h4, .prose h5, .prose h6,
.prose blockquote {
    text-align: left;
    hyphens: none;
}

.prose h1, .prose h2, .prose h3,
.prose h4, .prose h5, .prose h6 {
    color: var(--text);
    font-weight: 700;
    margin-top: 1.5em;
    margin-bottom: 0.6em;
    line-height: var(--lh-heading);
    scroll-margin-top: 5rem;
}

.prose h1 { font-size: var(--fs-3xl); }
.prose h2 { font-size: var(--fs-2xl); }
.prose h3 { font-size: var(--fs-xl); }
.prose h4 { font-size: var(--fs-lg); }

/* Markdown-style heading prefixes in accent green.
   Prefixes mirror the Markdown syntax the author typed, not the HTML level,
   because shift_headings() in linkcard.py bumps every heading down by one:
     markdown #   → <h2>  →  prefix "# "
     markdown ##  → <h3>  →  prefix "## "
     markdown ###  → <h4>  →  prefix "### "
   This way the author writes "## Section" and sees "## Section" on the page.
   ::before is CSS-generated: never appears in element.textContent, so the
   TOC JS still reads clean heading text.  The .heading-anchor <a> is
   absolutely positioned at left:-1.3em (outside the heading box) and never
   overlaps with the inline ::before content.  opacity is scoped to the
   pseudo-element only and does not bleed into the heading text. */
.prose h2::before {
    content: "# ";
    color: var(--green);
    opacity: 0.5;
    font-weight: 400;
}

.prose h3::before {
    content: "## ";
    color: var(--green);
    opacity: 0.4;
    font-weight: 400;
}

.prose h4::before {
    content: "### ";
    color: var(--green);
    opacity: 0.35;
    font-weight: 400;
}

.prose h5::before {
    content: "#### ";
    color: var(--green);
    opacity: 0.3;
    font-weight: 400;
}

/* First element in prose never needs top spacing — removes gap at article/page top */
.prose > *:first-child { margin-top: 0; }

.prose p { margin: 1.15em 0; }

.prose a { color: var(--prose-accent); text-decoration: underline; transition: opacity 0.15s; }
.prose a:hover { opacity: 0.75; }

.prose del { text-decoration: line-through; color: var(--secondary); opacity: 0.75; }

.prose ul, .prose ol { padding-left: 2em; margin: 1em 0; }
.prose li { margin: 0.4em 0; }
.prose ul li::marker { color: var(--prose-accent); }

.prose blockquote {
    border-left: 3px solid var(--prose-accent);
    margin: 0;
    padding: 0.5em 1em;
    color: var(--secondary);
    font-style: italic;
    background: var(--hover-bg);
}
.prose blockquote p { margin: 0; }

.prose hr { border: none; border-top: 1px solid var(--border); margin: 2em 0; }

.prose img { max-width: 100%; height: auto; display: block; margin: 1.5em auto; }

.prose figure { margin: 1.5em auto; max-width: 100%; text-align: center; }
.prose .photo-slider__slide { margin: 0; }
.prose figure img { margin: 0 auto; }
.prose figcaption {
    font-size: var(--fs-xs);
    color: var(--secondary);
    margin-top: 0.45em;
    font-style: italic;
    opacity: 0.8;
}

.prose code {
    background-color: var(--code-bg);
    color: var(--accent-text);
    padding: 2px 5px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.9em;
}

.prose pre {
    background-color: var(--term-bg);
    border: 1px solid var(--border);
    padding: 1em 1.25em;
    overflow-x: auto;
    margin: 1.5em 0;
}

/* Code blocks always use terminal-dark background in both themes.
   Use --term-text (always light) instead of --text (dark in light mode).
   NOTE: .prose pre code (specificity 0-0-3) loses to the hljs theme's .hljs
   class (0-1-0). The second, more-specific rule below ensures the override
   actually wins for code blocks inside a wrapper. */
.prose pre code { background: none; padding: 0; font-size: 0.875em; color: var(--term-text); }
.code-block-wrapper pre code {
    background: none;        /* beat hljs: #282c34 */
    display: block;          /* block + max-content ensures padding-right is always part of scrollWidth */
    width: max-content;      /* explicit intrinsic width = longest line; no overflow past the box */
    padding: 0 1.25em 0 0;  /* right buffer replaces pre's padding-right (see below) */
    overflow: visible;       /* let pre handle scrolling, not code */
    color: var(--term-text); /* beat hljs: #abb2bf */
    font-size: 0.875rem;     /* rem: immune to UA monospace scaling quirks */
    line-height: 1.75;       /* lock line-height so it matches the gutter */
}
/* hljs injects "pre code.hljs { padding:1em }" AFTER blog.css in the cascade
   (same specificity 0-1-2, later wins).  Raising specificity to 0-2-2 here
   ensures our padding:0 always wins and keeps gutter numbers aligned. */
.code-block-wrapper pre code.hljs {
    padding: 0 1.25em 0 0;  /* keep right buffer even when hljs overrides */
    overflow: visible;
    font-size: 0.875rem;
    line-height: 1.75;
}

/* ── Code block wrapper — flex layout: [gutter] [pre] ── */
.code-block-wrapper {
    position: relative;
    margin: 1.5em 0;
    display: flex;
    border: 1px solid var(--border);
    overflow: hidden;
}

/* Reset pre margin/border when inside wrapper; wrapper provides the border.
   NOTE: DOM order is .prose > .code-block-wrapper > pre, so the selector
   must be ".code-block-wrapper pre", NOT ".code-block-wrapper .prose pre"
   (which would require .prose to be a descendant of .code-block-wrapper). */
.code-block-wrapper pre {
    margin: 0;
    border: none;
    flex: 1;
    min-width: 0;
    padding-right: 0;    /* right buffer moved to code element; avoids double-spacing */
    position: relative;  /* positioning context for .code-lang label */
    overflow-x: auto;
    line-height: 1.75;   /* lock to body --lh-body; prevents hljs from overriding */
    /* Firefox scrollbar — terminal-themed thin track */
    scrollbar-width: thin;
    scrollbar-color: var(--border-accent) var(--term-bg);
}

/* Webkit (Chrome, Safari, Edge) — themed horizontal scrollbar */
.code-block-wrapper pre::-webkit-scrollbar {
    height: 5px;
}
.code-block-wrapper pre::-webkit-scrollbar-track {
    background: var(--term-bg);
}
.code-block-wrapper pre::-webkit-scrollbar-thumb {
    background: var(--border-accent);
    border-radius: 2px;
}
.code-block-wrapper pre::-webkit-scrollbar-thumb:hover {
    background: var(--border-strong);
}

/* ── Prose table horizontal scroll wrapper ── */
.table-scroll-outer {
    position: relative;
    margin: 1.5em 0;
}
.table-scroll-outer::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 4em;
    background: linear-gradient(to right, transparent, var(--bg));
    pointer-events: none;
    transition: opacity 0.15s;
    z-index: 1;
}
.table-scroll-outer.is-scroll-end::after { opacity: 0; }
.table-scroll-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: var(--border-accent) var(--bg);
}
.table-scroll-wrap::-webkit-scrollbar { height: 5px; }
.table-scroll-wrap::-webkit-scrollbar-track { background: var(--bg); }
.table-scroll-wrap::-webkit-scrollbar-thumb { background: var(--border-accent); border-radius: 2px; }
.table-scroll-wrap::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
.prose .table-scroll-wrap table { margin: 0; width: 100%; }
.prose .table-scroll-wrap td:has(> code:only-child),
.prose .table-scroll-wrap th:has(> code:only-child) { white-space: nowrap; }
@media (max-width: 768px) {
    .prose .table-scroll-wrap td:not(:has(> code:only-child)),
    .prose .table-scroll-wrap th:not(:has(> code:only-child)) { min-width: 90px; }
}

/* ── Line number gutter ── */
/* NOTE: Code blocks always sit on --term-bg (dark, both modes).
   Do NOT use var(--green) here — in light mode --green = #3a9e3e (dark, designed
   for the light page bg) which becomes nearly invisible at low opacity on dark term-bg.
   Use var(--term-text) instead: it is always light (#c2dcc2) regardless of theme. */
.code-gutter {
    background-color: var(--term-bg);
    border-right: 1px solid var(--term-border-dim);
    padding: 1rem 0.6em;
    /* padding-top must match .prose pre's padding-top (1em at 16px root = 1rem).
       Using rem keeps this immune to any parent font-size cascade. */
    flex-shrink: 0;
    user-select: none;
    pointer-events: none;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.875rem;  /* rem: matches .code-block-wrapper pre code exactly */
    line-height: 1.75;    /* matches --lh-body; must equal pre's line-height */
    /* white-space: pre renders all numbers as one continuous text block,
       so the browser uses the same text-flow engine as the code column —
       no per-element flex rounding that causes sub-pixel drift. */
    white-space: pre;
    text-align: right;
    color: var(--term-text);
    opacity: 0.4;
}

.copy-btn {
    position: absolute;
    top: 6px;
    right: 8px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px; /* bumped from 10px */
    color: var(--term-text);
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    padding: 2px 8px;
    cursor: pointer;
    line-height: 1.5;
    opacity: 0.35;
    transition: opacity 0.15s, color 0.15s, border-color 0.15s;
    z-index: 1;
}

/* Full opacity on wrapper hover or direct button hover/focus */
.code-block-wrapper:hover .copy-btn,
.copy-btn:hover,
.copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { color: var(--green); border-color: var(--green); }
.copy-btn--done { color: var(--green) !important; opacity: 1 !important; border-color: var(--green) !important; }

/* Language label — top-left, inside <pre> (position:relative).
   Same reason as gutter: use var(--term-text) not var(--green).
   --green in light mode (#3a9e3e) is too dark to read on dark term-bg. */
.code-lang {
    position: absolute;
    top: 7px;
    left: 10px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    color: var(--term-text);
    opacity: 0.5;
    letter-spacing: 0.5px;
    pointer-events: none;
    user-select: none;
    z-index: 1;
    line-height: 1;
    transition: opacity 0.15s;
}
.code-lang::before { content: '['; font-weight: 300; }
.code-lang::after  { content: ']'; font-weight: 300; }

/* Lift label opacity on wrapper hover (same trigger as copy-btn) */
.code-block-wrapper:hover .code-lang { opacity: 0.9; }

.prose table { width: 100%; border-collapse: collapse; margin: 1.5em 0; font-size: 0.9em; }
.prose th, .prose td { border: 1px solid var(--border); padding: 0.5em 0.75em; text-align: left; vertical-align: middle; }
.prose th { background-color: var(--code-bg); font-weight: 700; color: var(--accent-text); }
.prose tr:nth-child(even) { background-color: color-mix(in srgb, var(--accent) 4%, transparent); }

/* ============================================================
   Heading Anchor Links — click heading to navigate to its anchor
   ============================================================ */
.prose h1[id], .prose h2[id], .prose h3[id], .prose h4[id] { cursor: pointer; }

/* ============================================================
   Quiz Block — terminal themed, mirrors .post-nav aesthetic
   ============================================================ */
.quiz-block {
    background: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    margin: var(--sp-5) 0;
}

.quiz-block__hdr {
    padding: 0.35rem 1rem;
    border-bottom: 1px solid var(--term-border-dim);
    font-size: var(--fs-xs);
    color: var(--green-dm);
    opacity: 0.6;
    letter-spacing: 0.03em;
    user-select: none;
}

.quiz-block__body {
    padding: 0.85rem 1.25rem 0.875rem;
}

.quiz-block .quiz-block__q {
    margin: 0 0 0.85rem;
    color: var(--term-text);
    line-height: var(--lh-body);
    font-size: var(--fs-base);
}

.quiz-block__prefix {
    color: var(--green-dm);
    font-weight: 700;
    margin-right: 0.3em;
    user-select: none;
}

.quiz-block__opts {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.quiz-opt {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    width: 100%;
    padding: 0.45rem 0.75rem;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--term-text);
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

.quiz-opt:hover:not([disabled]) {
    border-color: var(--border-strong);
    background: rgba(81, 174, 85, 0.07);
}

.quiz-opt__key {
    flex-shrink: 0;
    color: var(--green-dm);
    opacity: 0.5;
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    padding-top: 0.22em;
    user-select: none;
}

.quiz-opt__text { flex: 1; }

/* Chosen option */
.quiz-opt--chosen {
    border-color: var(--green-dm) !important;
    background: rgba(94, 196, 98, 0.09) !important;
}

.quiz-opt--chosen .quiz-opt__key { opacity: 1; }

/* Unchosen options after answer — dimmed */
.quiz-opt--dim {
    opacity: 0.3;
    cursor: default;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

/* Feedback — terminal output style, no heavy box */
.quiz-block__fb {
    display: none;
    margin-top: 0.85rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--term-border-dim);
    color: var(--term-text);
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
    overflow: hidden;
}

.quiz-block__fb--visible {
    display: block;
}

.quiz-block__fb::before {
    content: '> ';
    color: var(--green-dm);
    opacity: 0.6;
    user-select: none;
}

/* Inline code inside quiz blocks always sits on --term-bg (dark in both themes).
   Override --fg-accent (dark in light mode) with --term-text (always light). */
.quiz-block code {
    color: var(--term-text);
}

/* ============================================================
   Share Bar
   ============================================================ */
.share-bar {
    margin: var(--sp-5) auto 0;
    width: fit-content;
    max-width: 90ch;
    padding: 0.5rem 1.25rem;
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.share-bar__label {
    color: var(--term-text);
    font-size: var(--fs-xs);
    white-space: nowrap;
    flex-shrink: 0;
    opacity: 0.6;
}

.share-bar__btns {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.share-bar__btn {
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--green);
    background: none;
    border: 1px solid var(--term-border-dim);
    padding: 2px 10px;
    cursor: pointer;
    text-decoration: none;
    line-height: 1.7;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.share-bar__btn:hover {
    background-color: var(--code-bg);
    border-color: var(--border-strong);
}

.share-bar__btn--copied {
    color: var(--term-text);
    opacity: 0.5;
    border-color: var(--term-border-dim);
}

/* ============================================================
   Related Posts
   ============================================================ */
.related-posts {
    max-width: 90ch;
    margin: var(--sp-5) auto 0;
    border: 1px solid var(--term-border-dim);
    background-color: var(--term-bg);
}

.related-posts__header {
    /* font-family/font-size/color/opacity/border-bottom shared via rule above */
    padding: 0.45rem 1rem;
    letter-spacing: 0.5px;
}

.related-item {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1.5rem;
    padding: 0.55rem 1rem;
    text-decoration: none;
    color: var(--term-text);
    border-bottom: 1px solid var(--term-border-dim);
    transition: background-color 0.15s;
}

.related-item:last-child { border-bottom: none; }

.related-item:hover { background-color: var(--hover-bg); }

.related-item--featured { background-color: var(--hover-bg); }
.related-item--featured:hover { background-color: var(--code-bg); }

.related-item__star {
    color: var(--green);
    opacity: 0.85;
    font-size: 0.9em;
    display: inline-block;
    line-height: 1;
    overflow: hidden;
    vertical-align: middle;
    margin-right: 0.3em;
}

.related-item__title {
    font-size: var(--fs-sm);
    font-weight: 500;
    line-height: 1.4;
    transition: color 0.15s;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.related-item:hover .related-item__title { color: var(--green); }

.related-item__meta {
    font-size: var(--fs-xs);
    color: var(--term-text);
    opacity: 0.5;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Extra-specificity (0,2,0) text-decoration overrides for elements that sit
   adjacent to .prose — ensures they never inherit underline from .prose a (0,1,1). */
.related-posts .related-item,
.post-nav .post-nav__link,
.share-bar .share-bar__btn { text-decoration: none; }

/* ============================================================
   Link Preview Cards — ```linkcard fences become cards
   Styled after .related-posts: left accent border, monospace
   throughout, hover: bg + title color only (no border/image animation).
   ============================================================ */
.lc-wrap { margin: 1.5em 0; }

/* Use .lc-wrap .link-card selectors (specificity 0,2,0) to beat
   .prose a (0,1,1) which would bleed color/text-decoration in. */
.lc-wrap .link-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--term-border-dim);
    background-color: var(--term-bg);
    text-decoration: none;
    color: var(--term-text);
    opacity: 1;
    overflow: hidden;
    max-width: 80ch;
    margin: 0 auto;
    line-height: var(--lh-card); /* reset .prose line-height: 1.75 */
    transition: background-color 0.15s;
}
/* Hover: color-mix keeps the card visually dark in ALL themes.
   var(--hover-bg) is nearly transparent (rgba ~0.06) — on light-mode pages
   it replaces solid --term-bg with near-transparency, making the card appear
   white. color-mix gives a subtle green-tinted dark surface that is always
   legible regardless of page background.
   opacity:1 (spec 0,2,1, later source) beats .prose a:hover {opacity:0.75}. */
.lc-wrap .link-card:hover {
    background-color: color-mix(in srgb, var(--term-bg) 88%, var(--color-success) 12%);
    opacity: 1;
}

/* Terminal domain header: "↗ domain.com" — matches .post-nav__hdr */
.lc-wrap .link-card__hdr {
    /* font-family/font-size/color/opacity/border-bottom shared via rule above */
    padding: 0.4rem 1rem;
    letter-spacing: 0.5px;
}

/* Content row: optional thumbnail + text body */
.lc-wrap .link-card__row {
    display: flex;
    align-items: stretch;
}
.lc-wrap .link-card__img {
    width: 80px;
    height: 65px;
    flex-shrink: 0;
    object-fit: cover;
    display: block;
    border-right: 1px solid var(--term-border-dim);
    /* Override .prose img { margin: 1.5em auto } */
    margin: 0;
    max-width: none;
}

.lc-wrap .link-card__body {
    flex: 1;
    min-width: 0;
    padding: 0.55rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 4px;
    justify-content: center;
}
.lc-wrap .link-card__title {
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--term-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color 0.15s;
}
.lc-wrap .link-card:hover .link-card__title { color: var(--green); }

/* Light-mode override: --green changes to #3a9e3e on light pages, but
   link-card always sits on the dark --term-bg surface. --green-dm stays
   #5ec462 in both themes (defined in style.css :root) so the header and
   hover-title stay legible on the always-dark card background. */
[data-theme="light"] .lc-wrap .link-card__hdr,
[data-theme="light"] .lc-wrap .link-card:hover .link-card__title {
    color: var(--green-dm);
}
.lc-wrap .link-card__desc {
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--term-text);
    opacity: 0.5;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
@media (max-width: 768px) {
    /* Remove the fixed height so flex align-items:stretch can actually stretch
       the image to fill the row's full cross-axis height (= body text height).
       min-height ensures the image never collapses below 52px on very short cards.
       object-fit:cover (set in base rule) crops the image to fill the space. */
    .lc-wrap .link-card__img { width: 65px; height: auto; min-height: 52px; }
}

/* ============================================================
   Related Posts + Post Navigation — Mobile
   ============================================================ */
@media (max-width: 600px) {
    /* Related posts: switch from single-line (title + meta side-by-side) to
       two-line layout (title above, meta below). Prevents meta from squishing
       the title and allows long titles to wrap naturally instead of truncating. */
    .related-item {
        flex-direction: column;
        gap: 3px;
        align-items: flex-start;
    }
    /* Make the title a flex row: star (flex-shrink:0) + title-text (flex:1) sit
       side-by-side. align-items:flex-start means each item keeps its own natural
       height — star is ~0.9em tall, text is line-height×font-size, so the
       container is always exactly as tall as the text. No star → no change. */
    .related-item__title {
        display: flex;
        align-items: flex-start;
        gap: 0.3em;
        white-space: normal;
        overflow: visible;
        text-overflow: unset;
    }
    .related-item__star       { flex-shrink: 0; margin-right: 0; }
    .related-item__title-text { flex: 1; min-width: 0; }
    .related-item__meta       { font-size: 10px; }

    /* Post nav: stack prev/next vertically so each title gets the full width
       instead of being compressed to 50% of the panel. Same flex-row trick
       inside the title so the star never inflates the link's height. */
    .post-nav__links { flex-direction: column; }
    .post-nav__next {
        text-align: left;
        border-left: none;
        border-top: 1px solid var(--term-border-dim);
    }
    .post-nav__title {
        display: flex;
        align-items: flex-start;
        gap: 0.3em;
        white-space: normal;
        overflow: visible;
        text-overflow: unset;
    }
    .post-nav__star        { flex-shrink: 0; margin-right: 0; }
    .post-nav__title-text  { flex: 1; min-width: 0; }
}

/* ============================================================
   Photo Slider — terminal image viewer theme
   ============================================================ */
.photo-slider {
    position: relative;
    max-width: 90ch;
    margin: var(--sp-5) auto;
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
    overflow: hidden;
    user-select: none;
}

/* ── Terminal header bar: counter + filename ── */
.photo-slider__counter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.75rem;
    border-bottom: 1px solid var(--term-border-dim);
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--term-text);
    background-color: rgba(81, 174, 85, 0.04);
}
.photo-slider__counter::before {
    content: '$';
    color: var(--green);
    opacity: 0.7;
}

/* ── Slide track: flex row, translate for smooth switching ── */
.photo-slider__track {
    display: flex;
    transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);
    will-change: transform;
}

/* Inactive slides are collapsed out of flow — only active determines height */
.photo-slider__slide {
    flex: none;
    width: 100%;
    margin: 0;
    height: 0;
    overflow: hidden;
}

.photo-slider__slide--active {
    height: auto;
    overflow: visible;
    margin-bottom: 0;
}

.photo-slider__slide img {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: contain;
    background-color: var(--term-bg);
}

/* ── Caption: terminal comment style ── */
.photo-slider__caption {
    padding: 0.25rem 0.75rem;
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--term-text);
    opacity: 0.6;
    border-top: 1px solid var(--term-border-dim);
}
.photo-slider__caption::before {
    content: '# ';
    color: var(--green);
    opacity: 0.5;
}

/* ── prev/next buttons: terminal bracket style ── */
.photo-slider__btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background-color: rgba(13, 17, 13, 0.82);
    border: 1px solid var(--term-border-dim);
    color: var(--green);
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    width: auto;
    height: auto;
    padding: 0.15rem 0.45rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
    z-index: 2;
    line-height: 1.4;
    letter-spacing: 0;
}
.photo-slider__btn::before { content: '['; opacity: 0.5; margin-right: 2px; }
.photo-slider__btn::after  { content: ']'; opacity: 0.5; margin-left:  2px; }

.photo-slider__btn:hover {
    background-color: var(--term-hover-bg);
    border-color: var(--term-hover-border);
    color: var(--green);
}

.photo-slider__btn--prev { left: 0.4rem; }
.photo-slider__btn--next { right: 0.4rem; }

/* ── dot nav ── */
.photo-slider__nav {
    display: flex;
    justify-content: center;
    gap: 6px;
    padding: 0.35rem 0 0.3rem;
    border-top: 1px solid var(--term-border-dim);
}

.photo-slider__dot {
    width: 8px;
    height: 8px;
    border: 1px solid var(--term-border-dim);
    background: transparent;
    cursor: pointer;
    padding: 0;
    transition: background-color 0.15s, border-color 0.15s;
}

.photo-slider__dot--active {
    background-color: var(--green);
    border-color: var(--green);
}
.photo-slider__dot:hover { border-color: var(--green); }

/* ── Single image (no controls) ── */
.photo-slider--single {
    max-width: 90ch;
    margin: var(--sp-5) auto;
    background-color: var(--term-bg);
    border: 1px solid var(--term-border-dim);
}
.photo-slider--single img {
    display: block;
    width: 100%;
    height: auto;
}
.photo-slider--single .photo-slider__caption {
    padding: 0.2rem 0.75rem;
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    color: var(--term-text);
    opacity: 0.6;
    border-top: 1px solid var(--term-border-dim);
}
.photo-slider--single .photo-slider__caption::before {
    content: '# ';
    color: var(--green);
    opacity: 0.5;
}

@media (max-width: 600px) {
    .photo-slider__btn { font-size: 0.8rem; padding: 0.1rem 0.35rem; }
    .photo-slider__btn--prev { left: 0.2rem; }
    .photo-slider__btn--next { right: 0.2rem; }
}

/* ── Slider images are clickable (open lightbox) ── */
.photo-slider__slide img,
.photo-slider--single img { cursor: zoom-in; }

/* ── Lightbox ── */
.photo-lightbox {
    position: fixed;
    inset: 0;
    z-index: var(--z-lightbox);
    background: rgba(0, 0, 0, 0.82);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: zoom-out;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
}
.photo-lightbox--open {
    opacity: 1;
    pointer-events: auto;
}

.photo-lightbox__img {
    max-width: 90vw;
    max-height: 88vh;
    object-fit: contain;
    display: block;
    cursor: default;
}
.photo-lightbox__close {
    position: absolute;
    top: 1rem;
    right: 1rem;
    background: rgba(0, 0, 0, 0.6);
    color: var(--green);
    border: 1px solid var(--term-border-dim);
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    padding: 0.2rem 0.6rem;
    cursor: pointer;
    z-index: 1;
    transition: background 0.15s, border-color 0.15s;
}
.photo-lightbox__close:hover {
    background: var(--term-hover-bg);
    border-color: var(--term-hover-border);
}
.photo-lightbox__btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(0, 0, 0, 0.55);
    color: var(--green);
    border: 1px solid var(--term-border-dim);
    font-family: 'JetBrains Mono', monospace;
    font-size: 1.1rem;
    padding: 0.5rem 0.55rem;
    cursor: pointer;
    z-index: 1;
    transition: background 0.15s, border-color 0.15s;
    line-height: 1;
    letter-spacing: 0;
}
.photo-lightbox__btn::before { content: '['; opacity: 0.5; margin-right: 2px; }
.photo-lightbox__btn::after  { content: ']'; opacity: 0.5; margin-left:  2px; }
.photo-lightbox__btn:hover {
    background: var(--term-hover-bg);
    border-color: var(--term-hover-border);
}
.photo-lightbox__btn--prev { left:  1rem; }
.photo-lightbox__btn--next { right: 1rem; }

.photo-lightbox__caption {
    position: absolute;
    bottom: 1.5rem;
    left: 50%;
    transform: translateX(-50%);
    color: var(--term-text);
    font-family: 'JetBrains Mono', monospace;
    font-size: var(--fs-xs);
    background: rgba(0, 0, 0, 0.65);
    padding: 0.25rem 0.75rem;
    border: 1px solid var(--term-border-dim);
    white-space: nowrap;
    max-width: 80vw;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ============================================================
   Print styles — Ctrl+P / Save as PDF
   ============================================================ */
@media print {
    /* Hide everything except article header + prose */
    .sticky-bar,
    .read-progress,
    .blog-sidebar--right,
    .share-bar,
    .related-posts,
    .post-nav,
    .site-footer,
    .cookie-banner,
    .btt { display: none !important; }

    .page { display: block; }
    .blog-layout { display: block; padding: 0; }
    .blog-content { padding: 0; }

    /* Full-width readable prose at print size */
    .prose { max-width: 100%; font-size: 11pt; line-height: 1.6; }

    /* Prevent code blocks from being cut mid-line */
    .prose pre { white-space: pre-wrap; word-break: break-all; }

    /* Show full URL after each link so the reader can find it */
    .prose a[href]::after { content: ' (' attr(href) ')'; font-size: 0.8em; color: #555; }

    /* Skip fragment-only links (#section) — they're useless on paper */
    .prose a[href^="#"]::after { content: ''; }
}
