/*
 * AxisOps Labs
 *
 * Dark by default, and not as a style choice: this site is photo-led, and
 * hardware photography — racks, boards, benches, solder — is mostly dark.
 * A dark ground lets a photo sit in the page instead of punching a hole in it.
 *
 * Brand tokens are the axisops.com palette so labs reads as a sibling, but the
 * density is deliberately different: longer measure, bigger images, no CTA.
 */

:root {
    --ao-blue:   #00E5E3;   /* AxisOps Blue   — links, accents */
    --ao-mint:   #69FFCD;   /* AxisOps Mint   — visited, secondary */
    --ao-green:  #B3F985;
    --ao-yellow: #F9F871;
    --ao-purple: #B388FF;
    --ao-red:    #FF6B7A;   /* defect/CVE marker only — never an accent */

    --bg:        #0B0D10;   /* near-black, slightly cool */
    --bg-raised: #14181D;
    --bg-code:   #0F1317;
    --rule:      #232A32;
    --ink:       #E8EDF2;
    --ink-dim:   #9AA7B4;

    --measure: 68ch;        /* long for prose, short enough to read */
    --wide: 1180px;

    --sans: 'Bai Jamjuree', system-ui, -apple-system, sans-serif;
    --mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', Consolas, monospace;
}

/* Light theme. Dark is the native state above; this block applies when
   the toggle or the OS sets data-theme="light" on <html>. Accent hues
   darken from the brand cyan family so text links hold contrast on
   paper; the header, footer and mobile menu are brand furniture with
   fixed colours and ignore the theme on purpose, as the main site's do. */
[data-theme="light"] {
    --ao-blue:   #087F7E;
    --ao-mint:   #0E8A66;
    --ao-green:  #2E7D32;
    --ao-yellow: #8A6D00;
    --ao-purple: #6A4FD8;
    --ao-red:    #C62828;

    --bg:        #F6F8F9;
    --bg-raised: #FFFFFF;
    --bg-code:   #E9EEF2;
    --rule:      #D9DFE5;
    --ink:       #15191E;
    --ink-dim:   #4A5866;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    font-size: 17px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

.inner { max-width: var(--wide); margin: 0 auto; padding: 0 20px; }

a { color: var(--ao-blue); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

.skip-link {
    position: absolute; left: -9999px; top: 0;
    background: var(--ao-blue); color: #000; padding: 10px 16px; z-index: 100;
}
.skip-link:focus { left: 0; }

:focus-visible { outline: 2px solid var(--ao-blue); outline-offset: 3px; }

/* ---------- head ---------- */

/* The two-bar family header, like-for-like with www.axisops.com: black
   utility strip, AxisOps-cyan primary bar, white pill CTA. The bars commit
   to one palette in both themes, exactly as the main site's do. */
.site-head-utility {
    background: #000;
    text-align: right;
}
/* The main site puts the side gutter on the BAR (30px) and runs the inner
   content flush to the 1200px box; measured there: content edges at
   (vw-1200)/2 exactly. The utility strip keeps 20px inner padding, as the
   main site's does. */
.site-head-utility .inner,
.site-head .inner,
.site-foot .inner { max-width: 1200px; }
.site-head, .site-foot { padding-left: 30px; padding-right: 30px; }
.site-head .inner, .site-foot .inner { padding-left: 0; padding-right: 0; }
.site-head-utility .inner {
    padding-top: 10px; padding-bottom: 15px;
}
.site-head-utility a {
    color: #00E5E3; font-size: 17px; font-weight: 600; margin-left: 30px;
}
.site-head-utility a:hover { color: #FFF; text-decoration: none; }

.site-head {
    position: sticky; top: 0; z-index: 50;
    background: #00E5E3;
}
/* The bar's metrics are the main site's: 95px tall, 200px logo, 23px/700
   links, the 10/40 pill. Below 900px the type steps down so the links
   survive without the main site's burger menu. */
.site-head .inner {
    display: flex; align-items: center; justify-content: space-between;
    gap: 24px; min-height: 95px; flex-wrap: wrap;
}
/* The masthead is the AxisOps logo with "Labs" set beside it. The artwork
   itself is never edited to add the sub-brand: the mark stays as supplied,
   and the word is typeset next to it. */
.wordmark {
    display: inline-flex;
    align-items: center;
    gap: 11px;
    font-weight: 700;
    font-size: 23px;
    letter-spacing: -0.01em;
    color: #000;
}
.wordmark:hover { text-decoration: none; }

/* Sized by height so the lockup holds at any font size; width follows the
   artwork's own 946.75 x 230.35 ratio. align-self keeps the mark optically
   seated against the baseline of the word beside it. */
.ao-logo {
    width: 200px;
    height: auto;
    display: block;
    align-self: center;
    color: #000;
}
/* On the cyan bar the device's own brand cyan would vanish into the
   background, so the whole mark renders black, exactly as the main
   site's header logo does. Scoped to the header: anywhere else the
   artwork keeps its supplied colours. */
.site-head .ao-logo path { fill: currentColor; }

/* Not accented: the logo beside it already carries the brand cyan, and one
   accent per lockup is the rule here. Weight does the separating. */
.wordmark-labs {
    color: rgba(0,0,0,0.6);
    font-weight: 600;
    letter-spacing: 0.02em;
}

.site-head nav { display: flex; gap: 25px; flex-wrap: wrap; align-items: center; }
.site-head nav a { color: #000; font-size: 23px; line-height: 35px; font-weight: 700; }
.site-head nav a:hover { color: #FFF; text-decoration: none; }
.nav-out::after { content: " \2197"; font-size: 0.85em; }
.nav-cta {
    background: #FFF; border-radius: 25px; padding: 10px 40px;
}
.site-head nav a.nav-cta:hover { background: #000; color: #00E5E3; }
/* The main site's mobile menu, copied: below 900px the inline nav hides,
   the burger shows, and the menu is a fullscreen white overlay of centred
   28px links with the cyan pill. Offset clears the utility strip + bar. */
.burger-wrapper { display: none; }
.mobile-nav {
    position: fixed; left: 0; top: 0; width: 100%; height: 0;
    padding: 0; background: #FFF; overflow: hidden;
    transition: all 0.3s ease-in-out; z-index: 40;
    display: none;
}
@media (max-width: 900px) {
    .site-head nav { display: none; }
    .burger-wrapper { display: block; }
    .mobile-nav { display: block; }
    /* One row, always: the lockup shrinks so logo, "// Labs" and the
       burger share the bar without wrapping. */
    .site-head .inner { flex-wrap: nowrap; }
    .ao-logo { width: 160px; }
    .wordmark { font-size: 18px; gap: 8px; }
    .site-head-utility a { font-size: 15px; }
}
.mobile-nav.active { height: 100vh; padding: 20px 40px; }
.mobile-nav-content {
    padding-top: 140px;
    display: flex; flex-direction: column; align-items: center;
    justify-content: center; height: calc(100vh - 180px); gap: 24px;
}
.mobile-nav-content .nav-item {
    margin: 0; font-size: 28px; font-weight: 700; color: #000;
    opacity: 0; transform: translateY(20px);
    transition: opacity 0.3s ease, transform 0.3s ease;
}
.mobile-nav-content .nav-item:hover { text-decoration: none; color: #000; }
.mobile-nav-content .cta {
    background: var(--ao-blue); border-radius: 25px; padding: 10px 40px;
}
.mobile-nav-content .mobile-portal { font-size: 18px; font-weight: 600; opacity: 0.85; }
.mobile-nav.active .nav-item { opacity: 1; transform: translateY(0); }
.mobile-nav.active .nav-item:nth-child(1) { transition-delay: 0.1s; }
.mobile-nav.active .nav-item:nth-child(2) { transition-delay: 0.2s; }
.mobile-nav.active .nav-item:nth-child(3) { transition-delay: 0.3s; }
.mobile-nav.active .nav-item:nth-child(4) { transition-delay: 0.4s; }

/* Lives in the black utility strip beside the Harbour link, so it wears
   the strip's colours: cyan at rest, white on hover, in both themes. */
.theme-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; padding: 0;
    margin-right: 24px; vertical-align: middle;
    background: transparent; border: 1px solid rgba(255,255,255,0.3);
    border-radius: 0; color: #00E5E3; cursor: pointer;
}
.theme-toggle:hover { border-color: #FFF; color: #FFF; }
.theme-toggle svg { display: none; width: 14px; height: 14px; }
/* The icon names the mode you are in: moon dark, sun light, the
   half-filled circle when following the system. */
html[data-theme-mode="dark"] .theme-toggle svg.theme-toggle__moon { display: block; }
html[data-theme-mode="light"] .theme-toggle svg.theme-toggle__sun { display: block; }
html[data-theme-mode="system"] .theme-toggle svg.theme-toggle__auto { display: block; }
html:not([data-theme-mode]) .theme-toggle svg.theme-toggle__auto { display: block; }

.burger-button {
    display: block; position: relative; width: 20px; height: 18px;
    color: #000; background: transparent; cursor: pointer;
}
.burger-button input { display: none; }
.burger-button span {
    display: block; position: absolute; width: 100%; height: 3px;
    background: #000; border-radius: 12px; opacity: 1; left: 0;
    transform: rotate(0deg); transition: .25s ease-in-out;
}
.burger-button span:nth-of-type(1) { top: 0; transform-origin: left center; }
.burger-button span:nth-of-type(2) { top: 50%; transform: translateY(-50%); transform-origin: left center; }
.burger-button span:nth-of-type(3) { top: 100%; transform-origin: left center; transform: translateY(-100%); }
.burger-button input:checked ~ span:nth-of-type(1) { transform: rotate(45deg); left: 3px; top: 0; }
.burger-button input:checked ~ span:nth-of-type(2) { width: 0%; opacity: 0; }
.burger-button input:checked ~ span:nth-of-type(3) { transform: rotate(-45deg); left: 3px; top: 14px; }

/* ---------- hero ---------- */

.hero { padding: 72px 0 40px; border-bottom: 1px solid var(--rule); }
.hero h1 {
    margin: 0 0 14px;
    font-size: clamp(2rem, 5vw, 3.1rem);
    line-height: 1.1; letter-spacing: -0.02em; text-wrap: balance;
}
.hero-sub { margin: 0; max-width: 60ch; color: var(--ink-dim); font-size: 1.08rem; }

.list-head { padding: 56px 0 28px; border-bottom: 1px solid var(--rule); }
.list-head h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 4vw, 2.4rem); letter-spacing: -0.02em; }
.list-head p { margin: 0; color: var(--ink-dim); }

.crumb { margin: 0 0 10px; font-size: 0.9rem; }
.crumb a { color: var(--ink-dim); }

/* ---------- entry stream ---------- */

.stream { padding: 40px 0 80px; }

.entry {
    display: grid; gap: 22px;
    padding: 28px 0; border-bottom: 1px solid var(--rule);
}
/* Why the lead entry is bigger: a photo-led site should open with a photo at
   a size that earns its place, not a row of equal thumbnails. */
.entry-lead { grid-template-columns: 1fr; padding-top: 8px; }
/* Why a height cap rather than a bare 16/9: the lead slot was designed for a
 * photograph, where filling the first screen is the point. Roughly half the
 * entries here carry generated geometry instead, and at full width that
 * spends the entire opening screen on decoration before a reader reaches a
 * single word. Capped, it reads as a banner. Checked against a rendered
 * screenshot, not reasoned about. */
.entry-lead .entry-img img {
    aspect-ratio: 16/9; max-height: 340px; object-position: center;
}
.entry-lead h2 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); }

.entry-img { display: block; }
.entry-img img {
    width: 100%; height: auto; display: block;
    border-radius: 3px; background: var(--bg-raised);
    aspect-ratio: 3/2; object-fit: cover;
}

.entry h2 { margin: 0 0 8px; font-size: 1.35rem; line-height: 1.25; letter-spacing: -0.01em; }
.entry h2 a { color: var(--ink); }
.entry h2 a:hover { color: var(--ao-blue); text-decoration: none; }

.entry-meta { margin: 0 0 10px; color: var(--ink-dim); font-size: 0.86rem; }
.entry-sum { margin: 0; color: var(--ink-dim); max-width: var(--measure); }

/* Listing rows carry their own spacing rather than relying on .entry's gap,
   which is the gutter between thumbnail and text and resolves to nothing on a
   single-column row. Row gap spaces the lines within an entry; the padding
   spaces one entry from the next. */
.entry-row {
    grid-template-columns: 1fr;
    gap: 0;
    padding: 20px 0 22px;
}
.entry-row .entry-body { display: flex; flex-direction: column; gap: 7px; }

/* ---------- lab furniture ----------
   This is a lab notebook, not a magazine. The entries are dated
   observations in a series, so the index is numbered like one and the
   structural type is mono: it reads as instrument output rather than as
   editorial, which is the one visual claim this site can make honestly.
   Index only; inside an entry the prose is left alone. */

.entry-row { position: relative; }

/* The ordinal is the entry's position in the archive, counted from the
   oldest. It is set by the template, not by CSS counters, so it stays
   correct on a tag page showing a subset. */
.entry-no {
    font-family: var(--mono);
    font-size: 0.72rem;
    /* Dimmed ink rather than --rule: that is a border colour and sits at
       about 1.3:1 on this ground, which is invisible rather than quiet. */
    color: var(--ink-dim);
    opacity: 0.55;
    letter-spacing: 0.04em;
    font-variant-numeric: tabular-nums;
    user-select: none;
}
.entry-row h3 { display: flex; align-items: baseline; gap: 12px; }

/* Hairline ticks either side of the year, like a scale division. */
.year-rule {
    display: flex;
    align-items: center;
    gap: 14px;
}
.year-rule::after {
    content: "";
    flex: 1;
    height: 1px;
    /* Holds its weight for the first part of the span, then fades: a scale
       division rather than a full-width border. */
    background: linear-gradient(to right, var(--rule) 0 42%, transparent);
}
.year-rule span { font-variant-numeric: tabular-nums; }

/* The index subhead carries the shape of the archive as data. */
.index-stats {
    font-family: var(--mono);
    font-size: 0.78rem;
    color: var(--ink-dim);
    letter-spacing: 0.02em;
}
.index-stats b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }

/* Why the separator is on the tag and not whitespace in the template: the
   loop emits the anchors with nothing between them, so an inline-block with
   only a right margin collapses to "#meta#verification". The archive index
   routinely carries five tags on one entry, where that reads as a single
   long token. A left margin on every tag but the first keeps each hash
   legible as the start of a word. */
.tag {
    display: inline-block;
    font-family: var(--mono); font-size: 0.78rem;
    color: var(--ao-mint);
}
.tag + .tag { margin-left: 10px; }
.tag::before { content: "#"; opacity: 0.6; }

.tag-cloud .inner { display: flex; flex-wrap: wrap; gap: 12px; padding-top: 36px; padding-bottom: 80px; }
/* The sibling margin above is for inline tag rows in article metadata; in
   the wrapped cloud the flex gap does the spacing, and the margin was
   indenting every wrapped row 10px past the first. */
.tag-cloud .tag + .tag { margin-left: 0; }
.tag-big {
    border: 1px solid var(--rule); border-radius: 3px;
    padding: 8px 14px; font-size: 0.95rem;
}
.tag-big span { color: var(--ink-dim); margin-left: 6px; }

/* Year rule on the archive index. A hairline with the year sitting in it,
 * rather than a heading with space around it — the list is long and the
 * separator should divide without interrupting the scan. */
.year-rule {
    display: flex; align-items: center; gap: 16px;
    margin: 44px 0 20px; font-family: var(--mono);
    font-size: 0.95rem; font-weight: 500; color: var(--ink-dim);
}
.year-rule::after {
    content: ""; flex: 1; height: 1px; background: var(--rule);
}
.entry-row h3 { margin: 0; font-size: 1.22rem; line-height: 1.3; }
/* Margins zeroed because .entry-body above spaces these with flex gap;
   leaving both in place double-spaced the title from its metadata. */
.entry-row .entry-meta, .entry-row .entry-sum { margin: 0; }
.entry-row .entry-sum { line-height: 1.55; }
.entry-row h3 a { color: var(--ink); text-decoration: none; }
.entry-row h3 a:hover { color: var(--ao-blue); text-decoration: underline; }

/* Archive badge — set on an entry imported from an acquired brand's site.
 * Purple rather than blue or mint: those already mean "link" and "tag" on
 * this site, and an archive marker must not look like either. Deliberately
 * quiet — it is a provenance note, not a banner. */
.archive-badge {
    margin: 0 0 10px;
    font-family: var(--mono);
    font-size: 0.74rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ao-purple);
}
.archive-badge a { color: inherit; text-decoration-color: var(--rule); }

/* Mitigation marker. Red is reserved for this one meaning across the site:
   there is code you can run. Laid out with flex/gap so the icon's optical
   centre sits on the cap height rather than the baseline. */
.mitig-badge {
    display: flex; align-items: center; gap: 7px;
    margin: 0 0 12px;
    font-family: var(--mono);
    font-size: 0.74rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ao-red);
}
.mitig-badge a { color: inherit; text-decoration-color: color-mix(in srgb, var(--ao-red) 45%, transparent); }
.mitig-badge a:hover { text-decoration-color: currentColor; }
.mitig-badge .bug-ico { flex: 0 0 auto; }
.mitig-cve {
    color: var(--ink-dim);
    text-transform: none;
    letter-spacing: 0;
}
/* On listings the marker is one signal among several, so it loses the CVE id
   and drops a step in size rather than competing with the entry title. */
.mitig-sm { font-size: 0.68rem; margin: 2px 0 8px; }

.empty { color: var(--ink-dim); padding: 40px 0; }

.pager { display: flex; gap: 20px; padding-top: 32px; }

/* ---------- full entry ---------- */

.entry-head { padding: 56px 0 26px; }
.entry-head h1 {
    margin: 0 0 12px; max-width: 24ch;
    font-size: clamp(1.9rem, 4.6vw, 3rem);
    line-height: 1.12; letter-spacing: -0.02em; text-wrap: balance;
}

/* Full-bleed lead image. The photo IS the evidence on this site, so it gets
   the viewport width, not a column.
   Why a fixed px cap and not vh: 72vh tied the image to the window height, so
   the larger the reader's monitor the further the first paragraph sat below
   the fold — worst exactly for the desktop engineer this site is written for.
   520px keeps a rack or board photo legible while leaving the opening of the
   article on screen at laptop height. */
.lead-figure { margin: 0 0 44px; }
/* Generated concept art is composed to 16:9 and loses its subject when
   cropped; a photograph survives it. So: cap by aspect-ratio rather than by
   a fixed pixel height, which keeps the whole frame at every width. On a
   very wide display 520px of a 3440px-wide image was a letterbox strip. */
.lead-figure img {
    width: 100%;
    height: auto;
    display: block;
    aspect-ratio: 16 / 9;
    max-height: min(58vh, 620px);
    object-fit: cover;
    object-position: center;
}

.entry-content { max-width: var(--measure); padding-top: 4px; }
.entry-content > * { margin-left: auto; margin-right: auto; }
.entry-content p { margin: 0 0 1.35em; }
.entry-content h2 {
    margin: 2.2em 0 0.6em; font-size: 1.45rem; letter-spacing: -0.01em;
    padding-top: 0.6em; border-top: 1px solid var(--rule);
}
.entry-content h3 { margin: 1.8em 0 0.5em; font-size: 1.15rem; }
.entry-content ul, .entry-content ol { margin: 0 0 1.35em; padding-left: 1.3em; }
.entry-content li { margin-bottom: 0.45em; }
.entry-content blockquote {
    margin: 1.6em 0; padding: 2px 0 2px 18px;
    border-left: 2px solid var(--ao-blue); color: var(--ink-dim);
}
.entry-content a { text-decoration: underline; text-underline-offset: 3px; }

/* Headings get a quiet anchor rather than a loud one. */
.entry-content .headerlink { opacity: 0; margin-left: 8px; font-weight: 400; color: var(--ink-dim); }
.entry-content h2:hover .headerlink,
.entry-content h3:hover .headerlink { opacity: 1; }

/* Figures inside prose break OUT of the measure — a board photo at 68ch is
   useless. Captions are content here, not a footnote. */
.entry-content figure {
    margin: 2.4em 0;
    width: min(var(--wide), calc(100vw - 40px));
    max-width: none;
    margin-left: 50%; transform: translateX(-50%);
}
.entry-content figure img { width: 100%; height: auto; display: block; border-radius: 3px; }

/* The TNF archive posts carry their own inline <img class="float-right">
   and float-left, written in 2017 against that site's stylesheet. Without
   these rules the images render full-width, which is not how the posts were
   published — and "reproduced as published" has to be true of the layout as
   well as the words. Capped and un-floated on narrow screens, where a 40%
   float would leave an unreadable column beside it. */
.entry-content img.float-right,
.entry-content img.float-left {
    width: 38%;
    max-width: 280px;
    height: auto;
    border-radius: 3px;
    margin: 4px 0 18px;
}
.entry-content img.float-right { float: right; margin-left: 26px; }
.entry-content img.float-left  { float: left;  margin-right: 26px; }

@media (max-width: 620px) {
    .entry-content img.float-right,
    .entry-content img.float-left {
        float: none;
        width: 100%;
        max-width: none;
        margin: 0 0 22px;
    }
}
.entry-content figcaption,
.lead-figure figcaption,
.page-body figcaption {
    margin: 12px auto 0; max-width: var(--measure);
    color: var(--ink-dim); font-size: 0.9rem; line-height: 1.5;
    border-left: 2px solid var(--rule); padding-left: 14px;
}
.lead-figure figcaption,
.page-body figcaption { margin-inline: 20px; }

/* Captions sit inside the frame, over a scrim at the foot of the image,
   like a plate caption. The scrim is always dark because it sits on a
   photograph, so the text colour is fixed rather than themed. Below
   700px the caption drops back under the image, where a long caption
   over a phone-width photo would cover the subject. */
@media (min-width: 700px) {
    .lead-figure,
    .entry-content figure,
    .page-body figure { position: relative; }
    .entry-content figcaption,
    .lead-figure figcaption,
    .page-body figcaption {
        position: absolute; inset: auto 0 0 0;
        margin: 0; max-width: none; border-left: 0;
        padding: 56px 22px 16px;
        background: linear-gradient(to top,
            rgba(4, 9, 11, 0.92) 0%, rgba(4, 9, 11, 0.62) 55%,
            rgba(4, 9, 11, 0) 100%);
        color: rgba(237, 244, 246, 0.94);
        font-size: 0.88rem; line-height: 1.5;
        border-radius: 0 0 3px 3px;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
    }
    /* The scrim spans the frame; the words keep a readable measure. */
    .entry-content figcaption,
    .page-body figcaption { padding-right: max(22px, calc(100% - 78ch)); }
    /* The lead is full-bleed; keep its caption text on the wide column. */
    .lead-figure figcaption {
        padding-inline: max(22px, calc((100% - var(--wide)) / 2));
        border-radius: 0;
    }
}

/* A gallery: several photos, no JavaScript. */
.entry-content .gallery {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
}
.entry-content .gallery img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/3; }

/* ---------- code ---------- */

.entry-content code {
    font-family: var(--mono); font-size: 0.88em;
    background: var(--bg-code); border: 1px solid var(--rule);
    border-radius: 3px; padding: 1px 5px;
}
.entry-content pre {
    /* Fixed dark in both themes: the pygments palette is drawn for a dark
       ground, and a dark code panel on paper reads as a terminal, which
       is what it is. Inline code follows the theme via --bg-code. */
    background: #0F1317; color: #E8EDF2; border: 1px solid var(--rule); border-radius: 4px;
    padding: 16px 18px; overflow-x: auto; line-height: 1.5;
    width: min(calc(var(--measure) + 160px), calc(100vw - 40px));
    margin-left: 50%; transform: translateX(-50%);
}
.entry-content pre code { background: none; border: 0; padding: 0; font-size: 0.86rem; }

.entry-content table {
    width: 100%; border-collapse: collapse; margin: 1.8em 0; font-size: 0.93rem;
}
.entry-content th, .entry-content td {
    text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--rule);
}
.entry-content th { color: var(--ink); font-weight: 600; }
.entry-content td { color: var(--ink-dim); }
.entry-content td:first-child { color: var(--ink); }

/* ---------- foot ---------- */

.entry-foot { padding: 44px 0 90px; max-width: var(--measure); }
.foot-tags { margin: 0 0 18px; color: var(--ink-dim); font-size: 0.9rem; }
.foot-next a { font-weight: 600; }

/* The footer mirrors www.axisops.com's: black ground, white prose on the
   left, the site's pages as large cyan links on the right with the pill CTA,
   a rule, then the legal row spread across the width. */
.site-foot { background: #000; color: #FFF; padding: 48px 30px 64px; }
.site-foot a { color: #FFF; font-weight: 600; }
/* The footer is brand furniture: its colours do not follow the theme. */
.site-foot a:hover { color: #00E5E3; text-decoration: none; }
.foot-grid { display: flex; justify-content: space-between; gap: 32px 48px; flex-wrap: wrap; }
.foot-left { max-width: 62ch; }
.foot-what { margin: 0 0 18px; color: rgba(255,255,255,0.85); font-size: 0.95rem; }
.foot-meta { margin: 0; font-size: 0.9rem; color: rgba(255,255,255,0.7); }
.foot-meta .foot-icon svg { vertical-align: -2px; }
.foot-where { margin: 16px 0 0; font-size: 0.9rem; color: rgba(255,255,255,0.7); }
/* Badge sizes are the main site's, rationale included: the CE mark is
   portrait where the ISO mark is landscape, so an equal height would
   render it visibly the weaker of the pair; 110px gives it ~87% of the
   ISO badge's area, bottom-aligned by the row. */
.cert-badges { display: flex; align-items: flex-end; gap: 20px; margin-top: 24px; flex-wrap: wrap; }
.cert-badges .iso-27001 { height: 97px; width: auto; }
.cert-badges .cyber-essentials { height: 110px; width: auto; }
.foot-right { text-align: right; }
.foot-right p { margin: 0 0 26px; }
.foot-right a { font-size: 23px; font-weight: 700; color: #00E5E3; }
.foot-right a:hover { color: #FFF; }
.foot-right a.foot-cta {
    background: #00E5E3; color: #000;
    border-radius: 25px; padding: 10px 40px; display: inline-block;
}
.foot-right a.foot-cta:hover { background: #FFF; color: #000; }
.foot-rule { border: 0; border-top: 1px solid rgba(255,255,255,0.25); margin: 30px 0 40px; }
.foot-bottom {
    display: flex; justify-content: space-between; gap: 12px 24px;
    flex-wrap: wrap; font-size: 0.85rem; color: rgba(255,255,255,0.7);
}
@media (max-width: 700px) {
    .foot-right { text-align: left; }
    .foot-bottom { display: block; }
    .foot-bottom span { display: block; margin-bottom: 14px; }
}

/* ---------- search ---------- */

/* min-height keeps the footer out of the first view: an empty results
   list otherwise pulls it up to half the screen. */
.search-body .inner { padding: 36px 20px 80px; max-width: 760px; min-height: 60vh; }
.search-input {
    width: 100%; padding: 14px 16px; font-size: 1.05rem;
    font-family: inherit; color: var(--ink);
    background: var(--bg-raised); border: 1px solid var(--rule);
    border-radius: 3px;
}
.search-input:focus { outline: 2px solid var(--ao-blue); outline-offset: 2px; }
.search-status { margin: 14px 0 0; font-size: 0.9rem; color: var(--ink-dim); }
.search-results { list-style: none; margin: 28px 0 0; padding: 0; }
.search-results li { margin: 0 0 30px; }
.search-results li > a { font-size: 1.15rem; font-weight: 700; }
.search-meta { margin: 4px 0 6px; font-family: var(--mono); font-size: 0.78rem; color: var(--ink-dim); }
.search-snip { margin: 0; font-size: 0.92rem; color: var(--ink-dim); }

/* ---------- responsive ---------- */

@media (min-width: 760px) {
    /* The homepage stream's 300px thumbnail column. Listing rows are
       excluded: they carry a narrower thumbnail of their own, laid out by
       .entry-has-thumb, and this width would squeeze their summary. */
    .entry:not(.entry-lead):not(.entry-row) { grid-template-columns: 300px 1fr; align-items: start; }
}

@media (max-width: 759px) {
    body { font-size: 16px; }
    .hero { padding: 48px 0 32px; }
    /* On a phone the breakout tricks would overflow; keep everything in flow. */
    .entry-content figure,
    .entry-content pre {
        width: auto; margin-left: 0; transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
}


/* Set below the registration line and quieter than it: the mark assertion is
   required, not something a reader needs to notice first. */
.foot-tm {
    margin: 6px 0 0;
    color: var(--ink-dim);
    font-size: 0.8rem;
}

/* A path separator between the parent mark and the sub-brand: reads as a
   namespace, which is the relationship. Dimmer than the word it precedes so
   it separates without becoming a third element competing for attention. */
.wordmark-sep {
    color: var(--rule);
    font-weight: 700;
    letter-spacing: -0.02em;
}

/* ---------- index thumbnails ----------
   Only rows whose lead is a photograph get one (see archives.html): concept
   art sits on nearly every entry, so a thumbnail on all of them would be
   decoration. On a photo row it is a signal — this entry has evidence in it.
   Hidden below the breakpoint where the text column needs the full width. */
.entry-has-thumb { grid-template-columns: 1fr; }

@media (min-width: 900px) {
    .entry-has-thumb {
        display: grid;
        grid-template-columns: 1fr 180px;
        gap: 28px;
        align-items: start;
    }
    .entry-thumb {
        display: block;
        margin-top: 4px;
        border-radius: 3px;
        overflow: hidden;
        line-height: 0;
    }
    .entry-thumb img {
        width: 100%;
        height: 112px;
        object-fit: cover;
        display: block;
        /* Quiet by default so the index still reads as a list of entries
           rather than a gallery; full strength on hover or keyboard focus
           of the row. */
        opacity: 0.72;
        transition: opacity 120ms ease;
    }
    .entry-has-thumb:hover .entry-thumb img,
    .entry-has-thumb:focus-within .entry-thumb img { opacity: 1; }
}

/* A correction on a reproduced archive piece. Quieter than the archive badge
   above it but not hidden: a reader about to act on 2017 advice needs it
   before the first paragraph, not in a footnote. */
.archive-caveat {
    margin: 10px 0 0;
    max-width: var(--measure);
    padding: 10px 14px;
    border-left: 2px solid var(--ao-red);
    background: var(--bg-raised);
    color: var(--ink-dim);
    font-size: 0.92rem;
    line-height: 1.55;
}
.archive-caveat strong { color: var(--ink); }

/* A standalone page is text only, with no full-bleed lead image to tie the
   header to the body, so .entry-content's centred measure left the h1
   starting a couple of hundred pixels to the left of the prose it
   introduces. On a page, align the two. */
.page-head .inner {
    /* --measure is in ch and resolves against the element's own font size, so
       putting it on a 2.4rem h1 yields a box far wider than the body's. Set
       the box on the container at body size instead, and let the h1 keep its
       own scale inside it. */
    max-width: calc(68 * 1ch);
    margin-left: auto;
    margin-right: auto;
}

/* The opening paragraph of a standalone page carries the argument the page
   exists to make, so it is set larger than the body that follows it. Pages
   are few and their first line is always the thesis; an article's lede is
   handled by its own summary, so this is scoped to .page-body. */
.page-body > p:first-of-type {
    font-size: 1.22rem;
    line-height: 1.5;
    color: var(--ink);
    max-width: 54ch;
    margin: 0 0 1.4em;
}
.page-body > p:first-of-type strong { color: var(--ao-blue); font-weight: 700; }

/* Read next. An archive whose argument runs across entries has to give the
   reader a path between them; a single link to the index is a dead end. */
.foot-related { margin: 2.6em 0 1.6em; }
.foot-related h2 {
    margin: 0 0 12px;
    font-family: var(--mono);
    font-size: 0.74rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-dim);
    border: 0;
    padding: 0;
}
.foot-related ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.foot-related li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; }
.foot-related a { font-weight: 600; }
.foot-related-date {
    font-family: var(--mono);
    font-size: 0.74rem;
    color: var(--ink-dim);
    opacity: 0.8;
}

/* 404 page */
.list-head .eyebrow-404 { margin: 0 0 10px; font-family: var(--mono); font-size: 0.76rem;
    letter-spacing: 0.1em; color: #00E5E3; }   /* brand moment: same in both themes */
.nf-body .inner { min-height: 0; }
.nf-links { display: flex; flex-wrap: wrap; gap: 10px 24px; margin: 22px 0 8px; }
.nf-links a { font-weight: 600; }
.nf-head { margin: 40px 0 0; font-size: 0.78rem; font-family: var(--mono);
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-dim); }
