/* ==========================================================================
   Projects — the hub's long-form reading surface.

   Everything colour-ish keys off two custom properties, --accent and
   --accent-alt, which each project sets inline from its project.json. That is
   the whole theming mechanism: a new project picks two colours and the cards,
   glows, rules and links follow.

   LIGHT AND DARK. Every non-accent colour is a token, declared dark-first on
   :root and re-declared under [data-bs-theme="light"]. Nothing below this
   block may hardcode a colour — a literal here is a bug that only shows up in
   one theme, which is exactly the kind nobody notices.

   The one subtlety: --accent and --accent-alt are set INLINE on the project
   element, so a token defined on :root as color-mix(... var(--accent) ...)
   would resolve against :root's default accent, not the project's. Hence
   --accent-mix-target: the mix stays at the use site and only the thing being
   mixed towards switches per theme.
   ========================================================================== */

:root {
    --accent: #7c5cff;
    --accent-alt: #22d3ee;

    --surface: #16181d;
    --surface-2: #1c1f26;
    --hairline: rgba(255, 255, 255, .09);
    --hairline-strong: rgba(255, 255, 255, .16);
    --ink: #dfe4ec;
    --ink-strong: #ffffff;
    --ink-soft: #cfd5e1;
    --ink-quote: #c7cedb;
    --ink-dim: #9aa3b2;
    --ink-dimmer: #6b7385;
    --wash: rgba(255, 255, 255, .04);
    --wash-head: rgba(255, 255, 255, .045);
    --wash-row: rgba(255, 255, 255, .022);
    --wash-code: rgba(255, 255, 255, .07);
    --code-bg: #101216;
    --code-ink: #cbd3e1;
    --code-inline-ink: #e8d9ff;
    --accent-mix-target: #ffffff;
}

[data-bs-theme="light"] {
    --surface: #ffffff;
    --surface-2: #f6f7f9;
    --hairline: rgba(17, 20, 26, .13);
    --hairline-strong: rgba(17, 20, 26, .24);
    --ink: #23272f;
    --ink-strong: #0d1015;
    --ink-soft: #3d434e;
    --ink-quote: #333842;
    --ink-dim: #4d5462;
    --ink-dimmer: #6b7280;
    --wash: rgba(17, 20, 26, .04);
    --wash-head: rgba(17, 20, 26, .05);
    --wash-row: rgba(17, 20, 26, .025);
    --wash-code: rgba(17, 20, 26, .06);
    --code-bg: #f4f5f7;
    --code-ink: #2c313a;
    --code-inline-ink: #5b21b6;
    --accent-mix-target: #000000;
}

/* --------------------------------------------------------------- shared -- */

.eyebrow {
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-dimmer);
}

.accent-text {
    background: linear-gradient(100deg, var(--accent), var(--accent-alt));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* background-clip: text paints the gradient only inside the element's own
       box, so anything the glyphs put below the baseline gets sheared off. At
       heading sizes a 1.2 line-height is not enough box, and the descenders of
       g, j and y lose their tails — invisible in English titles like "Crete",
       obvious the moment a Hungarian one says "legjobb". Give the box room
       here rather than patching each heading. */
    line-height: 1.3;
    padding-bottom: .08em;
    /* Same problem on the other axis: any negative letter-spacing set on a
       heading that uses this class is applied after the final character as
       well, so the box loses that width and the last glyph gets clipped.
       .04em covers the tracking these headings actually use. */
    padding-right: .04em;
}

.pill {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .22rem .7rem;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    font-size: .78rem;
    color: var(--ink-dim);
    white-space: nowrap;
}

.pill-accent {
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    color: color-mix(in srgb, var(--accent) 75%, var(--accent-mix-target));
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.pill .dot {
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* ---------------------------------------------------------- project card -- */

.project-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 1.15rem;
}

.project-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 1.5rem 1.5rem 1.25rem;
    border: 1px solid var(--hairline);
    border-radius: 1rem;
    background:
        radial-gradient(120% 100% at 0% 0%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 55%),
        var(--surface);
    text-decoration: none;
    color: inherit;
    overflow: hidden;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}

/* The gradient hairline along the top edge is the project's signature. */
.project-card::before,
.project-hero::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-alt), transparent 85%);
}

.project-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--accent) 40%, var(--hairline));
    box-shadow: 0 14px 40px -18px color-mix(in srgb, var(--accent) 55%, transparent);
}

.project-card h2 {
    font-size: 1.35rem;
    margin: .6rem 0 .35rem;
    letter-spacing: -.01em;
}

.project-card .tagline {
    color: var(--ink-dim);
    font-size: .93rem;
    line-height: 1.5;
    margin-bottom: 1.1rem;
}

.project-card .meta {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem;
    margin-top: auto;
}

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

.project-hero {
    position: relative;
    border: 1px solid var(--hairline);
    border-radius: 1.15rem;
    padding: 2.4rem 2.2rem 2rem;
    margin-bottom: 2.25rem;
    overflow: hidden;
    background:
        radial-gradient(90% 130% at 8% -10%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 60%),
        radial-gradient(70% 120% at 100% 0%, color-mix(in srgb, var(--accent-alt) 12%, transparent), transparent 55%),
        var(--surface);
}

.project-hero h1 {
    font-size: clamp(1.9rem, 3.4vw, 2.7rem);
    font-weight: 600;
    letter-spacing: -.025em;
    margin: .5rem 0 .6rem;
}

.project-hero .tagline {
    font-size: 1.05rem;
    color: var(--ink-soft);
    max-width: 62ch;
    margin-bottom: 1.1rem;
}

.project-hero .summary {
    color: var(--ink-dim);
    max-width: 78ch;
    line-height: 1.65;
    margin-bottom: 1.4rem;
}

.project-hero .meta {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* ------------------------------------------------------------- doc cards -- */

.doc-section + .doc-section {
    margin-top: 2.1rem;
}

.doc-section-head {
    display: flex;
    align-items: center;
    gap: .9rem;
    margin-bottom: .9rem;
}

.doc-section-head::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--hairline);
}

.doc-list {
    display: grid;
    gap: .6rem;
}

.doc-item {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: .95rem 1.15rem;
    border: 1px solid var(--hairline);
    border-radius: .8rem;
    background: var(--surface);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, background .15s ease, transform .15s ease;
}

.doc-item:hover {
    border-color: color-mix(in srgb, var(--accent) 38%, var(--hairline));
    background: var(--surface-2);
    transform: translateX(3px);
}

.doc-item .idx {
    flex: 0 0 auto;
    width: 1.7rem;
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
    color: var(--ink-dimmer);
    padding-top: .15rem;
}

.doc-item .title {
    font-weight: 500;
    margin-bottom: .18rem;
}

.doc-item .excerpt {
    font-size: .86rem;
    color: var(--ink-dim);
    line-height: 1.5;
}

.doc-item.is-index {
    background:
        linear-gradient(100deg, color-mix(in srgb, var(--accent) 13%, transparent), transparent 45%),
        var(--surface);
    border-color: color-mix(in srgb, var(--accent) 30%, var(--hairline));
}

/* ---------------------------------------------------------------- reader -- */

.reading-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 0;
    z-index: 1080;
    background: linear-gradient(90deg, var(--accent), var(--accent-alt));
    transition: width .1s linear;
}

.reader {
    max-width: 1680px;
}

.reader-crumbs {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .85rem;
    color: var(--ink-dimmer);
    margin-bottom: 1.4rem;
}

.reader-crumbs a {
    color: var(--ink-dim);
    text-decoration: none;
}

.reader-crumbs a:hover {
    color: var(--ink-strong);
}

.reader-title {
    font-size: clamp(1.6rem, 2.6vw, 2.15rem);
    font-weight: 600;
    letter-spacing: -.02em;
    margin-bottom: 1.6rem;
    padding-bottom: 1.1rem;
    border-bottom: 1px solid var(--hairline);
}

/* Contents rail */

.rail {
    position: sticky;
    top: 1.2rem;
    max-height: calc(100vh - 2.4rem);
    overflow-y: auto;
    padding-left: .9rem;
    border-left: 1px solid var(--hairline);
    font-size: .845rem;
    scrollbar-width: thin;
    scrollbar-color: var(--hairline-strong) transparent;
}

.rail::-webkit-scrollbar { width: 6px; }
.rail::-webkit-scrollbar-thumb {
    background: var(--hairline-strong);
    border-radius: 3px;
}

.rail ul {
    list-style: none;
    padding-left: .8rem;
    margin: 0;
}

.rail > ul { padding-left: 0; }

.rail li { margin: .12rem 0; }

.rail a {
    display: block;
    padding: .2rem .5rem;
    margin-left: -.5rem;
    border-radius: .35rem;
    color: var(--ink-dimmer);
    text-decoration: none;
    line-height: 1.4;
    transition: color .12s ease, background .12s ease;
}

.rail a:hover {
    color: var(--ink-strong);
    background: var(--wash);
}

.rail a.active {
    color: var(--ink-strong);
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    box-shadow: inset 2px 0 0 var(--accent);
}

/* Prev / next */

.reader-nav {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .8rem;
    margin-top: 3rem;
    padding-top: 1.6rem;
    border-top: 1px solid var(--hairline);
}

.reader-nav a {
    padding: .9rem 1.1rem;
    border: 1px solid var(--hairline);
    border-radius: .8rem;
    background: var(--surface);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, background .15s ease;
}

.reader-nav a:hover {
    border-color: color-mix(in srgb, var(--accent) 38%, var(--hairline));
    background: var(--surface-2);
}

.reader-nav .next { text-align: right; }

.reader-nav .label {
    font-size: .72rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-dimmer);
    display: block;
    margin-bottom: .2rem;
}

/* ------------------------------------------------------------- markdown -- */

.prose {
    line-height: 1.72;
    font-size: 1rem;
    color: var(--ink);
}

.prose > *:first-child { margin-top: 0; }

.prose h1, .prose h2, .prose h3, .prose h4 {
    color: var(--ink-strong);
    font-weight: 600;
    letter-spacing: -.015em;
    line-height: 1.3;
    margin: 2.4rem 0 .85rem;
    scroll-margin-top: 1.2rem;
}

.prose h1 { font-size: 1.55rem; }

.prose h2 {
    font-size: 1.28rem;
    padding-bottom: .45rem;
    border-bottom: 1px solid var(--hairline);
}

.prose h3 { font-size: 1.08rem; }

.prose h4 {
    font-size: .95rem;
    color: var(--ink-dim);
    text-transform: none;
}

.prose p, .prose ul, .prose ol { margin-bottom: 1.05rem; }

.prose li { margin-bottom: .4rem; }

.prose li::marker { color: var(--ink-dimmer); }

.prose a {
    color: color-mix(in srgb, var(--accent-alt) 80%, var(--accent-mix-target));
    text-decoration-color: color-mix(in srgb, var(--accent-alt) 40%, transparent);
    text-underline-offset: .18em;
}

.prose a:hover { color: var(--ink-strong); }

.prose strong { color: var(--ink-strong); font-weight: 600; }

.prose blockquote {
    position: relative;
    margin: 1.35rem 0;
    padding: .85rem 1.1rem;
    border-radius: .6rem;
    border: 1px solid var(--hairline);
    border-left: 2px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 7%, var(--surface));
    color: var(--ink-quote);
}

.prose blockquote p:last-child { margin-bottom: 0; }

.prose code {
    background: var(--wash-code);
    border: 1px solid var(--hairline);
    padding: .08rem .34rem;
    border-radius: .3rem;
    font-size: .86em;
    color: var(--code-inline-ink);
}

.prose pre {
    background: var(--code-bg);
    border: 1px solid var(--hairline);
    border-radius: .7rem;
    padding: 1rem 1.1rem;
    overflow-x: auto;
    margin: 1.35rem 0;
}

.prose pre code {
    background: none;
    border: 0;
    padding: 0;
    color: var(--code-ink);
    font-size: .84rem;
    line-height: 1.55;
}

.prose hr {
    margin: 2.4rem 0;
    border: 0;
    height: 1px;
    background: linear-gradient(90deg, var(--hairline-strong), transparent);
}

/* Tables carry most of the meaning in these documents. */

.table-wrap {
    overflow-x: auto;
    margin: 1.45rem 0;
    border: 1px solid var(--hairline);
    border-radius: .7rem;
}

.prose table {
    border-collapse: collapse;
    width: 100%;
    font-size: .875rem;
    margin: 0;
}

.prose th, .prose td {
    padding: .6rem .8rem;
    vertical-align: top;
    border-bottom: 1px solid var(--hairline);
    border-right: 1px solid var(--hairline);
}

.prose th:last-child, .prose td:last-child { border-right: 0; }

.prose tbody tr:last-child td { border-bottom: 0; }

.prose thead th {
    background: var(--wash-head);
    color: var(--ink-strong);
    text-align: left;
    font-weight: 600;
    white-space: nowrap;
    position: sticky;
    top: 0;
}

.prose tbody tr:hover td { background: var(--wash-row); }

@media (max-width: 991.98px) {
    .rail {
        position: static;
        max-height: none;
        border-left: 0;
        padding-left: 0;
        margin-bottom: 1.5rem;
    }
    .reader-nav { grid-template-columns: 1fr; }
}

/* ----------------------------------------------------- light-mode accents --

   The accent pair is chosen to sing on a dark surface. On white it needs
   darkening — and that is where the rule below comes from:

   **In light mode, TEXT is never derived from --accent-alt.**

   Darkening a colour toward black keeps its hue, and a dark yellow is mustard.
   Crete's accent-alt is #f0b429, so every text use of it on white came out
   olive. There is no mix ratio that fixes it: dark yellow simply IS that
   colour. The fix is to stop asking the secondary accent to be readable ink
   and let the primary accent carry the text instead — accent-alt keeps the
   jobs it is good at, which are borders, glows and gradients.

   Any project whose accent_alt is warm (yellow, gold, lime) hits this; the
   cool ones never would, which is exactly why it was invisible until a gold
   one existed.
   -------------------------------------------------------------------------- */

[data-bs-theme="light"] .accent-text {
    background: linear-gradient(100deg,
        color-mix(in oklab, var(--accent) 92%, black),
        color-mix(in oklab, var(--accent) 62%, black));
    -webkit-background-clip: text;
    background-clip: text;
}

[data-bs-theme="light"] .prose a {
    color: color-mix(in oklab, var(--accent) 78%, black);
    text-decoration-color: color-mix(in oklab, var(--accent) 45%, transparent);
}

[data-bs-theme="light"] .pill-accent {
    color: color-mix(in oklab, var(--accent) 78%, black);
}

/* --------------------------------------------------------- language pill --

   The three-language switcher on public reports. Same idea as the theme
   switcher in base.css: one control with three positions, not three buttons.
   Bootstrap's outline buttons put a border on every segment, which at this
   size reads as three separate controls rather than one choice.

   The selected thumb takes the project's accent, since this control only ever
   appears on a page that has one. The globe is there so the control says what
   it is without a label — a row of three language names is not self-evidently
   a switch until you have already understood it.
   -------------------------------------------------------------------------- */

.lang-switch {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
    padding: .2rem .2rem .2rem .55rem;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 7%, transparent);
    max-width: 100%;
    flex-wrap: wrap;
}

.lang-switch .globe {
    width: .95rem;
    height: .95rem;
    margin-right: .3rem;
    flex: 0 0 auto;
    fill: none;
    stroke: var(--ink-dimmer);
    stroke-width: 1.5;
}

.lang-switch a {
    padding: .32rem .78rem;
    border-radius: 999px;
    font-size: .845rem;
    line-height: 1.15;
    white-space: nowrap;
    color: var(--ink-dim);
    text-decoration: none;
    transition: color .15s ease, background .15s ease;
}

.lang-switch a:hover {
    color: var(--ink-strong);
    background: var(--wash);
}

.lang-switch a[aria-current] {
    color: var(--ink-strong);
    background: var(--surface);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--accent) 42%, transparent),
        0 1px 2px rgba(0, 0, 0, .16);
}

[data-bs-theme="light"] .lang-switch a[aria-current] {
    color: color-mix(in oklab, var(--accent) 72%, black);
}

/* On a phone the three names plus the globe are wider than the hero, so the
   control drops its horizontal padding and is allowed to wrap. */
@media (max-width: 575.98px) {
    .lang-switch { padding-left: .35rem; }
    .lang-switch a { padding: .3rem .6rem; font-size: .8rem; }
}

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

   The big photographic header on a public report. Text sits on a photograph,
   so it does not follow the light/dark tokens: the scrim is always dark and
   the type is always white, which is legible in both themes and does not
   depend on which part of the picture happens to be behind a given letter.

   The accent gradient stays as the hairline along the top edge — it is the
   project's signature and the one place the accent still reads over a photo.
   -------------------------------------------------------------------------- */

.report-hero {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 340px;
    padding: 1.5rem 1.9rem 1.7rem;
    margin-bottom: 1.4rem;
    border-radius: 1.15rem;
    overflow: hidden;
    border: 1px solid var(--hairline);
}

.report-hero .hero-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: -2;
}

.report-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg,
        rgba(6, 10, 16, .10) 0%,
        rgba(6, 10, 16, .38) 42%,
        rgba(6, 10, 16, .80) 100%);
}

.report-hero::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    z-index: 1;
    background: linear-gradient(90deg, var(--accent), var(--accent-alt), transparent 85%);
}

.report-hero .hero-top {
    /* In the flow, not absolutely positioned. It used to be pinned to the top
       with position:absolute, which took it out of the layout entirely: the
       hero bottom-aligns its text, so a long standfirst (the Hungarian one
       runs to six lines on a phone) grew upwards and ran under the language
       switcher. margin-bottom:auto keeps the text bottom-aligned exactly as
       before while guaranteeing the two can never overlap. */
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem 1rem;
    flex-wrap: wrap;
    margin-bottom: auto;
    padding-bottom: 1.4rem;
}

.report-hero .eyebrow { color: rgba(255, 255, 255, .8); }

.report-hero h1 {
    color: #fff;
    font-weight: 600;
    letter-spacing: -.025em;
    line-height: 1.15;
    margin: 0 0 .6rem;
    font-size: clamp(1.9rem, 3.8vw, 2.85rem);
    text-shadow: 0 2px 18px rgba(0, 0, 0, .45);
}

.report-hero .hero-lead {
    color: rgba(255, 255, 255, .92);
    max-width: 58ch;
    line-height: 1.6;
    margin: 0;
    text-shadow: 0 1px 12px rgba(0, 0, 0, .5);
}

.report-hero .pill {
    color: rgba(255, 255, 255, .92);
    border-color: rgba(255, 255, 255, .35);
    background: rgba(6, 10, 16, .35);
    backdrop-filter: blur(2px);
}

/* The language switcher sits on the photograph, so it gets its own dark
   treatment rather than the themed one. */
.report-hero .lang-switch {
    background: rgba(6, 10, 16, .45);
    border-color: rgba(255, 255, 255, .28);
    backdrop-filter: blur(3px);
}
.report-hero .lang-switch .globe { stroke: rgba(255, 255, 255, .7); }
.report-hero .lang-switch a { color: rgba(255, 255, 255, .78); }
.report-hero .lang-switch a:hover { color: #fff; background: rgba(255, 255, 255, .12); }
.report-hero .lang-switch a[aria-current] {
    color: #0d1015;
    background: #fff;
    box-shadow: none;
}


@media (max-width: 575.98px) {
    .report-hero {
        min-height: 280px;
        padding: 1.1rem 1.1rem 1.3rem;
    }

    /* On a narrow screen the eyebrow and the switcher stop competing for one
       line: the switcher takes its own row and sits left with everything else. */
    .report-hero .hero-top {
        padding-bottom: 1.1rem;
    }

    .report-hero h1 { font-size: clamp(1.7rem, 7.5vw, 2.1rem); }

    .report-hero .hero-lead {
        font-size: .95rem;
        line-height: 1.5;
    }
}

/* ------------------------------------------------- light-mode page ground --

   Dark mode separates a card from the page because --surface (#16181d) is
   lighter than the body behind it. Light mode had white cards on a white
   body, so the only thing holding a card together was its hairline and the
   layering read as flat.

   Fixed by moving the PAGE rather than the cards: the body takes a very light
   grey and --surface stays pure white, which restores the same figure/ground
   relationship in the opposite direction. --surface-2 (hover) has to go
   slightly darker than the new ground or hovering would appear to lift a
   card into the page colour.
   -------------------------------------------------------------------------- */

[data-bs-theme="light"] {
    --surface-2: #eceff3;
}

[data-bs-theme="light"] body {
    background-color: #f2f4f7;
}

/* ----------------------------------------------------- light-mode lift --

   A very quiet shadow on the report cards, light mode only. Dark mode does
   not get one: a shadow on a dark ground reads as smudge rather than lift,
   which is why the separation there comes from --surface being lighter than
   the body instead.

   Two layers on purpose. The tight 1px pass gives the card a defined edge so
   it does not rely on the hairline alone; the wide, heavily-offset pass is
   the ambient one and is what makes it feel raised. Both are far below the
   opacity you would notice as "a shadow" — the intent is that the card looks
   solid, not that anything looks shadowed.
   -------------------------------------------------------------------------- */

[data-bs-theme="light"] .trip-block,
[data-bs-theme="light"] .ten-card,
[data-bs-theme="light"] .report-hero,
[data-bs-theme="light"] .next-card {
    box-shadow:
        0 1px 2px rgba(16, 24, 40, .04),
        0 10px 28px -18px rgba(16, 24, 40, .16);
}

[data-bs-theme="light"] .next-card:hover {
    box-shadow:
        0 1px 2px rgba(16, 24, 40, .05),
        0 16px 34px -18px rgba(16, 24, 40, .22);
}

/* ------------------------------------------------- mobile page gutters --

   base.html wraps every page in a .container-fluid, and the report templates
   nest their own inside it. Both carry Bootstrap's gutter, so the two stack:
   on a 390px phone that was 36px of padding a side, 72px total, and the
   cards had barely 300px left to work with.

   The outer gutter goes to zero on phones and the inner one drops to
   something a thumb's width from the edge, which is what the cards actually
   want. Unchanged from `sm` up, where the width is not scarce.
   -------------------------------------------------------------------------- */

@media (max-width: 575.98px) {
    body > .container-fluid { --bs-gutter-x: 0; }

    .report-page {
        padding-left: .7rem !important;
        padding-right: .7rem !important;
    }
}

/* ================================================================= hub ====

   The index at /. It shares the projects' visual language deliberately: each
   card carries the accent pair of the project it belongs to, so the page
   reads as a set of distinct places rather than a uniform list. Reports with
   no project inherit the hub's own pair from the wrapper.
   ========================================================================== */

.hub-hero {
    position: relative;
    border: 1px solid var(--hairline);
    border-radius: 1.15rem;
    padding: 2.1rem 2rem 1.8rem;
    margin-bottom: 1.6rem;
    overflow: hidden;
    background:
        radial-gradient(90% 130% at 6% -20%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 62%),
        radial-gradient(70% 130% at 100% 0%, color-mix(in srgb, var(--accent-alt) 15%, transparent), transparent 58%),
        var(--surface);
}

.hub-hero::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-alt), transparent 85%);
}

.hub-hero h1 {
    font-size: clamp(2rem, 4vw, 2.9rem);
    font-weight: 600;
    letter-spacing: -.03em;
    margin: 0 0 .55rem;
}

.hub-lead {
    color: var(--ink-dim);
    max-width: 54ch;
    line-height: 1.6;
    margin-bottom: 1.2rem;
}

/* The one link out of the index that is not a report. */
.hub-feature {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.2rem;
    padding: 1.25rem 1.5rem;
    margin-bottom: 2rem;
    border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--hairline));
    border-radius: 1rem;
    text-decoration: none;
    color: inherit;
    background:
        linear-gradient(100deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent 55%),
        var(--surface);
    transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}

.hub-feature:hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--accent) 50%, transparent);
}

.hub-feature-title {
    display: block;
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--ink-strong);
    letter-spacing: -.015em;
}

.hub-feature-note { display: block; color: var(--ink-dim); margin-top: .15rem; }

.hub-feature-go,
.hub-card-go {
    color: color-mix(in srgb, var(--accent) 70%, var(--accent-mix-target));
    font-size: 1.1rem;
    transition: transform .16s ease;
}

.hub-feature:hover .hub-feature-go { transform: translateX(4px); }

.hub-section + .hub-section { margin-top: 2.1rem; }

/* auto-FIT, not auto-fill: a category with two reports would otherwise leave
   a third empty track hanging off the right of the row. Fit collapses the
   empty tracks and lets the cards share the width instead. */
.hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
    gap: 1rem;
}

.hub-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 1.35rem 1.45rem 1.3rem;
    border: 1px solid var(--hairline);
    border-radius: 1rem;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    background:
        radial-gradient(130% 110% at 0% 0%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 58%),
        var(--surface);
    transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}

/* The project's signature, same as on a project card. */
.hub-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-alt), transparent 88%);
}

.hub-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--accent) 42%, var(--hairline));
    box-shadow: 0 16px 40px -20px color-mix(in srgb, var(--accent) 60%, transparent);
}

.hub-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .7rem;
    margin-bottom: .4rem;
}

.hub-card-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--ink-strong);
    letter-spacing: -.012em;
    line-height: 1.3;
}

.hub-card-note {
    color: var(--ink-dim);
    font-size: .92rem;
    line-height: 1.55;
    margin-bottom: 1.5rem;
}

/* Pinned bottom-right so cards of different text lengths still line up. */
.hub-card-go {
    position: absolute;
    right: 1.25rem;
    bottom: 1rem;
}

.hub-card:hover .hub-card-go { transform: translateX(4px); }

.hub-empty {
    border: 1px solid var(--hairline);
    border-radius: 1rem;
    background: var(--surface);
    padding: 1.3rem 1.5rem;
    color: var(--ink-dim);
}

[data-bs-theme="light"] .hub-card,
[data-bs-theme="light"] .hub-feature,
[data-bs-theme="light"] .hub-hero {
    box-shadow:
        0 1px 2px rgba(16, 24, 40, .04),
        0 10px 28px -18px rgba(16, 24, 40, .16);
}

@media (max-width: 575.98px) {
    .hub-hero { padding: 1.5rem 1.2rem 1.35rem; }
    .hub-feature { padding: 1.1rem 1.15rem; }
    .hub-card { padding: 1.15rem 1.15rem 1.15rem; }
}
