/* Multi-Stream Theme - Minimal Base Styles

   ORGANISATION. The file is grouped by component, in this order:
      1. Tokens and themes            8. Apps
      2. Base and layout              9. Shared page furniture
      3. Site chrome                 10. Music (cards, release page, projects,
      4. Home page                       performances, credits, report, lists)
      5. Bio, social and contact     11. Genres
      6. Blog and Jogger             12. Print
      7. Shortcodes                  13. Utilities
   A component's responsive behaviour lives directly under its base rules: the
   base is the phone layout, then min-width: 768px for wider screens; a few
   components still declare the desktop look first and patch phones in a
   max-width: 767px block. The only widths used are 767px / 768px (enforced by
   the site expectations test). There is no separate "mobile styles" section; see "CSS Architecture"
   in Docs/2025-11-12-multi-stream-theme.md. */

/* ==========================================================================
   1. TOKENS AND THEMES
   ========================================================================== */

:root {
    color-scheme: light dark; /* form controls, scrollbars and the canvas follow the theme */

    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-serif: Charter, "Sitka Text", Georgia, "Palatino Linotype", "Book Antiqua", serif;

    /* Type scale. Anything else in the file is a one-off (icons, headings with their own role). */
    --text-xs: 0.8rem;
    --text-sm: 0.9rem;
    --text-lg: 1.125rem;
    --text-xl: 1.25rem;
    --text-2xl: 1.75rem;

    /* Shape. */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-card: 12px;
    --radius-pill: 999px;

    /* Spacing. --gutter is the space between content and the screen edge, and between cards on
       phones. The values below flip at 768px (see the media block under this one). */
    --gutter: 0.25rem;
    --card-pad: 1.25rem;
    --card-pad-lg: 1.5rem;
    --card-gap: var(--gutter);    /* between cards in a list: --gutter on phones, 1rem from 768px */
    --card-gap-lg: var(--gutter); /* between cards in a roomy list: --gutter on phones, 2rem from 768px */
    --page-pad: 0;                /* the container's side padding: none on phones, 20px from 768px */

    --ease: 0.2s;
    --tracking-caps: 0.05em; /* letter-spacing of every small uppercase label */
}

@media (min-width: 768px) {
    :root {
        --card-gap: 1rem;
        --card-gap-lg: 2rem;
        --page-pad: 20px;
    }
}

/* Light mode (default) */

:root {
    --bg-primary: #f5f5f7;
    --bg-secondary: #ffffff;
    --bg-nav: #fcfcfd; /* the navbar: almost white, brighter than the page */
    --text-primary: #1d1d1f;
    --text-secondary: #6e6e73;
    --text-tertiary: #a1a1a6;
    --border-color: #d2d2d7;
    --accent: #0071e3;
    --on-accent: #ffffff; /* text on an accent or success background */
    --success: #28a745;
    --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
    --tint-hover: color-mix(in srgb, var(--text-primary) 8%, transparent);
    --tint-accent: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* Dark mode (the dark-only effects elsewhere in the file are marked "Dark theme:") */

@media (prefers-color-scheme: dark) {
    :root {
        --bg-primary: #000000;
        --bg-secondary: #1d1d1f;
        --bg-nav: #0e0e10; /* a tinge brighter than the page */
        --text-primary: #f5f5f7;
        --text-secondary: #a1a1a6;
        --text-tertiary: #6e6e73;
        --border-color: #424245;
        --accent: #2997ff;
        --glow: drop-shadow(0 0 1px rgba(255, 255, 255, 0.3)) drop-shadow(0 0 5px rgba(255, 255, 255, 0.25)); /* the light halo of dark logos; only used in dark blocks */
    }
}

/* ==========================================================================
   2. BASE AND LAYOUT
   ========================================================================== */

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

body {
    font-family: var(--font-sans);
    line-height: 1.6;
    color: var(--text-primary);
    background-color: var(--bg-primary);
    padding: 0 var(--gutter);
}

/* Narrow screens: width is scarce, so content (cards, text, everything)
   uses the full viewport — no side padding. Wide screens: width is
   abundant, so a gutter is added instead of leaving content stretched
   edge-to-edge. Cards never need to bleed/cancel this — there's
   nothing to cancel on narrow screens in the first place. */

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 var(--page-pad);
}

/* Pages (and the header, footer and home streams) that run the full width of the window
   instead of the 1200px cap. */

.container-wide {
    max-width: none;
}

/* ==========================================================================
   3. SITE CHROME AND GLOBAL ELEMENTS
   ========================================================================== */

/* Header */

.site-header {
    --header-pad: 20px; /* the header's vertical padding; the nav highlight bleeds through it */
    background: color-mix(in srgb, var(--bg-nav) 50%, transparent); /* half see-through: a release cover's glow shows behind the bar */
    border-bottom: 1px solid var(--border-color);
    padding: var(--header-pad) 0;
    position: relative;
    z-index: 100;
}

.site-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
}

.site-title {
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--text-primary);
    text-decoration: none;
}

/* Breadcrumb trail in the header: Site > Section > Page */

.site-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.5rem;
    list-style: none;
    margin: 0;
    padding: 0;
    min-width: 0;
}

.site-breadcrumb li + li::before {
    content: "›";
    margin-right: 0.5rem;
    color: var(--text-secondary);
}

.site-crumb {
    color: var(--text-secondary);
    text-decoration: none;
}

.site-crumb:hover {
    color: var(--accent);
}

.site-crumb-current {
    color: var(--text-primary);
    max-width: 24ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Hamburger toggle button */

.nav-toggle {
    display: none;
    flex-direction: column;
    gap: 4px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 8px;
}

.nav-toggle span {
    display: block;
    width: 24px;
    height: 2px;
    background: var(--text-primary);
    transition: transform 0.3s, opacity 0.3s;
}

/* Each entry gets a background tint on hover, over the header's whole height (from the top of
   the window to the line under the bar) with all four corners rounded; the text colour does
   not change. The vertical padding and the equal negative margin cancel out, so the header is
   no taller than its content plus --header-pad, and the horizontal padding is paid for by the
   smaller gap and the nav's negative right margin: the text sits where it always did and the
   last entry still lines up with the container's edge. */

.nav-menu {
    display: flex;
    list-style: none;
    gap: 0;
    margin-right: -1rem;
}

.nav-menu a {
    display: block;
    margin: calc(-1 * var(--header-pad)) 0;
    padding: var(--header-pad) 1rem;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    text-decoration: none;
}

.nav-menu a:hover {
    background: var(--tint-hover);
}

/* Narrow screens: shorter header, and the nav collapses into the hamburger menu. */
@media (max-width: 767px) {
    .site-header {
        --header-pad: 10px;
    }

    /* Mobile navigation */

    .nav-toggle {
        display: flex;
    }

    .nav-menu {
        display: none;
        position: absolute;
        top: 100%;
        right: 0;
        background: var(--bg-nav);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        padding: 1rem;
        margin-top: 0.5rem;
        flex-direction: column;
        gap: 1rem;
        margin-right: 0;
        min-width: 200px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }

    .nav-menu.active {
        display: flex;
    }

    .nav-menu li {
        margin: 0;
    }

    /* In the dropdown the entries are plain rows with a fully rounded highlight. */
    .nav-menu a {
        margin: 0;
        padding: 0.5rem;
        border-radius: 6px;
    }

    .nav-toggle.active span:nth-child(1) {
        transform: rotate(45deg) translate(5px, 5px);
    }

    .nav-toggle.active span:nth-child(2) {
        opacity: 0;
    }

    .nav-toggle.active span:nth-child(3) {
        transform: rotate(-45deg) translate(7px, -6px);
    }
}

/* Main content */

main {
    min-height: 60vh;
    padding: 0 0 40px 0;
}

/* Footer */

.site-footer {
    border-top: 1px solid var(--border-color);
    padding: 40px 0;
    text-align: center;
    color: var(--text-secondary);
}

/* The header and footer are not capped at the container's 1200px (.container-wide in their
   templates): the navigation sits at the
   window's edges and the footer spans the whole width, on any screen. They also cancel the body's
   --gutter side padding (negative margins), so the bars, with their lines and highlights, run
   edge to edge; the header's content is then padded by the gutter again, so its text stays where
   it was (on phones the container adds no padding of its own; from 768px it adds 20px). */

.site-header,
.site-footer {
    margin: 0 calc(-1 * var(--gutter));
}

.site-header > .container {
    padding: 0 calc(var(--page-pad) + var(--gutter));
}

/* Links */

a {
    color: var(--accent);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

/* A link that looks like text: primary colour, never underlined. Add .link-plain-accent to
   turn accent-coloured on hover. */

.link-plain,
.link-plain:hover {
    color: var(--text-primary);
    text-decoration: none;
}

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

/* Headings. Every heading carries a top margin of 1rem; a component that starts with a heading
   (a card, a column) zeroes it on its own rule. */

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans);
    line-height: 1.2;
    margin-bottom: 0.5rem;
    margin-top: 1rem;
}

/* Card title: one heading level below a stream's h2, used by the app, blog and post cards
   (the music and project cards use .card-row-title instead). */

.app-preview h3,
.app-post-preview h3,
.blog-post-preview h3,
.post-card-content h3 {
    font-size: var(--text-xl);
    margin: 0;
}

/* The card primitive: the surface every card shares. A component adds only what is specific to
   it (layout, a different padding, a shadow with .card-shadow). */

.card {
    background: var(--bg-secondary);
    border-radius: var(--radius-card);
    padding: var(--card-pad);
}

/* A shadow, for cards that are not whole-card links; .card-shadow adds the hover ring of the
   whole-card links (see "Card chrome" below). */

.card-shadow,
.card-shadow-static {
    box-shadow: var(--shadow-card);
    transition: box-shadow var(--ease);
}

/* The ring only exists on wide screens whose pointer can hover. On phones (and on touch devices
   generally, where a tap leaves :hover stuck on) a card gets no hover affordance at all. */

@media (min-width: 768px) and (hover: hover) {
    .card-shadow:hover {
        box-shadow: 0 0 0 2px var(--text-secondary);
    }
}

/* A list whose items are separated by a line (not after the last one). */

.rule-list > * {
    border-bottom: 1px solid var(--border-color);
}

.rule-list > :last-child {
    border-bottom: none;
}

/* Card row: a card laid out as a row, with the artwork flush to its left, top and bottom
   edges and the text column beside it. Used by every artwork card: .project-card at every
   width, .music-preview below 768px (its own wider layouts reset what they change in
   "Music preview" in section 4, which must stay after this block). Markup is
   .card-row > .card-row-art > img  +  .card-row-body.

   Artwork is a 120px square (covers are 1:1; anything else is centre-cropped by
   object-fit). `.card-row-art` is `display:contents` — it stops existing as a box, so its
   child <img> is the actual flex item. The negative margins pull the image out over the
   card's padding so it is flush with the card edges; the card is therefore at least 120px
   tall, and taller when the text column needs more. Every line in the body is individually
   clamped (nowrap + ellipsis), which bounds the body's height predictably. */

.card-row {
    display: flex;
    align-items: stretch;
    gap: 1rem;
    overflow: hidden;
    padding: 1rem;
}

.card-row-art {
    display: contents;
}

.card-row-art img,
.card-row-art .waveform-placeholder {
    flex-shrink: 0;
    width: 120px;
    height: 120px;
    object-fit: cover;
    display: block;
    margin: -1rem 0 -1rem -1rem;
    /* All four corners: the image is flush with the card's top-left (the card's
       overflow clip matches), but when the text column is taller than the
       image (e.g. the service-logo row) its bottom-left corner is exposed. */
    border-radius: var(--radius-card);
}

.card-row-body {
    min-width: 0;
    overflow: hidden;
}

/* Title and secondary lines: one line each, same size and spacing in every card. */
.card-row .card-row-title,
.card-row .card-row-meta {
    display: block;
    margin: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-row .card-row-title {
    font-size: var(--text-lg);
    line-height: 1.2;
}

.card-row .card-row-meta {
    font-size: var(--text-sm);
    line-height: 1.6;
}

/* ==========================================================================
   4. HOME PAGE
   ========================================================================== */

/* The streams section opens the page. From 768px the space between the header's bottom line and
   the column headings is the .container's 20px side padding, so it equals the distance from the
   headings to the window's left edge (the headings' own top margin is cleared with their rule).
   Phones: the space under the header's line is --gutter, the same as to the screen edge. */

.streams {
    padding-top: max(var(--page-pad), var(--gutter)); /* --gutter on phones (--page-pad is 0 there), 20px from 768px */
}

/* Stream columns */

.stream-columns {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--card-gap-lg);
}

/* From 768px (the phone/wider cutoff) the streams sit in a grid that fits as many equal-width
   columns as have room, 2 or 3: a column is at least 300px, and the max() keeps the count from
   going past three however wide the page gets (auto-fill keeps the tracks equal). Music, Apps,
   Bio fill it in that order, so with two columns Bio wraps under Music. Below 768px it is the
   single-column phone stack. */

@media (min-width: 768px) {
    .stream-columns {
        grid-template-columns: repeat(auto-fill, minmax(max(300px, (100% - 2 * 2rem) / 3), 1fr));
        align-items: start;
    }
}

/* The homepage streams are not capped at the container's 1200px either (the .container-wide in
   index.html): the grid takes the whole width and stays at three columns on ultrawide screens. */

.stream-column h2 {
    margin-bottom: 1.5rem;
}

@media (min-width: 768px) {
    .stream-column h2 {
        margin-top: 0;
    }

    /* A second heading in the same column (Blog under Bio) is set off from the content above it
       by the grid's own gap; only the first heading of a column sits flush at the top. */
    .stream-column .stream-content + h2 {
        margin-top: 2rem;
    }
}

@media (max-width: 767px) {
    .music-stream h2,
    .apps-stream h2 {
        display: none;
    }
}

/* Spacing between cards lives on the list container (gap), not on
   each card (margin-bottom). A card's own margin can't tell whether
   it's followed by a sibling, so reusing a card in a longer list
   (e.g. /music/) would need a separate reset for every width the list
   can appear at — gap doesn't have that problem. .stream-cards is a
   column of cards (music, apps, blog); the Bio column's .stream-content
   holds prose and is not one. */

.stream-cards {
    display: flex;
    flex-direction: column;
    gap: var(--card-gap);
}

/* Music preview on homepage */

.music-preview {
    position: relative;
}

/* Card text is three lines at every width: title / artist / genres •
   month. Each line stays on one line (ellipsis) so long collaborator
   lists can't grow the card; .card-row-meta (section 3) does the clipping. */

.music-preview-artist {
    font-weight: 500;
    color: var(--text-secondary);
}

.music-preview-details {
    font-weight: 500;
    color: var(--text-tertiary);
}

.music-preview-details a {
    color: inherit;
    text-decoration: none;
}

/* Service logos at the foot of a card; above the card's stretched link so
   they open the service directly. */

.music-preview-services {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.6rem;
    align-items: center;
}

.music-preview-services a {
    position: relative;
    z-index: 2;
    display: block;
    line-height: 0;
}

.music-preview-services img {
    width: 20px;
    height: 20px;
    border-radius: var(--radius-sm);
}

/* Logos that are dark against a dark page (data/services/<key>.toml `effect`) get a light edge
   (outline) or a soft halo (glow). A drop-shadow follows the logo's own outline, rounded corners
   included, where a box-shadow would draw its square box. Dark theme only: on the light theme
   those logos are visible as they are. */
/* Bright logos (effect "aura"): the logo sits in a span that carries its own image as --aura; on
   the dark theme a blurred, faint copy of it behind the logo is a halo in the logo's own colours,
   the counterpart of the white glow of the dark logos. Light theme: no halo. */
.service-aura {
    position: relative;
    display: inline-block;
    isolation: isolate;
    line-height: 0;
}

@media (prefers-color-scheme: dark) {
    .service-aura::before {
        content: "";
        position: absolute;
        inset: -2px;
        z-index: -1;
        background: var(--aura) center / contain no-repeat;
        filter: blur(5px) saturate(1.3);
        opacity: 0.3;
        pointer-events: none;
    }

    /* The "Listen on ..." pills are solid accent-colour buttons; no halo there. */
    .streaming-link .service-aura::before {
        display: none;
    }

    .service-icon-outline {
        filter: drop-shadow(0 0 0.5px rgba(255, 255, 255, 0.6)) drop-shadow(0 0 0.5px rgba(255, 255, 255, 0.6));
    }

    .service-icon-glow {
        filter: var(--glow);
    }
}

/* "Presave" chip: a release whose only link is the pre-save page. */

.presave-chip {
    display: inline-block;
    padding: 0.05rem 0.6rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius-pill);
    color: var(--accent);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.5;
    text-decoration: none;
}

.music-quick-links a.presave-chip,
.music-quick-links a.presave-chip:hover {
    color: var(--accent);
    text-decoration: none;
}

.music-preview-services a.presave-chip {
    line-height: 1.5;
}

/* Unreleased music: unreleased.js adds .is-unreleased to the card. The
   overlay is hidden by default so no-JS visitors see a normal card. Its
   geometry (and radius) mirror the image's at each breakpoint below. */

.music-preview-image {
    position: relative;
}

.unreleased-overlay {
    display: none;
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--bg-secondary) 70%, transparent);
    pointer-events: none;
}

.is-unreleased .unreleased-overlay {
    display: block;
}

/* The role label on a card ("Original" / "Production" / "Engineering"): a coloured strip on the
   card's edge. Hidden by default; phones put it, rotated, at the right edge and wider screens
   at the bottom of the stacked card (music grids and homepage), in the blocks below. Original is
   solid, the other two share one tint. */

.music-preview-role {
    display: none;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--accent);
    background: var(--tint-accent);
}

.music-preview-role-original {
    color: var(--on-accent);
    background: var(--accent);
}

/* Narrow screens: the row layout, artwork and text lines come from the shared "Card row"
   (section 3). */

@media (max-width: 767px) {
    /* The artwork wrapper is display:contents (see .card-row), so the overlay is positioned against
       the card: same box as the image (120px wide, up to 120px tall,
       flush with the card's top/left edges). */
    .unreleased-overlay {
        inset: 0 auto 0 0;
        width: 120px;
        max-height: 120px;
        border-radius: var(--radius-card);
    }

    /* Half the desktop gap between the last text line and the service logos.
       Nothing below the logos may make the card taller than the 120px artwork:
       three text lines (~72px) + 0.3rem + the 20px row come to ~97px, but only 88px
       fit between the card's paddings, so the column's bottom margin is pulled into
       the card's bottom padding (the column's own box, and so the row, stay whole). */
    .music-preview-services {
        margin-top: 0.3rem;
    }

    .music-preview-content:has(.music-preview-services) {
        margin-bottom: -0.55rem;
    }

    .music-preview-services a.presave-chip {
        padding: 0 0.6rem;
        line-height: 18px;
    }

    /* The role label: a strip down the card's right edge, flush with it (the card's padding is
       1rem and it clips the corners; the auto left margin pushes it to the far end of the row,
       as the text column does not grow), text turned 90 degrees to the left so it reads bottom
       to top. It stretches to the card's height like the artwork, which sets the 120px minimum. */
    .music-preview-role {
        display: block;
        flex-shrink: 0;
        margin: -1rem -1rem -1rem auto;
        padding: 0 0.45rem;
        font-size: 0.7rem;
        line-height: 1.3;
        text-align: center;
        writing-mode: vertical-rl;
        transform: rotate(180deg);
    }
}

/* From 768px up the card is stacked, not a row (image on top, text below, role strip at the
   bottom): the card is the row layout of .card-row on phones only, so what that sets is undone
   here (and only what it sets: the card's flex alignment and gap do nothing once it is a block).
   Keeping one set of row rules for both this card and .project-card (a row at every width) costs
   this one block; a phone-only copy of the row rules would cost as many lines. */

@media (min-width: 768px) {
    .music-preview {
        display: block;
        padding: var(--card-pad);
        overflow: visible;
    }

    .music-preview-image {
        display: block;
    }

    .music-preview-image img,
    .music-preview-image .waveform-placeholder {
        margin: 0;
        object-fit: fill;
    }

    .music-preview-content {
        overflow: visible;
    }

    .music-preview .card-row-title {
        font-size: var(--text-xl);
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
    }

    .music-preview .music-preview-details {
        margin-bottom: 1rem;
    }
}

/* Stacked card (image on top, role strip at the bottom) from 768px, in the music grids and on
   the homepage. */

@media (min-width: 768px) {
    :is(.music-productions, .stream-columns) .music-preview-image {
        margin: calc(-1 * var(--card-pad)) calc(-1 * var(--card-pad)) 1rem;
    }

    :is(.music-productions, .stream-columns) .music-preview-image img,
    :is(.music-productions, .stream-columns) .music-preview-image .waveform-placeholder {
        display: block;
        width: 100%;
        height: auto;
        border-radius: var(--radius-card) var(--radius-card) 0 0;
        cursor: pointer;
        transition: opacity var(--ease);
    }

    :is(.music-productions, .stream-columns) .music-preview-image img:hover,
    :is(.music-productions, .stream-columns) .music-preview-image .waveform-placeholder:hover {
        opacity: 0.85;
    }

    :is(.music-productions, .stream-columns) .unreleased-overlay {
        border-radius: var(--radius-card) var(--radius-card) 0 0;
    }

    /* Desktop: a strip flush with the card's bottom edge. */
    :is(.music-productions, .stream-columns) .music-preview-role {
        display: block;
        margin: 1rem calc(-1 * var(--card-pad)) calc(-1 * var(--card-pad));
        padding: 0.45rem var(--card-pad);
        border-radius: 0 0 var(--radius-card) var(--radius-card);
    }
}

@media (max-width: 767px) {
    .music-preview-desktop-only {
        display: none;
    }
}

/* Apps preview on homepage (the .app-preview app cards and the .app-post-preview update posts
   beside them; both are .card in the markup). */

.app-preview {
    position: relative;
}

.app-preview .app-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.app-preview-icon {
    width: 40px;
    height: 40px;
    border-radius: 9px;
    flex-shrink: 0;
}

/* Blog preview on homepage: a .card in a .stream-cards column, so it needs no rule of its own
   beyond its title (see "Card title"). */

.app-preview .app-description {
    font-size: var(--text-sm);
    line-height: 1.5;
    margin: 0 0 0.75rem 0;
    color: var(--text-primary);
}

@media (max-width: 767px) {
    .app-preview-desktop-only {
        display: none;
    }
}

/* Default: hide mobile link, show desktop link. The phone block comes later with the same
   specificity, so source order alone makes it win. */

.view-all-link-mobile {
    display: none;
}

.view-all-link-desktop {
    display: inline-block;
}

@media (max-width: 767px) {
    .view-all-link-mobile {
        display: inline-block;
    }

    .view-all-link-desktop {
        display: none;
    }
}

.stream-footer {
    margin-top: 0;
    text-align: center;
}

.view-all-link {
    color: var(--accent);
    text-decoration: none;
    font-weight: 500;
}

.view-all-link:hover {
    text-decoration: underline;
}

/* ==========================================================================
   5. BIO, SOCIAL AND CONTACT
   ========================================================================== */

/* Bio stream content */

.bio-stream .bio-summary h2 {
    font-size: var(--text-xl);
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
}

.bio-stream .bio-summary h2:first-child {
    margin-top: 0;
}

.bio-stream .bio-summary p {
    margin-bottom: 1rem;
}

/* Lists in running text get an indent; every other list (navigation, grids, chips) is a plain
   element with the reset's zero padding. :where() keeps the container out of the specificity,
   so a component's own list rule always wins. */

:where(.post-content, .section-intro, .stream-content, .music-description, .project-content,
       .music-footer, .track-credits, .reminder-body) :is(ul, ol) {
    padding-left: 1.5rem;
}

.bio-stream .bio-summary hr {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: 1.5rem 0;
    opacity: 0.5;
}

/* Social links */

.social {
    margin: 1.5rem 0;
}

.social ul {
    display: flex;
    justify-content: flex-start;
    gap: 1rem;
    list-style: none;
    flex-wrap: wrap;
}

.social a {
    font-size: 1.5rem;
    color: var(--text-secondary);
    text-decoration: none;
    transition: color var(--ease);
}

.social a:hover {
    color: var(--accent);
}

.social a.bluesky-icon svg {
    display: block;
    width: 1em;
    height: 1em;
    position: relative;
    top: 0.25em;
}

.social a.text-icon {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: var(--text-sm);
    font-weight: 600;
}

/* A service's logo beside its name (service-link.html). */
.social a.text-icon .service-icon {
    width: 1.4em;
    height: 1.4em;
    border-radius: var(--radius-sm);
}

/* Contact section on homepage */

.contact {
    margin-top: 2em;
    padding: 1em 0;
    border-top: 1px solid var(--border-color);
    text-align: center;
}

.contact h2 {
    margin-bottom: 1rem;
}

.contact .social ul {
    justify-content: center;
}

.contact .contact-subheading {
    margin-top: 1.5rem;
}

/* ==========================================================================
   6. BLOG AND JOGGER
   ========================================================================== */

/* Post content */

.post-content {
    max-width: 720px;
    margin: 0 auto;
    font-family: var(--font-serif);
    font-size: var(--text-lg);
}

/* Ensure all embedded media within post content are responsive */

.post-content img,
.post-content video,
.post-content iframe {
    max-width: 100%;
    height: auto;
    display: block;
    box-sizing: border-box; /* Ensure padding and border are included in the element's total width and height */
}

.post-content img {
    border-radius: var(--radius-card);
    margin: 2rem 0;
}

.post-content p {
    text-indent: 0; /* Removed text indentation */
    margin-bottom: 0.45rem; /* Increased by 50% from 0.3rem */
}

.post-content blockquote,
.stream-content blockquote,
.section-intro blockquote,
.post-summary blockquote {
    position: relative;
    background-color: var(--bg-secondary);
    border-radius: var(--radius-md);
    padding: var(--card-pad-lg);
    margin: 1.5rem 0;
    font-style: italic;
}

.post-content blockquote::before,
.stream-content blockquote::before,
.section-intro blockquote::before,
.post-summary blockquote::before {
    content: '“';
    position: absolute;
    top: -0.2em;
    left: 0.1em;
    font-size: 4em;
    color: var(--border-color);
    opacity: 0.6;
    line-height: 1;
}

.post-content blockquote p,
.stream-content blockquote p,
.section-intro blockquote p,
.post-summary blockquote p {
    text-indent: 0;
    margin-bottom: 0;
}

.post-content li p {
    text-indent: 0;
}

.post-content table,
.stream-content table,
.section-intro table {
    width: 100%;
    border-collapse: separate; /* Allows for border-spacing */
    border-spacing: 0; /* Vertical spacing between rows */
    margin-bottom: 1rem;
}

.post-content th,
.post-content td,
.stream-content th,
.stream-content td,
.section-intro th,
.section-intro td {
    padding: 0 0.25rem; /* Further reduced horizontal padding for column margin */
    text-align: left;
    vertical-align: top;
}

.post-content th,
.stream-content th,
.section-intro th {
    background-color: var(--bg-secondary);
    font-weight: 600;
    color: var(--text-primary);
}

.post-content tr:nth-child(even),
.stream-content tr:nth-child(even),
.section-intro tr:nth-child(even) {
    background-color: var(--bg-secondary);
}

@media (max-width: 767px) {
    .post-content img {
        width: 100%;
        margin: 1rem 0;
    }

    /* Figure-shortcode images cap height instead of stretching to width on mobile */
    .post-content figure img,
    .stream-content figure img {
        width: auto;
        max-height: 300px;
        max-width: 100%;
        margin: 0 auto;
        display: block;
    }
}

.post-header {
    margin-bottom: 2rem;
}

.post-illustration {
    width: 100%;
    aspect-ratio: 3 / 1;
    margin: 0 auto 2rem auto;
    overflow: hidden;
    border-radius: var(--radius-card);
}

.post-illustration img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    box-sizing: border-box; /* Ensure padding and border are included in the element's total width and height */
}

@media (max-width: 767px) {
    /* Banner image: taller ratio on narrow screens so subjects aren't over-cropped */
    .post-illustration {
        aspect-ratio: 16 / 9;
    }
}

.post-meta {
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

.page-updated {
    color: var(--text-tertiary);
}

.post-date-link {
    color: inherit;
    text-decoration: none;
}

.post-date-link:hover {
    text-decoration: underline;
}

.posts-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--card-gap); /* the grid's gap is the whole spacing: the cards have no margin of their own */
}

/* Post cards: .post-card is the layout (artwork or text, side by side). The list pages
   (.posts-list: sections, app updates) show a card only from 768px up and a plain row on phones;
   the blog list (.blog-posts-list) is .card at every width. Both are rules of their own, so
   neither undoes the other. The surface comes from .card / .card-shadow / .card-shadow-static in
   the markup. */

.post-card {
    border-radius: var(--radius-card);
    overflow: hidden; /* To contain child elements with border-radius */
    display: flex;
    gap: var(--card-pad);
    text-decoration: none;
    color: inherit;
    height: 100%;
}

@media (min-width: 768px) {
    .posts-list .post-card {
        background: var(--bg-secondary);
        padding: var(--card-pad);
    }
}

.post-card-image {
    flex-shrink: 0;
    width: 200px;
    border-radius: var(--radius-md);
    overflow: hidden;
}

.post-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Blog post cards aren't whole-card links (title/image/read-more are separately clickable), so
   they use .card-shadow-static, which has no hover ring. The list's rhythm is the card's bottom
   margin: the year headings share the list with the cards, so a gap on the list would also
   separate a heading from its first card. */

.blog-posts-list .post-card {
    margin-bottom: var(--card-gap);
}

.blog-posts-list .post-card-content {
    display: flex;
    flex-direction: column;
}

.blog-posts-list .read-more {
    margin-top: auto;
    padding-top: 0.5rem;
    font-weight: 500;
    color: var(--accent);
}

.post-card-content {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
}

.post-card-content .post-meta {
    font-size: var(--text-xs);
    color: var(--text-secondary);
    margin-bottom: 0.2rem;
}

.post-card-content .post-description {
    margin-top: auto; /* Push description to bottom to ensure consistent card height */
}

@media (max-width: 767px) {
    .post-card .post-meta {
        margin-bottom: 0;
    }

    /* Narrow screens drop the description. */
    .post-card-content .post-description {
        display: none;
    }

    .blog-posts-list .post-card-image {
        width: 100%;
        max-height: 200px;
    }

    .blog-posts-list .post-card {
        flex-direction: column;
    }
}

/* ==========================================================================
   7. SHORTCODES
   ========================================================================== */

/* Reminders shortcode */

.reminders-container {
    display: flex;
    justify-content: center;
    gap: 1rem;
    margin: 2rem 0;
}

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

@media (max-width: 767px) {
    .reminders-container {
        flex-direction: column;
    }
}

.reminder-card {
    flex: 1;
    background-color: var(--bg-secondary);
    border-radius: var(--radius-card);
    padding: 1rem;
    border: 1px solid var(--border-color);
}

.reminder-title {
    font-weight: 600;
    font-size: var(--text-lg);
    color: var(--text-primary);
    margin-bottom: 0.5rem;
}

.reminder-body {
    font-size: 1rem;
    color: var(--text-secondary);
}

.reminder-body p {
    text-indent: 0;
    margin-bottom: 0;
}

/* Priority Task shortcode */

.priority-task-container {
    margin: 2rem 0;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.reminder-card.priority-task {
    max-width: 300px;
    border: 2px solid var(--accent);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* A shortcode used with a bad argument: a visible error on the page rather than silent output. */

.shortcode-error {
    padding: 1em;
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    background: var(--tint-accent);
}

/* Separators that used to be <hr> elements: a border on the item, not a rule element. */

.toc-block {
    margin: 1rem 0;
    padding: 0.5rem 0;
    border-top: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
}

.events-group {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-color);
}

/* ==========================================================================
   8. APPS
   ========================================================================== */

.testflight-banner {
    background: var(--success);
    border-radius: var(--radius-card);
    padding: var(--card-pad-lg);
    margin-top: 2rem;
    margin-bottom: 2rem;
    text-align: center;
}

.testflight-banner a {
    color: var(--on-accent);
    text-decoration: none;
    font-size: var(--text-xl);
    font-weight: 600;
    display: block;
}

.testflight-banner a:hover {
    opacity: 0.9;
}

.app-category {
    color: var(--text-secondary);
}

.apps-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--card-gap-lg);
    margin-top: 2rem;
}

/* From 768px (the phone/wider cutoff) as many equal-width columns as fit, with cards at least
   340px wide (two columns at 768px, as before) and no maximum number of columns: the page is
   not capped at the container's 1200px either (the .container-wide in apps.html; auto-fill keeps
   the tracks equal when there are few cards). Below 768px it is the single-column list. */

@media (min-width: 768px) {
    .apps-list {
        grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    }
}

.app-card {
    position: relative;
}

.stretched-link {
    position: absolute;
    inset: 0;
    z-index: 1;
}

.app-card-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.75rem;
}

.app-card-header h2 {
    margin: 0 0 0.25rem;
}

.app-card-icon {
    width: 64px;
    height: 64px;
    border-radius: 14px;
    flex-shrink: 0;
}

.app-card .app-description {
    margin: 0 0 1rem;
}

/* ==========================================================================
   9. SHARED PAGE FURNITURE
   ========================================================================== */

.section-header {
    margin-bottom: 2rem;
}

@media (max-width: 767px) {
    .section-header {
        margin-bottom: 0.5rem;
    }
}

.section-intro {
    margin-bottom: 2rem;
    font-family: var(--font-serif);
    font-size: var(--text-lg);
    line-height: 1.7;
}

.section-intro p {
    margin: 0 0 1rem 0;
}

.section-intro p:last-child {
    margin-bottom: 0;
}

.section-intro ul,
.section-intro ol {
    margin: 0 0 1rem 0;
}

.section-intro li {
    margin-bottom: 0.5rem;
}

.section-description {
    font-size: var(--text-lg);
    color: var(--text-secondary);
    margin-top: 1rem;
}

/* ==========================================================================
   10. MUSIC
   ========================================================================== */

/* Music page */

.music-page,
.music-project-page {
    padding: 2rem 0;
}

/* Phones: no bottom padding, and --gutter under the navbar's line (the same as to the screen
   edge) so the first card does not touch it. */

@media (max-width: 767px) {
    .music-page,
    .music-project-page {
        padding: var(--gutter) 0 0;
    }
}

/* Shared by /music/, /music/projects/<name>/, and /genres/<term>/ —
   whichever of .music-preview or .music-card the page uses. Spacing
   is gap, not each card's own margin-bottom, so it's correct at any
   list length and any width without a per-breakpoint reset. */

.music-productions {
    display: flex;
    flex-direction: column;
    gap: var(--card-gap-lg); /* --gutter on phones: the same as the space between a card and the screen edge */
    margin: 0 auto;
}

/* From 768px (the one phone/wider cutoff) the cards sit in a grid that fits as many columns as
   have room, 2 to 4 (auto-fill, so the columns stay equal width even with few cards): a card is
   at least 260px wide, and the max() keeps the count from going past four however wide the page
   gets. Below 768px it stays the single-column phone list. The `sizes` attribute in
   music-preview-card.html mirrors this arithmetic (260px minimum, 2rem gaps, 20px container
   padding); change them together. */

@media (min-width: 768px) {
    .music-productions {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(max(260px, (100% - 3 * 2rem) / 4), 1fr));
        align-items: start;
    }
}

/* /music/ only: the page is not capped at the container's 1200px (the .container-wide in
   music/list.html), so on ultrawide screens the grid keeps taking the space (and stays at four
   columns). */

.music-card {
    break-inside: avoid;
}

.music-card-top {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1rem;
}

.music-image {
    flex-shrink: 0;
}

.music-image img {
    width: auto;
    height: 6em;
    object-fit: cover;
    border-radius: var(--radius-md);
    display: block;
}

.music-header {
    min-width: 0;
}

.music-header h2 {
    font-size: var(--text-2xl);
    margin-top: 0;
    margin-bottom: 0.5rem;
}

.music-meta {
    color: var(--text-secondary);
    font-size: var(--text-sm);
    margin-bottom: 1rem;
}

.music-meta > span {
    display: inline;
}

.music-meta .artist {
    font-weight: 500;
}

.music-genre,
.music-genre a {
    color: var(--text-tertiary);
    text-decoration: none;
}

.music-description {
    margin: 1rem 0;
}

/* Cover layout (release and project pages): header, cover image and body. Stacked in that
   order on narrow screens; from 768px the cover sits in a right-hand column beside the
   header and body. Markup: .cover-layout > .cover-layout-header + .cover-layout-cover +
   .cover-layout-body. A page without a cover omits the .cover-layout class (no empty column). */

.cover-layout {
    display: grid;
    grid-template-areas:
        "header"
        "cover"
        "body";
    gap: 1.5rem;
}

.cover-layout-header {
    grid-area: header;
}

/* Release page: dedicated layout, description/links/tracks reuse music-content styles */

.music-release {
    margin-bottom: 2rem;
}

.music-quick-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.75rem;
    row-gap: 0.5rem;
    font-size: var(--text-sm);
    margin-bottom: 0.5rem;
}

@media (max-width: 767px) {
    .music-quick-links {
        margin-top: 0.5em;
    }
}

.music-quick-links a {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--text-secondary);
    text-decoration: none;
}

/* Services are icons only (name in the title attribute), so they get a tap-sized logo. */
.music-quick-links .service-icon {
    width: 1.6rem;
    height: 1.6rem;
}

.music-quick-links a:hover .service-icon {
    opacity: 0.8;
}

.music-quick-links a:hover {
    color: var(--accent);
    text-decoration: underline;
}

.music-quick-links .share-button {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font: inherit;
    font-size: inherit;
    color: var(--text-secondary);
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
}

/* Same size as the service icons beside it. The chain link (copy the link) is the default;
   the script adds .can-share where the device has a native share sheet, which swaps in the
   share icon. */
.music-quick-links .share-icon {
    width: 1.6rem;
    height: 1.6rem;
}

/* Dark theme: the same glow as the dark logos, and a black outline around the icon's own shape
   (a wider stroke drawn under the icon's: .share-icon-outline in the markup). */
.share-icon-outline {
    stroke: transparent;
}

@media (prefers-color-scheme: dark) {
    .music-quick-links .share-icon {
        filter: var(--glow);
    }

    .share-icon-outline {
        stroke: #000;
    }
}

.music-quick-links .share-icon-share,
.music-quick-links .share-icon-copied,
.music-quick-links .can-share .share-icon-link {
    display: none;
}

.music-quick-links .can-share .share-icon-share {
    display: inline;
}

/* For the 1.5 s after the link was copied: a check mark replaces whichever icon was showing. */
.music-quick-links .copied .share-icon-link,
.music-quick-links .copied .share-icon-share {
    display: none;
}

.music-quick-links .copied .share-icon-copied {
    display: inline;
}

.music-quick-links .share-button:hover,
.music-quick-links .share-button.copied {
    color: var(--accent);
}

.music-release-header h1 {
    font-size: var(--text-2xl);
    margin-top: 0;
    margin-bottom: 0.5rem;
}

/* The solid accent pill: the release page's button and the "Listen on ..." links. Their size
   and layout are in the rules of each below. */

.release-button,
.streaming-link a,
.listen-links a {
    background: var(--accent);
    color: var(--on-accent);
    border-radius: var(--radius-pill);
    font-weight: 600;
    text-decoration: none;
}

/* The one release button in the header: "Listen now", or "Pre-save" while .is-unreleased is on the
   article (see unreleased.js). */

.release-button {
    display: inline-block;
    margin: 0.25rem 0 0.5rem;
    padding: 0.7rem 1.75rem;
    font-size: var(--text-lg);
}

.release-button:hover {
    filter: brightness(1.1);
}

.release-button-presave,
.is-unreleased .release-button-listen {
    display: none;
}

.is-unreleased .release-button-presave {
    display: inline;
}

.cover-layout-cover {
    grid-area: cover;
    position: relative;
    isolation: isolate;
}

/* A blurred, grainy, brightened copy of the cover behind it: a glow in the cover's own colours.
   It is one inline SVG (partials/cover-glow.html) that holds the image and the whole filter chain
   (blur, saturation, brightness, grain); this CSS only places it. Decorative and dark theme only:
   hidden by default, so the light theme and the no-CSS page show just the cover.
   No transform and no sideways inset: either would make the box wider than the cover, and on a
   narrow screen that is horizontal overflow (a sideways-scrolling, off-centre page). The blur
   alone spreads the halo sideways (overflow: visible lets the SVG draw outside its box); filters
   do not add to a page's scrollable width. */
.cover-layout-cover .cover-backdrop {
    display: none;
}

@media (prefers-color-scheme: dark) {
    .cover-layout-cover .cover-backdrop {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        overflow: visible;
        z-index: -1;
        opacity: 0.5;
        pointer-events: none;
    }

    /* The cover casts a shadow onto the glow behind it: a tight, darker one close to the edge and
       a wide, softer one below, as if lit from above. */
    .cover-layout-cover > img {
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5), 0 14px 48px rgba(0, 0, 0, 0.7);
    }
}

.cover-layout-cover img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
}

.cover-layout-body {
    grid-area: body;
}

.music-release .music-playback {
    margin-top: 1.5rem;
}

/* An album's track listing: a plain numbered list in the body font and colour. */
.track-list {
    margin: 0 0 1rem;
    padding-left: 1.5rem;
    font-weight: normal;
    color: inherit;
}

@media (min-width: 768px) {
    .cover-layout {
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "header cover"
            "body cover";
        align-items: start;
        gap: 2rem;
    }

    .cover-layout-cover img {
        width: auto;
        max-width: 100%;
        height: auto;
        max-height: 90vh;
        margin-left: auto;
        margin-right: 0;
    }
}

.music-credits {
    margin: 1rem 0;
    font-size: var(--text-sm);
}

.music-credits ul {
    list-style: none;
    margin: 0.5rem 0 0 0;
}

.music-credits li {
    margin: 0.25rem 0;
    color: var(--text-secondary);
}

.embed {
    margin: 1rem 0;
}

.embed iframe {
    max-width: 100%;
    border-radius: var(--radius-md);
}

.streaming-link a,
.listen-links a {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.5rem;
    transition: opacity var(--ease);
}

.streaming-link a:hover,
.listen-links a:hover {
    opacity: 0.9;
}

/* The service's logo inside a "Listen on ..." pill. */
.streaming-link .service-icon {
    width: 1.4em;
    height: 1.4em;
    border-radius: var(--radius-sm);
}

.listen-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 1rem 0;
}

.music-footer {
    max-width: 800px;
    margin: 3rem auto 0;
    padding-top: 2rem;
    border-top: 1px solid var(--border-color);
    color: var(--text-secondary);
}

/* ================================================
   Projects feature
   ================================================ */

.music-releases-card {
    margin: 2rem 0;
}

.music-releases-years {
    list-style: none;
}

.music-releases-year {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    padding: 0.75rem 0;
}

@media (max-width: 767px) {
    .music-releases-year {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem;
    }
}

.music-releases-year-label {
    flex-shrink: 0;
    width: 3.5rem;
    font-weight: 600;
    color: var(--text-secondary);
}

.music-releases-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    list-style: none;
}

.release-chip {
    display: inline-flex;
    align-items: stretch;
    gap: 0.5rem;
    padding: 0 0.6rem 0 0;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    font-family: var(--font-sans);
    font-size: var(--text-lg);
    line-height: 2rem;
    text-decoration: none;
    color: var(--text-primary);
    transition: background var(--ease), border-color var(--ease), color var(--ease);
}

.release-chip.no-thumb {
    padding-left: 0.6rem;
}

.release-chip-thumb {
    height: 2rem;
    width: 2rem;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.release-chip:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

/* Small uppercase heading that introduces a group on a page: Active / Past on the
   projects list, Performances / Releases on a project page. */

.section-label {
    font-size: 1rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    color: var(--text-secondary);
    margin: 2rem 0 1rem;
}

.section-label:first-of-type {
    margin-top: 0;
}

/* Project cards grid on /music/projects/ */

.projects-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--card-gap-lg);
    margin-bottom: 2rem;
}

/* From 768px: as many columns as fit with cards at least 360px wide, but never more than 3
   (the max() makes each column at least a third of the width, minus the gaps). */
@media (min-width: 768px) {
    .projects-grid {
        grid-template-columns: repeat(auto-fill, minmax(max(360px, (100% - 4rem) / 3), 1fr));
    }
}

/* Project card: .card .card-row .card-shadow .link-plain in the markup: title, years and counts,
   laid out by the shared "Card row" (section 3) at every width; only what is specific to this
   card is here. */

.project-card .card-row-body {
    flex: 1;
}

.project-years {
    color: var(--text-secondary);
}

.project-stats {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
    /* Same gap under the text as the music cards' logo row. */
    margin-top: 0.3rem;
}

.project-stats span {
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--accent);
    background: var(--tint-accent);
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-sm);
}

/* "More from this project" link on music cards and previews */

.music-project-link {
    margin-top: 2rem;
}

.music-project-link a {
    font-size: var(--text-sm);
    color: var(--accent);
    text-decoration: none;
}

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

/* Individual project page */

.music-project-page .section-header dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.3rem 1rem;
    margin-top: 1rem;
    font-size: var(--text-sm);
}

.music-project-page .section-header dt {
    color: var(--text-secondary);
    font-weight: 500;
}

.music-project-page .section-header dd {
    margin: 0;
}

.music-project-page section {
    margin-top: 2.5rem;
}

.music-project-page section ul {
    list-style: none;
}

.music-project-page section ul li {
    padding: 0.4rem 0;
    font-size: var(--text-sm);
}

/* ============================================================
   Simple performances table (/music/performances/)
   ============================================================ */

.performances-simple-table {
    width: 100%;
    border-collapse: collapse;
}

.performances-simple-table td {
    padding: 0.3em 0.8em 0.3em 0;
    vertical-align: top;
    border-bottom: 1px solid var(--border-color);
    font-size: var(--text-sm);
}

.performances-simple-table td.perf-date {
    white-space: nowrap;
    color: var(--text-secondary);
    width: 10em;
}

.performances-simple-table td.perf-name {
    font-weight: 500;
}

.performances-simple-table td.perf-venue {
    color: var(--text-secondary);
}

/* ============================================================
   Music Credits (/music/credits/)
   ============================================================ */

.credits-toc {
    margin-bottom: 1.5rem;
}

.credits-toc ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0 1rem;
    list-style: none;
}

.credits-toc li {
    font-size: var(--text-sm);
}

.credits-divider,
.credits-person {
    border-style: solid;
    border-width: 0;
    border-color: var(--text-tertiary);
}

.credits-divider {
    border-top-width: 1px;
    margin: 0 0 1.5rem;
}

.credits-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.credits-person {
    padding-bottom: 0.5rem;
    border-bottom-width: 1px;
    scroll-margin-top: 1rem;
    break-inside: avoid;
}

.credits-person:last-child {
    border-bottom: none;
}

.credits-person h2 {
    font-size: var(--text-lg);
    margin-bottom: 0.3rem;
}

.credits-person-primary h2 {
    font-size: var(--text-xl);
}

.credits-role {
    margin-left: 1rem;
}

@media (min-width: 768px) {
    .credits-roles {
        column-width: 16rem;
        column-gap: 2.5rem;
    }

    .credits-role {
        break-inside: avoid;
    }
}

.credits-role-single {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-top: 0.15em;
    margin-bottom: 0.15em;
}

.credits-role summary {
    cursor: default;
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin: 0.15em 0;
    list-style: none;
}

.credits-role summary::-webkit-details-marker {
    display: none;
}

.credits-toggle-link {
    color: var(--accent);
    text-decoration: underline;
    cursor: pointer;
}

.credits-toggle-link:hover {
    text-decoration: none;
}

.credits-release-list {
    margin: 0.2rem 0 0.4rem;
    padding-left: 1.5rem;
    font-size: var(--text-sm);
}

/* ============================================================
   Music Activity Report (/music/report/)
   ============================================================ */

.music-report {
    max-width: 820px;
    margin: 0 auto;
    padding: 2rem 1rem;
    font-family: var(--font-serif);
    font-size: 1rem;
    line-height: 1.6;
}

.report-header {
    margin-bottom: 2.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 2px solid currentColor;
}

.report-header h1 {
    font-size: var(--text-2xl);
    margin-bottom: 1rem;
    letter-spacing: 0.02em;
}

.report-meta {
    border-collapse: collapse;
    margin-bottom: 1.2rem;
}

.report-meta td {
    padding: 0.15em 1.5em 0.15em 0;
    vertical-align: top;
    font-size: var(--text-sm);
}

.report-meta td:first-child {
    color: var(--text-secondary);
    white-space: nowrap;
}

.report-intro {
    font-size: var(--text-sm);
    font-style: italic;
    margin-bottom: 0.5rem;
}

.report-print-link {
    font-size: var(--text-sm);
    margin-top: 0.5rem;
}

.report-section {
    margin-bottom: 3rem;
}

.report-section h2 {
    font-size: var(--text-xl);
    border-bottom: 1px solid var(--text-secondary);
    padding-bottom: 0.3rem;
    margin-bottom: 1.2rem;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
}

.report-count {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-bottom: 1rem;
}

/* Summary tables */

.report-summary-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
    margin-bottom: 1rem;
}

.report-summary-table th {
    text-align: left;
    padding: 0.4em 0.5em;
    border-bottom: 1px solid currentColor;
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
}

.report-summary-table td {
    padding: 0.35em 0.5em;
    border-bottom: 1px solid var(--border-color);
}

.report-summary-table td:nth-child(3),
.report-summary-table td:nth-child(4),
.report-summary-table th:nth-child(3),
.report-summary-table th:nth-child(4) {
    text-align: right;
}

/* Projects table */

.report-projects-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.report-projects-table th {
    text-align: left;
    padding: 0.4em 0.5em;
    border-bottom: 1px solid currentColor;
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
}

.report-projects-table td {
    padding: 0.5em 0.5em;
    vertical-align: top;
    border-bottom: 1px solid var(--border-color);
}

/* Two classes, so it outranks `.report-projects-table td` without !important. */
.report-projects-table .report-details-cell {
    padding: 0;
}

.report-detail-section {
    display: block;
    padding: 0.4em 0.5em;
    border-bottom: 1px solid var(--border-color);
    font-size: var(--text-sm);
}

.report-detail-section:last-child {
    border-bottom: none;
}

.report-detail-label {
    display: block;
    font-family: var(--font-sans);
    font-size: 0.75em;
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    color: var(--text-secondary);
    margin-bottom: 0.2em;
}

.report-detail-value {
    display: block;
}

.report-releases-list {
    margin: 0;
    padding-left: 1.2em;
}

.report-releases-list li {
    word-break: break-all;
}

/* Performances list */

.report-perf-list {
    list-style: decimal;
    padding-left: 2.2em;
    margin: 0;
}

.report-perf-item {
    margin-bottom: 1rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border-color);
}

.report-perf-item:last-child {
    border-bottom: none;
}

.report-perf-header {
    font-size: var(--text-sm);
    margin-bottom: 0.2rem;
}

.report-perf-date {
    font-weight: bold;
    font-family: var(--font-sans);
}

.report-perf-name {
    font-weight: 600;
}

.report-perf-venue {
    color: var(--text-secondary);
}

.report-perf-details {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    font-family: var(--font-sans);
}

.report-perf-details span::after {
    content: " · ";
}

.report-perf-details span:last-child::after {
    content: "";
}

/* Per-streaming-service song lists (/music/spotify, /music/apple) */

.streaming-list {
    list-style: none;
    margin: 2rem 0;
}

.streaming-item {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.streaming-item-art {
    position: relative;
    flex: 0 0 96px;
    width: 96px;
    height: 96px;
    border-radius: var(--radius-md);
    overflow: hidden;
}

.streaming-item-art > img,
.streaming-item-art .waveform-placeholder {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Service logo (or the generic external-link icon) in the artwork's top right corner. */

.streaming-item-art-icon {
    position: absolute;
    top: 3px;
    right: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
}

.streaming-item-art-icon img {
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}

.streaming-item-art-icon-generic {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
}

.streaming-item-title {
    font-size: var(--text-lg);
    margin: 0;
}

.streaming-item-title small {
    display: block;
    margin-top: 0.15rem;
    font-size: var(--text-xs);
    font-weight: normal;
    color: var(--text-secondary);
}

.streaming-item-link {
    color: var(--accent);
    text-decoration: none;
}

.streaming-item-link:hover {
    text-decoration: underline;
}

.streaming-item-link .external-icon {
    margin-left: 0.3em;
    vertical-align: -0.05em;
}

/* Wider screens: two columns (newest first, left to right, then down). */
@media (min-width: 768px) {
    .streaming-list {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1.5rem 2rem;
    }

    .streaming-item {
        margin-bottom: 0;
    }
}

/* ==========================================================================
   11. GENRES
   ========================================================================== */

.genres-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    grid-auto-rows: 1fr;
    gap: var(--card-gap);
    list-style: none;
}

/* .card .card-shadow .link-plain in the markup; its padding differs from the card's. */

.genre-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    aspect-ratio: 1 / 1;
    padding: 1rem var(--card-pad) 20px;
    overflow: hidden;
}

.genre-card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding-bottom: 0.5rem;
}

.genre-card-name {
    font-weight: 400;
    font-size: var(--text-2xl);
    line-height: 1;
}

.genre-card-count {
    color: var(--text-secondary);
    font-size: 2rem;
    font-weight: 600;
    line-height: 1;
}

.genre-card-examples {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    list-style: none;
    overflow: hidden;
}

.genre-card-examples li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

.genre-card-examples img,
.genre-card-examples .waveform-placeholder {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    object-fit: cover;
    flex-shrink: 0;
}

.genre-card-examples span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 767px) {
    .genres-grid {
        grid-auto-rows: auto;
    }

    .genre-card {
        aspect-ratio: auto;
    }
}

/* ==========================================================================
   12. PRINT
   ========================================================================== */

/* Print styles */

@media print {
    nav, .site-header, .site-footer, .report-print-link, .no-print {
        display: none;
    }

    .music-report {
        max-width: 100%;
        padding: 0;
        font-size: 10pt;
        line-height: 1.5;
        color: #000;
    }

    .report-header h1 { font-size: 16pt; }
    .report-section h2 { font-size: 12pt; }

    .report-perf-item {
        break-inside: avoid;
    }

    .report-section {
        break-before: auto;
    }

    a { color: #000; text-decoration: none; }
    a[href]::after { content: " (" attr(href) ")"; font-size: 8pt; }
}

/* ==========================================================================
   13. UTILITIES
   ========================================================================== */

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
