/* Self-hosted OFL fonts (fonts/). CSP forbids a font CDN. */
@font-face {
    font-family: 'Space Grotesk';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/fonts/space-grotesk-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Space Grotesk';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/fonts/space-grotesk-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/source-sans-3-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/fonts/source-sans-3-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/fonts/source-sans-3-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Serif 4';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/source-serif-4-400-italic.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Serif 4';
    font-style: italic;
    font-weight: 600;
    font-display: swap;
    src: url('/fonts/source-serif-4-600-italic.woff2') format('woff2');
}

:root {
    --primary-color: #0f172a;
    /* sky-700. Don't lighten it: sky-600 (#0284c7) is 4.10:1 on --white and
       3.91:1 on --light-bg, under WCAG AA's 4.5:1 for normal text, and fails
       both as text on a light surface and as a surface under white text.
       sky-700 is 5.93:1 / 5.67:1. If a component needs an exception to this
       token, fix the token instead.

       Dark mode redefines this to #6fc3ff. */
    --secondary-color: #0369a1;
    /* Amber from the logo mark. Used for Friday/live emphasis and door
       kickers - not for primary actions (those are --action). */
    --accent-color: #f59e0b;
    /* One primary action fill site-wide: nav Zoom CTA, .btn-primary, hero
       CTAs. #0b1729 on #38bdf8 is 9.4:1. Hover lifts to #7dd3fc. */
    --action: #38bdf8;
    --action-hover: #7dd3fc;
    --action-ink: #0b1729;
    --font-display: 'Space Grotesk', 'Segoe UI', sans-serif;
    --font-body: 'Source Sans 3', 'Segoe UI', sans-serif;
    --font-quote: 'Source Serif 4', Georgia, 'Times New Roman', serif;
    --text-color: #1f2937;
    --light-bg: #f8fafc;
    --white: #ffffff;
    --border-color: #e2e8f0;
    --shadow: 0 2px 4px rgba(15, 23, 42, 0.08);
    --shadow-lg: 0 8px 24px rgba(15, 23, 42, 0.10);
    /* --bg-color and --card-bg point at --white, which the dark block
       redefines to #1c2530, so both follow the theme with no second
       declaration. `background: var(--bg-color)` on #searchInput has no
       fallback, so without this declaration the field computes to
       transparent.

       --text-muted is #5b6573, 6.2:1 on --light-bg. It needs a dark value and
       has one below. */
    --bg-color: var(--white);
    --card-bg: var(--white);
    --text-muted: #5b6573;
    /* The page's widths. Keep them here, together, because the number that
       matters is the relationship between them - a reading column that is a
       small fraction of the frame around it reads as a broken page, however
       correct its character count is.

       --frame is the outer frame: header, breadcrumb, footer and .container all
       sit in it, so the chrome and the content share one edge. Keep it at
       75rem: at 65rem the resource directory's card grid drops from three
       columns to two and that page grows 44% taller, which is a worse trade
       than a wide header.

       --measure is the reading column. 46rem is 736px, about 86 characters at
       the 1.0625rem body size. Article pages sit at --measure + 4rem = 800px
       inside the 1200px frame; the gap between the two is what the type size
       closes, not a narrower column.

       Both are rem and not ch on purpose. `ch` in a custom property resolves
       against the *using* element's font rather than against :root, so a single
       `60ch` resolves to different widths on a section and on a .pull-quote and
       the blocks stop lining up, which is the thing the measure exists to fix.

       --breakout is the width diagrams, comparison tables and code blocks widen
       to, centred on the column (the "Break-out" rule further down). 880px is
       the widest any of them is drawn at, hence px. It reaches 72px past the
       column on each side, so the floating table of contents measures its
       offset from this and not from the column. Change it and re-derive that
       rail's media-query floor as well, which cannot read a custom property. */
    --frame: 75rem;
    --measure: 46rem;
    --breakout: 880px;
    /* Type scale ------------------------------------------------------------
       Thirteen steps, none below --fs-12.

       Named in px and declared in rem on purpose. The name says what it
       renders at the default root size, which is what anyone reaching for a
       step is actually thinking about; the rem is what makes it honour a
       reader who has changed that size. Do not add a step without a reason
       that survives being asked twice: a scale sprawls one plausible one-off
       at a time. */
    --fs-12: 0.75rem;
    --fs-13: 0.8125rem;
    --fs-14: 0.875rem;
    --fs-15: 0.9375rem;
    --fs-16: 1rem;
    --fs-17: 1.0625rem;   /* running text */
    --fs-18: 1.125rem;
    --fs-20: 1.25rem;
    --fs-24: 1.5rem;
    --fs-28: 1.75rem;
    --fs-32: 2rem;
    --fs-40: 2.5rem;
    --fs-48: 3rem;
    /* Offset used for in-page anchor jumps so a linked heading lands below
       the sticky header instead of behind it. main.js refines this to the
       header's real measured height; the value here is the no-JS fallback
       for the wide-desktop header (~97px + breathing room). */
    --header-height: 7rem;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: var(--font-body);
    line-height: 1.6;
    color: var(--text-color);
    background-color: var(--light-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, .logo .logo-title, .door-card h2, .next-session__when, .stat-strip__num {
    font-family: var(--font-display);
    letter-spacing: -0.02em;
}

/* Keep anchor jumps (in-page #links and cross-page navigation to a fragment)
   from scrolling the target under the sticky header. Applies site-wide to
   every id target, not just specific elements. */
html {
    scroll-padding-top: var(--header-height);
}

/* In hamburger mode (<=1023px) the header is position:static (see the mobile
   nav rules) so it scrolls away and never covers anchor targets: no offset
   needed. main.js also detects this at runtime via the header's position. */
@media (max-width: 1023px) {
    :root {
        --header-height: 0px;
    }
}

/* Skip to content link (accessibility) */
.skip-link {
    position: absolute;
    top: -100%;
    left: 1rem;
    z-index: 9999;
    padding: 0.75rem 1.5rem;
    background: var(--primary-color);
    color: var(--white);
    text-decoration: none;
    border-radius: 0 0 6px 6px;
    font-weight: 600;
}

.skip-link:focus {
    top: 0;
}

/* Visually hidden (accessible to screen readers) */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Hamburger button (hidden on desktop) */
.hamburger {
    display: none;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    cursor: pointer;
    padding: 0;
    color: var(--white);
    width: 2.25rem;
    height: 2.25rem;
    font-size: var(--fs-18);
    line-height: 1;
    align-items: center;
    justify-content: center;
    transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.15s ease;
    flex-shrink: 0;
}

.hamburger:hover {
    border-color: rgba(255, 255, 255, 0.55);
    background-color: rgba(255, 255, 255, 0.14);
    transform: scale(1.06);
}

.hamburger:active {
    transform: scale(0.96);
}

.hamburger:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.85);
    outline-offset: 2px;
}

/* Header & Navigation */
header {
    background-color: var(--primary-color);
    color: var(--white);
    padding: 1rem 0;
    box-shadow: var(--shadow-lg);
    position: sticky;
    top: 0;
    z-index: 1000;
    contain: layout style;
}

.header-content {
    max-width: var(--frame);
    margin: 0 auto;
    padding: 0 2rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
}

.logo .logo-title {
    font-size: var(--fs-28);
    font-weight: 700;
}

.logo p {
    font-size: var(--fs-14);
    opacity: 0.9;
    margin-top: 0.25rem;
}

nav ul {
    display: flex;
    list-style: none;
    gap: 2rem;
    align-items: center;
}

nav a {
    color: var(--white);
    text-decoration: none;
    font-weight: 500;
    transition: opacity 0.3s;
}

nav a:hover {
    opacity: 0.8;
}

/* Keep individual top-level labels from breaking mid-phrase (e.g.
   "Behind the / Scenes"). Harmless on mobile, where items are full-width. */
header nav > ul > li > a,
header nav > ul > li > button {
    white-space: nowrap;
}

/* Single-row header nav on desktop.
   With 7 menu groups + Search + the Zoom CTA, the default 2rem gap plus the
   theme toggle's `margin-left:auto` pushed the whole <nav> onto a second row
   below the logo. Fix: push the menu (and toggle) to the right of the logo,
   order the toggle last, and compact spacing/labels so everything - including
   an interior page's wider `.dropdown-toggle.active` pill - fits within the
   1200px header. flex-wrap stays `wrap`, so narrower screens still degrade
   gracefully instead of clipping. Scoped to >=769px so the <=768px mobile
   menu (column layout, hamburger, toggle ordering) is untouched. */
@media (min-width: 1024px) {
    header nav {
        margin-left: auto;
    }

    header .theme-toggle {
        order: 1;
        margin-left: 0.25rem;
        margin-right: 0;
    }

    header nav ul {
        gap: 0.75rem;
        font-size: var(--fs-15);
    }

    .nav-cta {
        padding: 0.5rem 0.75rem;
    }

    .nav-cta-item {
        margin-left: 0.25rem;
    }

    .nav-coffee {
        padding: 0.375rem 0.625rem;
    }

    /* Compact, uniform "current page" pill so an active item (dropdown
       toggle OR plain link) doesn't widen the row enough to wrap. */
    header nav .dropdown-toggle.active,
    header nav a.active,
    header nav a[aria-current="page"] {
        padding: 0.25rem 0.5rem;
    }
}

/* Hide the "Cloud Security Office Hours" tagline below 1200px so the inline
   nav has room to stay on one line on laptops; it returns on wide screens. */
@media (max-width: 1200px) {
    .logo p {
        display: none;
    }
}

/* Laptop band: tagline is hidden here, so tighten nav spacing + padding a
   touch more so the full inline menu fits on one line right down to 1024px,
   below which it collapses to the hamburger (see the max-width: 1023px rules). */
@media (min-width: 1024px) and (max-width: 1200px) {
    .header-content {
        padding: 0 0.75rem;
    }
    /* nav's auto left-margin (set at >=1024px) would consume all the row's
       slack and shove the theme toggle onto a 2nd row; a small fixed margin
       keeps logo + nav + toggle on one line right down to 1024px. */
    header nav {
        margin-left: 0.5rem;
    }
    /* There's ~72px of slack at 1024, so items don't need crushing
       together. At 4px the plain "Resources" link (no caret to separate it)
       visually merges with "Threat Intel". */
    header nav ul {
        gap: 0.5rem;
    }
    .nav-cta {
        padding: 0.375rem 0.5rem;
    }
    .nav-coffee {
        padding: 0.25rem 0.5rem;
    }
}

/* Header tweaks: logo layout, active link, and focus states */
.logo {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.logo-link {
    text-decoration: none;
    color: inherit;
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
}

.logo-mark {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25));
}

@media (max-width: 480px) {
    .logo-mark { width: 30px; height: 30px; }
}

.header-content .logo .logo-title {
    margin: 0;
    line-height: 1;
}



nav a {
    transition: opacity 0.3s, background-color 0.15s, transform 0.12s;
}

nav a.active,
nav a[aria-current="page"] {
    background-color: rgba(255, 255, 255, 0.08);
    padding: 0.375rem 0.625rem;
    border-radius: 6px;
}

nav a:focus {
    outline: 3px solid rgba(52, 152, 219, 0.18);
    outline-offset: 2px;
    border-radius: 6px;
}

/* Hero Section */
.hero {
    background:
        radial-gradient(ellipse 70% 55% at 70% 20%, rgba(56, 189, 248, 0.22) 0%, transparent 60%),
        radial-gradient(ellipse 55% 70% at 15% 85%, rgba(245, 158, 11, 0.14) 0%, transparent 55%),
        linear-gradient(135deg, #0b1729 0%, #0f172a 45%, #0369a1 100%);
    color: #ffffff;
    padding: 4rem 2rem;
    text-align: center;
    position: relative;
    overflow: hidden;
}

/* Soften the dark-hero → light-page cliff. Absolute so it doesn't add height. */
.hero::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 4.5rem;
    background: linear-gradient(to bottom, transparent, var(--light-bg));
    pointer-events: none;
    z-index: 2;
}

.hero h1,
.hero h2 {
    font-size: var(--fs-40);
    margin-bottom: 1rem;
    color: #ffffff;
    font-family: var(--font-display);
    letter-spacing: -0.025em;
    text-wrap: balance;
}

.hero p {
    font-size: var(--fs-20);
    max-width: 800px;
    margin: 0 auto 2rem;
    opacity: 0.95;
    color: #ffffff;
}

.cta-buttons {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
}

/* Quiet one-liner under a CTA row; reads as an aside, not a fourth button.
   The link needs an explicit color: .cta-section only styles .btn children,
   so a bare inline <a> would otherwise fall back to the browser's default
   blue and go nearly unreadable on the dark-mode panel. */
.support-note {
    margin-top: 1rem;
    text-align: center;
    font-size: var(--fs-14);
    opacity: 0.8;
}

.support-note a {
    color: var(--primary-color);
    font-weight: 600;
    text-decoration: underline;
}

.btn {
    padding: 0.75rem 1.5rem;
    border-radius: 5px;
    text-decoration: none;
    font-weight: 600;
    transition: all 0.3s;
    display: inline-block;
}

/* One primary action colour with .nav-cta and hero CTAs: sky-400 fill,
   near-black ink (9.4:1). Do not swap back to sky-700/white - that split
   the site into two "primary" blues. */
.btn-primary {
    background-color: var(--action);
    color: var(--action-ink);
}

.btn-primary:hover {
    background-color: var(--action-hover);
    color: var(--action-ink);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

.btn-secondary {
    background-color: var(--white);
    color: var(--primary-color);
}

.btn-secondary:hover {
    background-color: var(--light-bg);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

/* Main Content */
.container {
    max-width: var(--frame);
    margin: 0 auto;
    padding: 2rem 2rem 3rem;
}

.section {
    margin-bottom: 2.5rem;
}

.section h2 {
    font-size: var(--fs-32);
    color: var(--primary-color);
    margin-bottom: 1.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border-color);
}

/* Marketing surfaces: hierarchy from type weight, not a wiki rule. */
.home-page .section h2 {
    border-bottom: none;
    padding-bottom: 0;
    letter-spacing: -0.02em;
}

.section h3 {
    font-size: var(--fs-24);
    color: var(--primary-color);
    margin: 2rem 0 1rem;
}

/* Meetings table of contents */
#table-of-contents ul {
    list-style: none;
    padding: 0;
    margin: 0;
    columns: 2;
    column-gap: 2rem;
}

#table-of-contents li {
    font-size: var(--fs-17);
    line-height: 1.5;
    padding: 0.5rem 0;
    break-inside: avoid;
}

#table-of-contents li a {
    text-decoration: none;
}

#table-of-contents li a:hover {
    text-decoration: underline;
}

@media (max-width: 768px) {
    #table-of-contents ul {
        columns: 1;
    }
}


/* Filter/Search Bar */

.filter-bar {
    padding: 1.25rem 1.5rem;
    background: var(--white);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    margin-bottom: 2rem;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.search-box {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Primary search input */
#searchInput, #meetingSearch {
    width: 100%;
    padding: 0.75rem 1rem 0.75rem 2.5rem;
    font-size: var(--fs-16);
    font-family: inherit;
    color: var(--text-color);
    background: var(--bg-color);
    border: 2px solid var(--border-color);
    border-radius: 8px;
    outline: none;
    box-sizing: border-box;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

#searchInput::placeholder, #meetingSearch::placeholder {
    color: var(--text-muted, #94a3b8);
    font-size: var(--fs-15);
}

#searchInput:focus, #meetingSearch:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(41, 128, 185, 0.14);
    background: var(--white);
}

/* Search icon injected via the .search-wrapper div that main.js creates */
.search-wrapper::before {
    content: '';
    position: absolute;
    left: 0.9rem;
    top: 50%;
    transform: translateY(-50%);
    width: 17px;
    height: 17px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%2394a3b8'%3E%3Cpath fill-rule='evenodd' d='M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z' clip-rule='evenodd'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-size: contain;
    pointer-events: none;
    z-index: 1;
}

/* Dark mode: search bar */
[data-theme="dark"] .filter-bar {
    background: #1a2535;
    border-color: #2d3f55;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
}

[data-theme="dark"] #searchInput, [data-theme="dark"] #meetingSearch {
    background: #1e2a3a;
    border-color: #2d3f55;
    color: var(--text-color);
}

[data-theme="dark"] #searchInput:focus, [data-theme="dark"] #meetingSearch:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
    background: #162030;
}

.filter-buttons {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.filter-buttons h4 {
    flex-basis: 100%;
    width: 100%;
    margin-top: 1.5rem;
    margin-bottom: 0.5rem;
    font-size: var(--fs-14);
    text-align: left;
}

.filter-buttons h4:first-child {
    margin-top: 0;
}

/* chat-resources.html: the category/contributor filter block sits under the
   search row, and the overflow contributors are revealed by a button. */
#filterButtons {
    margin-top: 1rem;
}

#showAllContributorsBtn {
    margin-top: 0.5rem;
}

/* `display: contents` so the contributor buttons join the parent flex row
   rather than forming a block of their own. `.is-hidden` overrides it with
   !important when the overflow group is collapsed. */
#topContributors,
#allContributors {
    display: contents;
}

.filter-btn {
    text-decoration: none;
    padding: 0.5rem 1rem;
    border: 2px solid var(--border-color);
    background-color: var(--white);
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s;
    font-size: var(--fs-14);
}

.filter-btn:hover,
.filter-btn.active {
    background-color: var(--secondary-color);
    color: var(--white);
    border-color: var(--secondary-color);
}

.filter-group-label {
    align-self: center;
    padding: 0 0.375rem;
    margin-left: 0.25rem;
    font-size: var(--fs-14);
    font-weight: 600;
    color: var(--text-color);
    opacity: 0.7;
    border-left: 2px solid var(--border-color);
}

.filter-btn.speaker {
    border-style: dashed;
}

.filter-btn.speaker.active,
.filter-btn.speaker:hover {
    border-style: solid;
}

/* Glossary search bar */
.glossary-filter-bar {
    margin: 1.5rem 0 0.5rem;
}

.glossary-filter-bar .search-box {
    width: 100%;
}

.glossary-filter-bar .search-wrapper {
    width: 100%;
}

.glossary-count {
    margin: 0.625rem 0 0;
    font-size: var(--fs-14);
    color: #666;
    opacity: 0.85;
}

[data-theme="dark"] .glossary-count {
    color: #aaa;
}

/* Glossary definition list */
.glossary-list {
    margin: 1rem 0 2.5rem;
    padding: 0;
}

.glossary-list dt {
    font-weight: 700;
    font-size: var(--fs-18);
    color: var(--primary-color);
    margin-top: 1.5rem;
    padding-bottom: 0.375rem;
    border-bottom: 1px solid var(--border-color);
    line-height: 1.35;
}

.glossary-list dt:first-of-type {
    margin-top: 0.5rem;
}

.glossary-list dd {
    margin: 0.625rem 0 0 0;
    padding: 0 0 0.25rem 0;
    line-height: 1.65;
    font-size: var(--fs-16);
    color: var(--text-color);
}

.glossary-list dd a {
    color: var(--secondary-color);
    text-decoration: underline;
}

/* Internal cross-references between glossary terms - styled like normal
   hyperlinks so they're obviously clickable. */
.glossary-list dd a.glossary-link {
    color: var(--secondary-color);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    font-weight: 500;
    transition: background-color 0.15s ease;
}

.glossary-list dd a.glossary-link:hover,
.glossary-list dd a.glossary-link:focus {
    background-color: rgba(52, 152, 219, 0.12);
    text-decoration-thickness: 2px;
}

.glossary-list dt:target {
    background-color: rgba(241, 196, 15, 0.22);
    padding: 0.25rem 0.5rem;
    border-radius: 4px;
    margin-left: -0.5rem;
}

[data-theme="dark"] .glossary-list dd a.glossary-link {
    color: #7cc1ee;
}

[data-theme="dark"] .glossary-list dd a.glossary-link:hover,
[data-theme="dark"] .glossary-list dd a.glossary-link:focus {
    background-color: rgba(124, 193, 238, 0.18);
}

[data-theme="dark"] a.glossary-link:focus,
[data-theme="dark"] a.glossary-link:hover {
    background-color: rgba(124, 193, 238, 0.18);
}

[data-theme="dark"] .glossary-list dt:target {
    background-color: rgba(241, 196, 15, 0.28);
}

[data-theme="dark"] .glossary-list dt {
    color: #7cc1ee;
}

[data-theme="dark"] .glossary-list dd {
    color: var(--text-color);
}

/* Heading anchors on the glossary page get a touch more breathing room.
   The sticky-header offset is handled globally via html { scroll-padding-top }. */
.content-section h2[id] {
    margin-top: 2.5rem;
    padding-top: 0.5rem;
}

.content-section h2[id]:first-of-type {
    margin-top: 1.5rem;
}

@media (max-width: 768px) {
    .glossary-list dt {
        font-size: var(--fs-16);
        margin-top: 1.25rem;
    }
    .glossary-list dd {
        font-size: var(--fs-15);
        line-height: 1.6;
    }
}

.show-all-btn {
    padding: 0.625rem 1.5rem;
    border: 2px solid var(--primary-color);
    background-color: var(--primary-color);
    color: var(--white);
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s;
    font-size: var(--fs-15);
    font-weight: 600;
    margin: 0.5rem 0;
}

.show-all-btn:hover {
    background-color: var(--secondary-color);
    border-color: var(--secondary-color);
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}

/* Search Filter Container Layout */
.search-filter-container {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 1rem;
}

.search-filter-container #searchInput, .search-filter-container #meetingSearch {
    flex: 1;
    min-width: min(300px, 100%);
}

.search-filter-container .show-all-btn {
    margin: 0;
}

/* chat-resources.html date filter. chat-resources.js toggles `.is-hidden`;
   don't use inline styles here, because the CSP drops them. */
.date-filter-controls {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-left: auto;
}

.date-select,
.date-input {
    padding: 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 4px;
    font-size: var(--fs-14);
    background-color: var(--white);
    color: var(--text-color);
}

.date-filter-btn {
    padding: 0.5rem 1rem;
    background: var(--primary-color);
    color: var(--white);
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: var(--fs-14);
}

/* Fixed grey in both themes, so the foreground is pinned rather than taken
   from --white (which is a dark surface under [data-theme="dark"] and would
   land at 3.3:1 on this background). */
.date-filter-btn--clear {
    background: #6c757d;
    color: #ffffff;
}

/* Resource Cards Grid */
/* auto-FILL, not auto-fit. auto-fit collapses the tracks it has no items for,
   and the 1fr then stretches whatever is left across the entire row: a section
   holding one card computed to `1136px 0px 0px` and rendered that card at the
   full container width, with a 1100x160 preview - a 6.88:1 letterbox out of a
   16:9 source, i.e. about a quarter of the picture. Two cards gave 552px each,
   3.23:1. auto-fill keeps the empty tracks at their share, so a card is always
   one column wide no matter how few of them a section has. Identical to auto-fit
   for any grid whose first row is full, which is every other grid on the site. */
.resource-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: 2rem;
    margin-top: 2rem;
}

/* News page: per-date H2 group headings sit inline in the grid and span
   the full row so subsequent cards wrap underneath. Visible headings, so
   each date's article cluster is part of the document outline for AT and
   crawlers. */
.news-date-heading {
    grid-column: 1 / -1;
    margin: 1.5rem 0 0;
    padding-bottom: 0.375rem;
    font-size: var(--fs-18);
    font-weight: 600;
    color: var(--text-color);
    border-bottom: 1px solid var(--border-color);
}
.news-date-heading:first-child {
    margin-top: 0;
}
.news-date-heading .news-date-count {
    font-weight: 400;
    /* Pinned to a literal: a translucent white tuned for a dark surface is
       1.05:1 on #f8fafc in light mode.
       Do NOT swap this for var(--text-muted) because the light values happen to
       match: the dark overrides below (line ~984 and in the generated mirror)
       put this back to translucent white on purpose, and the token resolves to
       #94a3b8 in dark, so the swap silently changes dark mode. */
    color: #5b6573;
    font-size: var(--fs-16);
}

/* ---------------------------------------------------------------------------
   Classes used instead of inline style= attributes.

   The CSP blocks style attributes under style-src-attr exactly as it blocks
   inline <style> under style-src-elem, so an inline style works on localhost
   and is dropped in production. Use a class from here instead.

   The u-* utilities carry !important on purpose. They stand in for inline
   attributes, which outrank every stylesheet rule, and several land on
   elements already matched by a descendant selector such as
   `.content-section p`, which would otherwise win on specificity. The
   semantic classes below do not need it and do not use it.
   --------------------------------------------------------------------------- */

/* Author card, emitted by tools/inject_session_blocks.py onto 91 pages. None
   of it applied in production: square avatar stacked above the text, full
   container width, no separator rule, default-blue underlined links. */
.author-card {
    max-width: var(--measure);
    margin: 3rem auto 2rem;
    padding: 1.5rem;
    border-top: 2px solid var(--border-color, #e0e0e0);
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
    flex-wrap: wrap;
}
.author-card .author-card__avatar { border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.author-card .author-card__body   { flex: 1; min-width: 240px; }
.author-card .author-card__kicker {
    margin: 0 0 0.25rem;
    font-size: var(--fs-14);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    /* #5b6573 is 5.65:1 on --light-bg and still reads as muted against
       --text-color. The fallback is redundant with the :root token. */
    color: var(--text-muted, #5b6573);
}
.author-card .author-card__name { margin: 0 0 0.5rem; font-size: var(--fs-18); font-weight: 600; }
.author-card .author-card__bio  { margin: 0; color: var(--text-muted, #555); }

/* #94a3b8 is the muted grey the dark theme uses elsewhere (6.04:1 here).
   Mirrored in the prefers-color-scheme block below; the two dark branches
   have to stay in step. */
[data-theme="dark"] .author-card .author-card__kicker,
[data-theme="dark"] .author-card .author-card__bio { color: #94a3b8; }

/* Built by main.js rather than written into a page, so it needs its own
   dark value: #666 is 3.22:1 on --light-bg in dark mode. */
[data-theme="dark"] .source-filter-label { color: #94a3b8; }
[data-theme="dark"] .news-date-heading .news-date-count { color: rgba(255, 255, 255, 0.85); }

/* Spacing utilities. See the !important note above. */
.u-mt-05  { margin: 0.5rem 0 0 0 !important; }
.u-mt-075 { margin-top: 0.75rem !important; }
.u-mt-1   { margin-top: 1rem !important; }
.u-mt-125 { margin-top: 1.25rem !important; }
.u-mt-15  { margin-top: 1.5rem !important; }
.u-mt-2   { margin-top: 2rem !important; }
.u-mt-25  { margin-top: 2.5rem !important; }
.u-mb-025 { margin: 0 0 0.25rem !important; }
.u-mb-05  { margin: 0 0 0.5rem !important; }
.u-m-0    { margin: 0 !important; }
.u-ml-05  { margin-left: 0.5rem !important; }
.u-my-15  { margin: 1.5rem 0 !important; }
.u-list-indent { margin: 0 0 0.5rem 1.5rem !important; }
.u-center { text-align: center !important; }

/* Hidden by default. Anything that reveals one of these must set an
   explicit display value: `el.style.display = ''` only clears an inline
   value, so it leaves an element hidden by a class still hidden. */
/* Revealed by 404.js when it recognises an old URL. An id selector so it
   outranks .info-box--blue's margin, while still losing to the inline
   display 404.js sets - which .is-hidden, carrying !important, would not. */
#redirect-hint { display: none; margin: 1.5rem 0; }
.no-results { display: none; }

/* Source-filter heading built by main.js. It used style.cssText, which CSP
   blocks exactly as it blocks a style attribute, so the heading rendered
   unstyled. */
.source-filter-label {
    clear: both;
    width: 100%;
    margin: 0 0 0.5rem;
    font-size: var(--fs-14);
    color: #666;
    text-align: left;
}

/* Horizontal scroller around a wide table. */
.table-scroll { overflow-x: auto; }

/* Comparison matrices on cloud-security-certifications and
   shared-responsibility-model. */
.matrix-table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
.matrix-table--wide { min-width: 780px; }
.matrix-table th { padding: 0.75rem; border-bottom: 2px solid #ddd; }
.matrix-table thead tr { background: #f4f6f8; text-align: left; }
.matrix-table td { padding: 0.625rem; border-bottom: 1px solid #eee; }
.matrix-table td.matrix-cell--no      { background: #fde2e2; }
.matrix-table td.matrix-cell--yes     { background: #d1fae5; }
.matrix-table td.matrix-cell--partial { background: #fef3c7; }

/* Narrow key/value table and figures on cloud-deployment.html. */
.compact-table { border-collapse: collapse; width: 100%; max-width: 600px; }
.compact-table th {
    text-align: left;
    padding: 0.5rem;
    border-bottom: 2px solid var(--border-color, #e0e0e0);
}
.compact-table td { padding: 0.5rem; }
.compact-table td.compact-table__key { font-weight: 600; }
.figure-wide   { margin: 1.5rem auto; max-width: 880px; }
.figure-narrow { margin: 2rem auto; max-width: 560px; }
/* The break-out rule further down narrows these containers to the viewport, but
   an inline SVG carries width/height attributes - the CLS gate requires them -
   so without these three declarations it keeps its intrinsic 880px inside a
   327px box, and `body { overflow-x: hidden }` crops the overflow away with no
   scrollbar and no hint that anything is missing. Every sibling figure class
   (.arch-figure, .killchain-figure, .workflow-figure, .diagram-figure,
   .cnapp-figure) carries them too; a new one needs them as well. */
.figure-wide > svg,
.figure-narrow > svg { width: 100%; height: auto; display: block; }
.figure-caption {
    text-align: center;
    font-size: var(--fs-14);
    color: var(--text-muted, #555);
    margin-top: 0.5rem;
}
.code-block--rounded { border-radius: 6px; }

/* Assorted single-page pieces. */
.container--tight-top { padding-top: 1.5rem; padding-bottom: 0; }
.lede-muted { font-size: var(--fs-15); color: var(--text-muted, #555); margin: 0 0 1.5rem !important; }
/* !important for the same reason the utilities carry it: `.contribute-article p`
   ties on specificity and wins on source order. */
p.backlink  { margin: -0.25rem 0 1.25rem !important; font-size: var(--fs-15); }
p.footnote  { margin-top: 0.75rem; font-size: var(--fs-14); opacity: 0.85; }
.resource-grid--dense {
    grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
    gap: 1rem;
}

.pillar-card.pillar-card-stage5 {
    --card-accent: #14b8a6;
    --card-accent-soft: rgba(20, 184, 166, 0.13);
    border-top: 4px solid #14b8a6;
}

/* ---------------------------------------------------------------------------
   Content links.

   Styles links in prose, including .glossary-link references outside
   glossary.html.

   Deliberately not a bare `a` selector. Nav, footer, breadcrumbs, dropdowns,
   cards and buttons all colour their own links through dozens of separate
   selectors, several of them at specificity a bare `a` rule would outrank, and
   they must not be disturbed. Scoping to `main`/`.hero` and to prose
   containers keeps this off them, and `:not([class])` keeps it off .btn,
   .card-link, .cta-button and the rest.

   Wrapped in :where() so the whole selector contributes zero specificity and
   *any* context rule outranks it. Without that it would be (0,1,3) and beat
   `.toc a`. A default this broad should lose every argument it gets into.

   Colour comes from --secondary-color, which is already redefined for both
   [data-theme="dark"] and the prefers-color-scheme fallback, so dark mode
   needs no separate rule here. --------------------------------------------- */
:where(main :is(p, li, dd, dt, td, th, blockquote, figcaption, h2, h3, h4) a:not([class])),
:where(.hero :is(p, li) a:not([class])) {
    color: var(--secondary-color);
    /* Underlined, always. Colour alone cannot carry a link inside a text
       block: Lighthouse's link-in-text-block wants either a non-colour
       distinguisher or 3:1 between the link and the surrounding text, and on
       this palette no colour satisfies both that and 4.5:1 on the background.
       --text-color #1f2937 needs the link at luminance >= 0.165, .lede's
       #334155 needs >= 0.254 and #475569 needs >= 0.366, while 4.5:1 against
       #f8fafc caps it at 0.173. The ranges do not overlap, so there is nothing
       to tune - the distinguisher has to be something other than hue. A
       hover-only underline would not satisfy WCAG 1.4.1 (Use of Color) either,
       since hover is not available to keyboard or touch. */
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

:where(main :is(p, li, dd, dt, td, th, blockquote, figcaption, h2, h3, h4) a:not([class])):hover,
:where(main :is(p, li, dd, dt, td, th, blockquote, figcaption, h2, h3, h4) a:not([class])):focus-visible,
:where(.hero :is(p, li) a:not([class])):hover,
:where(.hero :is(p, li) a:not([class])):focus-visible {
    /* Hover thickens rather than reveals; the glossary treatment does the
       same, so the two kinds of link behave alike under the pointer. */
    text-decoration-thickness: 2px;
}

/* Glossary links stay visually distinct from a link that navigates somewhere:
   same colour, but the underline and slightly heavier weight mark them as
   pointing at a definition. This is the treatment glossary.html already used
   via `.glossary-list dd a.glossary-link`, promoted site-wide. That rule is
   (0,2,2) and still wins on the glossary page itself, where it is identical. */
a.glossary-link {
    color: var(--secondary-color);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    font-weight: 500;
    transition: background-color 0.15s ease;
}

/* Diagram labels are <a class="glossary-link"> inside SVG <text>, and those
   nodes are filled with currentColor, so styling the link would repaint the
   drawing. Inside an SVG the link takes the diagram's own colour. */
svg a.glossary-link {
    color: inherit;
    text-decoration: none;
    font-weight: inherit;
    background-color: transparent;
}

a.glossary-link:focus,
a.glossary-link:hover {
    background-color: rgba(52, 152, 219, 0.12);
    text-decoration-thickness: 2px;
}

/* Every hero variant is a dark surface, in light mode too, and the .hero
   gradient ends on --secondary-color itself, so the prose-link colour has no
   contrast there. sky-100 clears 4.5:1 against every hero gradient stop; the
   underline, not the hue, is what separates it from the white hero text. The
   dark branch restores --secondary-color, which already clears the dark hero.
   Two classes are needed to outrank a.glossary-link. */
.hero :is(p, li) a:not([class]),
.hero :is(p, li) a.glossary-link {
    color: #e0f2fe;
}

/* Author "Find X on:" social groups (cloud-security-reading-list.html).
   Same story as the calendar below: these lived in an inline <style> block
   that production CSP drops, and nothing else on the site defines
   .follow-on, so all 17 groups rendered with the browser's default link
   styling - rgb(0,0,238) and underlined - instead of inheriting the
   surrounding text colour, wrapped mid-group, and sat their icons on the
   baseline. The !important flags are carried over from the original block;
   they beat the generic link colour rules. */
.follow-on {
    white-space: nowrap;
    margin-left: 0.5rem;
}

.follow-on a {
    color: inherit !important;
    text-decoration: none !important;
    transition: color .15s;
}

.follow-on a:hover,
.follow-on a:focus-visible {
    color: #38bdf8 !important;
}

.follow-on svg {
    vertical-align: -0.25em;
}

/* Reading measure -----------------------------------------------------------
   Comfortable prose is 45-75 characters per line, and the return sweep starts
   failing past about 90.

   The cap goes on the block, not the paragraph. Capping only the paragraphs
   leaves a .toc or .info-box spanning the full container while the text inside
   it stops short, and every h2's hairline rule running past the prose. A
   text-only section becomes the reading column outright - heading, rule,
   prose, list and quote all share one right edge - while a section carrying a
   grid keeps the full container so the grid stays wide and only its intro
   copy takes the measure. ------------------------------------------------- */

/* The auto margins on these two rules are load-bearing, not tidiness. A capped
   block with margin 0 hugs the container's left padding edge, off the centre
   line that the header, the next-session card, the stat strip, the card grids
   and the footer all share. A capped block has to opt back into it
   explicitly.

   Text-only sections. The :has() test is what separates a reading section from
   a grid band; it is deliberately alone in its own rule, because one invalid
   selector invalidates a whole comma list, and on an engine without :has()
   the boxes below must still be capped. Degrading here means a text section
   renders full width - not broken, just wide. */
main > section:not(:has([class*="grid"], table, .stat-strip, .ctf-calendar)) {
    max-width: var(--measure);
    margin-inline: auto;
}

/* Blocks that sit directly in main and hold running text. .page-meta is
   excluded: it is one line of byline and date, and capping it wraps it. */
/* Written as `main > :is(...)` rather than a comma list of `main > .thing`
   selectors on purpose. :is() takes the specificity of its heaviest argument,
   so this lands at (0,1,1) and beats the (0,1,0) margin shorthands the
   components set for themselves - .page-photo and .toc both say `margin: 2rem 0`, .info-box says `margin: 0 0 0.5rem 0`, .pull-quote `margin: 2rem 0`. A comma list would sit at (0,0,2), lose to every one of them, and leave those blocks pinned to the container's left edge while the rest of the page centres. Do not re-expand it. */ main > :is(.info-box, .toc, figure, blockquote, p:not(.page-meta), ul, ol, dl){
    max-width: var(--measure);
    margin-inline: auto;
}

/* The measure assumes the 1.0625rem body size that .contribute-article sets, so
   the running text on every other page has to come up with it, or a 736px column
   reads at 92 characters instead of 86. Applied to the blocks the measure caps
   and not to `p` generally: card, tag and caption text is deliberately smaller
   and sits deeper, and pulling it up to 17px would resize every card grid. */
main > section:not(:has([class*="grid"], table, .stat-strip, .ctf-calendar)),
main > section > :is(p, ul, ol, dl),
main > div > :is(p, ul, ol, dl),
main > .info-box,
main > .toc,
main > blockquote {
    font-size: var(--fs-17);
    line-height: 1.75;
}

/* Everywhere else the container stays wide and only the running text is capped:
   inside a grid section, and inside the div-based wrappers that some pages use
   instead of <section>, such as div.category-section.

   A plain descendant rule rather than one scoped by depth, because card, table
   and caption text is already far narrower than the measure, so the cap never
   binds there.

   .page-meta is the one exclusion. `ch` resolves against the element's own
   font-size, so on a 0.85rem meta line 60ch is 514px, which wraps a byline that
   fits comfortably in the 605px column. */
main :is(p, ul, ol, dl, blockquote):not(.page-meta) {
    max-width: var(--measure);
}

/* A capped block inside a centred container has to keep auto side margins, or
   the box narrows against the container's left edge and the centred text lands
   left of centre. .cta-section is on 96 pages. */
main .cta-section > :is(p, ul, ol, dl, blockquote) {
    margin-inline: auto;
}

/* Break-out: things that are genuinely wider than a sentence -------------------
   A measure is right for prose and wrong for an architecture diagram, a
   comparison table or a shell transcript. Squeezed into the column, an SVG
   spills out of its box and tables and code blocks start scrolling sideways.

   So they break out and centre on the same axis as the text instead. The column
   keeps its measure, these keep their width, and both share a centre line, which
   is what makes the result read as a decision rather than an accident. Capped at
   --breakout, the widest any of them declares, and at the viewport below that.
   The floating table of contents is placed outside this cap rather than
   outside the prose, because it has to clear these too.

   Not scoped to article pages: a diagram on glossary.html has the same claim to
   width as one in an article, and .page-photo is deliberately *not* in the list
   so decorative photography stays inside the column with the text. */
main :is(
    .figure-wide, .arch-figure, .killchain-figure, .workflow-figure,
    .diagram-figure, .cnapp-figure,
    .comparison-table, .table-scroll, .matrix-table, .matrix-table--wide,
    pre.code-block
) {
    width: min(var(--breakout), calc(100vw - 3rem));
    max-width: none;
    margin-left: 50%;
    transform: translateX(-50%);
}

/* Wiz Championship calendar (ctfs.html). These rules live here and not in
   the page because the CSP drops inline <style> blocks in production.

   auto-fill for the reason given on .resource-grid above, and min() so a
   260px floor cannot overflow a narrower viewport. */
.ctf-calendar {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.ctf-calendar .resource-card {
    position: relative;
}

.ctf-month-badge {
    position: absolute;
    top: 0.6rem;
    left: 0.6rem;
    background: rgba(44, 62, 80, 0.92);
    color: #fff;
    padding: 0.25rem 0.625rem;
    border-radius: 4px;
    font-size: var(--fs-12);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    z-index: 2;
}

/* Featured tier: 4 prominent cards across desktop, falling to 2 then 1. */
.resource-grid--featured {
    grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
    gap: 1.5rem;
}

/* Compact tier: denser secondary grid (up to 4 across) with tighter cards. */
.resource-grid--compact {
    grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
    gap: 1rem;
    margin-top: 1rem;
}
.resource-grid--compact .resource-card {
    padding: 1rem 1rem 1.25rem;
}
.resource-grid--compact .resource-card h3 {
    font-size: var(--fs-17);
    margin-bottom: 0.375rem;
}
.resource-grid--compact .resource-card p {
    font-size: var(--fs-15);
    line-height: 1.45;
    color: #475569;
    margin-bottom: 0.625rem;
}
[data-theme="dark"] .resource-grid--compact .resource-card p { color: #cbd5e1; }

/* Group heading for a band of cards. Selector is .section h2.featured-subhead,
   at (0,2,1), because `.section h2` at (0,1,1) would otherwise repaint these as
   full 2rem headings with a bottom rule.

   These are <h2> in their own sections, with the card titles beneath them as
   h3, so the grouping the eye sees is also in the document outline.

   1.25rem: at h2 level a 16px label reads as smaller than the body text it
   sits above. Still deliberately lighter than a plain section h2 - no rule,
   muted, uppercase - because "Start here" is the page's entry point and these
   are the shelves under it. */
.section h2.featured-subhead {
    margin: 2.5rem 0 0.75rem;
    font-size: var(--fs-20);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #64748b;
    padding-bottom: 0;
    border-bottom: 0;
}
[data-theme="dark"] .section h2.featured-subhead { color: #94a3b8; }

/* Inline text-link CTA used in community cards in place of full buttons.
   Colour is sky-700 (#0369a1), the light value of --secondary-color, for
   WCAG AA on white. Hover deepens to sky-800 for the press-state cue. */
.card-action {
    display: inline-block;
    margin-top: 0.75rem;
    color: #0369a1;
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 0.15s, color 0.15s;
}
.card-action:hover, .card-action:focus {
    border-bottom-color: currentColor;
    color: #075985;
}
[data-theme="dark"] .card-action { color: #7dd3fc; }
[data-theme="dark"] .card-action:hover, [data-theme="dark"] .card-action:focus { color: #bae6fd; }

/* Testimonial grid: typography, not cards. Markup stays .community-card so
   the Google Form injector keeps writing the same shape. */
.testimonial-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: 2rem 2.5rem;
    max-width: 960px;
    margin: 1.75rem auto 0;
}
.testimonial-grid .community-card {
    background: transparent;
    border: none;
    border-left: none;
    padding: 0.25rem 0;
    border-radius: 0;
    box-shadow: none;
    position: relative;
}
.testimonial-grid .community-card::before {
    content: '\201C';
    display: block;
    font-family: var(--font-quote);
    font-size: 3.5rem;
    line-height: 0.8;
    color: var(--accent-color);
    margin-bottom: 0.35rem;
}
.testimonial-grid .community-card p:first-child {
    margin: 0 0 1rem;
    font-family: var(--font-quote);
    font-style: italic;
    font-size: var(--fs-18);
    line-height: 1.55;
    color: var(--text-color);
}
.testimonial-grid .community-card p:first-child em {
    font-style: italic;
}
.testimonial-grid .community-card .community-heading {
    margin-top: 0.75rem;
    font-family: var(--font-display);
    font-size: var(--fs-14);
    font-style: normal;
    color: #475569;
    font-weight: 600;
}
[data-theme="dark"] .testimonial-grid .community-card {
    background: transparent;
    border-left-color: transparent;
}
[data-theme="dark"] .testimonial-grid .community-card .community-heading { color: #cbd5e1; }
[data-theme="dark"] .testimonial-grid .community-card p:first-child { color: #e2e8f0; }

/* Resources page: use flex layout to avoid empty grid slots */
.category-section .resource-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    margin-top: 2rem;
    font-size: 0;
}

.category-section .card-link {
    display: block;
    flex: 0 0 min(350px, 100%);
    font-size: var(--fs-16);
}

.presentations-page .resource-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    margin-top: 2rem;
}

.presentations-page .card-link {
    display: block;
    flex: 0 0 min(350px, 100%);
}

/* No results message for empty search/filter */
.no-results {
    text-align: center;
    padding: 3rem 1.5rem;
    color: #666;
    font-size: var(--fs-18);
}

/* Hide an element. Used in place of inline `style="display:none;"`
   so that strict CSPs and content-blocking shields (which can ignore
   inline styles) don't leave the element visible. */
.is-hidden {
    display: none !important;
}

.no-results p {
    margin: 0;
}

.clear-search-btn {
    background: none;
    border: none;
    color: var(--secondary-color);
    cursor: pointer;
    font-size: inherit;
    text-decoration: underline;
    padding: 0;
}

.clear-search-btn:hover {
    color: var(--primary-color);
}

.resource-card {
    background-color: var(--white);
    border-radius: 8px;
    padding: 1.5rem;
    box-shadow: var(--shadow);
    transition: all 0.3s;
    border-left: 4px solid var(--secondary-color);
    position: relative;
    /* Block layout: content stacks vertically inside card */
    display: block;
    /* Performance optimization: contain layout and paint */
    contain: layout style paint;
}

.resource-card .resource-preview {
    width: 100%;
    height: 160px;
    object-fit: cover;
    border-radius: 6px;
    margin: 0 0 1rem 0;
    border: 1px solid var(--border-color);
    display: block;
    /* Optimize image rendering */
    image-rendering: -webkit-optimize-contrast;
}

/* Two kinds of image sit in cards that point at our own pages, and neither
   wants the 160px box above. That height exists to normalise the
   heterogeneous third-party screenshots in img/previews/ (400x225,
   600x450, ...) into a tidy grid; applied to an in-house asset whose ratio
   we already know, object-fit: cover just eats the edges.

   --og: the four featured "start here" cards, which show the target page's
   1200x630 OG card. At 311px wide that is legible and the extra weight
   suits them. Pinning the box to the asset's own ratio makes cover a no-op.

   --thumb: the compact grids, which use the purpose-built 3:2 tiles from
   img/thumbs/ (see tools/generate_thumbnails.py). Those columns land at
   197-303px, where an OG card is both cropped and too small to read. The
   tiles are authored for that size: one glyph, one category word. */
/* The 160px box above normalises the heterogeneous third-party screenshots,
   but it is a fixed height against a fluid column, so the ratio it produces
   depends on the viewport. The sources are mostly 400x225, so pinning 16/9
   makes cover a no-op for those and takes a smaller, symmetric bite out of
   the rest. It matches the max-width:768px branch.

   Excluding the two modifiers matters: this selector is 0,3,0 and theirs are
   0,2,0, so without the :not() it would silently override the ratios they
   pin below and re-crop the assets they exist to protect. */
.resource-grid .resource-card .resource-preview:not(.resource-preview--og):not(.resource-preview--thumb),
.ctf-calendar .resource-card .resource-preview {
    height: auto;
    aspect-ratio: 16 / 9;
}

.resource-card .resource-preview--og {
    height: auto;
    aspect-ratio: 1200 / 630;
}

.resource-card .resource-preview--thumb {
    height: auto;
    aspect-ratio: 3 / 2;
}

/* News page: use the same card sizing as the rest of the site */
.resources-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: 2rem;
    margin-top: 2rem;
}

.card-link {
    text-decoration: none;
    color: inherit;
    display: block;
}

/* News page: use flex layout for fixed-width cards like Resources and Presentations */
.news-page .resources-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    margin-top: 2rem;
    font-size: 0;
}

.news-page .card-link {
    display: block;
    flex: 0 0 min(350px, 100%);
    font-size: var(--fs-16);
}

.card-link .resource-card {
    height: 100%;
}

.card-link:hover .resource-card {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
    border-left-color: var(--primary-color);
    cursor: pointer;
}

/* A search result deep links to one card by id (tools/stamp_card_ids.py).
   Scrolling to it is not enough on a page of 83 near-identical tiles: the
   reader has no way to tell which one the result meant, which is the same
   "did this even go anywhere?" the category-anchor link produced. Mirrors
   the .glossary-list dt:target treatment. --secondary-color flips per
   theme, so this needs no [data-theme="dark"] counterpart. */
.card-link:target .resource-card {
    outline: 3px solid var(--secondary-color);
    outline-offset: 3px;
    border-left-color: var(--secondary-color);
}

/* Inline slide-deck viewer (presentations archive). A deck card is a bare
   .resource-card (not wrapped in a .card-link anchor) so it can hold a
   <details> viewer; mirror the flex-basis the .card-link siblings get on
   this page so the grid stays uniform. */
.presentations-page .resource-grid > .resource-card {
    flex: 0 0 min(350px, 100%);
}
/* Deck-only cards have no video thumbnail; the primary accent border marks
   them as a distinct (slides-only) entry within the same grid. */
.resource-card--deck {
    border-left-color: var(--primary-color);
}
/* <details> gates the (lazy) Google Slides /embed or Drive /preview iframe -
   both carry their own prev/next controls - so the deck only loads when the
   reader opens it. */
.deck-embed {
    margin-top: 1rem;
    border-top: 1px solid var(--border-color);
    padding-top: 0.75rem;
}
.deck-embed > summary {
    cursor: pointer;
    font-weight: 600;
    color: #0369a1;
    list-style: none;
}
.deck-embed > summary::-webkit-details-marker { display: none; }
.deck-embed > summary::before { content: "\25B8\00A0"; }
.deck-embed[open] > summary::before { content: "\25BE\00A0"; }
.deck-embed > summary:hover,
.deck-embed > summary:focus-visible { color: #075985; }
[data-theme="dark"] .deck-embed > summary { color: #7dd3fc; }
.deck-frame {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    margin: 0.75rem 0;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--border-color);
}
.deck-frame iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

/* News page typography alignment */
.news-page .hero h1,
.news-page .hero h2 {
    font-size: var(--fs-40);
}

.news-page .hero p {
    font-size: var(--fs-20);
}

.news-page .resource-card h3 {
    font-size: var(--fs-20);
}

.news-page .resource-card p {
    font-size: var(--fs-16);
}

.news-page .filter-btn {
    font-size: var(--fs-14);
}

.resource-card-icon {
    width: 60px;
    height: 60px;
    margin-bottom: 1rem;
    font-size: var(--fs-40);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    /* Literals, deliberately, not var(--secondary-color)/var(--primary-color).
       Dark mode redefines both tokens as *text* colours for a dark background
       (#6fc3ff, #c2d9ec); used here as a *surface* they make a pale tile that
       washes out the emoji sitting on it. These are the light-mode values, so
       the tile is a dark badge in both themes. Contrast audits won't catch a
       regression here because the tile holds an emoji rather than text.
       The six variant rules below are (0,2,0) against this (0,1,0), so they
       still win and keep their own gradients. */
    background: linear-gradient(135deg, #0369a1, #0f172a);
    color: white;
    pointer-events: none;
}

.resource-card-icon.ctf {
    background: linear-gradient(135deg, #e65100, #ff6f00);
}

.resource-card-icon.tool {
    background: linear-gradient(135deg, #6a1b9a, #8e24aa);
}

.resource-card-icon.certification {
    background: linear-gradient(135deg, #1565c0, #1976d2);
}

.resource-card-icon.lab {
    background: linear-gradient(135deg, #c2185b, #d81b60);
}

.resource-card-icon.ai {
    background: linear-gradient(135deg, #00796b, #009688);
}

.resource-card-icon.kubernetes {
    background: linear-gradient(135deg, #326ce5, #5e8ceb);
}

.resource-card h3 {
    font-size: var(--fs-20);
    margin: 0 0 0.75rem 0;
    color: var(--primary-color);
    /* Better word wrapping for long URLs */
    word-wrap: break-word;
    overflow-wrap: break-word;
    word-break: break-word;
    hyphens: none;
}

.resource-card h3 a {
    color: var(--primary-color);
    text-decoration: none;
}

.resource-card h3 a:hover {
    color: var(--secondary-color);
}

.article-date {
    font-size: var(--fs-14);
    color: #666;
    margin: 0 0 1rem 0;
    font-weight: 500;
    flex-basis: 100%;
    width: 100%;
}

.resource-card p {
    color: #666;
    margin-bottom: 1rem;
    line-height: 1.5;
}

/* "Next: <dates>" line on conference cards - the actionable date, lifted out
   of the muted body-copy gray (.resource-card p) into the site accent blue so
   it reads at a glance directly under each title. The bold "Next:" label is
   inline in the HTML; the dark-mode colour override lives with the other
   dark-mode text rules further down. */
.resource-card p.conf-next {
    color: var(--secondary-color);
    font-weight: 600;
    font-size: var(--fs-15);
    margin: 0 0 1rem 0;
}

.resource-card p.conf-next strong {
    color: var(--secondary-color);
}

.resource-tags {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

.tag {
    background-color: var(--light-bg);
    color: var(--text-color);
    padding: 0.25rem 0.75rem;
    border-radius: 15px;
    font-size: var(--fs-14);
    font-weight: 500;
}

.tag.new {
    background-color: #e8f5e9;
    color: #2e7d32;
    font-weight: 600;
}

.source {
    font-size: var(--fs-14);
    color: var(--secondary-color);
    font-weight: 500;
}

.tag.ctf {
    background-color: #fff3e0;
    color: #7c2d12;
}

.tag.certification {
    background-color: #e3f2fd;
    color: #0d47a1;
}

.tag.tool {
    background-color: #f3e5f5;
    color: #4a148c;
}

.tag.lab {
    background-color: #fce4ec;
    color: #880e4f;
}

.tag.job {
    background-color: #e0f2f1;
    color: #00474f;
}

.tag.ai-security {
    background-color: #ede9fe;
    color: #4338ca;
}

.tag.newsletter {
    background-color: #fef9c3;
    color: #713f12;
}

.tag.github {
    background-color: #ede9fe;
    color: #5b21b6;
}

/* chat-resources.html category badges. Colours live here, not in style=
   attributes, because the CSP drops those. Three hues (Other, Training, GCP)
   are a shade darker than the brand values so white text clears 4.5:1; the
   five bright brand colours take dark text for the same reason, which is
   also how AWS and Spotify set their own type. */
.tag.tag-other {
    background-color: #5D7987;
    color: #ffffff;
}

.tag.tag-news {
    background-color: #2c3e50;
    color: #ffffff;
}

.tag.tag-youtube {
    background-color: #c4302b;
    color: #ffffff;
}

.tag.tag-github {
    background-color: #24292e;
    color: #ffffff;
}

.tag.tag-owasp {
    background-color: #333333;
    color: #ffffff;
}

.tag.tag-wikipedia {
    background-color: #636466;
    color: #ffffff;
}

.tag.tag-vendor {
    background-color: #6a1b9a;
    color: #ffffff;
}

.tag.tag-cisa {
    background-color: #003366;
    color: #ffffff;
}

.tag.tag-discord {
    background-color: #5865F2;
    color: #ffffff;
}

.tag.tag-azure {
    background-color: #0078D4;
    color: #ffffff;
}

.tag.tag-training {
    background-color: #E01D5F;
    color: #ffffff;
}

.tag.tag-gcp {
    background-color: #3972D2;
    color: #ffffff;
}

.tag.tag-wiz {
    background-color: #00C2A2;
    color: #1f2937;
}

.tag.tag-aws {
    background-color: #FF9900;
    color: #1f2937;
}

.tag.tag-spotify {
    background-color: #1DB954;
    color: #1f2937;
}

.tag.tag-jobs {
    background-color: #ff9800;
    color: #1f2937;
}

.tag.tag-csoh {
    background-color: #3498db;
    color: #1f2937;
}

/* Live indicator tag: pulsing dot signaling auto-refreshed content. */
.tag.tag-live {
    background-color: #ecfdf5;
    color: #065f46;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-weight: 600;
}
.tag-live-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #10b981;
    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
    animation: tag-live-pulse 1.8s ease-out infinite;
}
@keyframes tag-live-pulse {
    0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.55); }
    70% { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
    100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
/* Blanket reduced-motion guard -----------------------------------------------
   Covers every transition and animation in this file (card lifts, the
   hamburger's scale, hover transitions) for readers who ask the OS to reduce
   motion.

   Near-zero durations rather than `none`, so transitionend and animationend
   listeners still fire and nothing waits forever on an event that never comes.
   The two rules that need a real `animation: none` keep theirs: an infinite
   pulse at 0.01ms is still an infinite pulse. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .tag-live-dot { animation: none; }
}
[data-theme="dark"] .tag.tag-live {
    background-color: #022c22;
    color: #6ee7b7;
}

/* Category Sections */
.category-section {
    margin-bottom: 3rem;
}

/* Collapsible category sections --------------------------------------------
   Sections are <details>, closed by default, so expanding works with
   JavaScript off and the keyboard behaviour is the browser's. Keep these
   selectors in step with the markup: one that misses it fails silently,
   because a section that never collapses looks like one with no collapse
   feature. */
.category-summary {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    cursor: pointer;
    list-style: none;
    padding: 1rem;
    background-color: var(--white);
    border-radius: 8px;
    box-shadow: var(--shadow);
}

.category-summary:hover { background-color: var(--light-bg); }

/* Safari still paints the default triangle without this. */
.category-summary::-webkit-details-marker { display: none; }

.category-summary::before {
    content: '▶';
    flex: none;
    font-size: var(--fs-13);
    color: var(--secondary-color);
    transition: transform 0.2s ease;
}

.category-section[open] > .category-summary::before {
    transform: rotate(90deg);
}

.category-summary h2 {
    margin: 0;
    border-bottom: 0;
    padding-bottom: 0;
    flex: 1 1 auto;
}

.category-summary:hover h2 { color: var(--secondary-color); }

.category-summary:focus-visible {
    outline: 3px solid var(--secondary-color);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Filled in by main.js from the DOM rather than written into the page, so it
   cannot drift the way a hand-typed count would. */
.category-count {
    flex: none;
    font-size: var(--fs-13);
    font-weight: 600;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.category-content {
    margin-top: 1rem;
}

/* <details> hides its own content when closed; no rule needed here. */

/* Resource List (Alternative to Grid) */
.resource-list {
    list-style: none;
}

.resource-list li {
    background-color: var(--white);
    padding: 1rem 1.5rem;
    margin-bottom: 0.75rem;
    border-radius: 5px;
    box-shadow: var(--shadow);
    border-left: 4px solid var(--secondary-color);
    transition: all 0.3s;
}

.resource-list li:hover {
    transform: translateX(5px);
    box-shadow: var(--shadow-lg);
}

.resource-list a {
    color: var(--primary-color);
    text-decoration: none;
    font-weight: 500;
    font-size: var(--fs-17);
}

.resource-list a:hover {
    color: var(--secondary-color);
}

.resource-list .description {
    color: #666;
    font-size: var(--fs-14);
    margin-top: 0.5rem;
}

/* Stats/Info Boxes */
.info-boxes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}

.info-box {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow);
    text-align: center;
}

.info-box h3 {
    font-size: var(--fs-40);
    color: var(--secondary-color);
    margin: 0 0 0.5rem 0;
}

.info-box p {
    color: #666;
    font-size: var(--fs-16);
}

/* Footer */
footer {
    background-color: var(--primary-color);
    color: var(--white);
    padding: 2rem 2rem 1rem;
    margin-top: 3rem;
    font-size: var(--fs-14);
}

/* One section: the CSOH blurb. The link columns live in the top nav.
   auto-fit means the column count follows the markup instead of being
   asserted here, so adding a section cannot leave one wrapped onto a row of
   its own. */
.footer-content {
    max-width: var(--frame);
    margin: 0 auto 1.5rem;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
    gap: 1.5rem 3rem;
    align-items: start;
}

/* The blurb reads as a paragraph, not a column of links, so keep it to a
   comfortable measure rather than letting it span the full 1200px. */
.footer-about {
    max-width: 34rem;
}

/* The coffee ask, opposite the blurb. Filled rather than the outline the nav
   link uses: nothing down here competes with it, and a reader who got this far
   has read the page. Same accent as .nav-cta so the two read as one system.
   Selectors are scoped to .footer-section deliberately - `.footer-section a`
   sets links to translucent white and outranks a lone `.footer-coffee`, which
   would put white text on amber and fail contrast. */
.footer-section .footer-coffee {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 1rem;
    /* 0.7rem keeps the button at ~45px tall - 0.6rem landed on 42px, under the
       44px touch-target guideline. */
    padding: 0.75rem 1rem;
    background-color: var(--accent-color, #f59e0b);
    color: #1a202c;
    border-radius: 6px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.15s, transform 0.12s;
}

.footer-section .footer-coffee:hover,
.footer-section .footer-coffee:focus {
    background-color: #fbbf24;
    color: #1a202c;
    transform: translateY(-1px);
    text-decoration: none;
}

.footer-section .footer-coffee:focus-visible {
    outline: 3px solid rgba(245, 158, 11, 0.55);
    outline-offset: 2px;
}

.footer-section h3 {
    margin: 0 0 0.625rem;
    color: rgba(255, 255, 255, 0.95);
    font-size: var(--fs-12);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.footer-section ul {
    list-style: none;
    margin: 0;
    padding: 0;
    line-height: 1.85;
}

.footer-section li {
    margin: 0;
}

.footer-section p {
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.75);
    margin: 0;
}

.footer-section a {
    color: rgba(255, 255, 255, 0.75);
    text-decoration: none;
    transition: color 0.2s ease;
}

.footer-section a:hover {
    color: var(--white);
}

.footer-bottom {
    max-width: var(--frame);
    margin: 0 auto;
    padding-top: 1rem;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.55);
    font-size: var(--fs-13);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1.5rem;
}

.footer-bottom p {
    margin: 0;
}

.footer-legal {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.25rem;
}

/* inline-block + vertical padding takes the hit area from 15px to ~25px.
   WCAG 2.2 SC 2.5.8 wants 24x24 CSS px, and a row of legal links is a set of
   separate targets, not a link inside a sentence, so the inline exception does
   not apply. Padding rather than font-size: these should stay small. */
.footer-legal a {
    display: inline-block;
    padding: 0.25rem 0;
    color: rgba(255, 255, 255, 0.6);
    text-decoration: none;
    transition: color 0.2s ease;
}

.footer-legal a:hover {
    color: var(--white);
}

/* Responsive Design */
@media (max-width: 768px) {

    /* Typography adjustments */
    body {
        font-size: var(--fs-16);
    }

    /* Header and Navigation - MINIMAL HEADER */
    header {
        padding: 0.5rem 0;
        position: static;
        /* Not sticky to save space */
    }

    .header-content {
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        gap: 0.5rem;
        padding: 0 0.5rem;
        flex-wrap: nowrap;
    }

    /* Logo - TINY */
    .logo .logo-title {
        font-size: var(--fs-16);
        margin: 0;
        line-height: 1.2;
    }

    .logo p {
        display: none;
        /* Hide tagline on mobile */
    }

    /* Navigation - HAMBURGER TOGGLE WITH NO-JS FALLBACK */
    .hamburger {
        display: inline-flex;
        order: 3;
        margin-left: 0;
        width: 2.4rem;
        height: 2.4rem;
        font-size: var(--fs-18);
    }

    .theme-toggle {
        order: 2;
        margin-left: auto;
        margin-right: 0.375rem;
        width: 2.4rem;
        height: 2.4rem;
        font-size: var(--fs-17);
    }

    /* Without JS: nav stays visible as fallback */
    header nav {
        display: block;
        width: 100%;
        order: 3;
    }

    /* With JS: nav hidden until hamburger toggled */
    /* Scoped to header nav so breadcrumb-nav & incident-tabs stay visible */
    .js-enabled header nav {
        display: none;
    }
    .js-enabled header .nav-open nav {
        display: block;
    }

    /* Hero section - MINIMAL */
    .hero {
        padding: 1rem 1rem;
    }

    .hero h1,
    .hero h2 {
        font-size: var(--fs-20);
        line-height: 1.2;
        margin-bottom: 0.5rem;
    }

    .hero p {
        font-size: var(--fs-14);
        margin-bottom: 0.75rem;
    }

    /* Container padding - COMPACT */
    .container {
        padding: 0 0.75rem;
    }

    section {
        padding: 1.5rem 0;
    }

    /* Search and filters - COMPACT */
    .search-filter-container {
        padding: 0.75rem;
    }

    .search-box {
        flex-direction: column;
        gap: 1rem;
    }

    .search-box input,
    #searchInput, #meetingSearch {
        width: 100%;
        font-size: var(--fs-16);
        /* Prevents iOS zoom on focus */
    }

    .filter-buttons {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    .filter-btn {
        font-size: var(--fs-14);
        padding: 0.5rem 1rem;
        flex: 1 1 auto;
        min-width: fit-content;
    }

    /* Resource grid */
    .resource-grid:not(.resource-grid--compact),
    .resources-grid {
        grid-template-columns: 1fr;
        gap: 1rem;
        padding: 1rem;
    }

    /* ...except the compact glyph tiles, which go two-up. At one per row
       they would dominate the mobile homepage, and the tiles are built for
       the 197-303px columns they get on desktop, so 158px still holds the
       title. The featured grid keeps one column: those four cards carry a
       preview image, a description and tags.

       Placed here, immediately after the rule it has to beat, rather than with
       the other grid rules: `.resource-grid--compact` at (0,1,0) ties with both
       `.resource-grid` above and the 220px auto-fill rule further up, so source
       order is what decides it. Moving this block earlier silently restores one
       column. */
    .resource-grid--compact {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.75rem;
        padding: 0;
    }

    /* One column here, so a card spans the viewport, and a fixed-height box
       would flatten the preview to roughly 3:1. The 16/9 pin applies at
       every width (see .resource-grid .resource-card .resource-preview
       above) and excludes the --og/--thumb modifiers, so this branch needs
       no override. */

    .resource-card {
        padding: 1.25rem;
        overflow: hidden;
    }

    /* News page responsive: hide images on mobile, show text only */
    .resources-grid .resource-card {
        display: block;
    }

    .resources-grid .resource-card .resource-preview {
        display: none !important;
        visibility: hidden !important;
        width: 0 !important;
        height: 0 !important;
        max-width: 0 !important;
        margin: 0 !important;
    }

    .resource-card h3 {
        font-size: var(--fs-18);
    }

    .resource-card p {
        font-size: var(--fs-15);
    }

    .resource-tags {
        flex-wrap: wrap;
        gap: 0.375rem;
    }

    .tag {
        font-size: var(--fs-12);
        padding: 0.25rem 0.625rem;
    }

    /* Buttons */
    .btn {
        width: 100%;
        text-align: center;
        padding: 1rem;
        font-size: var(--fs-16);
    }

    .cta-buttons {
        flex-direction: column;
        gap: 1rem;
    }

    .cta-buttons .btn {
        width: 100%;
    }

    /* Statistics boxes */
    .stats-grid {
        grid-template-columns: 1fr;
        gap: 1rem;
    }

    .stat-box h3 {
        font-size: var(--fs-32);
    }

    /* Info boxes */
    .info-grid {
        grid-template-columns: 1fr;
        gap: 1.5rem;
    }

    .info-box {
        padding: 1.5rem;
    }

    /* Session cards */
    .sessions-grid {
        grid-template-columns: 1fr;
    }

    .session-card {
        padding: 1.25rem;
    }

    /* Footer */
    footer {
        padding: 1.75rem 1rem 0.75rem;
    }

    .footer-content {
        grid-template-columns: repeat(2, 1fr);
        gap: 1.25rem 1rem;
        text-align: left;
    }

    .footer-section ul {
        padding: 0;
        line-height: 1.75;
    }

    .footer-section h3 {
        font-size: var(--fs-12);
        margin-bottom: 0.375rem;
    }

    /* Memorial page specific */
    .memorial-content {
        padding: 1rem;
    }

    .memorial-image {
        max-width: 100%;
        margin: 1rem 0;
    }

    iframe {
        max-width: 100%;
        height: 400px;
    }

    /* Tables */
    table {
        font-size: var(--fs-14);
    }

    th,
    td {
        padding: 0.5rem;
    }

    /* Reduce whitespace */
    h2 {
        font-size: var(--fs-28);
    }

    h3 {
        font-size: var(--fs-20);
    }

    /* Contribute page - mobile adjustments */
    .contribute-article {
        padding: 0 0.25rem;
    }

    .contribute-article h2 {
        font-size: var(--fs-20);
        word-wrap: break-word;
    }

    .contribute-article h3 {
        font-size: var(--fs-18);
    }

    .contribute-article ol,
    .contribute-article ul {
        padding-left: 1.25rem;
    }

    .contribute-article ol li,
    .contribute-article ul li {
        margin-bottom: 0.5rem;
        line-height: 1.5;
    }

    .highlight-box ol,
    .highlight-box ul {
        padding-left: 1.25rem;
    }

    .code-block {
        font-size: 0.8em;
        padding: 0.75rem;
        word-break: break-word;
        white-space: pre-wrap;
    }

    .contribution-grid {
        grid-template-columns: 1fr;
        gap: 1rem;
    }

    .contribution-type {
        padding: 1rem;
    }

    .reader-card {
        padding-top: 0;
    }

    .reader-preview {
        margin: 0 -1rem;
        width: calc(100% + 2rem);
    }

    .toc {
        padding: 1rem;
    }

    .highlight-box {
        padding: 0.75rem;
    }

    .faq-item {
        padding: 0.75rem;
    }
}

/* Extra small devices (phones in portrait, less than 576px) */
@media (max-width: 576px) {
    .hero {
        padding: 0.75rem 0.75rem;
    }

    .hero h1,
    .hero h2 {
        font-size: var(--fs-18);
    }

    .hero p {
        font-size: var(--fs-14);
    }

    .filter-btn {
        font-size: var(--fs-13);
        padding: 0.375rem 0.75rem;
    }

    .tag {
        font-size: var(--fs-12);
        padding: 0.25rem 0.5rem;
    }

    .resource-card h3 {
        font-size: var(--fs-16);
    }

    /* News page phone: images still hidden */
    .resources-grid .resource-card .resource-preview {
        display: none !important;
    }

    .stat-box h3 {
        font-size: var(--fs-24);
    }
}

/* Utility Classes */
.text-center {
    text-align: center;
}

.mt-1 {
    margin-top: 1rem;
}

.mt-2 {
    margin-top: 2rem;
}

.mb-1 {
    margin-bottom: 1rem;
}

.mb-2 {
    margin-bottom: 2rem;
}

.hidden {
    display: none;
}

/* Mobile touch improvements */
@media (hover: none) and (pointer: coarse) {

    /* Improve touch targets on mobile */
    a,
    button,
    .filter-btn,
    .btn {
        min-height: 44px;
        /* iOS recommended touch target */
        min-width: 44px;
    }

    /* Remove hover effects on touch devices */
    .resource-card:hover,
    .session-card:hover {
        transform: none;
    }

    /* But keep active state for feedback */
    .resource-card:active,
    .session-card:active {
        transform: scale(0.98);
    }

    .btn:active,
    .filter-btn:active {
        transform: scale(0.95);
    }
}

/* Prevent text selection issues on mobile */
@media (max-width: 768px) {

    .filter-btn,
    .tag {
        user-select: none;
        -webkit-user-select: none;
        -webkit-tap-highlight-color: transparent;
    }
}

/* Improve scrolling performance on mobile */
@media (max-width: 768px) {
    * {
        -webkit-overflow-scrolling: touch;
    }

    body {
        overflow-x: hidden;
    }
}

/* ==========================================================================
   Utility classes (replacing inline styles)
   ========================================================================== */

.hero--compact {
    padding: 3rem 2rem;
}

.steps-box {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow);
}

.steps-box h3+h3,
.steps-box h3~h3 {
    margin-top: 2rem;
}

.btn--mt {
    margin-top: 1rem;
}

.link--accent {
    color: var(--secondary-color);
}

/* ==========================================================================
   Contribute page styles
   ========================================================================== */

.contribute-article {
    /* The measure plus the 2rem of side padding, so the content box is exactly
       one measure wide and the info box, table of contents, figures and author
       card all land on the same edge as the prose.

       The type comes up with it. Narrowing the column alone is the wrong lever
       on a page like this: an 836px column cannot reach a sane measure at 16px
       without shrinking to something that no longer belongs under a full-width
       header. At 1.0625rem the same 736px column reads at about 86 characters,
       and the page keeps its proportions.

       This block is declared twice in this file, byte for byte, here and again
       under the contribute-resources tutorial section. Both carry the value, so
       change both or neither. */
    max-width: calc(var(--measure) + 4rem);
    margin: 0 auto;
    font-size: var(--fs-17);
    line-height: 1.75;
}

.contribute-article h2 {
    margin-top: 2.5rem;
    margin-bottom: 1rem;
    color: var(--primary-color);
    border-bottom: 2px solid var(--primary-color);
    padding-bottom: 0.5rem;
}

.contribute-article h3 {
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
    color: var(--secondary-color);
}

.contribute-article p {
    margin-bottom: 1rem;
}

.contribute-article ol,
.contribute-article ul {
    padding-left: 1.5rem;
    margin-bottom: 1rem;
}

.contribute-article ol li,
.contribute-article ul li {
    margin-bottom: 0.375rem;
}


/* Explicit desktop article heading sizes, pinned to scale steps. Without
   them h2/h3 fall through to the UA's 1.5em / 1.17em, which at the 17px body
   size are off-scale.

   Guarded to >=769px rather than left unqualified, because the <=768px block
   that already sets these sits earlier in this file at equal specificity: an
   unguarded rule here would win on source order and take the mobile sizes with
   it. */
@media (min-width: 769px) {
    /* Nothing inside main falls through to a UA heading default. Those are em
       multiples of the parent - h2 is 1.5em, h3 1.17em - so against the 17px
       running text they land on 25.50px and 19.89px, sizes on no scale that
       nobody picked. glossary.html had 15 of them.

       At (0,0,2) this loses to every component rule that sets a heading size
       (.section h2 is (0,1,1) and keeps its 2rem), so it only fills the gaps.
       The <=768px block above sets bare `h2`/`h3` at (0,0,1) and would lose to
       this one, hence the min-width guard. */
    main :is(h2) { font-size: var(--fs-24); }
    main :is(h3) { font-size: var(--fs-20); }
    main :is(h4) { font-size: var(--fs-17); }

    .contribute-article h2 { font-size: var(--fs-24); }
    .contribute-article h3 { font-size: var(--fs-20); }
    .contribute-article h4 { font-size: var(--fs-17); }
}

/* The em font-sizes left in this file are the ones where the relationship is
   the point: .code-block scales with the prose around it, .breadcrumb with the
   body, .font-up is a relative utility by definition, and .nav-coffee-icon
   sizes to the label beside it. Everything else uses a scale step, because an
   em size depends on how deeply the element happens to be nested. If you add
   an em font-size, be able to say which of those two kinds it is. */

.code-block {
    background-color: #f5f5f5;
    border-left: 4px solid var(--primary-color);
    padding: 1rem;
    margin: 1rem 0;
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
    font-size: 0.9em;
}

.highlight-box {
    background-color: #e8f4f8;
    border-left: 4px solid #0096d1;
    padding: 1rem;
    margin: 1.5rem 0;
    border-radius: 4px;
    color: #1a232a;
    font-weight: 500;
}

.highlight-box strong {
    color: var(--primary-color);
}

.contribution-type {
    background-color: #f9f9f9;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 1.5rem;
    margin: 1.5rem 0;
    color: #1a232a;
    font-weight: 500;
}

.reader-card {
    padding-top: 0;
    overflow: hidden;
}

.reader-preview {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 8px 8px 0 0;
    margin: 0 -1.5rem;
    width: calc(100% + 3rem);
    margin-bottom: 1rem;
}

.contribution-type h4 {
    color: var(--primary-color);
    margin-top: 0;
    font-size: var(--fs-18);
}

.contribution-type .examples {
    background-color: #fffbea;
    padding: 0.75rem;
    border-radius: 4px;
    margin: 0.75rem 0;
}

.contribution-type .have-idea {
    background-color: #e8f4f8;
    padding: 0.75rem;
    border-radius: 4px;
    margin: 0.75rem 0;
}

.step-box {
    background-color: #f9f9f9;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 1.5rem;
    margin: 1.5rem 0;
}

.step-number {
    display: inline-block;
    background-color: var(--primary-color);
    color: white;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    text-align: center;
    line-height: 2.5rem;
    font-weight: bold;
    margin-bottom: 0.5rem;
}

.step-box h3 {
    margin-top: 0;
}

.link-box {
    background-color: #fffbea;
    border-left: 4px solid #ffc107;
    padding: 1rem;
    margin: 1rem 0;
    border-radius: 4px;
}

.link-box a {
    text-decoration: none;
    color: var(--primary-color);
    font-weight: 500;
}

.link-box a:hover {
    text-decoration: underline;
}

.faq-item {
    margin: 1.5rem 0;
    padding: 1rem;
    background-color: #f9f9f9;
    border-left: 4px solid var(--primary-color);
}

.faq-item strong {
    color: var(--primary-color);
}

.breadcrumb {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 2rem;
    font-size: 0.9em;
    color: #666;
}

.breadcrumb a {
    color: var(--primary-color);
    text-decoration: none;
}

.breadcrumb a:hover {
    text-decoration: underline;
}

.breadcrumb span {
    margin: 0 0.25rem;
}

.toc {
    background-color: #f0f7ff;
    border-left: 4px solid var(--primary-color);
    padding: 1.5rem;
    margin: 2rem 0;
    border-radius: 4px;
}

.toc h3 {
    margin-top: 0;
    color: var(--primary-color);
}

/* The contents heading is an h2. Inside .contribute-article it would otherwise
   keep the 2.5rem top margin every article h2 gets, on top of the box's own
   padding. Margin only, so each page keeps the h2 colour it already has. */
.toc h2 {
    margin-top: 0;
}

.toc ol {
    margin-bottom: 0;
}

.toc li {
    margin-bottom: 0.5rem;
}

/* Park the contents list in the left margin on wide screens and let it follow
   the reader. The guides it appears on run long (cloud-security-engineer.html
   has 18 h2s), and a static list gives no section context or way to jump past
   the first screen.

   Floating with a negative margin claims the empty margin without touching the
   markup on any page that carries a .toc, and because the float lands
   entirely outside the text column no prose wraps around it.

   Its inner edge is measured from --breakout, not from the column. Tables,
   diagrams and code blocks break out 72px past the column, and a sticky rail
   passes beside every one of them as the reader scrolls, so a rail measured
   from the prose would sit underneath each. It sits 1.5rem outside the
   break-out on every page, including pages with nothing wide, so it does not
   jump sideways between guides.

   The 1360px floor is arithmetic, not taste. From the centre line the rail
   needs half the break-out (440px), the 1.5rem gutter and the 12rem rail, which
   is 656px, plus 1.5rem to the window edge: 680px, half of 1360. A 17px
   classic scrollbar still leaves 15px. A media query cannot read a custom
   property, so re-derive this by hand if --breakout, --measure or the rail's
   width changes. Below it the rule does not apply at all and the list stays
   an in-flow callout, which is also what mobile keeps.

   max-height + overflow-y matter: a sticky box taller than the viewport only
   pins its bottom edge, and these lists can be taller than the viewport once
   the header is subtracted. */
@media (min-width: 1360px) {
    .contribute-article > .toc {
        float: left;
        width: 12rem;
        margin: 0 0 1.5rem calc(-12rem - 1.5rem - (var(--breakout) - var(--measure)) / 2);
        padding: 1rem 1.125rem;
        position: sticky;
        top: calc(var(--header-height) + 1rem);
        max-height: calc(100vh - var(--header-height) - 3rem);
        overflow-y: auto;
        overscroll-behavior: contain;
        font-size: var(--fs-13);
    }

    .contribute-article > .toc h2,
    .contribute-article > .toc h3 {
        font-size: var(--fs-14);
        margin-bottom: 0.5rem;
    }

    .contribute-article > .toc li {
        margin-bottom: 0.375rem;
        line-height: 1.4;
    }
}

/* Underlined, and coloured as a link. --primary-color against body text
   is a 1.22:1 difference, so without this colour would be the only cue and
   not a perceptible one. Same treatment as prose and .page-meta links, so the
   site has one link idiom rather than three.

   This block is declared twice in this file, byte for byte. Change both. */
.toc a {
    color: var(--secondary-color);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

.toc a:hover,
.toc a:focus-visible {
    text-decoration-thickness: 2px;
}

.contribution-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}

/* =========================================================================
   UTILITY CLASSES (replaces inline style="" attributes for CSP compliance)
   ========================================================================= */

.mt-1 {
    margin-top: 1rem;
}

.mt-1\.5 {
    margin-top: 1.5rem;
}

.mt-2 {
    margin-top: 2rem;
}

.mt-3 {
    margin-top: 3rem;
}

.mb-0 {
    margin-bottom: 0;
}

.mb-1 {
    margin-bottom: 1rem;
}

.text-center {
    text-align: center;
}

.text-muted {
    color: #666;
}

.font-up {
    font-size: 1.05em;
}

/* =========================================================================
   INDEX.HTML - Community section cards & grid
   ========================================================================= */

.community-card {
    background-color: var(--white);
    padding: 1rem;
    color: var(--text-color);
    font-weight: 600;
    font-size: var(--fs-17);
    border-radius: 8px;
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
}

.community-card .btn {
    margin-top: auto;
    align-self: flex-start;
}

.community-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 2rem;
    margin-top: 2rem;
}

/* Homepage: three choices, not another catalog grid. The whole block is the
   link. Amber left rule + underlined action line carry the cue (colour alone
   fails WCAG 1.4.1). No boxed card chrome - interaction doesn't need a
   border+shadow container. */
.door-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.75rem;
    margin-top: 1.5rem;
}
.door-card {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    height: 100%;
    padding: 0.35rem 0.25rem 0.35rem 1.15rem;
    background: transparent;
    border: none;
    border-left: 3px solid var(--accent-color);
    border-radius: 0;
    box-shadow: none;
    color: var(--text-color);
    text-decoration: none;
    transition: transform 0.25s ease, border-left-color 0.2s ease;
}
.door-card:hover {
    transform: translateY(-3px);
    border-left-color: #d97706;
}
.door-card h2 {
    margin: 0;
    font-size: var(--fs-24);
    line-height: 1.2;
    color: var(--primary-color);
}
.door-card p {
    margin: 0;
    flex: 1;
    color: var(--text-muted);
}
/* Beat .door-card p (0,1,1): kickers are <p class="door-kicker">. */
.door-card .door-kicker {
    margin: 0;
    flex: 0 0 auto;
    font-size: var(--fs-13);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #b45309;
}
.door-go {
    font-weight: 600;
    color: var(--secondary-color);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    transition: text-decoration-thickness 0.15s ease;
}
.door-card:hover .door-go,
.door-card:focus-visible .door-go {
    text-decoration-thickness: 2px;
}
.door-card:focus-visible {
    outline: 3px solid var(--secondary-color);
    outline-offset: 4px;
}
@media (max-width: 800px) {
    .door-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
    .door-card { transition: none; }
    .door-card:hover { transform: none; }
}

/* [data-theme="dark"] body a is (0,2,1) and would paint the whole card,
   heading included, in the link blue. One step of specificity keeps the
   card in body text and leaves the action line as the link. */
[data-theme="dark"] body a.door-card,
[data-theme="dark"] body a.door-card:hover {
    color: var(--text-color);
}
[data-theme="dark"] body a.door-card h2 {
    color: var(--primary-color);
}
[data-theme="dark"] body a.door-card .door-kicker,
[data-theme="dark"] .door-card .door-kicker {
    color: #fbbf24;
}
[data-theme="dark"] body a.door-card .door-go {
    color: var(--secondary-color);
}

.community-heading {
    color: var(--primary-color);
    margin-top: 0;
}

.info-list {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow);
    list-style-position: inside;
}

.info-list li {
    margin-bottom: 1rem;
}

.hero-img {
    max-width: 100%;
    height: auto;
    margin-bottom: 20px;
    border-radius: 8px;
}

.hero-logo {
    display: block;
    width: auto;
    height: 110px;
    max-width: 90%;
    margin: 0 auto 1.5rem;
    padding: 0.75rem 1.75rem;
    background: #f5f5f5;
    border-radius: 14px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
    box-sizing: content-box;
}

.memorial-link {
    color: #1d4ed8;
    text-decoration: underline;
}

/* =========================================================================
   KEVIN-MITNICK.HTML - Memorial page (moved from <style> block)
   ========================================================================= */

.memorial-content {
    max-width: 900px;
    margin: 0 auto;
    line-height: 1.8;
}

.memorial-content p {
    margin-bottom: 1.5em;
}

.memorial-content h2 {
    margin-top: 2em;
    margin-bottom: 1em;
    color: var(--primary-color);
}

.memorial-image {
    width: 100%;
    max-width: 600px;
    margin: 2em auto;
    display: block;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.signature {
    font-style: italic;
    margin-top: 2em;
}

.date {
    color: var(--text-muted, #666);
    font-size: var(--fs-14);
}

.iframe-container {
    margin: 2em 0;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    overflow: hidden;
}

.iframe-container iframe {
    width: 100%;
    height: 800px;
    border: none;
    background-color: #ffffff;
}

.memorial-title {
    text-align: center;
    margin-bottom: 0.5em;
}

.memorial-subtitle {
    text-align: center;
    color: var(--text-muted, #666);
    font-size: var(--fs-18);
    margin-bottom: 2em;
}

/* Wired article card */
.wired-card-link {
    display: inline-block;
    width: 100%;
    text-decoration: none;
    margin: 20px 0;
}

.wired-card {
    background: linear-gradient(135deg, #f0f0f0 0%, #ffffff 100%);
    border: 2px solid #dd4814;
    border-radius: 8px;
    padding: 24px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
    cursor: pointer;
}

.wired-card:hover {
    box-shadow: 0 8px 24px rgba(221, 72, 20, 0.2);
    transform: translateY(-2px);
}

.wired-card-inner {
    display: flex;
    align-items: center;
    gap: 16px;
}

.wired-card-icon {
    font-size: var(--fs-48);
    min-width: 60px;
}

.wired-card-label {
    color: #1a232a;
    font-weight: 700;
    font-size: var(--fs-18);
    letter-spacing: 1px;
}

.wired-card-title {
    color: #333;
    font-size: var(--fs-18);
    font-weight: bold;
    margin-bottom: 8px;
}

.wired-card-desc {
    color: #666;
    font-size: var(--fs-14);
}

/* =========================================================================
   CONTRIBUTE-RESOURCES.HTML - tag swatches
   -------------------------------------------------------------------------
   Only the tag swatches are unique to this page; everything else it uses
   (.contribute-article, .code-block, .highlight-box, .toc, ...) is the
   Contribute page block above. Don't duplicate those rules here.
   ========================================================================= */

.tag-example {
    display: inline-block;
    background-color: #ddd;
    padding: 0.25rem 0.75rem;
    border-radius: 20px;
    margin: 0.25rem;
    font-size: var(--fs-14);
}

/* Material-palette hues with lightness dropped until white clears 4.5:1;
   hue and saturation held, so each swatch still reads as the colour it names.
   Same treatment as the share buttons. .tag-example.tool (#9c27b0) is 6.30:1
   as-is.

     ctf            #158463   4.63:1
     lab            #39843c   4.60:1
     certification  #0b77cd   4.63:1
     job            #e3165c   4.61:1 */
.tag-example.ctf {
    background-color: #158463;
    color: white;
}

.tag-example.tool {
    background-color: #9c27b0;
    color: white;
}

.tag-example.lab {
    background-color: #39843c;
    color: white;
}

.tag-example.certification {
    background-color: #0b77cd;
    color: white;
}

.tag-example.job {
    background-color: #e3165c;
    color: white;
}

/* =========================================================================
   404.HTML / 403.HTML - Error pages (moved from <style> block)
   ========================================================================= */

.error-container {
    text-align: center;
    padding: 4rem 2rem;
    max-width: 600px;
    margin: 0 auto;
}

.error-code {
    font-size: 6rem;
    font-weight: 700;
    color: var(--primary-color);
    margin-bottom: 0;
    line-height: 1;
}

.error-message {
    font-size: var(--fs-24);
    color: var(--text-color);
    margin: 1rem 0 2rem;
}

.error-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1rem;
    margin-top: 2rem;
}

/* =========================================================================
   SECURITY-POLICY.HTML & PRESENTATIONS.HTML - Info boxes
   ========================================================================= */

.info-box--blue {
    background-color: #e8f4f8;
    padding: 1.5rem;
    border-left: 4px solid #3498db;
    margin: 2rem 0;
}

.info-box--yellow {
    background-color: #fff3cd;
    padding: 1.5rem;
    border-left: 4px solid #ffc107;
    margin: 2rem 0;
}

.cta-buttons--spaced {
    margin: 1.5rem 0;
}

.cta-buttons--spaced-lg {
    margin: 2rem 0;
}

.presentation-card {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow);
}

.presentation-list {
    list-style-position: inside;
    margin-top: 1rem;
}

/* CTA section (bottom of contribute-resources, etc.) */
.cta-section {
    text-align: center;
    margin-top: 3rem;
    padding: 2rem;
    background-color: #f0f7ff;
    border-radius: 8px;
}

.cta-section ul {
    text-align: left;
    /* Matches the reading measure, so this list lines up with the <p> that
       introduces it. */
    max-width: var(--measure);
    margin: 1rem auto 0;
    padding-left: 1.5rem;
}

.cta-section ul li + li {
    margin-top: 0.5rem;
}

.cta-link {
    color: var(--primary-color);
    text-decoration: none;
    font-weight: bold;
}

/* =========================================================================
   MOBILE REDO (canonical overrides)
   The nav collapses to the hamburger at <=1023px (so the wide inline menu
   never wraps on laptops/tablets); deep-mobile content compaction stays at
   <=768px in the second block below.
   ========================================================================= */

@media (max-width: 1023px) {
    header {
        position: static;
        padding: 0.5rem 0;
    }

    .header-content {
        padding: 0 1rem;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    .logo .logo-title {
        font-size: var(--fs-16);
        line-height: 1.2;
        margin: 0;
    }

    .logo p {
        display: none;
    }

    .hamburger {
        display: inline-flex;
        order: 3;
        margin-left: 0;
        width: 2.4rem;
        height: 2.4rem;
        font-size: var(--fs-18);
    }

    .theme-toggle {
        order: 2;
        margin-left: auto;
        margin-right: 0.375rem;
        width: 2.4rem;
        height: 2.4rem;
        font-size: var(--fs-17);
    }

    header nav {
        display: block;
        width: 100%;
        order: 4;
    }

    .js-enabled header nav {
        display: none;
    }

    .js-enabled header .nav-open nav {
        display: block;
    }

    header nav ul {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        padding: 0.5rem 0;
        margin: 0;
        overflow-x: visible;
    }

    header nav ul li a {
        display: block;
        padding: 0.75rem 1rem;
        font-size: var(--fs-14);
        border-top: 1px solid rgba(255, 255, 255, 0.1);
    }
}

/* Deep-mobile content compaction - phone widths only. */
@media (max-width: 768px) {
    .container {
        padding: 0 0.75rem;
    }

    section {
        padding: 1.25rem 0;
    }

    .hero {
        padding: 1.25rem 1rem;
    }

    .hero h1,
    .hero h2 {
        font-size: var(--fs-20);
        line-height: 1.2;
        margin-bottom: 0.625rem;
    }

    .hero p {
        font-size: var(--fs-15);
        margin-bottom: 1rem;
    }

    .hero-img {
        margin-bottom: 0.75rem;
    }

    /* :not(--compact) so the glyph-tile grids can go two-up below. This rule
       carries !important, and it is the second of two `.resource-grid { 1fr }`
       declarations inside a (max-width: 768px) block in this file, so a plain
       override placed anywhere loses to it. Narrowing the selector is the only
       fix that does not add a third !important. */
    .resource-grid:not(.resource-grid--compact),
    .resources-grid {
        grid-template-columns: 1fr !important;
        gap: 1rem;
        padding: 0.75rem !important;
    }

    .resource-card {
        padding: 1rem;
    }

    .cta-buttons {
        flex-direction: column;
        gap: 0.75rem;
    }

    .btn {
        width: 100%;
        text-align: center;
    }

    .footer-content {
        grid-template-columns: 1fr 1fr;
        gap: 1.25rem 1rem;
    }
    .footer-about {
        grid-column: 1 / -1;
    }
    .footer-bottom {
        flex-direction: column;
        align-items: flex-start;
        text-align: left;
    }
}

@media (max-width: 480px) {
    .hero h1,
    .hero h2 {
        font-size: var(--fs-20);
    }

    .hero p {
        font-size: var(--fs-14);
    }
}

/* =========================================================================
   DARK MODE
   ========================================================================= */

/* Dark mode toggle button in header */
.theme-toggle {
    background: transparent;
    border: none;
    border-radius: 999px;
    color: var(--white);
    cursor: pointer;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    font-size: var(--fs-16);
    line-height: 1;
    transition: background-color 0.2s ease, transform 0.15s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    margin-right: 0.5rem;
    flex-shrink: 0;
}

.theme-toggle:hover {
    background-color: rgba(255, 255, 255, 0.12);
    transform: scale(1.06);
}

.theme-toggle:active {
    transform: scale(0.96);
}

.theme-toggle:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.85);
    outline-offset: 2px;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        --primary-color: #c2d9ec;
        --secondary-color: #6fc3ff;
        --accent-color: #ff7b6b;
        --text-color: #e8ecf2;
        --light-bg: #0f1419;
        --white: #1c2530;
        --border-color: #30445a;
        --text-muted: #94a3b8;
        --shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
        --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.5);
    }

    :root:not([data-theme]) body {
        background-color: var(--light-bg);
        color: var(--text-color);
    }

    :root:not([data-theme]) body a {
        color: #6fc3ff;
    }

    :root:not([data-theme]) body a:hover {
        color: #9dd5ff;
    }

    /* The `body a` rule above (0,2,2) outranks `.btn-primary` (0,1,0), so
       without this a .btn-primary outside the hero gets link-blue text on its
       own link-blue background - contrast 1.00, text invisible. The
       [data-theme="dark"] branch already carries this override; this is its
       missing mirror for visitors on a dark OS who never touched the toggle.
       Keep the two branches in step. */
    :root:not([data-theme]) .btn-primary {
        background-color: #0ea5e9;
        color: #0b1729;
    }

    :root:not([data-theme]) .btn-primary:hover {
        background-color: #38bdf8;
    }

    /* .next-session__badge is pinned to sky-700 for AA on its white surface,
       so it does not follow --secondary-color into dark. Mirrors the
       [data-theme="dark"] override further down. */
    :root:not([data-theme]) .next-session__badge {
        color: #7dd3fc;
    }

    :root:not([data-theme]) header {
        background-color: #0b1018;
        color: #f5f7fa;
    }

    :root:not([data-theme]) .logo .logo-title,
    :root:not([data-theme]) .logo p,
    :root:not([data-theme]) .logo-link {
        color: #f5f7fa;
    }

    :root:not([data-theme]) nav a {
        color: #f5f7fa;
    }

    :root:not([data-theme]) .hero,
    :root:not([data-theme]) .hero--compact {
        background: linear-gradient(135deg, #0a1628 0%, #122a4a 100%) !important;
        color: #ffffff !important;
    }

    :root:not([data-theme]) .hero h1,
    :root:not([data-theme]) .hero h2,
    :root:not([data-theme]) .hero--compact h1,
    :root:not([data-theme]) .hero--compact h2 {
        color: #ffffff !important;
    }

    :root:not([data-theme]) .hero p,
    :root:not([data-theme]) .hero--compact p {
        color: #ffffff !important;
        opacity: 0.95;
    }

    :root:not([data-theme]) .hero .btn-secondary,
    :root:not([data-theme]) .hero--compact .btn-secondary {
        background-color: rgba(255, 255, 255, 0.15) !important;
        color: #ffffff !important;
        border: 1px solid rgba(255, 255, 255, 0.3);
    }

    :root:not([data-theme]) .hero .btn-secondary:hover,
    :root:not([data-theme]) .hero--compact .btn-secondary:hover {
        background-color: rgba(255, 255, 255, 0.25) !important;
        color: #ffffff !important;
    }

    :root:not([data-theme]) footer {
        background-color: #0b1018;
        color: #d8e0ea;
    }

    :root:not([data-theme]) .footer-section a {
        color: rgba(220, 230, 242, 0.9);
    }

    :root:not([data-theme]) .footer-bottom {
        border-top-color: rgba(255, 255, 255, 0.1);
        color: rgba(220, 230, 242, 0.7);
    }
}

/* Dark mode overrides */
[data-theme="dark"] {
    --primary-color: #c2d9ec;
    --secondary-color: #6fc3ff;
    --accent-color: #ff7b6b;
    --text-color: #e8ecf2;
    --light-bg: #0f1419;
    --white: #1c2530;
    --border-color: #30445a;
    --text-muted: #94a3b8;
    --shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
    --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.5);
}

/* --- Dark mode: base body ------------------------------------------------ */
[data-theme="dark"] body {
    background-color: var(--light-bg);
    color: var(--text-color);
}

[data-theme="dark"] body a {
    color: #6fc3ff;
}

[data-theme="dark"] body a:hover {
    color: #9dd5ff;
}

/* --- Dark mode: headings & titles ---------------------------------------- */
[data-theme="dark"] .resource-card h3,
[data-theme="dark"] .section h2,
[data-theme="dark"] .section h3,
[data-theme="dark"] .category-summary h2,
[data-theme="dark"] .resource-card h3 a {
    color: var(--secondary-color);
}

[data-theme="dark"] .contribute-article h2 {
    color: #7dccff;
    border-bottom-color: #30445a;
}

[data-theme="dark"] .memorial-content h2 {
    color: #7dccff;
}

[data-theme="dark"] .contribution-type h4 {
    color: #7dccff;
}

/* --- Dark mode: text & descriptions -------------------------------------- */
[data-theme="dark"] .resource-card p,
[data-theme="dark"] .article-date,
[data-theme="dark"] .no-results {
    color: #c8cfd8;
}

/* Conference "Next:" line - a brighter sky-blue in dark mode so it stays
   distinct from both the muted card body text (#c8cfd8) and the title. */
[data-theme="dark"] .resource-card p.conf-next,
[data-theme="dark"] .resource-card p.conf-next strong {
    color: #7dccff;
}

[data-theme="dark"] .resource-list a {
    color: var(--secondary-color);
}

[data-theme="dark"] .resource-list .description {
    color: #b8c2cc;
}

[data-theme="dark"] .info-box p {
    color: #c8cfd8;
}

[data-theme="dark"] .text-muted {
    color: #a0adb8;
}

[data-theme="dark"] .date,
[data-theme="dark"] .memorial-subtitle {
    color: #a0adb8;
}

[data-theme="dark"] .highlight-box strong {
    color: #7dccff;
}

[data-theme="dark"] .faq-item strong {
    color: #7dccff;
}

[data-theme="dark"] .link-box a {
    color: var(--secondary-color);
}

[data-theme="dark"] .cta-link {
    color: var(--secondary-color);
}

/* --- Dark mode: error pages ---------------------------------------------- */
[data-theme="dark"] .error-code {
    color: var(--secondary-color);
}

[data-theme="dark"] .error-message {
    color: var(--text-color);
}

/* --- Dark mode: tags ----------------------------------------------------- */
[data-theme="dark"] .tag {
    background-color: #1e2e3e;
    color: #dce2e8;
}

[data-theme="dark"] .tag.ctf { background-color: #3d2200; color: #ffc964; }
[data-theme="dark"] .tag.certification { background-color: #0d2744; color: #7ecbff; }
[data-theme="dark"] .tag.tool { background-color: #2a0845; color: #dea8ee; }
[data-theme="dark"] .tag.lab { background-color: #3b0d1e; color: #ffa4c4; }
[data-theme="dark"] .tag.job { background-color: #042f2e; color: #5eead4; }
[data-theme="dark"] .tag.ai-security { background-color: #1e1b4b; color: #c4b5fd; }
[data-theme="dark"] .tag.newsletter { background-color: #3a2f04; color: #fde047; }
[data-theme="dark"] .tag.github { background-color: #2e1065; color: #ddd6fe; }
[data-theme="dark"] .tag.new { background-color: #1b3a1b; color: #95db98; }

/* chat-resources.html category badges in dark mode. The generic
   `[data-theme="dark"] .tag` above is later in the file than the category
   colours and would otherwise flatten all 17 back to the default chip, so
   each is restated. Twelve keep their light-mode brand colour, which is
   saturated enough to sit on the dark card. The five near-black ones
   (News, GitHub, OWASP, CISA, Vendor) are lightened: at their brand values
   they land under 1.7:1 against the dark card surface (#1c2530) and the
   chip disappears into it. */
[data-theme="dark"] .tag.tag-other { background-color: #5D7987; color: #ffffff; }
[data-theme="dark"] .tag.tag-news { background-color: #496785; color: #ffffff; }
[data-theme="dark"] .tag.tag-youtube { background-color: #c4302b; color: #ffffff; }
[data-theme="dark"] .tag.tag-github { background-color: #5A6673; color: #ffffff; }
[data-theme="dark"] .tag.tag-owasp { background-color: #656565; color: #ffffff; }
[data-theme="dark"] .tag.tag-wikipedia { background-color: #636466; color: #ffffff; }
[data-theme="dark"] .tag.tag-vendor { background-color: #9526D9; color: #ffffff; }
[data-theme="dark"] .tag.tag-cisa { background-color: #1F68B5; color: #ffffff; }
[data-theme="dark"] .tag.tag-discord { background-color: #5865F2; color: #ffffff; }
[data-theme="dark"] .tag.tag-azure { background-color: #0078D4; color: #ffffff; }
[data-theme="dark"] .tag.tag-training { background-color: #E01D5F; color: #ffffff; }
[data-theme="dark"] .tag.tag-gcp { background-color: #3972D2; color: #ffffff; }
[data-theme="dark"] .tag.tag-wiz { background-color: #00C2A2; color: #1f2937; }
[data-theme="dark"] .tag.tag-aws { background-color: #FF9900; color: #1f2937; }
[data-theme="dark"] .tag.tag-spotify { background-color: #1DB954; color: #1f2937; }
[data-theme="dark"] .tag.tag-jobs { background-color: #ff9800; color: #1f2937; }
[data-theme="dark"] .tag.tag-csoh { background-color: #3498db; color: #1f2937; }

/* --- Dark mode: buttons & filters ---------------------------------------- */
[data-theme="dark"] .filter-btn {
    background-color: var(--white);
    border-color: var(--border-color);
    color: var(--text-color);
}

/* Deep navy, not #fff: in dark --secondary-color is the LIGHT blue #6fc3ff,
   so white on it would be 1.92:1. #0f1419 is 9.63:1. Comment sits above the rule
   rather than inside it because sync_dark_branch.py flattens each rule to one
   line when it mirrors it, and an inside comment lands mid-declaration. */
[data-theme="dark"] .filter-btn:hover,
[data-theme="dark"] .filter-btn.active {
    background-color: var(--secondary-color);
    color: #0f1419;
    border-color: var(--secondary-color);
}

/* Same inversion: dark redefines --primary-color to #c2d9ec, so white
   step badges on it would be 1.46:1. 12.35:1 with this. */
[data-theme="dark"] .step-number { color: #0b1729; }

[data-theme="dark"] .btn-secondary {
    background-color: #1e2e3e;
    color: var(--text-color);
}

[data-theme="dark"] .btn-secondary:hover {
    background-color: #2a3d52;
}

[data-theme="dark"] .show-all-btn {
    background-color: #1a2535;
    border-color: var(--secondary-color);
    color: var(--text-color);
}

[data-theme="dark"] .show-all-btn:hover {
    background-color: var(--secondary-color);
    color: #fff;
}

/* --- Dark mode: hero & footer -------------------------------------------- */
[data-theme="dark"] header {
    background-color: #0b1018;
    color: #f5f7fa;
}

[data-theme="dark"] .logo .logo-title,
[data-theme="dark"] .logo p,
[data-theme="dark"] .logo-link {
    color: #f5f7fa;
}

[data-theme="dark"] nav a,
[data-theme="dark"] .dropdown-toggle,
[data-theme="dark"] .dropdown-menu a {
    color: #f5f7fa;
}

[data-theme="dark"] .dropdown-menu {
    background-color: #1c2530;
    border: 1px solid rgba(255, 255, 255, 0.08);
}

[data-theme="dark"] .hamburger {
    color: #f5f7fa;
}

/* Both of these inherit `color: var(--white)`, which is white on the dark
   header in light mode - correct - but flips to #1c2530 in dark, i.e. near
   the header's own #0b1018. The toggle reads 1.23:1 that way. It *looks*
   fine only because its label is an emoji, and emoji ignore `color`; drop
   the glyph to a text fallback and the button vanishes. Pin it to the same
   foreground .hamburger uses. */
[data-theme="dark"] .theme-toggle {
    color: #f5f7fa;
}

/* .skip-link sets `background: var(--primary-color); color: var(--white)`,
   which flips coherently in dark - but `body a` (0,1,2) outranks `.skip-link`
   (0,1,0) and repaints the text link-blue on the now-light chip: 1.32:1, a
   keyboard user's first stop and unreadable. Pin both ends here, where the
   specificity (0,2,0) beats the link rule. */
[data-theme="dark"] .skip-link {
    background: #c2d9ec;
    color: #0b1729;
}

[data-theme="dark"] .hero,
[data-theme="dark"] .hero--compact {
    background: linear-gradient(135deg, #0a1628 0%, #122a4a 100%) !important;
    color: #ffffff !important;
}

[data-theme="dark"] .hero h1,
[data-theme="dark"] .hero h2,
[data-theme="dark"] .hero--compact h1,
[data-theme="dark"] .hero--compact h2 {
    color: #ffffff !important;
}

[data-theme="dark"] .hero p,
[data-theme="dark"] .hero--compact p {
    color: #ffffff !important;
    opacity: 0.95;
}

[data-theme="dark"] .hero :is(p, li) a:not([class]),
[data-theme="dark"] .hero :is(p, li) a.glossary-link {
    color: var(--secondary-color);
}

[data-theme="dark"] .hero .btn-primary,
[data-theme="dark"] .hero--compact .btn-primary {
    background-color: #0ea5e9;
    color: #0b1729 !important;
}

[data-theme="dark"] .hero .btn-secondary,
[data-theme="dark"] .hero--compact .btn-secondary {
    background-color: rgba(255, 255, 255, 0.15) !important;
    color: #ffffff !important;
    border: 1px solid rgba(255, 255, 255, 0.3);
}

[data-theme="dark"] .hero .btn-secondary:hover,
[data-theme="dark"] .hero--compact .btn-secondary:hover {
    background-color: rgba(255, 255, 255, 0.25) !important;
    color: #ffffff !important;
}

[data-theme="dark"] .btn-primary {
    background-color: #0ea5e9;
    color: #0b1729;
}

[data-theme="dark"] .btn-primary:hover {
    background-color: #38bdf8;
}

[data-theme="dark"] footer {
    background-color: #0b1018;
    color: #d8e0ea;
}

[data-theme="dark"] .footer-section h3 {
    color: #f0f3f7;
}

[data-theme="dark"] .footer-section a {
    color: rgba(220, 230, 242, 0.9);
}

[data-theme="dark"] .footer-section a:hover {
    color: #ffffff;
}

/* The coffee button keeps its dark ink on amber in dark mode too. The two
   rules above are `[data-theme="dark"] .footer-section a` (0,2,1), which
   outranks the plain `.footer-section .footer-coffee` (0,2,0) and would paint
   near-white text onto the amber fill - 1.7:1, unreadable. Matching their
   weight here keeps it at 7.6:1. The fill is the same in both themes, so the
   ink should be too. */
[data-theme="dark"] .footer-section a.footer-coffee {
    color: #1a202c;
}

[data-theme="dark"] .footer-section a.footer-coffee:hover,
[data-theme="dark"] .footer-section a.footer-coffee:focus {
    color: #1a202c;
}

[data-theme="dark"] .footer-bottom {
    border-top-color: rgba(255, 255, 255, 0.1);
    color: rgba(220, 230, 242, 0.7);
}

[data-theme="dark"] .source {
    color: var(--secondary-color);
}

/* --- Dark mode: breadcrumbs (both old .breadcrumb and new .breadcrumb-nav) */
[data-theme="dark"] .breadcrumb {
    color: #a0adb8;
}

[data-theme="dark"] .breadcrumb a {
    color: var(--secondary-color);
}

[data-theme="dark"] .breadcrumb-nav {
    background-color: #0b1018;
}

[data-theme="dark"] .breadcrumb-nav ol {
    color: rgba(245, 247, 250, 0.7);
}

[data-theme="dark"] .breadcrumb-nav a {
    color: rgba(245, 247, 250, 0.85);
}

[data-theme="dark"] .breadcrumb-nav a:hover {
    background-color: rgba(255, 255, 255, 0.1);
    color: #ffffff;
}

[data-theme="dark"] .breadcrumb-nav [aria-current="page"] {
    color: #ffffff;
}

[data-theme="dark"] .breadcrumb-nav li + li::before {
    color: rgba(245, 247, 250, 0.35);
}

/* --- Dark mode: code blocks, TOC, boxes ---------------------------------- */
[data-theme="dark"] .code-block {
    background-color: #121c28;
    border-left-color: var(--secondary-color);
    color: #dce2e8;
}

[data-theme="dark"] .highlight-box {
    background-color: #152535;
    border-left-color: #3a8ec2;
    color: #eaf6ff;
    font-weight: 500;
}

[data-theme="dark"] .toc {
    background-color: #141e2a;
    border-left-color: var(--secondary-color);
}

[data-theme="dark"] .toc h3 {
    color: #7dccff;
}

[data-theme="dark"] .toc a {
    color: var(--secondary-color);
}

[data-theme="dark"] .contribution-type {
    color: #eaf6ff;
    font-weight: 500;
    background-color: #1a2535;
    border-color: #2a3d52;
}

[data-theme="dark"] .faq-item {
    background-color: #1a2535;
    border-left-color: var(--secondary-color);
}

[data-theme="dark"] .step-box {
    background-color: #1a2535;
    border-color: #2a3d52;
}

[data-theme="dark"] .contribution-type .examples {
    background-color: #2a2510;
}

[data-theme="dark"] .contribution-type .have-idea {
    background-color: #152535;
}

[data-theme="dark"] .link-box {
    background-color: #221e0e;
    border-left-color: #b8950a;
}

[data-theme="dark"] .tag-example {
    background-color: #2a3d52;
    color: #dce2e8;
}

/* --- Dark mode: info boxes ----------------------------------------------- */
[data-theme="dark"] .info-box--blue {
    background-color: #152535;
    border-left-color: #3a8ec2;
}

[data-theme="dark"] .info-box--yellow {
    background-color: #2a2510;
    border-left-color: #b8950a;
}

/* --- Dark mode: CTA section ---------------------------------------------- */
[data-theme="dark"] .cta-section {
    background-color: #141e2a;
}

/* --- Dark mode: wired card (kevin-mitnick.html) -------------------------- */
[data-theme="dark"] .wired-card {
    background: linear-gradient(135deg, #1a2535 0%, #1e2e3e 100%);
    border-color: #c04015;
}

[data-theme="dark"] .wired-card:hover {
    box-shadow: 0 8px 24px rgba(200, 60, 15, 0.25);
}

[data-theme="dark"] .wired-card-label {
    color: #e8763a;
}

[data-theme="dark"] .wired-card-title {
    color: #f0f3f7;
}

[data-theme="dark"] .wired-card-desc {
    color: #b8c2cc;
}

/* --- Dark mode: form controls -------------------------------------------- */
[data-theme="dark"] input[type="text"] {
    background-color: #1a2535;
    color: var(--text-color);
    border-color: var(--border-color);
}

[data-theme="dark"] .category-summary:hover {
    background-color: #1e2e3e;
}

/* =========================================================================
   BREADCRUMB NAVIGATION (visual UI)
   ========================================================================= */

.breadcrumb-nav {
    background-color: var(--primary-color);
    padding: 0;
}

.breadcrumb-nav ol {
    max-width: var(--frame);
    margin: 0 auto;
    padding: 0.5rem 2rem 0.625rem;
    list-style: none;
    font-size: var(--fs-14);
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.75);
}

.breadcrumb-nav li {
    display: inline;
    white-space: nowrap;
}

.breadcrumb-nav li + li::before {
    content: '/';
    color: rgba(255, 255, 255, 0.4);
    padding: 0 0.5rem;
    font-weight: 300;
}

.breadcrumb-nav a {
    color: rgba(255, 255, 255, 0.85);
    text-decoration: none;
    transition: color 0.15s ease;
}

.breadcrumb-nav a:hover {
    color: #ffffff;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.breadcrumb-nav a:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.8);
    outline-offset: 2px;
    border-radius: 2px;
}

.breadcrumb-nav [aria-current="page"] {
    color: #ffffff;
    font-weight: 600;
}

@media (max-width: 768px) {
    .breadcrumb-nav ol {
        padding: 0.5rem 0.75rem 0.5rem;
        font-size: var(--fs-13);
    }
    .breadcrumb-nav li + li::before {
        padding: 0 0.375rem;
    }
}

/* =========================================================================
   SEARCH SUGGESTIONS / AUTOCOMPLETE
   ========================================================================= */

.search-wrapper {
    position: relative;
    flex: 1;
    min-width: min(300px, 100%);
}

.search-suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background-color: var(--white);
    border: 1px solid var(--border-color);
    border-top: none;
    border-radius: 0 0 6px 6px;
    box-shadow: var(--shadow-lg);
    max-height: 300px;
    overflow-y: auto;
    z-index: 100;
    display: none;
}

.search-suggestions.visible {
    display: block;
}

.search-suggestion {
    padding: 0.625rem 1rem;
    cursor: pointer;
    font-size: var(--fs-14);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.search-suggestion:last-child {
    border-bottom: none;
}

.search-suggestion:hover,
.search-suggestion.highlighted {
    background-color: var(--light-bg);
}

.search-suggestion .suggestion-type {
    font-size: var(--fs-12);
    color: #999;
    background-color: var(--light-bg);
    padding: 0.125rem 0.375rem;
    border-radius: 3px;
    white-space: nowrap;
}

[data-theme="dark"] .search-suggestions {
    background-color: #1c2530;
    border-color: #30445a;
}

[data-theme="dark"] .search-suggestion:hover,
[data-theme="dark"] .search-suggestion.highlighted {
    background-color: #243242;
}

[data-theme="dark"] .search-suggestion .suggestion-type {
    background-color: #243242;
    color: #b8c2cc;
}

/* =========================================================================
   SOCIAL SHARING BUTTONS (news articles)
   ========================================================================= */

.share-buttons {
    display: flex;
    gap: 0.375rem;
    margin-top: 0.75rem;
}

.share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 4px;
    text-decoration: none;
    font-size: var(--fs-13);
    font-weight: 600;
    color: #fff;
    transition: opacity 0.2s, transform 0.2s;
}

/* `[data-theme="dark"] body a` is (0,1,2) and outranks the (0,1,0) rule above,
   so in dark mode these labels rendered #6fc3ff on the brand fill - 1.47:1 on
   Twitter blue where the intended white is 2.83:1. The fills are brand values
   and stay as they are; this only restores the colour the button asked for. */
[data-theme="dark"] .share-btn,
[data-theme="dark"] body a.share-btn { color: #fff; }

.share-btn:hover {
    opacity: 0.85;
    transform: translateY(-1px);
}

.share-btn--linkedin {
    background-color: #0077b5;
}

/* Each platform's own blue/orange, taken down in lightness only until white
   text clears 4.5:1. The buttons carry text, not icons - main.js writes "in",
   "X", a butterfly and "r/" into them - so this is WCAG 1.4.3 at 4.5:1, not
   1.4.11 at 3:1, and at 13px/600 they do not qualify as large text.

   Hue and saturation are untouched: converted to HLS, only L moves, so the
   shift is 0.0 degrees of hue in all three cases and each stays recognisably
   the platform's colour.

       X         #0b7abf   4.60:1
       Bluesky   #0074de   4.61:1
       Reddit    #d83b00   4.62:1

   LinkedIn's #0a66c2 measures 5.69:1 and is left alone.

   The Bluesky glyph is an emoji, so it paints in its own colours rather than
   `color` and this ratio does not describe it. The darker fill helps it either
   way, and the button's accessible name comes from aria-label regardless. */
.share-btn--twitter {
    background-color: #0b7abf;
}

.share-btn--bluesky {
    background-color: #0074de;
}

.share-btn--reddit {
    background-color: #d83b00;
}

/* Prevent share buttons from triggering parent card-link navigation */
.card-link .share-buttons {
    position: relative;
    z-index: 2;
}

@media (max-width: 768px) {
    .share-btn {
        width: 28px;
        height: 28px;
        font-size: var(--fs-12);
    }
}

/* =========================================================================
   RESOURCE CARD TOOLTIPS
   ========================================================================= */

.resource-tooltip {
    position: fixed;
    z-index: 9000;
    max-width: 360px;
    width: max-content;
    padding: 0.75rem 1rem;
    background-color: var(--white);
    color: var(--text-color);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    font-size: var(--fs-14);
    line-height: 1.5;
    /* break-word only, never break-all: break-all chops ordinary words
       mid-word at the 360px cap. Nothing here is longer than the cap, so
       this is a safety net for a future long URL, not the normal path. */
    overflow-wrap: break-word;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease-in-out;
}

.resource-tooltip.visible {
    opacity: 1;
}

[data-theme="dark"] .resource-tooltip {
    background-color: #1c2530;
    color: #e8ecf2;
    border-color: #30445a;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}

@media (hover: none) and (pointer: coarse) {
    .resource-tooltip {
        display: none !important;
    }
}

/* =========================================================================
   DROPDOWN NAVIGATION
   ========================================================================= */

.has-dropdown {
    position: relative;
}

.dropdown-toggle {
    background: none;
    border: none;
    color: var(--white);
    font: inherit;
    font-weight: 500;
    padding: 0;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    transition: opacity 0.3s, background-color 0.15s;
}

.dropdown-toggle:hover {
    opacity: 0.8;
}

.dropdown-toggle .caret {
    font-size: var(--fs-12);
    transition: transform 0.2s;
    line-height: 1;
}

.has-dropdown[aria-expanded="true"] > .dropdown-toggle .caret,
.has-dropdown.open > .dropdown-toggle .caret {
    transform: rotate(180deg);
}

.dropdown-toggle.active {
    background-color: rgba(255, 255, 255, 0.08);
    padding: 0.375rem 0.625rem;
    border-radius: 6px;
}

.dropdown-toggle:focus-visible {
    outline: 3px solid rgba(52, 152, 219, 0.4);
    outline-offset: 2px;
    border-radius: 6px;
}

.dropdown-menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: 0;
    min-width: 220px;
    background-color: var(--primary-color);
    list-style: none;
    margin: 0;
    padding: 0.5rem 0;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
    z-index: 1001;
    display: block;
    flex-direction: column;
    gap: 0;
}

/* The focus test is on the MENU, not on the <li>. The <li> contains the
   toggle button, so `.has-dropdown:focus-within > .dropdown-menu` would stay
   true after a click that closes the menu (the button keeps focus), and on
   desktop the menu could not be closed by clicking its toggle.

   Scoping :focus-within to the menu keeps what it is there for: keyboard
   users tabbing THROUGH the items hold their own menu open, while focus
   parked on the toggle does not. Specificity is (0,3,0), so every override
   below lands the same way. */
.has-dropdown:hover > .dropdown-menu,
.has-dropdown > .dropdown-menu:focus-within,
.has-dropdown.open > .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-menu li {
    display: block;
}

.dropdown-menu a {
    display: block;
    padding: 0.5rem 1rem;
    color: var(--white);
    text-decoration: none;
    font-weight: 500;
    white-space: nowrap;
    border-radius: 0;
}

.dropdown-menu a:hover,
.dropdown-menu a:focus {
    background-color: rgba(255, 255, 255, 0.1);
    opacity: 1;
}

.dropdown-menu a[aria-current="page"] {
    background-color: rgba(255, 255, 255, 0.15);
}

/* Bridge the gap between toggle and menu so hover doesn't drop */
.has-dropdown::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    height: 0.5rem;
    width: 100%;
    pointer-events: auto;
}

.has-dropdown:not(:hover):not(:focus-within):not(.open)::after {
    pointer-events: none;
}

/* Hamburger mode (<=1023px): flatten dropdowns into accordion */
@media (max-width: 1023px) {
    .has-dropdown {
        position: static;
    }

    .dropdown-toggle {
        display: flex;
        width: 100%;
        justify-content: space-between;
        align-items: center;
        padding: 0.75rem 1rem;
        font-size: var(--fs-14);
        border-top: 1px solid rgba(255, 255, 255, 0.1);
        border-radius: 0;
        text-align: left;
    }

    .dropdown-toggle.active {
        padding: 0.75rem 1rem;
        border-radius: 0;
    }

    .dropdown-menu {
        position: static;
        /* Stronger tint + accent left bar make the "this is a sub-list"
           relationship visually unambiguous on mobile. Without these,
           sub-items at 2rem indent (e.g. "Best Practices") look almost
           identical to top-level toggles like "Defend ▾", and a long
           dropdown like Learn (10 items) becomes a flat wall of links. */
        background-color: rgba(0, 0, 0, 0.28);
        box-shadow: none;
        border-radius: 0;
        border-left: 3px solid var(--secondary-color, #0369a1);
        padding: 0;
        min-width: 0;
        opacity: 1;
        visibility: hidden;
        transform: none;
        max-height: 0;
        overflow: hidden;
        transition: max-height 0.2s ease, visibility 0s linear 0.2s;
    }

    /* Dark theme adds a 1px box border to .dropdown-menu for the desktop
       floating popover. On mobile the menu is an in-flow accordion collapsed
       with max-height:0 + overflow:hidden, which clips content but NOT the
       border - so that top+bottom border kept 2px of height per *collapsed*
       dropdown, making the dark mobile navbar ~10px taller than light (5
       dropdowns x 2px) and shoving lower items down. Zero the top/bottom
       border on mobile (the 3px left accent + tint already signal the
       sub-list); desktop popover keeps its border. Matches the light theme. */
    [data-theme="dark"] .dropdown-menu {
        border-top-width: 0;
        border-bottom-width: 0;
    }

    .has-dropdown.open > .dropdown-menu {
        visibility: visible;
        /* Bumped from 600px so the Learn dropdown's 10 items don't clip on
           narrow phones. */
        max-height: 800px;
        transition: max-height 0.25s ease, visibility 0s;
    }

    /* Disable desktop hover-reveal on mobile. Mirrors the menu-scoped
       :focus-within above so both breakpoints test the same thing. */
    .has-dropdown:hover > .dropdown-menu,
    .has-dropdown > .dropdown-menu:focus-within {
        opacity: 1;
        visibility: hidden;
        max-height: 0;
    }

    .has-dropdown.open:hover > .dropdown-menu,
    .has-dropdown.open > .dropdown-menu:focus-within,
    .has-dropdown.open > .dropdown-menu {
        visibility: visible;
        max-height: 600px;
    }

    .dropdown-menu a {
        /* Deeper left padding (2.5rem) + a subtle ›-style chevron pseudo
           gives sub-items a clearer "I am inside Learn / Defend / …"
           feel that sets them apart from the top-level toggles above. */
        padding: 0.5rem 1rem 0.5rem 2.5rem;
        font-size: var(--fs-14);
        border-top: 1px solid rgba(255, 255, 255, 0.05);
        position: relative;
        opacity: 0.92;
    }
    .dropdown-menu a::before {
        content: "›";
        position: absolute;
        left: 1.25rem;
        top: 50%;
        transform: translateY(-50%);
        opacity: 0.5;
        font-size: var(--fs-15);
        line-height: 1;
        pointer-events: none;
    }

    /* Mobile: the current-page indicator keeps the row's full-width
       shape - desktop's small rounded pill (padding 0.35rem 0.6rem,
       border-radius 6px) shrinks the active row vs its siblings and
       drops the dropdown sub-item's left indent. Override here so the
       active row is the same height & indent as everything else; just
       the background highlight differs. Specificity is bumped (header
       prefix; `.dropdown-menu` for sub-items) to outrank the global
       `nav a[aria-current="page"]` rule. */
    header nav a.active,
    header nav a[aria-current="page"] {
        padding: 0.75rem 1rem;
        border-radius: 0;
    }

    header .dropdown-menu a.active,
    header .dropdown-menu a[aria-current="page"] {
        /* Match the indented + chevron-prefixed shape of sibling sub-items
           so the current-page row stays aligned in the open dropdown. */
        padding: 0.5rem 1rem 0.5rem 2.5rem;
        border-radius: 0;
        opacity: 1;
    }

    .has-dropdown::after {
        display: none;
    }
}

/* =========================================================================
   MEGA-MENU + NAV CTA
   ========================================================================= */

/* Mega-menu container - wider than the standard dropdown, multi-column.
   The .dropdown-menu base styles still apply (positioning, hover reveal,
   etc.); these rules override the width/layout so it can hold columns. */
.has-mega > .dropdown-menu.mega-menu {
    min-width: 580px;
    max-width: 92vw;
    padding: 1rem 1.25rem 1rem;
    /* Align the menu's left edge with the toggle. Centering with
       translateX(-50%) overflows the viewport when the toggle is near
       the left edge of the nav (e.g. "Learn"). */
    left: 0;
    transform: translateY(-4px);
    display: grid;
    gap: 1.5rem;
}

.has-mega:hover > .dropdown-menu.mega-menu,
.has-mega:focus-within > .dropdown-menu.mega-menu,
.has-mega.open > .dropdown-menu.mega-menu {
    transform: translateY(0);
}

.has-mega > .dropdown-menu.mega-3col {
    grid-template-columns: repeat(3, minmax(150px, 1fr));
    min-width: 680px;
}

/* "Learn" uses 4 columns. Kept only ~80px wider than mega-3col and capped by
   the inherited max-width: 92vw so it stays within the viewport when opened
   from the left-most nav toggle (left: 0 anchor) down to the 1024px breakpoint
   where the whole bar collapses to the hamburger. */
.has-mega > .dropdown-menu.mega-4col {
    grid-template-columns: repeat(4, minmax(140px, 1fr));
    min-width: 760px;
}

/* Five-column mega menu. 5 columns is wide and this menu anchors left: 0
   from the left-most toggle, so it's sized + gap-tightened to stay within the
   viewport down to the 1024px breakpoint (it opens at ~200px left at 1024, so
   width must stay under ~820px). */
.has-mega > .dropdown-menu.mega-5col {
    grid-template-columns: repeat(5, minmax(128px, 1fr));
    min-width: 800px;
    gap: 1rem 0.75rem;
}

/* Six-column mega menu. Same left: 0 anchor and the same ~820px ceiling as
   mega-5col above, so the sixth column is paid for out of column width
   (112px) rather than total width; at 1024px the panel's right edge stays
   inside the viewport. */
.has-mega > .dropdown-menu.mega-6col {
    grid-template-columns: repeat(6, minmax(112px, 1fr));
    min-width: 812px;
    gap: 1rem 0.75rem;
}

.has-mega > .dropdown-menu.mega-2col {
    grid-template-columns: repeat(2, minmax(150px, 1fr));
    min-width: 440px;
}

/* Right-side mega menus (any .has-mega after the first one - i.e. "Careers"
   and "Community", not the left-most "Learn") center under their toggle so a
   wide panel uses the space on BOTH sides and stays on-screen. The left-most
   "Learn" keeps the default left: 0 (centering it would clip the left edge);
   anchoring these to left: 0 overflowed the right edge, and right: 0 clipped
   the left edge on laptops - centering fits at every width >= 1024px. */
/* Scoped to desktop: below 1024px the nav is a hamburger and the mega menus
   flatten to a static accordion (see the max-width: 1023px block below), which
   must NOT inherit this translateX(-50%) - these selectors outrank the mobile
   reset, so keep them inside the desktop media query. */
@media (min-width: 1024px) {
    .has-mega ~ .has-mega > .dropdown-menu.mega-menu {
        left: 50%;
        right: auto;
        transform: translateX(-50%) translateY(-4px);
    }

    .has-mega ~ .has-mega:hover > .dropdown-menu.mega-menu,
    .has-mega ~ .has-mega:focus-within > .dropdown-menu.mega-menu,
    .has-mega ~ .has-mega.open > .dropdown-menu.mega-menu {
        transform: translateX(-50%) translateY(0);
    }
}

.mega-col > ul {
    /* Override the global `nav ul { display: flex; gap: 2rem }` rule so
       links inside a mega-menu column stack vertically. */
    display: block;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 0;
    align-items: stretch;
}

.mega-col > ul > li {
    display: block;
}

.mega-heading {
    display: block;
    font-size: var(--fs-12);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* 0.88 alpha passes WCAG AA (4.5:1) against the dark nav surface. */
    color: rgba(255, 255, 255, 0.88);
    margin: 0 0 0.375rem;
    padding: 0 0.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
    padding-bottom: 0.375rem;
}

.mega-menu .mega-col a {
    display: block;
    padding: 0.375rem 0.5rem;
    border-radius: 4px;
    white-space: normal;
    line-height: 1.25;
}

.mega-menu .mega-col a:hover,
.mega-menu .mega-col a:focus {
    background-color: rgba(255, 255, 255, 0.1);
}

.mega-menu .mega-col a[aria-current="page"] {
    background-color: rgba(255, 255, 255, 0.18);
}

/* Featured "hero" link inside a mega column (the Multi-Cloud Secure Deploy
   blueprint in Learn > Build It): accent + bold with a muted one-line
   subtitle. Specificity (0,3,1) outranks [data-theme="dark"] .dropdown-menu a
   (0,2,1), so the accent holds in both light and dark. */
.mega-menu .mega-col .mega-featured > a {
    font-weight: 700;
    color: #7cc0ff;
}

.mega-menu .mega-col .mega-featured .mega-tag {
    display: block;
    margin-top: 2px;
    font-weight: 400;
    font-size: var(--fs-12);
    line-height: 1.2;
    letter-spacing: 0;
    text-transform: none;
    /* 0.64, not 0.62. Against the mega panel's own translucent fill this text
       measures 4.4987:1, which fails 4.5 by a thousandth - and it is 12px/400,
       so it is normal text and 4.5 is the bar. It only looked fine because a
       contrast scan that resolves the backdrop by walking to the first *opaque*
       ancestor skips that fill entirely and reports 7.4:1 against #0f172a.
       0.64 lands at 4.63:1. Blend translucent layers when you check this. */
    color: rgba(255, 255, 255, 0.64);
}

/* Full-width hub row closing a mega menu (".mega-all"). One pattern, used
   wherever a dropdown has a real hub page: the hub is promoted out of its
   column into a footer row spanning the whole grid, so it reads as "and here
   is everything" rather than as one more sibling link.

   Learn needs it structurally: its subject pages live on topics.html, and
   this row is the only thing in the nav pointing at them. Careers uses it
   for cloud-security-careers.html.

   `grid-column: 1 / -1` is what spans it; the mega menu is a grid, so without
   it this lands as a 4th/5th column. On mobile the mega menu switches to
   display:block, the span becomes inert, and the row inherits the standard
   `.dropdown-menu a` accordion shape - see the overrides in the max-width
   block below, which only restore the separator and the accent. */
.mega-menu .mega-all {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.25rem;
    padding: 0.625rem 0.5rem 0.125rem;
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    font-size: var(--fs-13);
    font-weight: 600;
    /* Same accent as .mega-featured, so "this is a destination, not a link in
       a list" is one colour across the whole nav. */
    color: #7cc0ff;
    white-space: nowrap;
    border-radius: 0;
}

.mega-menu .mega-all:hover,
.mega-menu .mega-all:focus {
    background-color: transparent;
    color: #a8d5ff;
}

.mega-all-arrow {
    transition: transform 0.12s ease;
}

.mega-menu .mega-all:hover .mega-all-arrow,
.mega-menu .mega-all:focus .mega-all-arrow {
    transform: translateX(3px);
}

/* Persistent nav CTA - promotes Friday Zoom out of a dropdown.
   Designed to stand out without fighting the logo or hero CTA. */
.nav-cta-item {
    margin-left: 0.5rem;
}

/* Sky-400, the same fill .hero--illustrated .btn-primary uses, so every
   primary call to action on the site is one colour in both themes. Amber is
   the donate colour (.nav-coffee) and nothing else in the header, so the two
   loudest things there never share a hue. #0b1729 on #38bdf8 is 9.4:1. */
.nav-cta {
    display: inline-block;
    background-color: #38bdf8;
    color: #0b1729 !important;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    font-weight: 600;
    text-decoration: none;
    transition: background-color 0.15s, transform 0.12s;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}

.nav-cta:hover,
.nav-cta:focus {
    background-color: #7dd3fc;
    transform: translateY(-1px);
    opacity: 1 !important;
}

/* Make sure the global nav active-pill style doesn't accidentally wrap the
   CTA, and that aria-current pill doesn't apply to the CTA button. */
.nav-cta-item .nav-cta:focus {
    outline: 3px solid rgba(245, 158, 11, 0.5);
    outline-offset: 2px;
}

/* The breadcrumb link sat at 39x17 on every inner page - chrome, not prose, so
   the inline exception in WCAG 2.2 SC 2.5.8 does not cover it. Padding rather
   than font-size, and vertical only, so the bar keeps its height and the
   separator spacing does not move. */
.breadcrumb-nav a {
    display: inline-block;
    padding: 0.25rem 0;
    min-height: 24px;
}

/* A link whose only child is an image is still `display: inline`, so its box is
   the line box - 400x20 for a 400x300 picture - and only that strip is
   clickable. Making it inline-block hands the whole image to the pointer. */
main a:has(> picture:only-child),
main a:has(> img:only-child) {
    display: inline-block;
}

/* main.js injects this one, so on the desktop menu bar it never picked up the
   padding the dropdown toggles beside it carry and sat at 68x18 - under the
   24x24 CSS px that WCAG 2.2 SC 2.5.8 asks of a pointer target. It is an item
   in a menu bar, not a link inside a sentence, so the inline exception does not
   cover it.

   Scoped to >=1024px, and that is the whole point: below it the nav is the
   hamburger accordion, where this link already gets a full 44px row. An
   unscoped version of this rule overrode that padding and took the row down to
   31px, which is above the WCAG minimum and below the 40px that
   tools/check_mobile_layout.py enforces - so it failed CI on 20 pages while
   looking like an accessibility improvement. */
@media (min-width: 1024px) {
    .nav-search-link {
        display: inline-flex;
        align-items: center;
        min-height: 24px;
        padding: 0.25rem 0.125rem;
    }
}

/* Mobile CTA -----------------------------------------------------------------
   Below 1024px the nav collapses behind the hamburger, so this keeps the
   Friday session one tap away after the reader scrolls past the hero button.

   Same href and same words as the nav CTA, and exactly one of the two is ever
   displayed, so the site keeps one name for the action and screen readers never
   meet it twice. Sized down rather than relabelled: at 375px the header has
   200px of slack, which this fits inside with room to spare. */
.nav-cta--mobile {
    display: none;
}

@media (max-width: 1023px) {
    .nav-cta--mobile {
        display: inline-block;
        order: 2;
        margin-right: 0.5rem;
        padding: 0.375rem 0.625rem;
        font-size: var(--fs-13);
    }
    /* The push to the right lives on the logo, not on the CTA. An auto margin
       on a flex item consumes all the free space before wrapping is considered,
       so on the CTA it would send the hamburger to a second row at 320px even
       though the four items fit across in 309px. One auto margin, on the first
       item, and the other three group at the end. */
    .header-content > .logo { margin-right: auto; }
    .nav-cta--mobile ~ .theme-toggle { margin-left: 0; }
    /* One CTA, not two: the in-menu copy is redundant once this is visible. */
    .nav-cta-item { display: none; }
}

/* Narrow phones: drop the lead-in rather than the pill. At 320px the full
   "Get the Zoom link" wrapped the header on to two rows. Hiding the span takes
   the accessible name down to "Zoom link" too, so the visible text and the
   name still match, which is what WCAG 2.5.3 asks for - an aria-label saying
   something the button does not visibly say would fail it. */
@media (max-width: 419px) {
    .nav-cta--mobile { padding: 0.375rem 0.5rem; font-size: var(--fs-12); }
    .nav-cta__lead { display: none; }
}

/* 320px is the narrowest viewport worth supporting and the row lands 1px over
   there: logo 87 + CTA 90 + toggle 44 + hamburger 44, three 8px gaps and 32px
   of padding is 321. Tighten the gap and the CTA's own spacing rather than drop
   the button, which is the one thing on this row a visitor might be looking
   for. */
@media (max-width: 360px) {
    .header-content { gap: 0.25rem; }
    .nav-cta--mobile { padding: 0.25rem 0.375rem; margin-right: 0; }
    /* The toggle keeps a 0.4rem right margin from the wider breakpoint. With
       logo 87 + CTA 90 + toggle 44 + hamburger 44 = 265 and 12px of gaps in a
       288px content box, those last 8px are exactly what pushed the hamburger
       to a second row. */
    .header-content > .theme-toggle { margin-right: 0; }
}

/* Buy Me a Coffee link. Deliberately quieter than .nav-cta next to it: an
   outline, not a filled button, so the Friday Zoom CTA stays the loudest
   thing in the row. */
.nav-coffee {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    /* Neutral, not amber. The comment above already called this link
       "deliberately quieter", but amber-on-amber next to .nav-cta meant it
       read as a second primary button rather than a quieter one. White at 78%
       on the navy header is 11.4:1, so it stays perfectly legible while
       clearly ranking below the CTA. */
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 6px;
    color: rgba(255, 255, 255, 0.78) !important;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.15s, border-color 0.15s, transform 0.12s;
}

.nav-coffee:hover,
.nav-coffee:focus {
    background-color: rgba(245, 158, 11, 0.15);
    border-color: var(--accent-color, #f59e0b);
    transform: translateY(-1px);
    opacity: 1 !important;
}

.nav-coffee-item .nav-coffee:focus {
    outline: 3px solid rgba(245, 158, 11, 0.5);
    outline-offset: 2px;
}

.nav-coffee-icon {
    font-size: 0.95em;
}

/* Nav glyphs (Search, Support) are inline SVG rather than emoji. Emoji are
   drawn by the platform emoji font, which ignores `color`, so the magnifier
   rendered as a small dark blob on the dark header and both glyphs changed
   shape between operating systems - the one thing site chrome should not do.
   currentColor makes these follow the link colour in both themes with no
   dark-mode override needed, which is why there is no mirrored rule below.

   Sized in em so they track the label. Both alignment properties are load
   bearing: .nav-search-link is inline-flex on desktop (flex-shrink keeps the
   icon from collapsing) and a plain inline box in the mobile menu
   (vertical-align does the optical centring there). */
.nav-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
    flex-shrink: 0;
}

/* Mobile: mega-menu flattens to the accordion pattern already used for
   standard dropdowns. We reset the desktop grid + centering so it stacks
   like a regular dropdown, but keep the section headings visible to
   preserve the grouped structure. */
@media (max-width: 1023px) {
    .has-mega > .dropdown-menu.mega-menu {
        display: block;
        min-width: 0;
        max-width: none;
        padding: 0;
        left: 0;
        transform: none;
    }

    .has-mega.open > .dropdown-menu.mega-menu {
        transform: none;
        /* Mega-menus hold ~34 items + 3 section headings (Learn column is
           the tallest). Give them enough room to render fully inside the
           mobile accordion without scroll-clipping. iOS Safari renders
           rows a touch taller than Chromium, so a ceiling that fits in a
           Chromium preview can still clip the last items on an iPhone. */
        max-height: 2400px;
    }

    .has-mega:hover > .dropdown-menu.mega-menu,
    .has-mega:focus-within > .dropdown-menu.mega-menu {
        transform: none;
    }

    .mega-col {
        padding: 0.375rem 0 0.625rem;
    }

    .mega-heading {
        font-size: var(--fs-12);
        margin: 0;
        padding: 0.75rem 1.5rem 0.25rem;
        border-bottom: none;
        color: rgba(255, 255, 255, 0.55);
    }

    .mega-menu .mega-col a {
        /* Match the indented chevron sub-item shape used by standard
           dropdowns on mobile (defined ~line 3441). */
        padding: 0.5rem 1rem 0.5rem 2.5rem;
        font-size: var(--fs-14);
        border-radius: 0;
        border-top: 1px solid rgba(255, 255, 255, 0.05);
        position: relative;
        opacity: 0.92;
    }

    /* The hub row is not inside a .mega-col, so it does not pick up the
       chevron + 2.5rem indent the column links get above. Match that indent
       anyway: every row inside one open accordion has to share a left edge or
       the list reads as two lists, and tools/check_mobile_layout.py asserts
       exactly that (it fails the build on any padding-left variance within a
       dropdown). The row still reads as different from its siblings via the
       accent colour and the separator above it, which costs no alignment.
       Do not "promote" this row by out-denting it to the toggles' 1rem. */
    .mega-menu .mega-all {
        display: block;
        padding: 0.75rem 1rem 0.75rem 2.5rem;
        margin-top: 0;
        font-size: var(--fs-14);
        border-top: 1px solid rgba(255, 255, 255, 0.14);
        white-space: normal;
    }

    .mega-menu .mega-col a::before {
        content: "›";
        position: absolute;
        left: 1.25rem;
        top: 50%;
        transform: translateY(-50%);
        opacity: 0.5;
        font-size: var(--fs-15);
        line-height: 1;
        pointer-events: none;
    }

    /* One row = one line = 44px, structurally. `.mega-menu .mega-col a` sets
       white-space: normal for the desktop grid, where columns are as narrow
       as 119px and titles have to wrap; this column is ~355px, so nothing
       needs to wrap here and a wrap only ever means a jagged row.

       Pinning it is the point. The longest label clears the 299px content
       box by 9px - fine in Chromium, but that is a 3% margin against a
       device whose font metrics we do not control (iOS Safari renders rows
       taller), and CI runs Chromium. Rather than trust the margin, remove the
       failure mode: the row cannot grow a second line no matter how the text
       measures. A label too long for the box truncates with an ellipsis -
       visible, and local to that row - instead of re-jagging the menu. */
    .mega-menu .mega-col a {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Featured rows lose their subtitle and their bold here. Both are grid
       affordances: on desktop the mega-menu is a 3-6 column grid, so a
       featured cell running three lines tall only makes its own column
       taller and reads as a deliberate promo. This accordion flattens that
       grid into one ~40-row list, where the same treatment makes the row
       taller than its siblings, and taller still where the bold title
       wraps. A row half again as tall as its neighbours reads as a layout
       bug in a vertical list, not as emphasis.

       500 is what plain rows carry (.dropdown-menu a), so featured rows
       match them in weight and height; the accent colour from the desktop
       rule still marks them as promoted, and costs neither. Every row lands
       on the shared 44px tap-target floor.

       These two selectors are (0,3,1) and (0,4,0) - the same weight as the
       desktop rules they override (~line 4251), so they must stay AFTER
       them in the file. tools/check_mobile_layout.py asserts the result. */
    .mega-menu .mega-col .mega-featured > a {
        font-weight: 500;
    }

    .mega-menu .mega-col .mega-featured .mega-tag {
        display: none;
    }

    /* CTA on mobile: full-width button at the bottom of the accordion. */
    .nav-cta-item {
        margin: 0.75rem 1rem 0.5rem;
    }

    .nav-cta {
        display: block;
        text-align: center;
        padding: 0.75rem 1rem;
    }

    /* Coffee link matches the CTA's full-width shape inside the accordion,
       and keeps its label here since there's room for it. */
    .nav-coffee-item {
        margin: 0.75rem 1rem 0;
    }

    .nav-coffee {
        display: flex;
        justify-content: center;
        padding: 0.75rem 1rem;
    }
}

/* Dark mode: mega-menu inherits the dropdown-menu dark styles since it
   uses the same `.dropdown-menu` class, but the section headings need an
   explicit color so they remain legible. */
[data-theme="dark"] .mega-heading {
    color: rgba(255, 255, 255, 0.7);
    border-bottom-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .nav-cta {
    color: #1a202c !important;
}

/* end of stylesheet - v2 */
/* =========================================================================
   MEETINGS.HTML INDEX (after split into per-meeting pages)
   Card-list layout for the directory page that links to /meetings/<date>.html
   ========================================================================= */

.meeting-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
    gap: 1rem;
    margin: 1.5rem 0 3rem;
    padding: 0;
}

.meeting-card {
    background: #fff;
    border: 1px solid #e2e8f0;
    border-left: 4px solid var(--secondary-color, #0369a1);
    border-radius: 8px;
    padding: 0;
    margin: 0;
    transition: transform 0.12s, box-shadow 0.15s, border-left-color 0.15s;
}
.meeting-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0,0,0,0.08);
    border-left-color: var(--accent-color, #e74c3c);
}
.meeting-card-link {
    display: block;
    padding: 1rem 1.25rem;
    color: inherit;
    text-decoration: none;
    height: 100%;
}
.meeting-card-link h2 {
    font-size: var(--fs-16);
    line-height: 1.35;
    margin: 0 0 0.5rem;
    color: var(--primary-color, #2c3e50);
    font-weight: 600;
}
.meeting-card-link h2 time {
    color: var(--text-muted, #64748b);
    font-weight: 500;
    font-size: var(--fs-14);
    display: block;
    margin-bottom: 0.125rem;
}
.meeting-card-summary {
    color: var(--text-muted, #475569);
    font-size: var(--fs-14);
    line-height: 1.45;
    margin: 0 0 0.625rem;
}
.meeting-card .meeting-tags {
    margin-bottom: 0.5rem;
}
.meeting-card-cta {
    color: var(--secondary-color, #0369a1);
    font-weight: 600;
    font-size: var(--fs-14);
}

.meeting-count {
    color: var(--text-muted, #64748b);
    font-size: var(--fs-14);
    margin: 0.5rem 0 1rem;
}

/* =========================================================================
   Per-meeting recap page (e.g. /meetings/2026-05-08.html). All recap pages
   share the same markup pattern, so all styling happens here - no per-page
   edits.
   ========================================================================= */
.meeting-page {
    background: var(--white);
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 14px;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
    padding: 1.75rem 1.75rem 1.5rem;
    margin-top: 1.25rem;
}
.meeting-page h2 {
    font-size: var(--fs-24);
    line-height: 1.3;
    margin: 0 0 0.75rem;
    color: var(--primary-color, #0f172a);
}
.meeting-page h2 time {
    color: var(--secondary-color, #0369a1);
    font-weight: 600;
}
/* Quick-recap callout: the first <p> after the h2 is always the TL;DR */
.meeting-page > h2 + p {
    background: linear-gradient(180deg, #f8fafc 0%, #eef4fb 100%);
    border-left: 4px solid var(--secondary-color, #0369a1);
    border-radius: 0 8px 8px 0;
    padding: 1rem 1rem;
    margin: 0 0 1rem;
    line-height: 1.6;
    color: #1e293b;
}
.meeting-page > h2 + p > strong:first-child {
    display: inline-block;
    text-transform: uppercase;
    font-size: var(--fs-12);
    letter-spacing: 0.08em;
    color: var(--secondary-color, #0369a1);
    margin-right: 0.5rem;
    vertical-align: 0.06em;
}
.meeting-page .meeting-tags { margin: 0 0 1rem; }

/* The discussion topics are the body of the recap, so they always render
   (no <details>). The wrapper exists so `> p` can style topic paragraphs
   without reaching the quick-recap paragraph above it.

   Keep every rule below scoped under .meeting-page. faq.html borrows the
   same class name for its FAQ accordion, where <details> IS the
   right control and must stay collapsible; faq.html has no .meeting-page,
   which is the only thing keeping the two apart. An unscoped
   `.meeting-topics` rule would hit both. */
.meeting-page .meeting-topics {
    margin: 0.5rem 0 1.25rem;
    border-top: 1px solid var(--border-color, #e2e8f0);
    padding-top: 1rem;
}

.meeting-page .meeting-topics h3 {
    font-size: var(--fs-17);
    color: var(--primary-color, #0f172a);
    margin: 1rem 0 0.375rem;
    padding-left: 0.75rem;
    border-left: 3px solid var(--accent-color, #f59e0b);
    line-height: 1.35;
}
.meeting-page .meeting-topics h3:first-of-type { margin-top: 0; }
.meeting-page .meeting-topics > p {
    margin: 0 0 0.625rem;
    line-height: 1.65;
    max-width: 70ch;
}

.meeting-page > p.small {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-color, #e2e8f0);
}
.meeting-page > p.small a {
    display: inline-block;
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 6px;
    color: var(--secondary-color, #0369a1);
    text-decoration: none;
    font-weight: 500;
    font-size: var(--fs-14);
    transition: background 0.15s, border-color 0.15s;
}
.meeting-page > p.small a:hover {
    background: rgba(2, 132, 199, 0.06);
    border-color: var(--secondary-color, #0369a1);
}

/* Bottom prev/next/index pager. Styles live in breach-timeline.css for the
   breach pages, but meeting pages only load style.css - so duplicate here. */
.meeting-page + .incident-pager,
main > .incident-pager {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin: 2rem auto 1rem;
    padding: 0;
}
main > .incident-pager .pager-link {
    color: var(--secondary-color, #0369a1);
    text-decoration: none;
    padding: 0.5rem 1rem;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
    font-weight: 500;
    background: var(--white);
    transition: background 0.15s, border-color 0.15s, transform 0.15s;
}
main > .incident-pager .pager-link:hover {
    background: rgba(2, 132, 199, 0.06);
    border-color: var(--secondary-color, #0369a1);
    transform: translateY(-1px);
}
main > .incident-pager .pager-index { font-weight: 600; }

[data-theme="dark"] .meeting-page {
    background: #0d1424;
    border-color: #1e293b;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
[data-theme="dark"] .meeting-page h2 { color: #e2e8f0; }
[data-theme="dark"] .meeting-page h2 time { color: #38bdf8; }
[data-theme="dark"] .meeting-page > h2 + p {
    background: rgba(56, 189, 248, 0.06);
    border-left-color: #38bdf8;
    color: #cbd5e1;
}
[data-theme="dark"] .meeting-page > h2 + p > strong:first-child { color: #38bdf8; }
[data-theme="dark"] .meeting-page .meeting-topics {
    border-top-color: rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] .meeting-page .meeting-topics h3 { color: #e2e8f0; }
[data-theme="dark"] .meeting-page .meeting-topics > p { color: #cbd5e1; }
[data-theme="dark"] .meeting-page > p.small { border-top-color: rgba(255, 255, 255, 0.08); }
[data-theme="dark"] .meeting-page > p.small a {
    background: rgba(255, 255, 255, 0.03);
    border-color: rgba(255, 255, 255, 0.12);
    color: #38bdf8;
}
[data-theme="dark"] .meeting-page > p.small a:hover {
    background: rgba(56, 189, 248, 0.08);
    border-color: #38bdf8;
}
[data-theme="dark"] main > .incident-pager .pager-link {
    background: #0d1424;
    color: #38bdf8;
    border-color: #1e293b;
}
[data-theme="dark"] main > .incident-pager .pager-link:hover {
    background: rgba(56, 189, 248, 0.08);
    border-color: #38bdf8;
}

@media (max-width: 600px) {
    .meeting-page { padding: 1.25rem 1rem 1rem; }
    .meeting-page > h2 + p { padding: 0.75rem 1rem; }
    main > .incident-pager { flex-direction: column; align-items: stretch; }
    main > .incident-pager .pager-link { text-align: center; }
}

/* Dark mode */
[data-theme="dark"] .meeting-card {
    background: #0d1424;
    border-color: #1e293b;
    border-left-color: #38bdf8;
}
[data-theme="dark"] .meeting-card:hover { border-left-color: #f87171; }
[data-theme="dark"] .meeting-card-link h2 { color: #e2e8f0; }
[data-theme="dark"] .meeting-card-link h2 time { color: #94a3b8; }
[data-theme="dark"] .meeting-card-summary { color: #94a3b8; }
[data-theme="dark"] .meeting-card-cta { color: #38bdf8; }
[data-theme="dark"] .meeting-count { color: #94a3b8; }

/* Mobile */
@media (max-width: 600px) {
    .meeting-list { grid-template-columns: 1fr; }
}

/* =========================================================================
   Pillar-page meta line: Last updated + author + source link
   Shown just below the hero on educational/pillar pages. Helps trust
   (visible date), E-E-A-T (author byline), and openness (source link).
   ========================================================================= */
.page-meta {
    color: var(--text-muted, #64748b);
    font-size: var(--fs-14);
    margin: 0 0 1.25rem;
    padding: 0.5rem 0 0.75rem;
    border-bottom: 1px solid var(--border-color, #e0e0e0);
}
.page-meta strong { color: var(--text-color, #333); font-weight: 600; }
/* Underlined, like every other link in running text on this site. Colour
   alone is not enough here: #0369a1 against the #64748b of the surrounding
   meta line is 1.25:1, where WCAG wants 3:1 from a link that carries no other
   cue, and a hover-only underline reaches neither keyboard nor touch.

   The site-wide prose-link rule can't do this for us: it is wrapped in
   :where() for zero specificity, so this rule at (0,1,1) outranks it and has
   to carry the underline itself. */
.page-meta a {
    color: var(--secondary-color, #0369a1);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    font-weight: 500;
}

/* Hover thickens rather than reveals, matching the prose and glossary links. */
.page-meta a:hover,
.page-meta a:focus-visible { text-decoration-thickness: 2px; }
[data-theme="dark"] .page-meta {
    color: #94a3b8;
    border-bottom-color: #1e293b;
}
[data-theme="dark"] .page-meta strong { color: #e2e8f0; }
[data-theme="dark"] .page-meta a { color: #38bdf8; }


/* =========================================================================
   Illustrated hero - gradient + dot pattern + side-by-side text & SVG art.
   Used on pillar pages to replace the generic banner.
   ========================================================================= */
.hero--illustrated {
    background:
        radial-gradient(ellipse 80% 60% at 78% 18%, rgba(56,189,248,0.22) 0%, transparent 60%),
        radial-gradient(ellipse 60% 80% at 18% 82%, rgba(245,158,11,0.16) 0%, transparent 60%),
        linear-gradient(135deg, #0b1729 0%, #1e293b 55%, #0c4a6e 100%);
    color: #fff;
    padding: 2.5rem 1.5rem 3rem;
    position: relative;
    overflow: hidden;
}
.hero--illustrated::before {
    content: '';
    position: absolute; inset: 0;
    background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.07) 1px, transparent 0);
    background-size: 28px 28px;
    pointer-events: none;
    mask-image: linear-gradient(180deg, #000 30%, transparent 100%);
    -webkit-mask-image: linear-gradient(180deg, #000 30%, transparent 100%);
}
.hero--illustrated .hero-inner {
    max-width: 1180px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1.4fr 0.85fr;
    gap: 2rem;
    align-items: center;
    position: relative;
    z-index: 1;
}
/* Left, not centred. .hero inherits `text-align: center`, so on this two-column
   hero the h1 and the paragraph centred while .cta-buttons - a flex row - began
   at the left, putting three alignments in one block. Left is the right answer
   here because the illustration already occupies the right column. Below 880px
   the layout stacks and centring returns. */
.hero--illustrated .hero-text {
    text-align: left;
}
.hero--illustrated .hero-text h1 { text-wrap: balance; }

.hero--illustrated .hero-illustration { max-width: 360px; justify-self: end; }
.hero--illustrated h1 {
    font-size: clamp(1.7rem, 3.6vw, 2.6rem);
    line-height: 1.15;
    margin: 0 0 0.75rem;
    color: #fff;
    letter-spacing: -0.015em;
}
.hero--illustrated p {
    font-size: clamp(0.95rem, 1.15vw, 1.05rem);
    line-height: 1.55;
    color: #cbd5e1;
    margin: 0 0 1.25rem;
    max-width: 58ch;
}
.hero--illustrated .cta-buttons { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.hero--illustrated .btn-primary {
    background: var(--action);
    color: var(--action-ink);
    border: 2px solid var(--action);
}
.hero--illustrated .btn-primary:hover {
    background: var(--action-hover);
    border-color: var(--action-hover);
    color: var(--action-ink);
}
.hero--illustrated .btn-secondary {
    background: transparent;
    color: #fff;
    border: 2px solid #ffffff;
}
.hero--illustrated .btn-secondary:hover { background: #fff; color: #0b1729; border-color: #fff; }
.hero-illustration { width: 100%; position: relative; }
.hero-illustration svg {
    width: 100%;
    height: auto;
    display: block;
    filter: drop-shadow(0 18px 40px rgba(0,0,0,0.35));
}
@media (max-width: 880px) {
    .hero--illustrated { padding: 2.5rem 1rem 3rem; }
    .hero--illustrated .hero-inner { grid-template-columns: 1fr; gap: 2rem; }
    /* Stacked: centred again, and the button row centres with it. */
    .hero--illustrated .hero-text { text-align: center; }
    .hero--illustrated .cta-buttons { justify-content: center; }
    .hero-illustration { max-width: 420px; margin: 0 auto; }
}

/* Mobile: hide the decorative hero illustration entirely so the H1, intro,
   and CTAs sit above the fold. The SVG is decorative (aria-hidden) so this
   has no a11y impact. */
@media (max-width: 768px) {
    .hero--illustrated .hero-illustration { display: none; }
    .hero--illustrated { padding: 2rem 1rem 2rem; }
}

/* Article pages lead with the 30-second summary in the main column. The
   illustrated hero's SVG is decorative and, on a desktop, pushes that
   summary below the fold. Hide it and collapse the two-column grid so the
   title banner is one column. Scoped to main.contribute-article so hubs
   without that class are unaffected; the homepage and sessions use
   hero--compact and no longer carry an illustration. */
body:has(main.contribute-article) .hero--illustrated .hero-illustration {
    display: none;
}
body:has(main.contribute-article) .hero--illustrated .hero-inner {
    grid-template-columns: 1fr;
}
body:has(main.contribute-article) .hero--illustrated {
    padding-bottom: 2rem;
}

/* Light theme variants for the illustrated hero - keep brand accents
   but lift the base so light mode reads bright on mobile. */
[data-theme="light"] .hero--illustrated {
    background:
        radial-gradient(ellipse 80% 60% at 78% 18%, rgba(56,189,248,0.28) 0%, transparent 60%),
        radial-gradient(ellipse 60% 80% at 18% 82%, rgba(245,158,11,0.14) 0%, transparent 60%),
        linear-gradient(135deg, #e0f2fe 0%, #f5fbff 55%, #ffffff 100%);
    color: #0b1729;
}
[data-theme="light"] .hero--illustrated::before {
    background-image: radial-gradient(circle at 1px 1px, rgba(15,23,42,0.08) 1px, transparent 0);
}
[data-theme="light"] .hero--illustrated h1 {
    color: #0b1729;
}
[data-theme="light"] .hero--illustrated p {
    color: #334155;
}
[data-theme="light"] .hero--illustrated .btn-primary {
    /* Same --action as every other primary CTA; dark ink on sky-400. */
    background: var(--action);
    color: var(--action-ink);
    border-color: var(--action);
}
[data-theme="light"] .hero--illustrated .btn-primary:hover {
    background: var(--action-hover);
    border-color: var(--action-hover);
    color: var(--action-ink);
}
[data-theme="light"] .hero--illustrated .btn-secondary {
    background: #ffffff;
    color: #0b1729;
    border: 2px solid #0b1729;
}
[data-theme="light"] .hero--illustrated .btn-secondary:hover {
    background: #0b1729;
    color: #ffffff;
    border-color: #0b1729;
}
[data-theme="light"] .hero-illustration svg {
    filter: drop-shadow(0 18px 32px rgba(2,132,199,0.18));
}

/* Hero illustration tiles - theme-aware fills so they don't look out of place
   in light mode. The line-icon strokes inside each tile keep their accent
   colors from the inline SVG. */
.hero-svg-tile {
    fill: #1e293b;
    stroke: rgba(255, 255, 255, 0.18);
    stroke-width: 1;
}
[data-theme="light"] .hero-svg-tile {
    fill: #ffffff;
    stroke: rgba(15, 23, 42, 0.12);
}

/* =========================================================================
   Pillar-card grid - color-coded gradient icon tiles for pillar pages.
   ========================================================================= */
.pillar-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
    margin: 1.25rem 0 2rem;
}
.pillar-card {
    --card-accent: #3498db;
    --card-accent-soft: rgba(52,152,219,0.13);
    background: var(--white);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 1rem 1rem 1.25rem;
    text-decoration: none;
    color: inherit;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    position: relative;
    overflow: hidden;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.pillar-card::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--card-accent);
    opacity: 0;
    transition: opacity 0.2s ease;
}
.pillar-card:hover, .pillar-card:focus-visible {
    transform: translateY(-3px);
    box-shadow: 0 14px 28px rgba(0,0,0,0.10);
    border-color: var(--card-accent);
}

/* .pillar-card is an <a>, and keyboard focus needs a visible ring. Hover
   styling is not a focus indicator - it is the same cue the pointer already
   gets, and a tinted border against an existing border is not a state change
   anyone can find. Split out so the ring applies on focus only and never
   under the pointer; don't end the block above in `outline: none`. */
.pillar-card:focus-visible {
    outline: 3px solid var(--secondary-color);
    outline-offset: 2px;
}
.pillar-card:hover::before, .pillar-card:focus-visible::before { opacity: 1; }
.pillar-icon {
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: var(--card-accent-soft);
    color: var(--card-accent);
    position: relative;
    overflow: hidden;
}
.pillar-icon::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 10px;
    background: linear-gradient(135deg, var(--card-accent) 0%, transparent 70%);
    opacity: 0.18;
}
.pillar-icon svg { width: 22px; height: 22px; stroke-width: 1.85; position: relative; z-index: 1; }
.pillar-card h3 {
    margin: 0;
    font-size: var(--fs-16);
    color: var(--primary-color);
    line-height: 1.3;
}
.pillar-card p {
    margin: 0;
    font-size: var(--fs-14);
    color: #555;
    line-height: 1.5;
}
.pillar-card-iam        { --card-accent: #8b5cf6; --card-accent-soft: rgba(139,92,246,0.13); }
.pillar-card-posture    { --card-accent: #10b981; --card-accent-soft: rgba(16,185,129,0.13); }
.pillar-card-network    { --card-accent: #06b6d4; --card-accent-soft: rgba(6,182,212,0.13); }
.pillar-card-data       { --card-accent: #f59e0b; --card-accent-soft: rgba(245,158,11,0.15); }
.pillar-card-workload   { --card-accent: #f43f5e; --card-accent-soft: rgba(244,63,94,0.13); }
.pillar-card-detect     { --card-accent: #6366f1; --card-accent-soft: rgba(99,102,241,0.13); }
.pillar-card-compliance { --card-accent: #14b8a6; --card-accent-soft: rgba(20,184,166,0.13); }
.pillar-card-customer   { --card-accent: #e11d48; --card-accent-soft: rgba(225,29,72,0.10); }
.pillar-card-provider   { --card-accent: #059669; --card-accent-soft: rgba(5,150,105,0.10); }
.pillar-card-supply     { --card-accent: #db2777; --card-accent-soft: rgba(219,39,119,0.13); }
.pillar-card-ai         { --card-accent: #0ea5e9; --card-accent-soft: rgba(14,165,233,0.13); }

/* Roadmap-stage cards */
.pillar-card-stage1 { --card-accent: #10b981; --card-accent-soft: rgba(16,185,129,0.13); }
.pillar-card-stage2 { --card-accent: #06b6d4; --card-accent-soft: rgba(6,182,212,0.13); }
.pillar-card-stage3 { --card-accent: #6366f1; --card-accent-soft: rgba(99,102,241,0.13); }
.pillar-card-stage4 { --card-accent: #db2777; --card-accent-soft: rgba(219,39,119,0.13); }

/* Colored table cells (red/green/yellow inline backgrounds) need dark text
   since their bg stays light in dark mode. */
[data-theme="dark"] table td[style*="background"] { color: #1e293b; }
[data-theme="dark"] table th { color: #e2e8f0; }

[data-theme="dark"] .pillar-card {
    background: rgba(255,255,255,0.04);
    border-color: rgba(255,255,255,0.10);
}
[data-theme="dark"] .pillar-card h3 { color: #e2e8f0; }
[data-theme="dark"] .pillar-card p { color: #94a3b8; }

/* =========================================================================
   Threats grid - illustrated tiles with red accent for breach patterns.
   ========================================================================= */
.threats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.75rem;
    margin: 1.25rem 0 1.75rem;
}
.threat-tile {
    background: var(--white);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 1rem 1rem;
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.threat-tile:hover { border-color: #f43f5e; box-shadow: 0 8px 20px rgba(244,63,94,0.10); }
.threat-icon {
    width: 34px; height: 34px;
    flex-shrink: 0;
    border-radius: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(244,63,94,0.10);
    color: #e11d48;
}
.threat-icon svg { width: 18px; height: 18px; stroke-width: 1.85; }
.threat-tile h4 {
    margin: 0 0 0.125rem;
    font-size: var(--fs-15);
    color: var(--primary-color);
    line-height: 1.3;
}
.threat-tile p {
    margin: 0;
    font-size: var(--fs-13);
    color: #555;
    line-height: 1.45;
}
[data-theme="dark"] .threat-tile {
    background: rgba(255,255,255,0.04);
    border-color: rgba(255,255,255,0.10);
}
[data-theme="dark"] .threat-tile h4 { color: #e2e8f0; }
[data-theme="dark"] .threat-tile p { color: #94a3b8; }

/* =========================================================================
   CNAPP umbrella diagram container.
   ========================================================================= */
/* breach-list divider variant: a non-card list item that just hosts a page-photo */
.breach-divider { list-style: none; padding: 0; margin: 0; }

/* Memorial-hero: full-width photo background with overlaid name + dates.
   Used on kevin-mitnick.html. */
/* This band is much wider than it is tall, so a 4:3 source could only ever
   show a slice of its height, and the slice has to be the part with the faces.

   The source is kev-09-hero, a 960x430 (2.23:1) crop of kev-09 taken from
   the top of the frame down to just under their shoulders. Cropping costs no
   sharpness: `cover` scales by width here, so the horizontal 960 -> viewport
   upscale is identical either way, and the rows removed are ones the browser
   would throw away regardless.

   Height scales with width, 42vw between a 480px floor and a 680px cap,
   so the slice stays roughly constant instead of shrinking as the viewport
   grows. The faces sit at 9%-84% of the crop and stay fully in frame from
   375px through 1920px.

   Past ~2000px it clips, and no crop fixes that: the faces span 44% of
   the original's height, so holding them whole needs a container no wider
   than about 3:1, while 2560x680 is 3.76:1. The lever there is hero height,
   not the picture - and the height it would take (~1080px at 2560) is worse
   than the clipping.

   If you change the clamp, check both ends: too short and the heads clip, too
   tall and the centred title lands across their faces. */
.memorial-hero {
    position: relative;
    width: 100%;
    min-height: clamp(480px, 42vw, 680px);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: #fff;
}
.memorial-hero-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    display: block;
}
.memorial-hero-bg img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* 33% and not `center`: the faces sit at 9%-84% of kev-09-hero, so they
       are slightly high in the crop and splitting the overflow evenly clips
       the tops of their heads once the band gets wide (it does at 1920).
       Solving `band_top <= 9%` and `band_bot >= 84%` across every width the
       clamp produces leaves a feasible window of 22%-44%; 33% is the middle
       of it. Past ~2000px the window closes entirely - see .memorial-hero. */
    object-position: center 33%;
    display: block;
}
.memorial-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(180deg, rgba(11,23,41,0.55) 0%, rgba(11,23,41,0.78) 60%, rgba(11,23,41,0.92) 100%);
}
.memorial-hero-overlay {
    position: relative;
    z-index: 2;
    padding: 4rem 1.5rem;
    text-align: center;
    max-width: 760px;
}
.memorial-hero-eyebrow {
    margin: 0 0 0.5rem;
    font-size: var(--fs-14);
    letter-spacing: 0.4em;
    text-transform: uppercase;
    color: #fbbf24;
    font-weight: 600;
}
.memorial-hero h1 {
    margin: 0 0 0.5rem;
    font-size: clamp(2.4rem, 5vw, 3.6rem);
    line-height: 1.05;
    letter-spacing: -0.01em;
    color: #fff;
}
.memorial-hero-dates {
    margin: 0 0 1.25rem;
    font-size: var(--fs-17);
    color: #cbd5e1;
    font-style: italic;
}
.memorial-hero-subtitle {
    margin: 0;
    font-size: var(--fs-16);
    color: #e2e8f0;
    line-height: 1.5;
}
@media (max-width: 600px) {
    .memorial-hero { min-height: 380px; }
    .memorial-hero-overlay { padding: 2.5rem 1rem; }
}

/* Photo gallery for the kev-gallery section: 2-column grid on wide,
   single-column on mobile. Uses existing .page-photo internally. */
.kev-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
    gap: 1.25rem;
    margin: 1.5rem 0;
}
.kev-gallery .page-photo { margin: 0; }

/* Final signature on the memorial page: italic, larger, centered. */
.signature--final {
    text-align: center;
    font-style: italic;
    font-size: var(--fs-18);
    margin: 2.5rem 0 1rem;
    color: var(--text-muted, #5b6573);
}
[data-theme="dark"] .signature--final { color: #94a3b8; }

/* Profile-page stats chips (about-shawn-nunley.html hero). */
.profile-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 1.25rem;
}
.profile-stat {
    display: inline-flex;
    align-items: baseline;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 999px;
    font-size: var(--fs-14);
    color: #cbd5e1;
}
.profile-stat strong {
    color: #fbbf24;
    font-weight: 700;
}

/* Lede paragraph: emphasized intro on profile/showcase pages. */
.lede {
    font-size: var(--fs-18);
    line-height: 1.6;
    color: var(--text-color);
    font-weight: 500;
    margin-bottom: 1.25rem;
}
[data-theme="dark"] .lede { color: #e2e8f0; }

/* H2 accent - small colored bar above each section's h2 on long pillar pages.
   Adds visual rhythm so each section starts with a clear anchor instead of
   landing in another wall of text. Scoped to <main class="contribute-article">
   so listing/utility pages keep their existing styling. */
.contribute-article section > h2 {
    position: relative;
    padding-top: 0.75rem;
    margin-top: 2.5rem;
}
.contribute-article section > h2::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 56px;
    height: 4px;
    background: linear-gradient(90deg, var(--secondary-color), transparent);
    border-radius: 2px;
}
[data-theme="dark"] .contribute-article section > h2::before {
    background: linear-gradient(90deg, #38bdf8, transparent);
}

/* Pull-quote callout - pulls a key sentence out of paragraph runs into a
   visually-distinct, magazine-style block. Use sparingly, ~1 per long page. */
.pull-quote {
    margin: 2rem 0;
    padding: 1.25rem 1.5rem 1.25rem 2.5rem;
    border-left: 4px solid var(--secondary-color);
    background: linear-gradient(135deg, rgba(52,152,219,0.06) 0%, transparent 100%);
    border-radius: 0 12px 12px 0;
    font-size: var(--fs-18);
    line-height: 1.55;
    color: var(--text-color);
    font-style: italic;
    position: relative;
    font-weight: 500;
}
.pull-quote::before {
    content: '\201C';
    position: absolute;
    left: 0.7rem;
    top: -0.25rem;
    font-size: 3.2rem;
    color: var(--secondary-color);
    font-family: Georgia, 'Times New Roman', serif;
    font-style: normal;
    line-height: 1;
    opacity: 0.8;
}
.pull-quote cite {
    display: block;
    margin-top: 0.5rem;
    font-size: var(--fs-14);
    color: var(--text-muted, #5b6573);
    font-style: normal;
    font-weight: 400;
}
[data-theme="dark"] .pull-quote {
    background: linear-gradient(135deg, rgba(56,189,248,0.10) 0%, transparent 100%);
    border-left-color: #38bdf8;
    color: #e2e8f0;
}
[data-theme="dark"] .pull-quote::before { color: #38bdf8; }
[data-theme="dark"] .pull-quote cite { color: #94a3b8; }

/* Page photos - Pexels-sourced JPGs with webp fallback. */
.page-photo {
    margin: 2rem 0;
    border-radius: 14px;
    overflow: hidden;
    position: relative;
    background: var(--white);
    border: 1px solid var(--border-color);
    box-shadow: 0 8px 24px rgba(0,0,0,0.08);
}
/* WebP delivery: <img> tags are wrapped in <picture><source type="image/webp">
   for transparent WebP with a raster fallback (see tools/wrap_img_webp.py).
   display:contents makes the wrapper layout-neutral, so the <img> keeps
   behaving as the flex/grid child it was before wrapping. More specific rules
   below (e.g. .page-photo picture) intentionally override this where the
   <picture> itself is the sized box. */
picture {
    display: contents;
}
.page-photo picture, .page-photo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
    object-fit: cover;
}

/* A portrait source in this 3:2 box keeps only 50% of its height and, centred,
   throws away 25% off each end - and on a photo of people the top 25% is their
   heads. The landscape photos are unaffected; they keep 89% and lose 6% a side.

   15% puts the visible band at 7.5%-57.5% of the source, which clears the top
   of the head in all three portraits on kevin-mitnick.html. It is a modifier
   rather than a change to the rule above because `.page-photo` covers mostly
   diagrams and screenshots, where centred is correct and a bias upward would
   start cutting off captions and legends.

   Any new portrait .page-photo needs this class - CSS cannot see a file's
   aspect ratio, so nothing will tell you but the picture looking wrong. */
.page-photo--portrait img {
    object-position: center 15%;
}
.page-photo figcaption {
    padding: 0.625rem 1rem;
    font-size: var(--fs-12);
    color: #595959;
    background: rgba(0,0,0,0.02);
    text-align: right;
    font-style: italic;
}
[data-theme="dark"] .page-photo {
    background: rgba(255,255,255,0.03);
    border-color: rgba(255,255,255,0.10);
}
[data-theme="dark"] .page-photo figcaption {
    color: #94a3b8;
    background: rgba(255,255,255,0.02);
}

.cnapp-figure, .killchain-figure, .arch-figure, .workflow-figure {
    background: linear-gradient(180deg, #f8fafc 0%, #fff 100%);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 1.25rem 1rem 1rem;
    margin: 1.5rem 0;
}
.killchain-figure svg, .arch-figure svg, .workflow-figure svg { width: 100%; height: auto; display: block; max-width: 820px; margin: 0 auto; }
.killchain-figure figcaption, .arch-figure figcaption, .workflow-figure figcaption {
    text-align: center; font-size: var(--fs-13); color: #555; font-style: italic; margin-top: 0.625rem;
}
[data-theme="dark"] .killchain-figure, [data-theme="dark"] .arch-figure, [data-theme="dark"] .workflow-figure {
    background: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 100%);
    border-color: rgba(255,255,255,0.10);
}
[data-theme="dark"] .killchain-figure figcaption,
[data-theme="dark"] .arch-figure figcaption,
[data-theme="dark"] .workflow-figure figcaption { color: #94a3b8; }
.cnapp-figure svg { width: 100%; height: auto; display: block; max-width: 580px; margin: 0 auto; }
.cnapp-figure figcaption {
    text-align: center;
    font-size: var(--fs-13);
    color: #555;
    font-style: italic;
    margin-top: 0.625rem;
}
[data-theme="dark"] .cnapp-figure {
    background: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 100%);
    border-color: rgba(255,255,255,0.10);
}
[data-theme="dark"] .cnapp-figure figcaption { color: #94a3b8; }

/* =========================================================================
   Inline SVG diagram figure - wraps responsive SVG with caption.
   ========================================================================= */
.diagram-figure {
    margin: 1.5rem 0;
    padding: 1.25rem;
    background: var(--white);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    overflow: hidden;
}
.diagram-figure svg {
    display: block;
    width: 100%;
    height: auto;
    max-width: 640px;
    margin: 0 auto;
}
.diagram-figure figcaption {
    margin-top: 0.75rem;
    font-size: var(--fs-14);
    color: #555;
    text-align: center;
    font-style: italic;
}
[data-theme="dark"] .diagram-figure {
    background: rgba(255,255,255,0.03);
    border-color: rgba(255,255,255,0.12);
}

/* =========================================================================
   Comparison table - used for side-by-side multi-column comparisons
   (e.g. AWS / Azure / GCP on landing-zones.html). Replaces the older
   inline-styled tables which got cramped at narrow viewports.
   ========================================================================= */
.comparison-table {
    margin: 1.5rem 0 2rem;
    overflow-x: auto;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 12px;
    background: var(--card-bg, #fff);
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
    -webkit-overflow-scrolling: touch;
}
.comparison-table table {
    width: 100%;
    min-width: 720px;
    border-collapse: collapse;
    margin: 0;
    font-size: var(--fs-15);
    table-layout: fixed;
}
.comparison-table thead th {
    text-align: left;
    padding: 1rem 1rem;
    font-weight: 700;
    font-size: var(--fs-15);
    letter-spacing: 0.01em;
    background: linear-gradient(180deg, #f8fafc 0%, #eef2f7 100%);
    border-bottom: 2px solid var(--border-color, #cbd5e1);
    white-space: nowrap;
    color: #0f172a;
}
.comparison-table thead th.ct-row-head {
    width: 18%;
    min-width: 160px;
}
.comparison-table thead th.ct-aws,
.comparison-table thead th.ct-azure,
.comparison-table thead th.ct-gcp {
    width: 27.33%;
}
.comparison-table .ct-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin-right: 0.5rem;
    vertical-align: middle;
    box-shadow: 0 0 0 2px rgba(255,255,255,0.7);
}
.comparison-table .ct-dot-aws   { background: #ff9900; }
.comparison-table .ct-dot-azure { background: #0078d4; }
.comparison-table .ct-dot-gcp   { background: #34a853; }
.comparison-table tbody th {
    text-align: left;
    padding: 1rem 1rem;
    font-weight: 600;
    color: #0f172a;
    background: #f8fafc;
    border-right: 1px solid var(--border-color, #e2e8f0);
    border-bottom: 1px solid var(--border-color, #eef2f7);
    vertical-align: top;
    white-space: normal;
}
.comparison-table tbody td {
    padding: 1rem 1rem;
    vertical-align: top;
    border-bottom: 1px solid var(--border-color, #eef2f7);
    line-height: 1.5;
    color: #1e293b;
}
.comparison-table tbody tr:nth-child(even) td {
    background: #fafbfc;
}
.comparison-table tbody tr:nth-child(even) th {
    background: #f3f5f8;
}
.comparison-table tbody tr:last-child th,
.comparison-table tbody tr:last-child td {
    border-bottom: none;
}
.comparison-table tbody tr:hover td,
.comparison-table tbody tr:hover th {
    background: #eff6ff;
}

[data-theme="dark"] .comparison-table {
    background: rgba(255,255,255,0.03);
    border-color: rgba(255,255,255,0.12);
    box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
[data-theme="dark"] .comparison-table thead th {
    background: linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0.02) 100%);
    color: #e2e8f0;
    border-bottom-color: rgba(255,255,255,0.18);
}
[data-theme="dark"] .comparison-table tbody th {
    background: rgba(255,255,255,0.04);
    color: #e2e8f0;
    border-right-color: rgba(255,255,255,0.08);
    border-bottom-color: rgba(255,255,255,0.06);
}
[data-theme="dark"] .comparison-table tbody td {
    color: #cbd5e1;
    border-bottom-color: rgba(255,255,255,0.06);
}
[data-theme="dark"] .comparison-table tbody tr:nth-child(even) td {
    background: rgba(255,255,255,0.02);
}
[data-theme="dark"] .comparison-table tbody tr:nth-child(even) th {
    background: rgba(255,255,255,0.05);
}
[data-theme="dark"] .comparison-table tbody tr:hover td,
[data-theme="dark"] .comparison-table tbody tr:hover th {
    background: rgba(56, 189, 248, 0.10);
}

@media (max-width: 720px) {
    .comparison-table table { min-width: 760px; font-size: var(--fs-14); }
    .comparison-table thead th,
    .comparison-table tbody th,
    .comparison-table tbody td { padding: 0.75rem 0.75rem; }
}

/* Stat strip - quantified social proof row used near the top of the home page. */
/* Subscribe callout on the recaps index. The head already carries a
   rel="alternate" link for /recaps.xml, but feed autodiscovery is invisible in
   every modern browser, so the affordance has to be on the page. Links the
   explainer alongside the feed because a raw XML URL is a dead end for anyone
   who does not already use a reader. */
.recap-subscribe {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    max-width: 760px;
    margin: 1.5rem auto 0;
    padding: 1rem 1rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--accent-color);
    border-radius: 10px;
}
.recap-subscribe__icon {
    flex: 0 0 auto;
    margin-top: 0.125rem;
    color: var(--accent-color);
}
.recap-subscribe p {
    margin: 0;
    font-size: var(--fs-15);
    line-height: 1.5;
    color: #475569;
}
[data-theme="dark"] .recap-subscribe {
    background: #1a2535;
    border-color: #2d3f55;
    border-left-color: var(--accent-color);
}
[data-theme="dark"] .recap-subscribe p { color: #cbd5e1; }

/* "From the Friday sessions" block, stamped onto topic pages by
   tools/inject_session_blocks.py. Deliberately reads as a list of dated
   conversations rather than more reference content: the point is to show the
   reader that this topic is live and argued about, not to add another article. */
.session-echo { border-top: 1px solid var(--border-color); padding-top: 1.5rem; }
.session-echo-list {
    list-style: none;
    margin: 1.25rem 0 0;
    padding: 0;
    display: grid;
    gap: 1rem;
}
.session-echo-item {
    padding: 1rem 1.25rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 10px;
}
.session-echo-link { display: block; text-decoration: none; }
.session-echo-link time {
    display: block;
    font-size: var(--fs-12);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--secondary-color);
}
.session-echo-title {
    display: block;
    margin-top: 0.25rem;
    font-size: var(--fs-17);
    font-weight: 700;
    line-height: 1.3;
    color: var(--primary-color);
}
.session-echo-link:hover .session-echo-title { text-decoration: underline; }
.session-echo-summary {
    margin: 0.5rem 0 0;
    font-size: var(--fs-15);
    line-height: 1.5;
    color: #475569;
}
.session-echo-cta { margin-top: 1.25rem; font-weight: 600; }
[data-theme="dark"] .session-echo { border-top-color: #2d3f55; }
[data-theme="dark"] .session-echo-item { background: #1a2535; border-color: #2d3f55; }
[data-theme="dark"] .session-echo-link time { color: #7dd3fc; }
[data-theme="dark"] .session-echo-title { color: #f1f5f9; }
[data-theme="dark"] .session-echo-summary { color: #cbd5e1; }

/* Next live session banner. The server-rendered text is the weekly cadence
   ("Friday at 7:00 AM PT"); main.js upgrades it in place to the concrete
   upcoming date plus the viewer's local time, so the no-JS state still reads
   correctly. This is the homepage's primary conversion surface, so it sits
   above the stat strip and carries the two session CTAs. */
.next-session {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.25rem 2rem;
    max-width: 960px;
    margin: 2rem auto 1.25rem;
    padding: 1.5rem 1.75rem;
    background: var(--white);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--secondary-color);
    border-radius: 14px;
    box-shadow: var(--shadow-lg);
}
.next-session__body { flex: 1 1 22rem; min-width: 0; }
.next-session__badge {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--fs-12);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* sky-700, not var(--secondary-color): sky-600 on the card's white
       surface is 4.10:1, under AA for this 12px text. */
    color: #0369a1;
}
.next-session__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    /* Amber: the logo accent, reserved for Friday/live presence. */
    background: var(--accent-color);
    box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.55);
    animation: next-session-pulse 2.4s ease-out infinite;
}
@keyframes next-session-pulse {
    70% { box-shadow: 0 0 0 8px rgba(245, 158, 11, 0); }
    100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .next-session__dot { animation: none; }
}
.next-session__when {
    margin: 0.375rem 0 0;
    font-size: clamp(1.25rem, 2.4vw, 1.6rem);
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -0.01em;
    color: var(--primary-color);
}
/* Countdown to the next session. Hidden until main.js adds --on, via
   :not() rather than an ordering-dependent override, so a rule inserted
   above cannot silently reveal a stale "0 days" to visitors without JS. */
.next-session__countdown { margin: 0.75rem 0 0; }
.next-session__countdown:not(.next-session__countdown--on) { display: none; }
.next-session__units {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* Four units do not fit one row on a phone, and flex-wrap drops SECONDS on to a
   line of its own, which reads as a rendering fault rather than a countdown.
   A 2x2 grid keeps the unit reading as one object. */
@media (max-width: 480px) {
    .next-session__units {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .next-session__unit { min-width: 0; }
}
.next-session__unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 3.6rem;
    padding: 0.375rem 0.625rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 10px;
}
/* [hidden] is display:none only in the UA sheet, which the flex above beats.
   main.js drops the days tile on the last day, so this has to be restated. */
.next-session__unit[hidden] { display: none; }
.next-session__unit-num {
    font-size: var(--fs-20);
    font-weight: 800;
    line-height: 1.1;
    color: var(--primary-color);
    /* Without tabular figures the tiles resize on every tick. */
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}
.next-session__unit-label {
    margin-top: 0.125rem;
    font-size: var(--fs-12);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: #475569;
}
/* green-700: 5.02:1 on the card's white surface, where green-600 is 3.7:1. */
.next-session__live {
    margin: 0;
    font-size: var(--fs-17);
    font-weight: 700;
    color: #15803d;
}
.next-session__countdown:not(.next-session__countdown--live) .next-session__live { display: none; }
.next-session__countdown--live .next-session__units { display: none; }
.next-session__note {
    margin: 0.5rem 0 0;
    font-size: var(--fs-15);
    line-height: 1.45;
    color: #475569;
}
.next-session__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    flex: 0 1 auto;
}
.next-session__actions .btn { margin: 0; }
@media (max-width: 640px) {
    .next-session { padding: 1.25rem; }
    .next-session__actions { width: 100%; }
    .next-session__actions .btn { flex: 1 1 auto; text-align: center; }
}
[data-theme="dark"] .next-session {
    background: #1a2535;
    border-color: #2d3f55;
    border-left-color: #38bdf8;
}
[data-theme="dark"] .next-session__badge { color: #7dd3fc; }
[data-theme="dark"] .next-session__when { color: #f1f5f9; }
[data-theme="dark"] .next-session__note { color: #cbd5e1; }
[data-theme="dark"] .next-session__unit { background: #22304a; border-color: #2d3f55; }
[data-theme="dark"] .next-session__unit-num { color: #f1f5f9; }
[data-theme="dark"] .next-session__unit-label { color: #cbd5e1; }
[data-theme="dark"] .next-session__live { color: #4ade80; }

.stat-strip {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 1rem;
    margin: 2rem auto 3rem;
    max-width: 960px;
    padding: 1.75rem 1.5rem;
    background: var(--white);
    border: 1px solid var(--border-color);
    border-radius: 14px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
.stat-strip__item {
    text-align: center;
    padding: 0 0.5rem;
    border-right: 1px solid var(--border-color);
}
.stat-strip__item:last-child { border-right: none; }
.stat-strip__num {
    display: block;
    font-size: clamp(1.7rem, 3vw, 2.25rem);
    font-weight: 800;
    line-height: 1.1;
    color: var(--primary-color);
    letter-spacing: -0.02em;
}
.stat-strip__label {
    display: block;
    margin-top: 0.375rem;
    font-size: var(--fs-14);
    color: #475569;
    line-height: 1.35;
}
/* Four or more tiles do not fit on one line of an article column, so they
   pair up, and an odd tile out takes the whole last row. */
@media (min-width: 641px) {
    .stat-strip:has(> .stat-strip__item:nth-child(4)) {
        grid-auto-flow: row;
        grid-template-columns: repeat(2, 1fr);
        row-gap: 1.5rem;
    }
    .stat-strip:has(> .stat-strip__item:nth-child(4)) > .stat-strip__item:nth-child(even) { border-right: none; }
    .stat-strip:has(> .stat-strip__item:nth-child(4)) > .stat-strip__item:last-child:nth-child(odd) { grid-column: 1 / -1; border-right: none; }
}
@media (max-width: 640px) {
    .stat-strip { grid-auto-flow: row; grid-template-columns: 1fr; gap: 1.25rem; padding: 1.25rem; }
    .stat-strip__item { border-right: none; border-bottom: 1px solid var(--border-color); padding-bottom: 1rem; }
    .stat-strip__item:last-child { border-bottom: none; padding-bottom: 0; }
}
[data-theme="dark"] .stat-strip {
    background: #1a2535;
    border-color: #2d3f55;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
[data-theme="dark"] .stat-strip__item { border-right-color: #2d3f55; }
[data-theme="dark"] .stat-strip__item:last-child { border-right: none; }
[data-theme="dark"] .stat-strip__num { color: #7dd3fc; }
[data-theme="dark"] .stat-strip__label { color: #cbd5e1; }
@media (max-width: 640px) {
    [data-theme="dark"] .stat-strip__item { border-bottom-color: #2d3f55; }
}
[data-theme="dark"] .diagram-figure figcaption { color: #94a3b8; }

/* ---- How-to exercises (howto/*.html) ------------------------------------
   A prompt block plus a <details> the reader opens only after attempting the
   exercise. These rules live here rather than in the pages because style-src
   is 'self' with no 'unsafe-inline', so an inline <style> block is dropped in
   production while working perfectly on localhost - see "No inline `<style>`
   blocks" in CLAUDE.md. Comments sit above each selector, never inside a rule,
   because sync_dark_branch.py flattens every rule onto one line. */
.exercise {
    background-color: var(--white);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--accent-color);
    border-radius: 8px;
    padding: 1.25rem 1.5rem;
    margin: 1.5rem 0;
}
.exercise__label {
    display: block;
    margin: 0 0 0.5rem;
    font-size: var(--fs-13);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--secondary-color);
}
/* Scoped under .exercise so it outranks .contribute-article p at (0,1,1);
   a bare .exercise p would lose to the article's own prose rule. */
.exercise p {
    margin: 0 0 0.75rem;
    line-height: 1.65;
}
.exercise p:last-child { margin-bottom: 0; }
.exercise ul, .exercise ol { margin: 0 0 0.75rem 1.25rem; line-height: 1.65; }
.exercise .code-block { margin: 0.75rem 0; }
.exercise details { margin-top: 1rem; }
.exercise summary {
    cursor: pointer;
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.9rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 999px;
    font-weight: 600;
    font-size: var(--fs-14);
    color: var(--secondary-color);
    transition: background 0.15s, border-color 0.15s;
}
.exercise summary::-webkit-details-marker { display: none; }
.exercise summary::after {
    content: "\25B8";
    font-size: var(--fs-12);
    transition: transform 0.18s ease;
    display: inline-block;
}
.exercise details[open] > summary::after { transform: rotate(90deg); }
.exercise summary:hover,
.exercise summary:focus-visible {
    background: rgba(3, 105, 161, 0.08);
    border-color: var(--secondary-color);
}
.exercise details[open] > summary { margin-bottom: 0.75rem; }
[data-theme="dark"] .exercise {
    background-color: #1a2535;
    border-color: #2d3f55;
    border-left-color: var(--accent-color);
}
[data-theme="dark"] .exercise__label { color: #7dd3fc; }
[data-theme="dark"] .exercise summary {
    background: #1e2a3a;
    border-color: #2d3f55;
    color: #7dd3fc;
}
[data-theme="dark"] .exercise summary:hover,
[data-theme="dark"] .exercise summary:focus-visible {
    background: rgba(125, 211, 252, 0.12);
    border-color: #7dd3fc;
}

/* ---- Code block chrome, syntax tokens, and reading aids -----------------
   Everything here is keyed off [data-lang], which tools/stamp_code_langs.py
   stamps onto `<div class="code-block"><pre>` blocks only. The site also has
   a `<div class="code-block">` shape holding <br>-separated text, some of
   which is not code at all; those match none of these rules.

   The language label is a ::before on the container rather than an element
   injected by main.js, so a reader with JavaScript off still sees it. Only
   the copy button needs script. Comments sit above each selector because
   sync_dark_branch.py flattens rules onto one line. */

/* Inline <code> in prose. A long dotted path such as
   `change.after.metadata_options.http_tokens` is a single unbreakable token to
   the line breaker, so without overflow-wrap it pushes a 375px screen into
   sideways scrolling. Scoped with :not(pre) so it can never reach a code
   block, where breaking a line would change what the code says. */
:not(pre) > code {
    overflow-wrap: break-word;
    word-break: break-word;
}

/* The container stops scrolling and stops padding; the <pre> inside takes
   over both. Without this the header bar scrolls sideways out of view along
   with a long line of code. */
.code-block[data-lang] {
    position: relative;
    padding: 0;
    overflow: visible;
    border-radius: 0 6px 6px 0;
}
.code-block[data-lang] > pre {
    margin: 0;
    padding: 0.875rem 1rem 1rem;
    overflow-x: auto;
}
.code-block[data-lang]::before {
    content: attr(data-lang);
    display: flex;
    align-items: center;
    box-sizing: border-box;
    min-height: 1.75rem;
    padding: 0 1rem;
    border-bottom: 1px solid rgba(15, 23, 42, 0.1);
    font-size: var(--fs-12);
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #5f6b7a;
    font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
}
/* `text` is the honest label for scanner output and directory trees, but it
   tells a reader nothing, so it is not worth a row of chrome. */
.code-block[data-lang="text"]::before { content: none; }
.code-block[data-lang="text"] > pre { padding-top: 1rem; }
/* No header bar to sit in, so float clear of the first line of content. */
.code-block[data-lang="text"] .cb-copy,
.code-block:not([data-lang]) .cb-copy { top: 0.35rem; right: 0.35rem; }

/* Height is pinned to the header bar's min-height so the two line up exactly.
   Blocks with no bar (data-lang="text", and the older <br> shape that carries
   no data-lang at all) get the floating placement below instead. */
.cb-copy {
    position: absolute;
    top: 0;
    right: 0.375rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0 0.55rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 5px;
    font: inherit;
    font-size: var(--fs-12);
    font-weight: 600;
    line-height: 1.6;
    color: #5f6b7a;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, border-color 0.15s, color 0.15s;
}
/* Revealed on hover for pointer users, and always visible once focused so a
   keyboard user can find it at all. Touch devices get it permanently via the
   hover:none query below. */
.code-block:hover .cb-copy,
.cb-copy:focus-visible { opacity: 1; }
.cb-copy:hover {
    background: rgba(3, 105, 161, 0.08);
    border-color: rgba(3, 105, 161, 0.35);
    color: var(--secondary-color);
}
.cb-copy:focus-visible {
    outline: 2px solid var(--secondary-color);
    outline-offset: 1px;
}
.cb-copy[data-state="done"] { color: #0f6b3f; opacity: 1; }
/* Touch devices: the button is always visible because there is no hover to
   reveal it, and style.css already gives every button a 44px minimum touch
   target. That is taller than the 28px header bar, so grow the bar to match
   rather than shrink the target below the guideline. */
@media (hover: none) {
    .cb-copy { opacity: 1; height: 44px; }
    .code-block[data-lang]::before { min-height: 44px; }
}

/* Syntax tokens. Ratios against #f5f5f5 (light) and #121c28 (dark) are all
   at or above 4.98:1; see "Colour and contrast" in CLAUDE.md before changing
   one. */
.tok-com   { color: #5f6b7a; font-style: italic; }
.tok-str   { color: #0f6b3f; }
.tok-kw    { color: #7c3aed; font-weight: 600; }
.tok-num   { color: #9a4a06; }
.tok-key   { color: #0e6f74; }
.tok-tag   { color: #b3261e; }
.tok-attr  { color: #7c3aed; }
.tok-punct { color: #4b5563; }

/* A heading's own link, revealed on hover or focus. aria-hidden in the markup
   because the heading text already names the destination for a screen reader. */
.head-anchor {
    margin-left: 0.4rem;
    font-size: 0.75em;
    font-weight: 400;
    color: var(--secondary-color);
    text-decoration: none;
    opacity: 0;
    transition: opacity 0.15s;
}
h2:hover > .head-anchor,
h3:hover > .head-anchor,
.head-anchor:focus-visible { opacity: 1; }
.head-anchor:focus-visible { outline: 2px solid var(--secondary-color); outline-offset: 2px; }

/* Scroll-spy state on the existing table of contents. The sticky sidebar
   treatment at >=1360px is defined further up this file; this only marks the
   entry for the section currently on screen. */
.toc a.is-current {
    font-weight: 700;
    color: var(--secondary-color);
}
/* A background chip rather than a bar in the left margin: the table of
   contents is an <ol>, and an absolutely positioned marker landed on top of
   the list number. */
.toc a.is-current {
    background: rgba(3, 105, 161, 0.09);
    border-radius: 4px;
    box-shadow: 0 0 0 3px rgba(3, 105, 161, 0.09);
}

.to-top {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 60;
    width: 2.75rem;
    height: 2.75rem;
    display: grid;
    place-items: center;
    border: 1px solid var(--border-color);
    border-radius: 50%;
    background: var(--white);
    color: var(--secondary-color);
    font-size: var(--fs-18);
    line-height: 1;
    box-shadow: 0 2px 10px rgba(15, 23, 42, 0.18);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s, visibility 0.18s, transform 0.18s;
}
.to-top.is-visible { opacity: 1; visibility: visible; }
.to-top:hover { transform: translateY(-2px); }
.to-top:focus-visible { outline: 2px solid var(--secondary-color); outline-offset: 2px; }
@media print {
    .to-top, .cb-copy { display: none; }
}

.exercise-controls {
    display: flex;
    justify-content: flex-end;
    margin: 0 0 1rem;
}
.exercise-toggle-all {
    padding: 0.35rem 0.85rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 999px;
    font: inherit;
    font-size: var(--fs-14);
    font-weight: 600;
    color: var(--secondary-color);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.exercise-toggle-all:hover {
    background: rgba(3, 105, 161, 0.08);
    border-color: var(--secondary-color);
}
.exercise-toggle-all:focus-visible {
    outline: 2px solid var(--secondary-color);
    outline-offset: 2px;
}

[data-theme="dark"] .code-block[data-lang]::before {
    border-bottom-color: rgba(226, 232, 240, 0.12);
    color: #93a1b1;
}
[data-theme="dark"] .cb-copy { color: #93a1b1; }
[data-theme="dark"] .cb-copy:hover {
    background: rgba(125, 211, 252, 0.12);
    border-color: rgba(125, 211, 252, 0.45);
    color: #7dd3fc;
}
[data-theme="dark"] .cb-copy[data-state="done"] { color: #9ece6a; }
[data-theme="dark"] .tok-com   { color: #93a1b1; }
[data-theme="dark"] .tok-str   { color: #9ece6a; }
[data-theme="dark"] .tok-kw    { color: #c9a3ff; }
[data-theme="dark"] .tok-num   { color: #ffb86c; }
[data-theme="dark"] .tok-key   { color: #7dd3fc; }
[data-theme="dark"] .tok-tag   { color: #ff8f8f; }
[data-theme="dark"] .tok-attr  { color: #c9a3ff; }
[data-theme="dark"] .tok-punct { color: #aab8c8; }
[data-theme="dark"] .head-anchor { color: #7dd3fc; }
[data-theme="dark"] .toc a.is-current {
    color: #7dd3fc;
    background: rgba(125, 211, 252, 0.14);
    box-shadow: 0 0 0 3px rgba(125, 211, 252, 0.14);
}
[data-theme="dark"] .to-top {
    background: #1a2535;
    border-color: #2d3f55;
    color: #7dd3fc;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
}
[data-theme="dark"] .exercise-toggle-all {
    background: #1e2a3a;
    border-color: #2d3f55;
    color: #7dd3fc;
}
[data-theme="dark"] .exercise-toggle-all:hover {
    background: rgba(125, 211, 252, 0.12);
    border-color: #7dd3fc;
}

/* =========================================================================
   Homepage hero: brand + one lede + one CTA + illustration. It is sized to
   its content, not to the viewport, so the doors start on the first screen.
   The bottom padding keeps the CTA clear of the .hero::after fade.
   ========================================================================= */
.hero--home {
    padding: 2.5rem 2rem 4.5rem;
    text-align: left;
}
/* .container's content box (the frame minus its 2rem side padding), so the
   hero's left edge lines up with the header logo and the doors below. */
.hero--home .hero-inner {
    max-width: calc(var(--frame) - 4rem);
    margin: 0 auto;
    width: 100%;
    display: grid;
    grid-template-columns: 1.15fr 0.85fr;
    gap: 2.5rem;
    align-items: center;
    position: relative;
    z-index: 1;
}
.hero--home .hero-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    margin: 0 0 1rem;
    font-family: var(--font-display);
    font-size: clamp(2.25rem, 4vw, 3rem);
    font-weight: 700;
    letter-spacing: -0.04em;
    line-height: 1;
    color: #fff;
}
.hero--home .hero-brand svg {
    width: clamp(2rem, 3.5vw, 2.75rem);
    height: clamp(2rem, 3.5vw, 2.75rem);
    flex-shrink: 0;
}
.hero--home h1 {
    font-size: clamp(1.35rem, 2.4vw, 1.75rem);
    font-weight: 600;
    letter-spacing: -0.015em;
    margin: 0 0 0.85rem;
    color: #e0f2fe;
}
.hero--home .hero-lede {
    font-size: clamp(1.05rem, 1.4vw, 1.2rem);
    line-height: 1.55;
    max-width: 36rem;
    margin: 0 0 1.5rem;
    color: #cbd5e1;
    opacity: 1;
}
.hero--home .cta-buttons {
    justify-content: flex-start;
}
.hero--home .hero-illustration {
    max-width: 300px;
    justify-self: end;
    animation: hero-illustration-in 0.9s ease-out both;
}
.hero--home .hero-text {
    animation: hero-text-in 0.7s ease-out both;
}
@keyframes hero-text-in {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: none; }
}
@keyframes hero-illustration-in {
    from { opacity: 0; transform: translateY(16px) scale(0.98); }
    to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .hero--home .hero-text,
    .hero--home .hero-illustration { animation: none; }
}
@media (max-width: 880px) {
    .hero--home {
        padding: 2.5rem 1.25rem 3.25rem;
        text-align: center;
    }
    .hero--home .hero-inner {
        grid-template-columns: 1fr;
        gap: 1.75rem;
    }
    .hero--home .hero-brand { justify-content: center; }
    .hero--home .hero-lede { margin-left: auto; margin-right: auto; }
    .hero--home .cta-buttons { justify-content: center; }
    .hero--home .hero-illustration {
        max-width: 240px;
        margin: 0 auto;
        justify-self: center;
    }
}
@media (max-width: 768px) {
    .hero--home .hero-illustration { display: none; }
    .hero--home { padding: 2rem 1rem 2.5rem; }
    .hero--home .hero-lede { font-size: var(--fs-17); margin-bottom: 1.15rem; }
}

.theme-toggle svg {
    width: 1.15rem;
    height: 1.15rem;
    display: block;
}
.theme-toggle .theme-icon-sun { display: none; }
.theme-toggle[data-theme-icon="light"] .theme-icon-moon { display: none; }
.theme-toggle[data-theme-icon="light"] .theme-icon-sun { display: block; }

[data-theme="dark"] .hero::after {
    background: linear-gradient(to bottom, transparent, var(--light-bg));
}
[data-theme="dark"] .home-page .section h2 {
    border-bottom: none;
}

/* ==== BEGIN generated: system-dark branch (tools/sync_dark_branch.py) ====
   Mirrors every [data-theme="dark"] rule onto :root:not([data-theme]) so a
   dark-OS visitor gets the same colours at first paint that the toggle gives
   them after main.js runs. Do not hand-edit - run tools/sync_dark_branch.py.
   To make the two branches differ on purpose, add the rule to the @media
   block above by hand; anything already there is skipped here. ==== */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .filter-bar { background: #1a2535; border-color: #2d3f55; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); }
    :root:not([data-theme]) #searchInput,
    :root:not([data-theme]) #meetingSearch { background: #1e2a3a; border-color: #2d3f55; color: var(--text-color); }
    :root:not([data-theme]) #searchInput:focus,
    :root:not([data-theme]) #meetingSearch:focus { border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); background: #162030; }
    :root:not([data-theme]) .glossary-count { color: #aaa; }
    :root:not([data-theme]) .glossary-list dd a.glossary-link { color: #7cc1ee; }
    :root:not([data-theme]) .glossary-list dd a.glossary-link:hover,
    :root:not([data-theme]) .glossary-list dd a.glossary-link:focus { background-color: rgba(124, 193, 238, 0.18); }
    :root:not([data-theme]) a.glossary-link:focus,
    :root:not([data-theme]) a.glossary-link:hover { background-color: rgba(124, 193, 238, 0.18); }
    :root:not([data-theme]) .glossary-list dt:target { background-color: rgba(241, 196, 15, 0.28); }
    :root:not([data-theme]) .glossary-list dt { color: #7cc1ee; }
    :root:not([data-theme]) .glossary-list dd { color: var(--text-color); }
    :root:not([data-theme]) .author-card .author-card__kicker,
    :root:not([data-theme]) .author-card .author-card__bio { color: #94a3b8; }
    :root:not([data-theme]) .source-filter-label { color: #94a3b8; }
    :root:not([data-theme]) .news-date-heading .news-date-count { color: rgba(255, 255, 255, 0.85); }
    :root:not([data-theme]) .resource-grid--compact .resource-card p { color: #cbd5e1; }
    :root:not([data-theme]) .section h2.featured-subhead { color: #94a3b8; }
    :root:not([data-theme]) .card-action { color: #7dd3fc; }
    :root:not([data-theme]) .card-action:hover,
    :root:not([data-theme]) .card-action:focus { color: #bae6fd; }
    :root:not([data-theme]) .testimonial-grid .community-card { background: transparent; border-left-color: transparent; }
    :root:not([data-theme]) .testimonial-grid .community-card .community-heading { color: #cbd5e1; }
    :root:not([data-theme]) .testimonial-grid .community-card p:first-child { color: #e2e8f0; }
    :root:not([data-theme]) .deck-embed > summary { color: #7dd3fc; }
    :root:not([data-theme]) .tag.tag-live { background-color: #022c22; color: #6ee7b7; }
    :root:not([data-theme]) body a.door-card,
    :root:not([data-theme]) body a.door-card:hover { color: var(--text-color); }
    :root:not([data-theme]) body a.door-card h2 { color: var(--primary-color); }
    :root:not([data-theme]) body a.door-card .door-kicker,
    :root:not([data-theme]) .door-card .door-kicker { color: #fbbf24; }
    :root:not([data-theme]) body a.door-card .door-go { color: var(--secondary-color); }
    :root:not([data-theme]) .resource-card h3,
    :root:not([data-theme]) .section h2,
    :root:not([data-theme]) .section h3,
    :root:not([data-theme]) .category-summary h2,
    :root:not([data-theme]) .resource-card h3 a { color: var(--secondary-color); }
    :root:not([data-theme]) .contribute-article h2 { color: #7dccff; border-bottom-color: #30445a; }
    :root:not([data-theme]) .memorial-content h2 { color: #7dccff; }
    :root:not([data-theme]) .contribution-type h4 { color: #7dccff; }
    :root:not([data-theme]) .resource-card p,
    :root:not([data-theme]) .article-date,
    :root:not([data-theme]) .no-results { color: #c8cfd8; }
    :root:not([data-theme]) .resource-card p.conf-next,
    :root:not([data-theme]) .resource-card p.conf-next strong { color: #7dccff; }
    :root:not([data-theme]) .resource-list a { color: var(--secondary-color); }
    :root:not([data-theme]) .resource-list .description { color: #b8c2cc; }
    :root:not([data-theme]) .info-box p { color: #c8cfd8; }
    :root:not([data-theme]) .text-muted { color: #a0adb8; }
    :root:not([data-theme]) .date,
    :root:not([data-theme]) .memorial-subtitle { color: #a0adb8; }
    :root:not([data-theme]) .highlight-box strong { color: #7dccff; }
    :root:not([data-theme]) .faq-item strong { color: #7dccff; }
    :root:not([data-theme]) .link-box a { color: var(--secondary-color); }
    :root:not([data-theme]) .cta-link { color: var(--secondary-color); }
    :root:not([data-theme]) .error-code { color: var(--secondary-color); }
    :root:not([data-theme]) .error-message { color: var(--text-color); }
    :root:not([data-theme]) .tag { background-color: #1e2e3e; color: #dce2e8; }
    :root:not([data-theme]) .tag.ctf { background-color: #3d2200; color: #ffc964; }
    :root:not([data-theme]) .tag.certification { background-color: #0d2744; color: #7ecbff; }
    :root:not([data-theme]) .tag.tool { background-color: #2a0845; color: #dea8ee; }
    :root:not([data-theme]) .tag.lab { background-color: #3b0d1e; color: #ffa4c4; }
    :root:not([data-theme]) .tag.job { background-color: #042f2e; color: #5eead4; }
    :root:not([data-theme]) .tag.ai-security { background-color: #1e1b4b; color: #c4b5fd; }
    :root:not([data-theme]) .tag.newsletter { background-color: #3a2f04; color: #fde047; }
    :root:not([data-theme]) .tag.github { background-color: #2e1065; color: #ddd6fe; }
    :root:not([data-theme]) .tag.new { background-color: #1b3a1b; color: #95db98; }
    :root:not([data-theme]) .tag.tag-other { background-color: #5D7987; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-news { background-color: #496785; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-youtube { background-color: #c4302b; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-github { background-color: #5A6673; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-owasp { background-color: #656565; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-wikipedia { background-color: #636466; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-vendor { background-color: #9526D9; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-cisa { background-color: #1F68B5; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-discord { background-color: #5865F2; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-azure { background-color: #0078D4; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-training { background-color: #E01D5F; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-gcp { background-color: #3972D2; color: #ffffff; }
    :root:not([data-theme]) .tag.tag-wiz { background-color: #00C2A2; color: #1f2937; }
    :root:not([data-theme]) .tag.tag-aws { background-color: #FF9900; color: #1f2937; }
    :root:not([data-theme]) .tag.tag-spotify { background-color: #1DB954; color: #1f2937; }
    :root:not([data-theme]) .tag.tag-jobs { background-color: #ff9800; color: #1f2937; }
    :root:not([data-theme]) .tag.tag-csoh { background-color: #3498db; color: #1f2937; }
    :root:not([data-theme]) .filter-btn { background-color: var(--white); border-color: var(--border-color); color: var(--text-color); }
    :root:not([data-theme]) .filter-btn:hover,
    :root:not([data-theme]) .filter-btn.active { background-color: var(--secondary-color); color: #0f1419; border-color: var(--secondary-color); }
    :root:not([data-theme]) .step-number { color: #0b1729; }
    :root:not([data-theme]) .btn-secondary { background-color: #1e2e3e; color: var(--text-color); }
    :root:not([data-theme]) .btn-secondary:hover { background-color: #2a3d52; }
    :root:not([data-theme]) .show-all-btn { background-color: #1a2535; border-color: var(--secondary-color); color: var(--text-color); }
    :root:not([data-theme]) .show-all-btn:hover { background-color: var(--secondary-color); color: #fff; }
    :root:not([data-theme]) nav a,
    :root:not([data-theme]) .dropdown-toggle,
    :root:not([data-theme]) .dropdown-menu a { color: #f5f7fa; }
    :root:not([data-theme]) .dropdown-menu { background-color: #1c2530; border: 1px solid rgba(255, 255, 255, 0.08); }
    :root:not([data-theme]) .hamburger { color: #f5f7fa; }
    :root:not([data-theme]) .theme-toggle { color: #f5f7fa; }
    :root:not([data-theme]) .skip-link { background: #c2d9ec; color: #0b1729; }
    :root:not([data-theme]) .hero :is(p, li) a:not([class]),
    :root:not([data-theme]) .hero :is(p, li) a.glossary-link { color: var(--secondary-color); }
    :root:not([data-theme]) .hero .btn-primary,
    :root:not([data-theme]) .hero--compact .btn-primary { background-color: #0ea5e9; color: #0b1729 !important; }
    :root:not([data-theme]) .footer-section h3 { color: #f0f3f7; }
    :root:not([data-theme]) .footer-section a:hover { color: #ffffff; }
    :root:not([data-theme]) .footer-section a.footer-coffee { color: #1a202c; }
    :root:not([data-theme]) .footer-section a.footer-coffee:hover,
    :root:not([data-theme]) .footer-section a.footer-coffee:focus { color: #1a202c; }
    :root:not([data-theme]) .source { color: var(--secondary-color); }
    :root:not([data-theme]) .breadcrumb { color: #a0adb8; }
    :root:not([data-theme]) .breadcrumb a { color: var(--secondary-color); }
    :root:not([data-theme]) .breadcrumb-nav { background-color: #0b1018; }
    :root:not([data-theme]) .breadcrumb-nav ol { color: rgba(245, 247, 250, 0.7); }
    :root:not([data-theme]) .breadcrumb-nav a { color: rgba(245, 247, 250, 0.85); }
    :root:not([data-theme]) .breadcrumb-nav a:hover { background-color: rgba(255, 255, 255, 0.1); color: #ffffff; }
    :root:not([data-theme]) .breadcrumb-nav [aria-current="page"] { color: #ffffff; }
    :root:not([data-theme]) .breadcrumb-nav li + li::before { color: rgba(245, 247, 250, 0.35); }
    :root:not([data-theme]) .code-block { background-color: #121c28; border-left-color: var(--secondary-color); color: #dce2e8; }
    :root:not([data-theme]) .highlight-box { background-color: #152535; border-left-color: #3a8ec2; color: #eaf6ff; font-weight: 500; }
    :root:not([data-theme]) .toc { background-color: #141e2a; border-left-color: var(--secondary-color); }
    :root:not([data-theme]) .toc h3 { color: #7dccff; }
    :root:not([data-theme]) .toc a { color: var(--secondary-color); }
    :root:not([data-theme]) .contribution-type { color: #eaf6ff; font-weight: 500; background-color: #1a2535; border-color: #2a3d52; }
    :root:not([data-theme]) .faq-item { background-color: #1a2535; border-left-color: var(--secondary-color); }
    :root:not([data-theme]) .step-box { background-color: #1a2535; border-color: #2a3d52; }
    :root:not([data-theme]) .contribution-type .examples { background-color: #2a2510; }
    :root:not([data-theme]) .contribution-type .have-idea { background-color: #152535; }
    :root:not([data-theme]) .link-box { background-color: #221e0e; border-left-color: #b8950a; }
    :root:not([data-theme]) .tag-example { background-color: #2a3d52; color: #dce2e8; }
    :root:not([data-theme]) .info-box--blue { background-color: #152535; border-left-color: #3a8ec2; }
    :root:not([data-theme]) .info-box--yellow { background-color: #2a2510; border-left-color: #b8950a; }
    :root:not([data-theme]) .cta-section { background-color: #141e2a; }
    :root:not([data-theme]) .wired-card { background: linear-gradient(135deg, #1a2535 0%, #1e2e3e 100%); border-color: #c04015; }
    :root:not([data-theme]) .wired-card:hover { box-shadow: 0 8px 24px rgba(200, 60, 15, 0.25); }
    :root:not([data-theme]) .wired-card-label { color: #e8763a; }
    :root:not([data-theme]) .wired-card-title { color: #f0f3f7; }
    :root:not([data-theme]) .wired-card-desc { color: #b8c2cc; }
    :root:not([data-theme]) input[type="text"] { background-color: #1a2535; color: var(--text-color); border-color: var(--border-color); }
    :root:not([data-theme]) .category-summary:hover { background-color: #1e2e3e; }
    :root:not([data-theme]) .search-suggestions { background-color: #1c2530; border-color: #30445a; }
    :root:not([data-theme]) .search-suggestion:hover,
    :root:not([data-theme]) .search-suggestion.highlighted { background-color: #243242; }
    :root:not([data-theme]) .search-suggestion .suggestion-type { background-color: #243242; color: #b8c2cc; }
    :root:not([data-theme]) .share-btn,
    :root:not([data-theme]) body a.share-btn { color: #fff; }
    :root:not([data-theme]) .resource-tooltip { background-color: #1c2530; color: #e8ecf2; border-color: #30445a; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); }
    :root:not([data-theme]) .mega-heading { color: rgba(255, 255, 255, 0.7); border-bottom-color: rgba(255, 255, 255, 0.1); }
    :root:not([data-theme]) .nav-cta { color: #1a202c !important; }
    :root:not([data-theme]) .meeting-page { background: #0d1424; border-color: #1e293b; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }
    :root:not([data-theme]) .meeting-page h2 { color: #e2e8f0; }
    :root:not([data-theme]) .meeting-page h2 time { color: #38bdf8; }
    :root:not([data-theme]) .meeting-page > h2 + p { background: rgba(56, 189, 248, 0.06); border-left-color: #38bdf8; color: #cbd5e1; }
    :root:not([data-theme]) .meeting-page > h2 + p > strong:first-child { color: #38bdf8; }
    :root:not([data-theme]) .meeting-page .meeting-topics { border-top-color: rgba(255, 255, 255, 0.08); }
    :root:not([data-theme]) .meeting-page .meeting-topics h3 { color: #e2e8f0; }
    :root:not([data-theme]) .meeting-page .meeting-topics > p { color: #cbd5e1; }
    :root:not([data-theme]) .meeting-page > p.small { border-top-color: rgba(255, 255, 255, 0.08); }
    :root:not([data-theme]) .meeting-page > p.small a { background: rgba(255, 255, 255, 0.03); border-color: rgba(255, 255, 255, 0.12); color: #38bdf8; }
    :root:not([data-theme]) .meeting-page > p.small a:hover { background: rgba(56, 189, 248, 0.08); border-color: #38bdf8; }
    :root:not([data-theme]) main > .incident-pager .pager-link { background: #0d1424; color: #38bdf8; border-color: #1e293b; }
    :root:not([data-theme]) main > .incident-pager .pager-link:hover { background: rgba(56, 189, 248, 0.08); border-color: #38bdf8; }
    :root:not([data-theme]) .meeting-card { background: #0d1424; border-color: #1e293b; border-left-color: #38bdf8; }
    :root:not([data-theme]) .meeting-card:hover { border-left-color: #f87171; }
    :root:not([data-theme]) .meeting-card-link h2 { color: #e2e8f0; }
    :root:not([data-theme]) .meeting-card-link h2 time { color: #94a3b8; }
    :root:not([data-theme]) .meeting-card-summary { color: #94a3b8; }
    :root:not([data-theme]) .meeting-card-cta { color: #38bdf8; }
    :root:not([data-theme]) .meeting-count { color: #94a3b8; }
    :root:not([data-theme]) .page-meta { color: #94a3b8; border-bottom-color: #1e293b; }
    :root:not([data-theme]) .page-meta strong { color: #e2e8f0; }
    :root:not([data-theme]) .page-meta a { color: #38bdf8; }
    :root:not([data-theme]) table td[style*="background"] { color: #1e293b; }
    :root:not([data-theme]) table th { color: #e2e8f0; }
    :root:not([data-theme]) .pillar-card { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.10); }
    :root:not([data-theme]) .pillar-card h3 { color: #e2e8f0; }
    :root:not([data-theme]) .pillar-card p { color: #94a3b8; }
    :root:not([data-theme]) .threat-tile { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.10); }
    :root:not([data-theme]) .threat-tile h4 { color: #e2e8f0; }
    :root:not([data-theme]) .threat-tile p { color: #94a3b8; }
    :root:not([data-theme]) .signature--final { color: #94a3b8; }
    :root:not([data-theme]) .lede { color: #e2e8f0; }
    :root:not([data-theme]) .contribute-article section > h2::before { background: linear-gradient(90deg, #38bdf8, transparent); }
    :root:not([data-theme]) .pull-quote { background: linear-gradient(135deg, rgba(56,189,248,0.10) 0%, transparent 100%); border-left-color: #38bdf8; color: #e2e8f0; }
    :root:not([data-theme]) .pull-quote::before { color: #38bdf8; }
    :root:not([data-theme]) .pull-quote cite { color: #94a3b8; }
    :root:not([data-theme]) .page-photo { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.10); }
    :root:not([data-theme]) .page-photo figcaption { color: #94a3b8; background: rgba(255,255,255,0.02); }
    :root:not([data-theme]) .killchain-figure,
    :root:not([data-theme]) .arch-figure,
    :root:not([data-theme]) .workflow-figure { background: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 100%); border-color: rgba(255,255,255,0.10); }
    :root:not([data-theme]) .killchain-figure figcaption,
    :root:not([data-theme]) .arch-figure figcaption,
    :root:not([data-theme]) .workflow-figure figcaption { color: #94a3b8; }
    :root:not([data-theme]) .cnapp-figure { background: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 100%); border-color: rgba(255,255,255,0.10); }
    :root:not([data-theme]) .cnapp-figure figcaption { color: #94a3b8; }
    :root:not([data-theme]) .diagram-figure { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.12); }
    :root:not([data-theme]) .comparison-table { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.12); box-shadow: 0 1px 3px rgba(0,0,0,0.4); }
    :root:not([data-theme]) .comparison-table thead th { background: linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0.02) 100%); color: #e2e8f0; border-bottom-color: rgba(255,255,255,0.18); }
    :root:not([data-theme]) .comparison-table tbody th { background: rgba(255,255,255,0.04); color: #e2e8f0; border-right-color: rgba(255,255,255,0.08); border-bottom-color: rgba(255,255,255,0.06); }
    :root:not([data-theme]) .comparison-table tbody td { color: #cbd5e1; border-bottom-color: rgba(255,255,255,0.06); }
    :root:not([data-theme]) .comparison-table tbody tr:nth-child(even) td { background: rgba(255,255,255,0.02); }
    :root:not([data-theme]) .comparison-table tbody tr:nth-child(even) th { background: rgba(255,255,255,0.05); }
    :root:not([data-theme]) .comparison-table tbody tr:hover td,
    :root:not([data-theme]) .comparison-table tbody tr:hover th { background: rgba(56, 189, 248, 0.10); }
    :root:not([data-theme]) .recap-subscribe { background: #1a2535; border-color: #2d3f55; border-left-color: var(--accent-color); }
    :root:not([data-theme]) .recap-subscribe p { color: #cbd5e1; }
    :root:not([data-theme]) .session-echo { border-top-color: #2d3f55; }
    :root:not([data-theme]) .session-echo-item { background: #1a2535; border-color: #2d3f55; }
    :root:not([data-theme]) .session-echo-link time { color: #7dd3fc; }
    :root:not([data-theme]) .session-echo-title { color: #f1f5f9; }
    :root:not([data-theme]) .session-echo-summary { color: #cbd5e1; }
    :root:not([data-theme]) .next-session { background: #1a2535; border-color: #2d3f55; border-left-color: #38bdf8; }
    :root:not([data-theme]) .next-session__when { color: #f1f5f9; }
    :root:not([data-theme]) .next-session__note { color: #cbd5e1; }
    :root:not([data-theme]) .next-session__unit { background: #22304a; border-color: #2d3f55; }
    :root:not([data-theme]) .next-session__unit-num { color: #f1f5f9; }
    :root:not([data-theme]) .next-session__unit-label { color: #cbd5e1; }
    :root:not([data-theme]) .next-session__live { color: #4ade80; }
    :root:not([data-theme]) .stat-strip { background: #1a2535; border-color: #2d3f55; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); }
    :root:not([data-theme]) .stat-strip__item { border-right-color: #2d3f55; }
    :root:not([data-theme]) .stat-strip__item:last-child { border-right: none; }
    :root:not([data-theme]) .stat-strip__num { color: #7dd3fc; }
    :root:not([data-theme]) .stat-strip__label { color: #cbd5e1; }
    :root:not([data-theme]) .diagram-figure figcaption { color: #94a3b8; }
    :root:not([data-theme]) .exercise { background-color: #1a2535; border-color: #2d3f55; border-left-color: var(--accent-color); }
    :root:not([data-theme]) .exercise__label { color: #7dd3fc; }
    :root:not([data-theme]) .exercise summary { background: #1e2a3a; border-color: #2d3f55; color: #7dd3fc; }
    :root:not([data-theme]) .exercise summary:hover,
    :root:not([data-theme]) .exercise summary:focus-visible { background: rgba(125, 211, 252, 0.12); border-color: #7dd3fc; }
    :root:not([data-theme]) .code-block[data-lang]::before { border-bottom-color: rgba(226, 232, 240, 0.12); color: #93a1b1; }
    :root:not([data-theme]) .cb-copy { color: #93a1b1; }
    :root:not([data-theme]) .cb-copy:hover { background: rgba(125, 211, 252, 0.12); border-color: rgba(125, 211, 252, 0.45); color: #7dd3fc; }
    :root:not([data-theme]) .cb-copy[data-state="done"] { color: #9ece6a; }
    :root:not([data-theme]) .tok-com { color: #93a1b1; }
    :root:not([data-theme]) .tok-str { color: #9ece6a; }
    :root:not([data-theme]) .tok-kw { color: #c9a3ff; }
    :root:not([data-theme]) .tok-num { color: #ffb86c; }
    :root:not([data-theme]) .tok-key { color: #7dd3fc; }
    :root:not([data-theme]) .tok-tag { color: #ff8f8f; }
    :root:not([data-theme]) .tok-attr { color: #c9a3ff; }
    :root:not([data-theme]) .tok-punct { color: #aab8c8; }
    :root:not([data-theme]) .head-anchor { color: #7dd3fc; }
    :root:not([data-theme]) .toc a.is-current { color: #7dd3fc; background: rgba(125, 211, 252, 0.14); box-shadow: 0 0 0 3px rgba(125, 211, 252, 0.14); }
    :root:not([data-theme]) .to-top { background: #1a2535; border-color: #2d3f55; color: #7dd3fc; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); }
    :root:not([data-theme]) .exercise-toggle-all { background: #1e2a3a; border-color: #2d3f55; color: #7dd3fc; }
    :root:not([data-theme]) .exercise-toggle-all:hover { background: rgba(125, 211, 252, 0.12); border-color: #7dd3fc; }
    :root:not([data-theme]) .hero::after { background: linear-gradient(to bottom, transparent, var(--light-bg)); }
    :root:not([data-theme]) .home-page .section h2 { border-bottom: none; }
}

@media (prefers-color-scheme: dark) and (max-width: 1023px) {
    :root:not([data-theme]) .dropdown-menu { border-top-width: 0; border-bottom-width: 0; }
}

@media (prefers-color-scheme: dark) and (max-width: 640px) {
    :root:not([data-theme]) .stat-strip__item { border-bottom-color: #2d3f55; }
}

/* ==== END generated: system-dark branch ==== */
