/*
 * northstardata.com — the blog sheet. No generated page loads this.
 *
 * GENERATED by build.php from the sources below. DO NOT EDIT THIS FILE —
 * the next build overwrites it. Edit the source and run build.php.
 *
 *   _inc/css/blog.css
 *
 * asset_version 9
 */
        /* ====================================================================
         * THE BLOG SHEET — the one sheet no generated page loads.
         *
         * Emitted as /css/blog.css by _inc/assets.php and linked, after
         * /css/site.css, from every blog page: the NSBIZ-generated ones
         * through the chrome_head fragment, and the hand-written article
         * through tools/wrap-hand-article.php.
         *
         * WHAT IT IS FOR
         * It does two jobs that site.css cannot. First, it overrides the
         * inline <style> the NSBIZ blog generator writes into every page it
         * renders — that block is the generator's and cannot be removed from
         * this side, only out-specified. Second, it styles the things the site
         * itself has never had: a card index, a pager, tag chips, an archive
         * list, and long-form post typography with code blocks, tables and
         * blockquotes.
         *
         * CASCADE ORDER IS THE WHOLE TRICK. In manual mode the generator
         * prints its <style>, THEN chrome_head. So both files land after it and
         * win every equal-specificity contest, including :root custom
         * properties: the generator's `--primary` and `--accent` defaults are
         * re-declared by site.css's theme block one declaration later. See
         * templates/layout.php in the NSBIZ tree — read-only from here.
         *
         * TOKENS ONLY, NO HEX. Every colour is a var() off
         * _inc/theme-light.css / theme-dark.css, so the blog follows the theme
         * switch in _inc/head.php with no second step. tools/contrast-check.php
         * checks the pairs this sheet produces in both themes.
         *
         * --accent IS NEVER TEXT, A BORDER OR A FOCUS RING. It measures 1.86:1
         * on --bg. It appears below exactly twice, both times as a decorative
         * fill: the blockquote's edge and the featured bar's rule.
         *
         * SCALES AND CHROME COME FROM site.css, which carries the same
         * css/chrome.css the 18 generated pages inline. Nothing here restates
         * a scale, a reset, or a header / bottom-nav / footer rule.
         * ==================================================================== */

        /* ------------------------------------------------- generator reset --
         * The generator's inline block sets these on raw elements with
         * hardcoded hex. Restated here off tokens, at the same specificity,
         * later in the cascade.
         *
         * `main` is the one that matters: the generator gives it
         * `max-width: 760px; padding: 2rem 1.25rem 4rem`, which is a measure
         * chosen before this chrome existed and a top padding that puts the
         * first line under the fixed 64px header. --container and --nav-h
         * decide both now.
         */
        body {
            background: var(--bg);
            color: var(--text);
            font: 400 var(--fs)/1.6 var(--font);
        }
        /*
         * THE NAV OFFSET IS ON .blog-featured, NOT HERE.
         *
         * css/chrome.css gives <main> padding-top: var(--nav-h) to clear the
         * fixed header, which is right on a generated page where <main> is the
         * first thing in the body. On a blog page it is not: the featured
         * strip sits between the header and <main>, so the offset has to be on
         * whatever comes first or the strip renders UNDERNEATH the header. It
         * did, in the first preview — a white bar legible through the blur.
         */
        main {
            max-width: 76ch;
            margin: 0 auto;
            padding: var(--sp-7) var(--sp-5) var(--sp-8);
        }
        a { color: var(--link); }
        h1, h2, h3 { color: var(--text); font-family: var(--font-head); letter-spacing: -0.01em; }

        /* ------------------------------------------------- featured strip --
         * The slim bar under the header, carried by out/blog-chrome/
         * before.html. Not in css/chrome.css on purpose: it is the one piece
         * of this chrome that exists only on the blog, and putting it there
         * would ship it inline on all 18 generated pages that never render it.
         */
        .blog-featured {
            /* Clears the fixed header — see the note on main above. */
            margin-top: var(--nav-h);
            border-bottom: 1px solid var(--border);
            background: var(--surface);
        }
        .blog-featured-inner {
            display: flex;
            align-items: baseline;
            gap: var(--sp-2) var(--sp-3);
            flex-wrap: wrap;
            padding: var(--sp-3) 0;
            font-size: var(--fs-sm);
        }
        .blog-featured-label {
            font-family: var(--font-head);
            font-size: var(--fs-xs);
            font-weight: 700;
            letter-spacing: .06em;
            text-transform: uppercase;
            color: var(--accent-ink);
            flex: none;
        }
        .blog-featured-inner a {
            color: var(--link);
            font-weight: 500;
            text-decoration: none;
        }
        .blog-featured-inner a:hover { text-decoration: underline; }
        /* The one decorative use of --accent in this file. */
        .blog-featured-inner .blog-featured-label::after {
            content: "";
            display: inline-block;
            width: var(--sp-4);
            height: 2px;
            margin-left: var(--sp-2);
            vertical-align: middle;
            background: var(--accent);
        }

        /* ------------------------------------------- generator navigation --
         * Breadcrumb, "All posts" back-link and the auto-injected Archive
         * slot. All three are the generator's markup, emitted inside or just
         * outside <main>, and none of them can be removed from this side.
         */
        .b-crumbs {
            font-size: var(--fs-sm);
            color: var(--text-dim);
            margin: 0 0 var(--sp-5);
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: var(--sp-1) var(--sp-2);
        }
        .b-crumbs a { color: var(--link); text-decoration: none; }
        .b-crumbs a:hover { text-decoration: underline; }
        .b-crumbs [aria-current="page"] { color: var(--text-2); }
        .b-crumb-sep { color: var(--text-dim); }
        .b-backlink {
            margin: 0 0 var(--sp-4);
            font-size: var(--fs-sm);
            font-weight: 500;
        }
        .b-backlink a { color: var(--link); text-decoration: none; }
        .b-backlink a:hover { text-decoration: underline; }
        .b-archive-link-bar {
            max-width: 76ch;
            margin: 0 auto;
            padding: var(--sp-2) var(--sp-5) 0;
            font-size: var(--fs-sm);
        }
        .b-archive-link { color: var(--link); text-decoration: none; font-weight: 500; }
        .b-archive-link:hover { text-decoration: underline; }

        /* ------------------------------------------------------- the index --
         * One card per post. The generator wraps the whole card in an <a> and
         * puts the tag chips in a SIBLING .card-tags row, because an <a>
         * inside an <a> is invalid HTML — so the two have to be pulled back
         * together visually.
         */
        .card {
            display: block;
            color: inherit;
            text-decoration: none;
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: var(--r);
            overflow: hidden;
            margin: var(--sp-5) 0 0;
            box-shadow: var(--shadow);
            transition: border-color .15s, transform .15s;
        }
        .card:hover { border-color: var(--border-strong); transform: translateY(-2px); }
        .card-cover { aspect-ratio: 16 / 9; background: var(--surface-2); overflow: hidden; }
        .card-cover img { width: 100%; height: 100%; object-fit: cover; }
        .card-body { padding: var(--sp-5); }
        .card-title {
            font-family: var(--font-head);
            font-size: var(--fs-lg);
            font-weight: 700;
            line-height: 1.25;
            margin: 0;
            color: var(--text);
        }
        .card-meta {
            font-size: var(--fs-sm);
            color: var(--text-dim);
            margin-top: var(--sp-2);
        }
        .card-snippet {
            margin-top: var(--sp-3);
            color: var(--text-2);
            font-size: 15px;
        }
        /* Negative top margin pulls the chip row back under its own card. */
        .card-tags {
            margin: calc(-1 * var(--sp-4)) 0 0;
            padding: 0 var(--sp-5) var(--sp-5);
            background: var(--surface);
            border: 1px solid var(--border);
            border-top: 0;
            border-radius: 0 0 var(--r) var(--r);
        }

        /* --------------------------------------------------------- a post -- */
        .post-title {
            font-size: var(--fs-2xl);
            font-weight: 800;
            line-height: 1.1;
            margin: 0;
        }
        .post-meta {
            color: var(--text-dim);
            font-size: var(--fs-sm);
            margin-top: var(--sp-3);
            padding-bottom: var(--sp-5);
            border-bottom: 1px solid var(--border);
        }
        .post-photos {
            margin: var(--sp-6) 0;
            display: grid;
            grid-template-columns: minmax(0, 1fr);
            gap: var(--sp-5);
        }
        .post-photo { margin: 0; }
        .post-photo img { width: 100%; border-radius: var(--r); border: 1px solid var(--border); }
        .post-photo figcaption {
            margin-top: var(--sp-2);
            font-size: var(--fs-sm);
            color: var(--text-dim);
        }
        .post-tags { margin: var(--sp-6) 0 0; }

        /* ------------------------------------------------ post typography --
         * .post-body is the generator's wrapper around body_html, and the
         * hand-written article's wrapper around its own prose. Everything in
         * here has to survive arbitrary HTML, so it is set on elements rather
         * than on classes.
         */
        .post-body {
            margin-top: var(--sp-6);
            font-size: var(--fs-md);
            color: var(--text-2);
        }
        .post-body h2 {
            font-size: var(--fs-xl);
            font-weight: 700;
            line-height: 1.2;
            margin: var(--sp-7) 0 var(--sp-3);
        }
        .post-body h3 {
            font-size: var(--fs-lg);
            font-weight: 700;
            margin: var(--sp-6) 0 var(--sp-2);
        }
        .post-body p { margin: 0 0 var(--sp-4); }
        .post-body p:last-child { margin-bottom: 0; }
        .post-body strong { color: var(--text); font-weight: 600; }
        .post-body a { color: var(--link); }
        .post-body ul, .post-body ol { margin: 0 0 var(--sp-4); padding-left: var(--sp-5); }
        .post-body li { margin-bottom: var(--sp-2); }
        .post-body img {
            margin: var(--sp-5) 0;
            border-radius: var(--r);
            border: 1px solid var(--border);
        }
        .post-body hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-7) 0; }

        /*
         * Inline code takes the site's treatment, declaration for
         * declaration: the `code` rule at the foot of css/services.css, down
         * to the font stack, the .88em and the 2px 6px. --tile behind
         * --accent-ink is 4.97:1 and there is no separate code token.
         *
         * It is scoped to .post-body rather than left bare because the
         * generator's <pre> blocks and the hand-written article's both nest a
         * <code> inside, and the reset two rules down needs something to
         * out-specify.
         */
        .post-body code {
            font-family: 'JetBrains Mono', ui-monospace, 'Courier New', monospace;
            font-size: .88em;
            background: var(--tile);
            color: var(--accent-ink);
            padding: 2px 6px;
            border-radius: var(--r-sm);
        }
        /*
         * A code BLOCK is the site's dark band instead: --cta-bg behind
         * --cta-text, 17.75:1, and a pair tools/contrast-check.php already
         * measures in both themes. Reusing --tile here would put a long
         * monospace passage at 4.97:1, which clears the target and still reads
         * worse than this does.
         */
        .post-body pre {
            margin: 0 0 var(--sp-5);
            padding: var(--sp-4) var(--sp-5);
            background: var(--cta-bg);
            color: var(--cta-text);
            border-radius: var(--r);
            overflow-x: auto;
            font-size: var(--fs-sm);
            line-height: 1.6;
        }
        .post-body pre code {
            background: none;
            color: inherit;
            padding: 0;
            border-radius: 0;
            font-size: inherit;
        }

        .post-body blockquote {
            margin: 0 0 var(--sp-5);
            padding: var(--sp-4) var(--sp-5);
            background: var(--surface-2);
            color: var(--text-2);
            /* Decorative fill, not text and not a control edge — the second
               and last place --accent appears in this file. */
            border-left: 3px solid var(--accent);
            border-radius: 0 var(--r-sm) var(--r-sm) 0;
        }
        .post-body blockquote p:last-child { margin-bottom: 0; }

        .post-body table {
            width: 100%;
            margin: 0 0 var(--sp-5);
            border-collapse: collapse;
            font-size: 15px;
        }
        .post-body th, .post-body td {
            padding: var(--sp-2) var(--sp-3);
            border: 1px solid var(--border);
            text-align: left;
            vertical-align: top;
        }
        .post-body th {
            background: var(--surface-2);
            color: var(--text);
            font-family: var(--font-head);
            font-size: var(--fs-sm);
            font-weight: 700;
        }
        .post-body td { color: var(--text-2); }

        /* ------------------------------------------------------ tag chips -- */
        .badge {
            display: inline-block;
            margin: 0 var(--sp-1) var(--sp-1) 0;
            padding: 3px 10px;
            background: var(--tile);
            color: var(--accent-ink);
            border-radius: 999px;
            font-size: var(--fs-xs);
            font-weight: 600;
            text-decoration: none;
        }
        .badge:hover { background: var(--primary); color: var(--on-primary); }

        /* ---------------------------------------------------------- pager -- */
        .pager {
            margin: var(--sp-7) 0 0;
            display: flex;
            flex-wrap: wrap;
            gap: var(--sp-2);
            justify-content: center;
            align-items: center;
        }
        .pager a, .pager span {
            min-width: 38px;
            padding: var(--sp-2) var(--sp-3);
            border-radius: var(--r-sm);
            text-align: center;
            font-size: var(--fs-sm);
            font-weight: 600;
            text-decoration: none;
        }
        .pager a {
            background: var(--surface);
            color: var(--link);
            border: 1px solid var(--border-strong);
        }
        .pager a:hover { border-color: var(--link); }
        /* The current page is a filled pill, not a link. */
        .pager span { background: var(--primary); color: var(--on-primary); }
        /* Windowed pager (>7 pages): the ellipsis gaps are neutral text, not a
           pill. Specificity beats `.pager span`, as it does in the
           generator's own block. */
        .pager span.pager-gap {
            background: none;
            color: var(--text-dim);
            font-weight: 400;
            min-width: 0;
            padding: var(--sp-2) var(--sp-1);
        }

        /* ------------------------------------------------- empty / search -- */
        .empty {
            margin: var(--sp-6) 0;
            padding: var(--sp-4) var(--sp-5);
            background: var(--tile);
            color: var(--text-2);
            border-left: 3px solid var(--accent-ink);
            border-radius: 0 var(--r-sm) var(--r-sm) 0;
        }
        /*
         * The search RESULTS page. The generator's own search box lives in
         * .b-subnav, which manual mode never renders, so there is no input to
         * style here — only the heading the results template prints and the
         * <q> inside it.
         */
        main > h2 { font-size: var(--fs-xl); font-weight: 700; margin: 0 0 var(--sp-2); }
        main > h2 q { color: var(--accent-ink); font-style: normal; }
        main > p.card-meta { margin: 0 0 var(--sp-5); }

        /* -------------------------------------------------------- archive -- */
        .archive-title { font-size: var(--fs-2xl); font-weight: 800; line-height: 1.1; }
        .archive-intro {
            color: var(--text-dim);
            font-size: var(--fs-sm);
            margin: var(--sp-3) 0 var(--sp-5);
        }
        /* The one real text input on a manual-mode blog page. --border-strong
           is the control-edge token: 3.37:1 on --bg, over the non-text
           target. --border would be a hairline on a control. */
        .archive-filter {
            width: 100%;
            height: var(--ctl-h);
            padding: 0 var(--sp-3);
            margin: 0 0 var(--sp-6);
            background: var(--surface);
            color: var(--text);
            border: 1px solid var(--border-strong);
            border-radius: var(--r-sm);
            font: 400 var(--fs)/1 var(--font);
        }
        .archive-filter:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
        .archive-list {
            list-style: none;
            margin: 0;
            padding: 0;
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: var(--r);
        }
        .archive-row {
            padding: var(--sp-3) var(--sp-4);
            border-bottom: 1px solid var(--border);
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: var(--sp-1) var(--sp-3);
        }
        .archive-row:last-child { border-bottom: 0; }
        /* `display:flex` overrides the UA [hidden] rule — restore it, or the
           client-side filter stops hiding rows. Same fix as the generator's. */
        .archive-row[hidden] { display: none; }
        .archive-link {
            flex: 1 1 16rem;
            color: var(--link);
            font-weight: 600;
            text-decoration: none;
        }
        .archive-link:hover { text-decoration: underline; }
        .archive-date { color: var(--text-dim); font-size: var(--fs-sm); white-space: nowrap; }
        .archive-tags { display: flex; flex-wrap: wrap; gap: var(--sp-1); flex-basis: 100%; }
        .archive-noresults { color: var(--text-dim); padding: var(--sp-4) 0; }

        /* ------------------------------------------- the hand-written post --
         * blog/extending-pick-with-command-line-php.html predates NSBIZ and
         * carries its own body markup, which tools/wrap-hand-article.php keeps
         * byte for byte. These are the classes only that file uses.
         *
         * Its .post-cta is NOT the .post-cta in css/services.css: that one is
         * a two-column grid of .cta-copy and .cta-go, and this one is an h3, a
         * p and a bare <a>. Same tokens, same band, different markup — so it
         * is a different rule, not a copy of one.
         */
        .container { max-width: 100%; }
        .post-header { margin-bottom: 0; }
        .post-breadcrumb {
            display: inline-block;
            margin-bottom: var(--sp-4);
            color: var(--link);
            font-size: var(--fs-sm);
            font-weight: 500;
            text-decoration: none;
        }
        .post-breadcrumb:hover { text-decoration: underline; }
        .post-breadcrumb i { font-size: .85em; margin-right: var(--sp-1); }
        .post-kicker {
            font-family: var(--font);
            font-size: var(--fs-xs);
            font-weight: 600;
            letter-spacing: .08em;
            text-transform: uppercase;
            color: var(--accent-ink);
            margin-bottom: var(--sp-2);
        }
        /* The dot separators in the byline. Decorative — the dates and labels
           either side carry the meaning. */
        .post-meta .dot {
            display: inline-block;
            width: 3px;
            height: 3px;
            margin: 0 var(--sp-2);
            border-radius: 50%;
            background: var(--border-strong);
            vertical-align: middle;
        }
        .post-cta {
            margin-top: var(--sp-7);
            padding: var(--sp-6);
            background: var(--cta-bg);
            border: 1px solid var(--art-border);
            border-radius: var(--r-lg);
        }
        .post-cta h3 {
            color: var(--cta-text);
            font-size: var(--fs-xl);
            font-weight: 800;
            line-height: 1.15;
        }
        .post-cta p { color: var(--cta-dim); margin-top: var(--sp-2); max-width: 60ch; }
        .post-cta a {
            display: inline-flex;
            align-items: center;
            gap: var(--sp-2);
            height: var(--ctl-h);
            margin-top: var(--sp-5);
            padding: 0 20px;
            background: var(--cta-mail-bg);
            color: var(--cta-mail-ink);
            border-radius: var(--r);
            font: 600 15px/1 var(--font);
            text-decoration: none;
        }
        .post-cta a:hover { background: var(--cta-mail-hover); }
        /* The focus ring is re-scoped inside the dark band, as it is for
           .post-cta in css/services.css: --focus would reach 3.14:1 there. */
        .post-cta a:focus-visible { outline-color: var(--cta-accent); }

        /* ===================================================== <= 720px ==== */
        @media (max-width: 720px) {
            main { padding: var(--sp-5) var(--sp-4) var(--sp-7); }
            .b-archive-link-bar { padding: var(--sp-2) var(--sp-4) 0; }
            .post-title, .archive-title { font-size: var(--fs-xl); }
            .post-body { font-size: var(--fs); }
            .post-body h2 { font-size: var(--fs-lg); }
            .post-cta { padding: var(--sp-5) var(--sp-4); }
            .post-cta h3 { font-size: var(--fs-lg); }
            .card-body, .card-tags { padding-left: var(--sp-4); padding-right: var(--sp-4); }
        }
