/* Screenshots embedded via the publish pipeline: shown as a centered
   thumbnail by default, sized down more or less depending on how big the
   original screenshot already was - see publish_inbox.py for the width
   check that picks which class gets applied. The lightbox click-to-expand
   (Featherlight, wired up via the ?lightbox= query param) works the same
   either way; this only affects the inline display size. */
.thumb-half {
    display: block;
    width: 50%;
    margin: 1.5em auto;
    cursor: zoom-in;
}
.thumb-large {
    display: block;
    width: 75%;
    margin: 1.5em auto;
    cursor: zoom-in;
}

/* Featherlight (the lightbox library) ships with a thick 25px white
   padding/background around the enlarged image by default. custom.css
   loads *before* featherlight.min.css in base.html.twig, so an override
   here needs the extra "body" ancestor purely to win on specificity,
   not because the selector is semantically about <body>. */
body .featherlight .featherlight-content {
    padding: 6px 6px 0;
    border-bottom: 6px solid transparent;
}
@media only screen and (max-width: 1024px) {
    body .featherlight .featherlight-content {
        padding: 4px 4px 0;
        border-bottom: 4px solid transparent;
    }
}

/* h1 defaults to 3.25rem (nucleus.css) with no responsive downscaling at
   all, so a long article title overflows and wraps awkwardly on a phone
   screen. custom.css loads after nucleus.css/theme.css in base.html.twig,
   so a plain override here already wins the cascade, no extra specificity
   needed. */
@media only screen and (max-width: 768px) {
    /* h2/h3 default to 2.55rem/2.15rem (nucleus.css), both larger than
       the h1 override above - keeping the same relative scale
       as the desktop sizes (h2 ~78% of h1, h3 ~66%) rather than
       arbitrary numbers, so the heading hierarchy stays correct. */
    h1 {
        font-size: 2.1rem;
    }
    h2 {
        font-size: 1.7rem;
    }
    h3 {
        font-size: 1.4rem;
    }
}

/* Text wordmark replacing the default Grav SVG logo in the sidebar
   header (partials/logo.html.twig). #header already sets a fixed
   height/centered layout expecting the old #logo-svg (8rem x 2rem), so
   this is sized/spaced to sit comfortably in that same header block
   rather than relying on the old SVG-specific CSS. */
#site-logo {
    display: inline-block;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: #fff;
    line-height: 2rem;
}
/* #header centers its children as plain inline content (vendor CSS,
   theme.css), so #logo and #theme-toggle next to it align on their
   default text baseline rather than visually centering with each other.
   vertical-align: middle here (matched on #theme-toggle below) lines
   the button up with the middle of the wordmark's own line-height
   instead. */
#logo {
    vertical-align: middle;
}
#site-logo-suffix {
    font-weight: 400;
    opacity: 0.85;
}

/* Profile photo on the About Jan homepage: floats right so it sits
   alongside the intro paragraphs instead of pushing all the text below
   it. Drops to a centered block above the text on narrow screens, where
   there's no width left for a float to make sense next to prose. */
.profile-photo {
    float: right;
    width: 150px;
    max-width: 40%;
    margin: 0 0 1.5em 2em;
    border-radius: 6px;
}
@media only screen and (max-width: 600px) {
    .profile-photo {
        float: none;
        display: block;
        width: 130px;
        max-width: 45%;
        margin: 0 auto 1.5em;
    }
}

/* Stat counter row (About Jan homepage): four numbers in a row on
   desktop, wrapping to two-per-row on narrow screens rather than
   stacking to one column, since a single big number + label is short
   enough that a 2x2 grid still reads fine on a phone. */
.stat-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5em;
    margin: 2em 0;
    padding: 1.5em 0;
    border-top: 1px solid #e0e0e0;
    border-bottom: 1px solid #e0e0e0;
    clear: both;
}
.stat-box {
    flex: 1 1 120px;
    min-width: 120px;
    text-align: center;
}
.stat-number {
    font-size: 2.4rem;
    font-weight: 700;
    line-height: 1;
}
.stat-number::after {
    content: '+';
}
.stat-label {
    display: block;
    margin-top: 0.4em;
    font-size: 0.85rem;
    color: #666;
}

/* "How I Work" step carousel: only the active step is shown, switched
   via the dots or prev/next buttons (see custom.js). All steps are
   present in the DOM from the start (not injected by JS), so the
   content is still there - and still searchable/readable - even if
   JS fails to load. */
.step-carousel {
    margin: 1.5em 0;
    clear: both;
}
.step-panels {
    position: relative;
    min-height: 8em;
}
.step-panel {
    display: none;
    padding: 0.5em 0;
}
.step-panel.is-active {
    display: block;
}
.step-panel h3 {
    margin-top: 0;
}
.step-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1em;
    margin-top: 1em;
}
.step-dots {
    display: flex;
    gap: 0.5em;
}
.step-dot {
    width: 0.7em;
    height: 0.7em;
    border-radius: 50%;
    border: 1px solid #999;
    background: transparent;
    padding: 0;
    cursor: pointer;
}
.step-dot.is-active {
    background: #333;
    border-color: #333;
}
.step-prev,
.step-next {
    background: none;
    border: 1px solid #999;
    border-radius: 4px;
    padding: 0.2em 0.7em;
    cursor: pointer;
    font-size: 1rem;
}
.step-prev:hover,
.step-next:hover,
.step-dot:hover {
    border-color: #333;
}

/* Previous-projects accordion: plain native <details>/<summary>, no JS
   needed at all. Styled to look like a real accordion (custom marker,
   border between entries) rather than the browser's default triangle
   and bare-bones look. */
.project-accordion {
    margin: 1.5em 0;
    clear: both;
}
.project-accordion details {
    border-bottom: 1px solid #e0e0e0;
    padding: 0.8em 0;
}
.project-accordion summary {
    cursor: pointer;
    font-weight: 600;
    list-style: none;
    position: relative;
    padding-left: 1.3em;
}
.project-accordion summary::-webkit-details-marker {
    display: none;
}
.project-accordion summary::before {
    content: '+';
    position: absolute;
    left: 0;
    font-weight: 700;
}
.project-accordion details[open] summary::before {
    content: '\2212';
}
.project-accordion .project-body {
    padding: 0.8em 0 0.2em 1.3em;
}
.project-accordion .project-body p {
    margin: 0 0 0.8em;
}

/* Learn2's built-in sidebar search only searches within the current
   page/session rather than the whole site, not useful in practice.
   Hidden rather than removed from the template, so it is easy to bring
   back later if a real site-wide search ever gets wired up instead. */
.searchbox {
    display: none;
}

/* theme.css's generic `code` rule sets padding: .2rem .4rem, meant for
   short inline spans in prose (like `git clone`). pre code (which
   overrides color/background/font-size there) never resets padding back
   to 0, so it leaks through onto every fenced code block. Because <code>
   stays display:inline and contains real newlines (inherits white-space:
   pre from its <pre> ancestor), CSS only ever applies an inline box's
   left/right padding at the very start/end of its first/last line, never
   at an internal line break - so only the block's first line visibly
   shifts right, indistinguishable at a glance from a stray leading
   space, confirmed directly by measuring per-character positions across
   both lines (uniform 7.5px advance within each line, a flat 6.4px - the
   exact .4rem value - offset between line 1's start and every other
   line's). */
pre code {
    padding: 0;
}

/* Print / "Save as PDF" (triggered by the button in partials/page.html.twig):
   strip everything that only makes sense on-screen (sidebar nav, header,
   footer, breadcrumbs, search, the copy-to-clipboard buttons, the download
   buttons themselves) and let the article content use the full page width,
   since #body normally reserves 300px on the left for the fixed sidebar. */
@media print {
    #sidebar,
    #header-wrapper,
    #top-bar,
    #navigation,
    #body .nav,
    .copy-to-clipboard,
    .page-downloads,
    #footer,
    .searchbox,
    #overlay {
        display: none !important;
    }
    #body {
        margin-left: 0 !important;
    }
    #body .padding {
        padding: 0 !important;
    }
    /* Browsers print the target URL after every link by default, which
       gets noisy fast on a page as link-heavy as a tutorial article. */
    a[href]:after {
        content: "" !important;
    }
}

/* The "Download Markdown" link and "Save as PDF" button (partials/page.html.
   twig) look mismatched by 1-2px vertically even though both use the same
   .button-secondary class and render at the exact same height - confirmed
   directly, both boxes measure 42.5px tall. The actual cause: <a> defaults
   to vertical-align: baseline, <button> defaults to vertical-align: middle
   (ordinary browser UA defaults, inline text element vs. form control), so
   the button sits 1.5px lower on the line even though its box is identical
   in size, reads as "taller" since its bottom edge sticks out further. */
.page-downloads a,
.page-downloads button {
    vertical-align: middle;
}

/* Blog homepage (01.home, template: blog -> blog.html.twig). Category
   comes from each article's own taxonomy.category frontmatter (set via
   Obsidian YAML frontmatter, see extract_frontmatter() in
   publish_inbox.py), so this is styled generically, no per-category
   colors hardcoded anywhere, a brand-new category just works. */
.blog-hero {
    margin-bottom: 1.5em;
}
.blog-hero h1 {
    margin-bottom: 0.3em;
}

/* Category filter row: plain toggle buttons, no page reload, just
   show/hide on the cards below (see initBlogFilter() in custom.js). The
   active button uses the same accent blue (#1694CA) already used for
   links and the header/sidebar throughout the theme, not a new color. */
.category-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6em;
    margin: 1.5em 0;
}
.cat-btn {
    background: #fff;
    border: 1px solid #c4c4c4;
    border-radius: 999px;
    padding: 0.4em 1em;
    font-size: 0.9rem;
    cursor: pointer;
    color: #555;
}
.cat-btn:hover {
    border-color: #1694CA;
    color: #1694CA;
}
.cat-btn.is-active {
    background: #1694CA;
    border-color: #1694CA;
    color: #fff;
}
.cat-count {
    opacity: 0.75;
    font-size: 0.85em;
}

/* Card grid: auto-fill so it naturally goes from 1 column on a phone up
   to as many ~280px cards fit the sidebar-reduced content width, no
   fixed breakpoint list to keep in sync as the sidebar width changes. */
.article-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 1.2em;
    margin: 1.5em 0;
}
.article-card {
    display: block;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 1.2em;
    color: #333;
    text-decoration: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.article-card:hover {
    border-color: #1694CA;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    text-decoration: none;
}
.article-card h3 {
    margin: 0.4em 0 0.2em;
    font-size: 1.15rem;
}
.card-category {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #1694CA;
}
.card-date {
    margin: 0 0 0.5em;
    font-size: 0.8rem;
    color: #888;
}
.card-summary {
    margin: 0.3em 0 0;
    font-size: 0.9rem;
    color: #666;
    line-height: 1.4;
}
.card-tags {
    margin-top: 0.8em;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4em;
}
.tag-pill {
    background: #f0f2f4;
    color: #555;
    border-radius: 4px;
    padding: 0.15em 0.6em;
    font-size: 0.75rem;
}
.card-series-badge {
    display: inline-block;
    margin-top: 0.8em;
    font-size: 0.75rem;
    color: #888;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    padding: 0.15em 0.6em;
}
.blog-search-link {
    margin-top: 2em;
    color: #666;
}

@media only screen and (max-width: 600px) {
    .article-grid {
        grid-template-columns: 1fr;
    }
}

/* "More in <category>" (partials/page.html.twig): a small, unobtrusive
   list at the bottom of an article linking to other articles sharing its
   own taxonomy.category. Deliberately plain (a bordered box + a link
   list), not another card grid, this is a footer aside, not a second
   homepage. */
.related-articles {
    margin: 2.5em 0 1em;
    padding: 1.2em 1.5em;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    clear: both;
}
.related-articles h3 {
    margin: 0 0 0.6em;
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #1694CA;
}
.related-articles ul {
    margin: 0;
    padding-left: 1.2em;
}
.related-articles li {
    margin: 0.3em 0;
}

/* ==========================================================================
   Dark mode
   ==========================================================================
   nucleus.css/theme.css (the vendor CSS, never edited directly, see the top
   of this file) hardcode every color with no CSS custom properties at all,
   so this can't just "swap variables" globally. Instead: define this
   theme's own token set here, apply `var(--token)` overrides to the actual
   selectors that need to change for dark mode (the main content area and
   its components - code blocks, tables, cards, buttons, etc.), and leave
   the header/sidebar alone, both are already dark-colored (#1694CA blue,
   #38424D navy) and read fine against either mode without help.

   Two ways in: automatic, via the OS-level `prefers-color-scheme` media
   query, and manual, via the toggle button in the header (id="theme-toggle",
   see custom.js's initThemeToggle()), which sets `data-theme="dark"` or
   `"light"` on <html> and persists the choice in localStorage. An explicit
   manual choice always wins over the OS setting, in both directions, that's
   what the `:not([data-theme="light"])` guard on the media-query block is
   for: without it, choosing "light" manually on a dark-mode OS would do
   nothing, the media query's own dark values would keep winning the
   cascade. A small inline script in base.html.twig's <head> (before any
   CSS renders) applies a saved localStorage choice immediately, this is
   what avoids a flash of the wrong theme on every page load. */
:root {
    --bg-body: #fff;
    --bg-content: #fff;
    --text-main: #555;
    --text-heading: #333;
    --text-muted: #888;
    --border-color: #e0e0e0;
    --border-color-strong: #c4c4c4;
    --code-bg: #f6f6f6;
    --code-text: #555;
    --table-border: #eaeaea;
    --table-header-bg: #f7f7f7;
    --card-bg: #fff;
    --tag-bg: #f0f2f4;
    --tag-text: #555;
    --blockquote-border: #f0f2f4;
    --input-bg: #fff;
    --input-border: #c4c4c4;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg-body: #1a1d21;
        --bg-content: #1a1d21;
        --text-main: #c8ccd1;
        --text-heading: #f0f2f4;
        --text-muted: #8b929b;
        --border-color: #33383e;
        --border-color-strong: #454b52;
        --code-bg: #24282d;
        --code-text: #d8dbe0;
        --table-border: #33383e;
        --table-header-bg: #24282d;
        --card-bg: #23262b;
        --tag-bg: #2d3238;
        --tag-text: #c8ccd1;
        --blockquote-border: #33383e;
        --input-bg: #24282d;
        --input-border: #454b52;
    }
}

:root[data-theme="dark"] {
    --bg-body: #1a1d21;
    --bg-content: #1a1d21;
    --text-main: #c8ccd1;
    --text-heading: #f0f2f4;
    --text-muted: #8b929b;
    --border-color: #33383e;
    --border-color-strong: #454b52;
    --code-bg: #24282d;
    --code-text: #d8dbe0;
    --table-border: #33383e;
    --table-header-bg: #24282d;
    --card-bg: #23262b;
    --tag-bg: #2d3238;
    --tag-text: #c8ccd1;
    --blockquote-border: #33383e;
    --input-bg: #24282d;
    --input-border: #454b52;
}

/* Base page surface and text. */
body,
#body {
    background: var(--bg-body);
    color: var(--text-main);
}
h1, h2, h3, h4, h5, h6 {
    color: var(--text-heading);
}
hr {
    border-color: var(--border-color);
}

/* Code blocks and inline code spans. */
pre,
code {
    background: var(--code-bg);
    color: var(--code-text);
}
pre code {
    background: transparent;
}

/* Tables. */
table {
    border-color: var(--table-border);
}
th {
    background: var(--table-header-bg);
    color: var(--text-heading);
}
td {
    border-color: var(--table-border);
}

/* Blockquotes (plain ones; the github-markdown-alerts boxes below have
   their own border/title colors, already readable on a dark background
   since GitHub's own alert palette is deliberately high-contrast). */
blockquote {
    border-left-color: var(--blockquote-border);
}
blockquote p {
    color: var(--text-muted);
}

/* Search box and any other text input. */
input[type="text"],
input[type="search"],
textarea {
    background: var(--input-bg);
    border-color: var(--input-border);
    color: var(--text-main);
}

/* Blog homepage: card grid, category filter, tags. */
.article-card {
    background: var(--card-bg);
    border-color: var(--border-color);
    color: var(--text-main);
}
.article-card h3 {
    color: var(--text-heading);
}
.card-date {
    color: var(--text-muted);
}
.card-summary {
    color: var(--text-main);
}
.tag-pill {
    background: var(--tag-bg);
    color: var(--tag-text);
}
.cat-btn {
    background: var(--card-bg);
    border-color: var(--border-color-strong);
    color: var(--text-main);
}
.card-series-badge {
    border-color: var(--border-color);
    color: var(--text-muted);
}

/* "More in <category>" widget. */
.related-articles {
    border-color: var(--border-color);
}

/* Project accordion / step carousel (About page). */
.stat-row {
    border-color: var(--border-color);
}
.stat-label {
    color: var(--text-muted);
}
.project-accordion details {
    border-color: var(--border-color);
}
.step-dot {
    border-color: var(--border-color-strong);
}
.step-dot.is-active {
    background: var(--text-heading);
    border-color: var(--text-heading);
}
.step-prev,
.step-next {
    border-color: var(--border-color-strong);
    color: var(--text-main);
}

/* GitHub-style alert boxes (Obsidian callouts): the plugin's own CSS
   references --gh-alert-*-border-color / --gh-alert-*-title-color custom
   properties but never actually defines them anywhere in its own
   stylesheet, so they were resolving to nothing at all before this, not a
   dark-mode-specific problem. Defined here with GitHub's own real alert
   palette (both a light and dark set), fixing that gap as a side effect
   of adding dark mode support, not a change in behavior either mode used
   to have. */
:root {
    --gh-alert-note-border-color: #0969da;
    --gh-alert-note-title-color: #0969da;
    --gh-alert-tip-border-color: #1a7f37;
    --gh-alert-tip-title-color: #1a7f37;
    --gh-alert-important-border-color: #8250df;
    --gh-alert-important-title-color: #8250df;
    --gh-alert-warning-border-color: #9a6700;
    --gh-alert-warning-title-color: #9a6700;
    --gh-alert-caution-border-color: #cf222e;
    --gh-alert-caution-title-color: #cf222e;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --gh-alert-note-border-color: #58a6ff;
        --gh-alert-note-title-color: #58a6ff;
        --gh-alert-tip-border-color: #3fb950;
        --gh-alert-tip-title-color: #3fb950;
        --gh-alert-important-border-color: #a371f7;
        --gh-alert-important-title-color: #a371f7;
        --gh-alert-warning-border-color: #d29922;
        --gh-alert-warning-title-color: #d29922;
        --gh-alert-caution-border-color: #f85149;
        --gh-alert-caution-title-color: #f85149;
    }
}
:root[data-theme="dark"] {
    --gh-alert-note-border-color: #58a6ff;
    --gh-alert-note-title-color: #58a6ff;
    --gh-alert-tip-border-color: #3fb950;
    --gh-alert-tip-title-color: #3fb950;
    --gh-alert-important-border-color: #a371f7;
    --gh-alert-important-title-color: #a371f7;
    --gh-alert-warning-border-color: #d29922;
    --gh-alert-warning-title-color: #d29922;
    --gh-alert-caution-border-color: #f85149;
    --gh-alert-caution-title-color: #f85149;
}

/* Theme toggle button (id="theme-toggle", partials/base.html.twig, in
   #header next to the logo). Plain emoji glyph, not a Font Awesome icon,
   the bundled FA version's exact icon set wasn't worth depending on for
   one button. */
#theme-toggle {
    display: inline-block;
    vertical-align: middle;
    background: none;
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 4px;
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    padding: 0.3em 0.5em;
    margin-left: 0.6em;
    cursor: pointer;
}
#theme-toggle:hover {
    background: rgba(255, 255, 255, 0.15);
}
