/* ─────────────────────────────────────────────────────────────
   v2 — layout and components
   Single centred column, section rhythm of 80px, rows that fill
   the full column width and highlight on hover.
   ───────────────────────────────────────────────────────────── */

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

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

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--background);
    color: var(--text-secondary);
    font-family: var(--font-sans);
    font-size: var(--text-size);
    font-weight: 400;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: 'kern' 1;
}

img,
svg {
    display: block;
    max-width: 100%;
}

a {
    color: inherit;
    text-decoration: none;
}

h1,
h2,
h3,
h4 {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 500;
    color: var(--text-primary);
}

p {
    margin: 0;
}

ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

:focus-visible {
    outline: 2px solid var(--text-primary);
    outline-offset: 4px;
    border-radius: var(--r-sm);
}

/* ── Shell ───────────────────────────────────────────────── */

.shell {
    width: 100%;
    max-width: var(--content-width);
    margin-inline: auto;
    padding: 24px var(--gutter) 0;
    display: flex;
    flex: 1;
    flex-direction: column;
}

/* ── Header ──────────────────────────────────────────────── */

.site-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.crumb {
    display: inline-flex;
    align-items: center;
    margin-inline-start: -8px;
    padding: 4px 8px;
    border-radius: 12px;
    color: var(--text-primary);
    transition: scale 0.15s ease-out;
}

.crumb:active {
    scale: 0.98;
}

/* The signature inherits currentColor, so it is ink-black on the light
   background and paper-white on the dark one with no second asset. */
.sig {
    display: block;
    height: 34px;
    width: auto;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: 16;
    stroke-linecap: round;
    stroke-linejoin: round;
    overflow: visible;
}

.sig--sm {
    height: 20px;
    stroke-width: 20;
}

/* One quick pass of the pen on load — not a loading screen. The dash length
   is the path's measured total (1999 units) rounded up.

   The delay waits out the shell's own fade, so the stroke is not spent
   on a header that is still arriving, and the curve is a pen rather
   than an object in transit: it touches down, keeps a steady hand, and
   settles. A symmetric ease-in-out left it crawling, sprinting, then
   crawling again. */
.sig--draw path {
    stroke-dasharray: 2000;
    stroke-dashoffset: 2000;
    animation: sign 0.7s cubic-bezier(0.4, 0.15, 0.3, 1) 0.22s forwards;
}

@keyframes sign {
    to {
        stroke-dashoffset: 0;
    }
}

/* Replay the pen twice on hover or keyboard focus, then leave it signed. */
@keyframes signature-hover {
    from { stroke-dashoffset: 2000; }
    80%, to { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: no-preference) {
    .crumb:is(:hover, :focus-visible) .sig path {
        stroke-dasharray: 2000;
        animation: signature-hover 1s cubic-bezier(0.4, 0.15, 0.3, 1) 0s 2 forwards;
    }
}

@media (prefers-reduced-motion: reduce) {
    .sig--draw path {
        animation: none;
        stroke-dashoffset: 0;
    }
}

.header-actions {
    display: flex;
    align-items: center;
    gap: 2px;
}

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    flex-shrink: 0;
    border-radius: 9px;
    /* A soft plate of its own. With a transparent background the glyph
       vanished against whatever sat behind it as the page scrolled. */
    background: var(--control);
    color: var(--text-secondary);
    cursor: pointer;
    transition: background-color 0.15s ease-out, color 0.15s ease-out;
}

.icon-btn:hover {
    background: var(--control-hover);
    color: var(--text-primary);
}

.icon-btn svg {
    width: 17px;
    height: 17px;
}

/* Only one of the two theme glyphs shows at a time */
.icon-btn__moon {
    display: none;
}

.dark .icon-btn__sun {
    display: none;
}

.dark .icon-btn__moon {
    display: block;
}

/* ── Secondary-page header ──────────────────────────────── */

.site-header--sticky {
    position: sticky;
    top: max(12px, env(safe-area-inset-top, 0px));
    z-index: 20;
    flex-shrink: 0;
    width: 100%;
    max-width: 100%;
    min-height: 58px;
    padding: 12px 0;
    margin: -12px auto 0;
    border-radius: 0;
    background: transparent;
    transition: max-width 0.3s ease, padding 0.3s ease,
        border-radius 0.3s ease, background-color 0.3s ease,
        box-shadow 0.3s ease;
}

.site-header--sticky.is-scrolled {
    max-width: 440px;
    padding-inline: 18px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--background) 92%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: var(--popover-shadow);
}

.site-header--sticky .crumbs { flex: 1; }
.site-header--sticky .crumbs > :not(.crumbs__current),
.site-header--sticky .header-actions { flex-shrink: 0; }

@media (prefers-reduced-motion: reduce) {
    .site-header--sticky { transition: none; }
}

/* ── Breadcrumbs ─────────────────────────────────────────── */

.crumbs {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 15px;
}

.crumbs .sig {
    height: 26px;
}

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

.crumbs__link {
    color: var(--text-secondary);
    transition: color 0.15s ease-out;
}

.crumbs__link:hover {
    color: var(--text-primary);
}

.crumbs__current {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
    font-weight: 500;
}

/* ── Hero ────────────────────────────────────────────────── */

.intro {
    /* The header needs room to breathe before the hero starts: 72px on a
       desktop, a little less where the screen is short. */
    margin-top: 72px;
    max-width: 600px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    font-size: 17px;
    line-height: 1.55;
    color: var(--text-secondary);
    text-wrap: pretty;
}

.identity {
    display: flex;
    align-items: center;
    gap: 16px;
}

.identity__avatar {
    position: relative;
    /* A span, and now inside .identity__portrait rather than a flex item of
       its own, so it has to be told to take a box. */
    display: block;
    width: 64px;
    height: 64px;
    flex-shrink: 0;
    border-radius: var(--r-xl);
    overflow: hidden;
    outline: 1px solid rgb(from var(--ink) r g b / 0.1);
    outline-offset: -1px;
}

.identity__avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── Portrait: the second photo and the bubble above it ──── */

.identity__portrait {
    position: relative;
    flex-shrink: 0;
}

/* The two photos are stacked and crossfaded, so the swap on hover has
   nothing to load and nothing to flash. */
.identity__avatar-alt {
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity 0.2s ease-out;
}

.identity__presence {
    position: absolute;
    bottom: calc(100% + 10px);
    left: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    border-radius: var(--r-md);
    background: var(--btn-bg);
    color: var(--btn-fg);
    font-size: 14px;
    line-height: 1.4;
    white-space: nowrap;
    /* Never in the way of the click that opens the contact card. */
    pointer-events: none;
    opacity: 0;
    translate: 0 4px;
    transition: opacity 0.18s ease-out, translate 0.18s ease-out;
}

/* The tail: a square turned on its corner and tucked halfway under the
   bubble. A clipped triangle sitting below the edge left a hairline of
   background showing through the seam. */
.identity__presence::after {
    content: "";
    position: absolute;
    top: calc(100% - 6px);
    left: 18px;
    width: 12px;
    height: 12px;
    border-radius: 0 0 2px;
    background: inherit;
    rotate: 45deg;
}

.identity__presence svg {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
}

/* Hover only where there is a pointer: on a phone the tap belongs to the
   contact card, and a bubble would simply stick. */
@media (hover: hover) {
    .identity__avatar:hover .identity__avatar-alt,
    .identity__avatar:focus-visible .identity__avatar-alt {
        opacity: 1;
    }

    .identity__avatar:hover ~ .identity__presence,
    .identity__avatar:focus-visible ~ .identity__presence {
        opacity: 1;
        translate: 0 0;
    }

    /* Steam off the cup, the two wisps lifting out of frame in turn. */
    .identity__avatar:hover ~ .identity__presence .presence__steam path,
    .identity__avatar:focus-visible ~ .identity__presence .presence__steam path {
        animation: steam 2.4s ease-in-out infinite;
    }

    .identity__avatar:hover ~ .identity__presence .presence__steam path:last-child,
    .identity__avatar:focus-visible ~ .identity__presence .presence__steam path:last-child {
        animation-delay: 0.6s;
    }
}

@keyframes steam {
    0% { opacity: 0.35; translate: 0 1px; }
    45% { opacity: 1; translate: 0 -1px; }
    100% { opacity: 0.35; translate: 0 1px; }
}

@media (prefers-reduced-motion: reduce) {
    .identity__presence {
        translate: none;
        transition: opacity 0.18s ease-out;
    }

    .identity__presence .presence__steam path {
        animation: none !important;
    }
}

.identity__text {
    line-height: 1.35;
}

.identity__name {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 25px;
    line-height: 1.2;
}

.identity__role {
    font-size: 14px;
    color: var(--text-secondary);
}

/* ── Status ──────────────────────────────────────────────
   One of four, set by data-status on .identity (see presence.js).
   Each state gives the dot its own colour. */

.identity[data-status="available"] {
    --status: var(--live);
    --status-soft: var(--live-soft);
}

.identity[data-status="partial"] {
    --status: var(--partial);
    --status-soft: var(--partial-soft);
}

.identity[data-status="booked"] {
    --status: var(--booked);
    --status-soft: var(--booked-soft);
}

.identity[data-status="away"] {
    --status: var(--text-tertiary);
    --status-soft: rgb(from var(--ink) r g b / 0.06);
}

/* The dot pinned to the portrait's corner, which opens into a pill with
   the longer line on hover. A sibling of the avatar rather than a child,
   so the avatar's clipping and its scale on hover leave it alone. The
   ring is the page colour, cutting the dot out of the photo.

   Pinned by its top-left so it grows rightward, away from the face, and
   drops a little as it opens so it clears the role line beside it. */
.identity__status {
    position: absolute;
    top: 49px;
    left: 49px;
    z-index: 3;
    display: flex;
    align-items: center;
    min-width: 17px;
    height: 17px;
    border: 3px solid var(--background);
    border-radius: 999px;
    background: var(--status);
    color: var(--background);
    box-sizing: border-box;
    outline: none;
    cursor: default;
    transition: height 0.2s ease-out, translate 0.2s ease-out;
}

/* A larger, invisible target than an 11px dot. */
.identity__status::after {
    content: '';
    position: absolute;
    inset: -6px;
}

/* The ping, only while the dot is a dot and the state is a live one. */
.identity__status::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: var(--status);
    opacity: 0;
}

.identity[data-status="available"] .identity__status::before,
.identity[data-status="partial"] .identity__status::before {
    animation: ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}

.identity__status-label {
    display: block;
    max-width: 0;
    overflow: hidden;
    font-size: 12px;
    line-height: 1;
    white-space: nowrap;
    opacity: 0;
    transition: max-width 0.25s ease-out, padding 0.25s ease-out, opacity 0.15s ease-out;
}

.identity__status:hover,
.identity__status:focus-visible {
    height: 26px;
    translate: 0 4px;
}

/* Matches the specificity of the state rules above, or the ping keeps
   running round the open pill. */
.identity[data-status] .identity__status:hover::before,
.identity[data-status] .identity__status:focus-visible::before,
.identity[data-status] .identity__status:focus::before {
    animation: none;
}

.identity__status:hover .identity__status-label,
.identity__status:focus-visible .identity__status-label {
    max-width: 180px;
    padding: 0 8px;
    opacity: 1;
    transition: max-width 0.25s ease-out, padding 0.25s ease-out, opacity 0.2s ease-out 0.05s;
}

/* No hover on a phone: a tap focuses it instead. */
@media (hover: none) {
    .identity__status:focus {
        height: 26px;
        translate: 0 4px;
    }

    .identity__status:focus .identity__status-label {
        max-width: 180px;
        padding: 0 8px;
        opacity: 1;
    }
}

@keyframes ping {
    0% {
        transform: scale(1);
        opacity: 0.6;
    }

    75%,
    100% {
        transform: scale(2);
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .identity__status,
    .identity__status-label {
        transition: none;
    }

    .identity__status::before {
        animation: none !important;
    }
}

.intro strong {
    font-weight: 400;
    color: var(--text-primary);
}

/* Inline chip link — the reference's signature move */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-inline: 2px;
    padding: 2px 6px;
    border-radius: var(--r-sm);
    background: var(--control);
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.4;
    translate: 0 -1px;
    white-space: nowrap;
    transition: background-color 0.15s ease-out;
}

.chip:hover {
    background: var(--control-hover);
}

.chip svg,
.chip img {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

/* The brand marks are square app icons now, so keep their corner radius */
.chip img {
    border-radius: 3px;
}

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

/* A run of client chips folded into one pill of overlapping logos. Hover,
   focus or a tap unfolds it: the logos slide apart and each name grows back
   in. Collapsed, the wrapper's font-size is zero so the spaces between the
   chips take no room and their backgrounds butt into a single pill. */
.chip-stack {
    font-size: 0;
    white-space: nowrap;
    cursor: pointer;
}

.chip-stack .chip,
.chip-stack__count {
    margin-inline: 0;
    padding-inline: 0;
    border-radius: 0;
    gap: 0;
    transition:
        margin 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        padding 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        gap 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        border-radius 0.35s ease-out,
        background-color 0.15s ease-out;
}

.chip-stack .chip + .chip,
.chip-stack .chip + .chip-stack__and + .chip {
    margin-inline-start: -4px;
}

.chip-stack .chip:first-child {
    padding-inline-start: 6px;
    border-start-start-radius: var(--r-sm);
    border-end-start-radius: var(--r-sm);
}

/* A ring in the pill's own colour separates the overlapping marks */
.chip-stack .chip img {
    position: relative;
    box-shadow: 0 0 0 1.5px var(--control);
}

.chip-stack .chip__label {
    display: inline-block;
    max-width: 0;
    overflow: hidden;
    opacity: 0;
    transition:
        max-width 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        opacity 0.2s ease-out;
}

.chip-stack__and {
    opacity: 0;
    transition: opacity 0.2s ease-out;
}

.chip-stack__count {
    display: inline-flex;
    align-items: center;
    max-width: 120px;
    padding: 2px 6px 2px 6px;
    border-start-end-radius: var(--r-sm);
    border-end-end-radius: var(--r-sm);
    background: var(--control);
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1.4;
    translate: 0 -1px;
    white-space: nowrap;
    overflow: hidden;
    transition:
        max-width 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        padding 0.35s cubic-bezier(0.22, 1, 0.36, 1),
        opacity 0.15s ease-out;
}

/* A chip's baseline is its icon's bottom edge. This empty icon-height box
   gives the count the same one, so the two halves of the pill line up. */
.chip-stack__count::before {
    content: '';
    height: 14px;
}

.chip-stack.is-open {
    font-size: inherit;
    white-space: normal;
    cursor: auto;
}

.chip-stack.is-open .chip {
    margin-inline: 2px;
    padding-inline: 6px;
    border-radius: var(--r-sm);
    gap: 6px;
}

.chip-stack.is-open .chip img {
    box-shadow: 0 0 0 0 var(--control);
}

.chip-stack.is-open .chip__label {
    max-width: 160px;
    opacity: 1;
    transition-delay: 0s, 0.08s;
}

.chip-stack.is-open .chip-stack__and {
    opacity: 1;
}

.chip-stack.is-open .chip-stack__count {
    max-width: 0;
    padding-inline: 0;
    opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
    .chip-stack .chip,
    .chip-stack .chip__label,
    .chip-stack__and,
    .chip-stack__count {
        transition: none;
    }
}

/* A dialog takes focus on open so the close button does not show a ring.
   The dialog is only a focus holder, so it should not show one either —
   real keyboard navigation inside still does. */
.contact-card:focus,
.contact-card:focus-visible,
.lightbox:focus,
.lightbox:focus-visible,
dialog[tabindex="-1"]:focus-visible {
    outline: none;
}

/* ── Contact card ────────────────────────────────────────── */

.identity__avatar--button {
    cursor: pointer;
    transition: scale 0.15s ease-out;
}

.identity__avatar--button:hover {
    scale: 1.04;
}

.identity__avatar--button:active {
    scale: 0.98;
}

/* A dialog takes focus on open so the close button does not show a ring.
   The dialog is only a focus holder, so it should not show one either —
   real keyboard navigation inside still does. */
.contact-card:focus,
.contact-card:focus-visible,
.lightbox:focus,
.lightbox:focus-visible {
    outline: none;
}

.contact-card {
    position: fixed;
    inset: 0;
    margin: auto;
    width: min(calc(var(--content-width) - 2 * var(--gutter)), calc(100vw - 2 * var(--gutter)));
    max-width: none;
    max-height: calc(100dvh - 48px);
    padding: 0;
    border: 0;
    /* No panel: the pieces sit straight on the blurred scrim so they can
       breathe, the way Koto's city card sits on the page. */
    background: transparent;
    box-shadow: none;
    color: #fff;
    overflow: auto;
    overscroll-behavior: contain;
    font: 15px/1.5 var(--font-sans);
}

.contact-card[open] {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.contact-card::backdrop {
    background: #000000a6;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.card-city {
    font-size: 17px;
    font-weight: 500;
    color: #fff;
}

.card-head button {
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: #fff;
    cursor: pointer;
}

/* esc is one of the card's chips — same padding, radius and type as the
   social links at the foot, so the two ends of the card match. */
.contact-card kbd,
.lightbox kbd,
.lightbox__name {
    padding: 6px 12px;
    border-radius: var(--r-sm);
    background: #ffffff1f;
    color: inherit;
    font: 14px/1.4 var(--font-sans);
    transition: background-color 0.15s ease-out;
}

.card-head button:hover kbd,
.lightbox__top button:hover kbd {
    background: #ffffff33;
}

/* ── Hero: the studio, carrying the address ──────────────── */

.card-hero {
    position: relative;
    display: block;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: var(--r-lg);
    background: #ffffff14;
}

.card-hero img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: scale 0.4s cubic-bezier(0.23, 1, 0.32, 1);
}

.card-hero:hover img {
    scale: 1.03;
}

/* Scrim so the address stays readable over any part of the photo */
.card-hero::after {
    content: '';
    position: absolute;
    inset: auto 0 0;
    height: 55%;
    background: linear-gradient(to top, #000000b8, transparent);
    pointer-events: none;
}

.card-hero__hello {
    position: absolute;
    left: 12px;
    bottom: 10px;
    z-index: 1;
    display: flex;
    flex-direction: column;
    line-height: 1.3;
    color: #fff;
}

.card-hero__label {
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.75;
}

.card-hero__value {
    font-size: 15px;
    font-weight: 500;
    overflow-wrap: anywhere;
}

/* ── Three tiles: disc, clock, map ───────────────────────── */

.card-tiles {
    display: grid;
    /* minmax(0,…) not 1fr: the tags are nowrap, and their min-content width
       would otherwise push the three tiles onto two rows.
       16px to match the gap the card stacks its rows on. */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
}

.card-tile {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--r-md);
    background: #ffffff14;
    color: #fff;
}

.card-tile__tag {
    position: absolute;
    left: 6px;
    bottom: 6px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: calc(100% - 12px);
    min-height: 32px;
    padding: 6px 7px;
    border: 0;
    border-radius: 7px;
    background: #000000a3;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
    font: 11px/20px var(--font-sans);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── The player ──────────────────────────────────────────────
   After the Figma player: the record rises out of the top of the
   card and is cut off by it, the transport sits underneath, and the
   sleeve name closes it off. It keeps the square the clock and the
   map keep, so the three read as one row.

   Every measurement is the drawing's own, carried across as a share
   of the player's width — 120/828 of it for the play button, 48/828
   for the two beside it. Sized in cqw against the player itself, so
   the whole composition holds together at whatever width the row
   hands it.
   ─────────────────────────────────────────────────────────── */

.card-player {
    container-type: inline-size;
    display: block;
}

/* 825.667 / 828 wide, pulled up by 379 / 828 so only the lower arc shows */
.card-player__disc {
    position: absolute;
    top: -45.77cqw;
    left: 50%;
    width: 99.72cqw;
    aspect-ratio: 1;
    translate: -50% 0;
    border-radius: 999px;
    overflow: hidden;
    background: #d9d9d9;
    border: 0.6cqw solid #000;
    box-shadow: 0 7.7cqw 7.2cqw #00000026;
}

/* 889.828 of the disc's 825.667 in the drawing, nudged 13.97 off centre:
   the sleeve is larger than the record and cropped by it. */
.card-player__disc > img {
    position: absolute;
    top: 50%;
    left: calc(50% - 1.69cqw);
    width: 107.77%;
    height: 107.77%;
    /* Past the global img cap, which would otherwise fit the sleeve to
       the record instead of letting the record crop it. */
    max-width: none;
    translate: -50% -50%;
    object-fit: cover;
    border-radius: 999px;
    animation: spin 14s linear infinite;
    animation-play-state: paused;
}

.card-player.is-playing .card-player__disc > img {
    animation-play-state: running;
}

/* The label: four rings, largest first, sitting still while the art turns */
.card-player__ring {
    position: absolute;
    top: 50%;
    left: 50%;
    aspect-ratio: 1;
    translate: -50% -50%;
    border-radius: 999px;
}

.card-player__ring--1 { width: 33.19%; background: #b0b0b066; }
.card-player__ring--2 { width: 28.57%; background: #d0d0d0; }
.card-player__ring--3 { width: 17.44%; background: #8a8a8a40; }
.card-player__ring--4 {
    width: 12.60%;
    background: #fff;
    border: 0.73cqw solid #aaa;
}

.card-player__controls {
    position: absolute;
    top: 60.63cqw;
    left: 50%;
    translate: -50% 0;
    display: flex;
    align-items: center;
    gap: 5.24cqw;
}

.card-player__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    /* Flat, like the design. The supplied icons carry a stroke and a fill,
       so a translucent colour doubles up along every edge and the glyph
       reads as an outline instead of a solid. */
    color: #bebebe;
    cursor: pointer;
    transition: color 0.15s ease-out, scale 0.15s ease-out;
}

.card-player__btn:hover { color: #949494; }
.card-player__btn:active { scale: 0.94; }

.card-player__btn svg {
    display: block;
    width: 100%;
    height: 100%;
}

.card-player__btn:not(.card-player__btn--main) {
    width: 5.8cqw;
    height: 5.8cqw;
}

.card-player__btn--main,
.card-player__icon,
.card-player__icon svg {
    width: 14.49cqw;
    height: 14.49cqw;
}

/* Keep the shared labels clear of the transport in narrow three-tile rows. */
@media (max-width: 480px) {
    .card-tile__tag {
        min-height: 24px;
        padding: 2px 5px;
    }
    .card-player__controls {
        top: auto;
        bottom: 35px;
    }
}

@keyframes spin {
    to {
        rotate: 360deg;
    }
}

@media (prefers-reduced-motion: reduce) {
    .card-player__disc > img {
        animation: none;
    }
}

.card-clock__face {
    width: 76%;
    height: 76%;
}

.card-clock__zone {
    margin-left: 4px;
    opacity: 0.6;
}

.card-clock__time {
    font-variant-numeric: tabular-nums;
}

/* The scrim is dark in both themes, so the map always inverts */
.card-map img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: invert(1);
    opacity: 0.75;
}

.card-map__credit {
    position: absolute;
    top: 5px;
    right: 6px;
    font-size: 8px;
    line-height: 1;
    color: #ffffff8c;
}

.card-links {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
}

.card-links .chip {
    margin-inline: 0;
    translate: none;
    padding: 6px 12px;
    background: #ffffff1f;
    color: #fff;
}

.card-links .chip:hover {
    background: #ffffff33;
}

/* A dialog takes focus on open so the close button does not show a ring.
   The dialog is only a focus holder, so it should not show one either —
   real keyboard navigation inside still does. */
.contact-card:focus,
.contact-card:focus-visible,
.lightbox:focus,
.lightbox:focus-visible,
dialog[tabindex="-1"]:focus-visible {
    outline: none;
}


/* ── Index pages (/work, /archive) ───────────────────────── */

/* These pages lead straight with the grid — the breadcrumb already names
   the page, so there is no heading above it to supply the spacing. */
.index-grid {
    margin: 40px 0 96px;
}

/* ── Sections ────────────────────────────────────────────── */

.section {
    margin-top: 80px;
}

.section__head {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

.section__title {
    font-size: 16px;
}

.section__count {
    font-size: 14px;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
}

.section__more {
    margin-inline-start: auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 14px;
    color: var(--text-secondary);
    transition: color 0.15s ease-out;
}

.section__more:hover {
    color: var(--text-primary);
}

.section__more svg {
    width: 14px;
    height: 14px;
    transition: translate 0.15s ease-out;
}

.section__more:hover svg {
    translate: 2px 0;
}

.section__body {
    margin-top: 20px;
}

.section__note {
    font-size: 15px;
    color: var(--text-secondary);
}

/* ── Experience rows ────────────────────────────────────── */

.row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 0;
    border-radius: var(--r-lg);
}

.row__mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: var(--r-md);
    background: var(--surface);
    transition: background-color 0.15s ease-out;
}

.row:hover .row__mark {
    background: var(--surface-hover);
}

/* Wordmark lockups are horizontal, so the plate gives them the full width
   and only pads the short edge. */
.row__mark img {
    width: 36px;
    height: 26px;
    object-fit: contain;
}

/* Most lockups are near-black, which would vanish on the dark plate */
.dark .row__mark img {
    filter: invert(1) hue-rotate(180deg);
}

/* A square brand icon brings its own fill and corner radius, so the plate
   steps out of the way and the artwork runs edge to edge — and it keeps its
   own colours in dark mode rather than being inverted. */
.row__mark--icon,
.row:hover .row__mark--icon {
    background: none;
}

.row__mark--icon img,
.dark .row__mark--icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    filter: none;
}

.row__monogram {
    font-size: 16px;
    font-weight: 500;
    color: var(--text-primary);
}

.row__text {
    min-width: 0;
    flex: 1;
    line-height: 1.4;
}

.row__title {
    display: block;
    font-weight: 500;
    color: var(--text-primary);
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 3px;
    transition: text-decoration-color 0.15s;
}

.row:hover .row__title {
    text-decoration-color: currentColor;
}

.row__desc {
    display: block;
    font-size: 15px;
    color: var(--text-secondary);
}

.row__ext {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    color: var(--text-tertiary);
    transition: color 0.15s ease-out;
}

.row__ext svg {
    width: 16px;
    height: 16px;
    transition: translate 0.15s ease-out;
}

.row:hover .row__ext {
    color: var(--text-primary);
}

.row:hover .row__ext svg {
    translate: 2px -2px;
}

/* ── Case cards ─────────────────────────────────────────── */

.case-grid {
    columns: 1;
    column-gap: 20px;
}

.case-grid > li {
    display: inline-block;
    width: 100%;
    margin-bottom: 32px;
    break-inside: avoid;
    vertical-align: top;
}

@media (min-width: 560px) {
    .case-grid {
        columns: 2;
    }

    /* Alternate tall and wide covers without forcing shared row heights. */
    .case-grid > li:nth-child(4n + 1) .case-card__art,
    .case-grid > li:nth-child(4n) .case-card__art {
        aspect-ratio: 1;
    }
}

.case-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.case-card__art {
    position: relative;
    display: block;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--tray-shadow);
    transition: box-shadow 0.2s ease-out;
}

.case-card:hover .case-card__art {
    box-shadow: var(--tray-shadow-hover);
}

.case-card__art img,
.case-card__art video {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: scale 0.35s cubic-bezier(0.23, 1, 0.32, 1);
}

.case-card:hover .case-card__art img,
.case-card:hover .case-card__art video {
    scale: 1.03;
}

.case-card__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.45;
}

.case-card__title {
    display: flex;
    align-items: center;
    gap: 4px;
    font-weight: 500;
    color: var(--text-primary);
}

.case-card__arrow {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: var(--text-tertiary);
    transition: color 0.15s ease-out, translate 0.15s ease-out;
}

.case-card:hover .case-card__arrow {
    color: var(--text-primary);
    translate: 2px 0;
}

.case-card__desc {
    font-size: 15px;
    color: var(--text-secondary);
    text-wrap: pretty;
}

.case-card__meta {
    margin-top: 4px;
    font-size: 14px;
    color: var(--text-tertiary);
}

/* ── Archive ─────────────────────────────────────────────── */

.archive-grid {
    margin-top: 20px;
    columns: 2;
    column-gap: 12px;
}

@media (min-width: 640px) {
    .archive-grid {
        columns: 3;
    }
}

.archive-grid li {
    margin-bottom: 12px;
    break-inside: avoid;
}

.archive-tile {
    position: relative;
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    overflow: hidden;
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--tray-shadow);
    cursor: zoom-in;
    transition: box-shadow 0.2s ease-out, scale 0.2s ease-out;
}

.archive-tile:hover {
    box-shadow: var(--tray-shadow-hover);
    scale: 1.01;
}

.archive-tile img,
.archive-tile video {
    width: 100%;
    height: auto;
}

.archive-tile__label {
    position: absolute;
    bottom: 12px;
    left: 12px;
    display: flex;
    align-items: center;
    height: 24px;
    padding: 0 8px;
    border-radius: var(--r-sm);
    background: var(--raised);
    box-shadow: var(--tray-shadow);
    color: var(--text-primary);
    font-size: 12px;
    white-space: nowrap;
    opacity: 0;
    translate: 0 4px;
    pointer-events: none;
    transition: opacity 0.2s ease-out, translate 0.2s ease-out;
}

.archive-tile:hover .archive-tile__label,
.archive-tile:focus-visible .archive-tile__label {
    opacity: 1;
    translate: 0 0;
}

/* The archive is the one page that leads with artwork rather than text, so
   on a desktop screen it leaves the reading column behind: the whole width,
   two up and large, instead of three narrow plates adrift in the middle.
   Only the grid widens — the header and footer keep the reading column they
   hold on every other page, so the fixture at the top stays one shape
   wherever you are. */
@media (min-width: 1024px) {
    .shell--wide {
        /* One measure for every gap on the page: the two outer edges, the
           channel between the columns and the space between stacked plates. */
        --archive-gap: 16px;
        max-width: none;
        padding-inline: var(--archive-gap);
    }

    .shell--wide .site-header--sticky,
    .shell--wide .site-footer {
        width: 100%;
        max-width: calc(var(--content-width) - 2 * var(--gutter));
        margin-inline: auto;
    }

    /* Contracting into the pill still wins over the reading column. */
    .shell--wide .site-header--sticky.is-scrolled {
        max-width: 440px;
    }

    .shell--wide .archive-grid {
        columns: 2;
        column-gap: var(--archive-gap);
    }

    .shell--wide .archive-grid li {
        margin-bottom: var(--archive-gap);
    }

    /* A plate this much bigger needs a corner to match. The lightbox only
       runs below this width, where the plates are small enough to be worth
       opening — here they are already the artwork, so they drop the zoom
       cursor and the lift rather than promise a click that does nothing.
       The label still arrives on hover. */
    .shell--wide .archive-tile {
        border-radius: var(--r-xl);
        cursor: default;
    }

    .shell--wide .archive-tile:hover {
        box-shadow: var(--tray-shadow);
        scale: 1;
    }
}

/* ── Lightbox ────────────────────────────────────────────── */

/* Same construction as .command-menu: a native <dialog> opened with
   showModal(), so focus trapping, Escape and the backdrop come free. */
.lightbox {
    position: fixed;
    inset: 0;
    margin: auto;
    width: min(var(--content-width), calc(100vw - 24px));
    max-width: none;
    max-height: calc(100dvh - 48px);
    padding: 0;
    border: 0;
    /* The image is the whole panel now, so there is no surface behind it */
    background: transparent;
    box-shadow: none;
    color: #fff;
    overflow: visible;
    font: 15px/1.5 var(--font-sans);
}

.lightbox[open] {
    display: flex;
    flex-direction: column;
}

.lightbox::backdrop {
    background: #0006;
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
}

.lightbox__figure {
    margin: 0;
    min-height: 0;
    flex: 0 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    /* panel max-height minus the esc row above it */
    max-height: calc(100dvh - 48px - 44px);
    border-radius: var(--r-lg);
    /* No fill: a surface here showed as grey margins around any image that
       did not happen to match the panel's proportions. */
    background: transparent;
}

.lightbox__img[hidden] { display: none; }

.lightbox__img {
    display: block;
    max-width: 100%;
    max-height: calc(100dvh - 48px - 44px);
    width: auto;
    height: auto;
    object-fit: contain;
}

/* The name and esc sit above the image rather than on a bar across it,
   the same head the contact card uses. */
.lightbox__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.lightbox__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
    color: #fff;
}

.lightbox__top button {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: #fff;
    cursor: pointer;
}

/* ── Wandering ───────────────────────────────────────────── */

/* Small plates, all one proportion, run edge to edge, uncaptioned. */
.wander-wall {
    --wander-gap: 8px;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--wander-gap);
}

@media (min-width: 480px) {
    .wander-wall { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

@media (min-width: 640px) {
    .wander-wall { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

.wander-cell {
    position: relative;
    aspect-ratio: 3 / 4;
    transition: opacity 0.26s ease-out;
}

.wander-frame {
    position: absolute;
    inset: 0;
    display: block;
    padding: 0;
    border: 0;
    overflow: hidden;
    border-radius: 4px;
    background: var(--surface);
    cursor: pointer;
}

.wander-frame:focus-visible {
    outline: 2px solid var(--text-primary);
    outline-offset: 2px;
}

.wander-frame img,
.wander-frame > .image-skeleton {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The plate after the sixth stands in for the rest: its photo darkened
   under a count of what is still to see. */
.wander-more {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #0000008c;
    color: #fff;
    font-size: 15px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    transition: background-color 0.2s ease-out;
}

.wander-frame--more:hover .wander-more {
    background: #000000a6;
}

/* Pointing at a place fades every other one back. */
.wander-wall.is-focusing > :not(.is-active) {
    opacity: 0.12;
}

/* The rail. A full-screen <dialog>: the head keeps to the reading column,
   the rail runs the whole width, and every slide is half that column wide
   so two sit under the title — capped by height on a short screen. */
.wander-modal {
    --wander-column: min(calc(var(--content-width) - 2 * var(--gutter)), calc(100vw - 2 * var(--gutter)));
    --wander-start: calc((100vw - var(--wander-column)) / 2);
    --wander-gap: 12px;
    --wander-slide: min(calc((var(--wander-column) - var(--wander-gap)) / 2), calc((100dvh - 160px) * 0.75));
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100dvh;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: #fff;
    overflow: hidden;
    font: 15px/1.5 var(--font-sans);
}

.wander-modal[open] {
    display: flex;
}

.wander-modal:focus { outline: none; }

.wander-modal::backdrop {
    background: #0009;
    backdrop-filter: blur(50px);
    -webkit-backdrop-filter: blur(50px);
    animation: fade-in var(--wander-duration, 0.24s) cubic-bezier(0.33, 1, 0.68, 1) both;
}

/* The head floats over the rail so the slides centre on the screen, and
   the rail fills it top to bottom so a plate travelling in from anywhere
   on the wall is never clipped on the way. */
.wander-modal__head {
    position: absolute;
    z-index: 50;
    top: 0;
    inset-inline: 0;
    width: var(--wander-column);
    margin-inline: auto;
    padding: 20px 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    opacity: 0;
    transition: opacity var(--wander-duration, 0.24s) cubic-bezier(0.33, 1, 0.68, 1);
}

.wander-modal:is([data-state="opening"], [data-state="open"]) .wander-modal__head {
    opacity: 1;
}

.wander-modal__title {
    margin: 0;
    font-size: 16px;
    font-weight: 500;
    color: #fff;
}

/* esc rather than a cross — the same chip the lightbox and the contact
   card close with, so every overlay on the site is dismissed the same way. */
.wander-modal__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: #fff;
    cursor: pointer;
}

.wander-modal__close kbd {
    padding: 6px 12px;
    border-radius: var(--r-sm);
    background: #ffffff1f;
    color: inherit;
    font: 14px/1.4 var(--font-sans);
    transition: background-color 0.15s ease-out;
}

.wander-modal__close:hover kbd {
    background: #ffffff33;
}

.wander-modal__close:focus-visible {
    outline: none;
}

.wander-modal__close:focus-visible kbd {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.wander-modal__rail {
    position: relative;
    flex: 1;
    display: flex;
    align-items: center;
    gap: var(--wander-gap);
    overflow: auto hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    cursor: grab;
}

.wander-modal__rail::-webkit-scrollbar { display: none; }
.wander-modal__rail[data-dragging="true"] { cursor: grabbing; }

.wander-modal__cycle {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--wander-gap);
}

.wander-modal__lead {
    flex: 0 0 calc(var(--wander-start) - var(--wander-gap));
}

.wander-modal__rail[data-gaps="collapsed"] .wander-modal__lead {
    display: none;
}

.wander-slide {
    position: relative;
    flex: 0 0 var(--wander-slide);
    width: var(--wander-slide);
    aspect-ratio: 3 / 4;
    margin: 0;
    overflow: hidden;
    border-radius: var(--r-md);
    background: #ffffff14;
}

.wander-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
}

/* Leaving is quieter than arriving: everything fades together. */
.wander-modal[data-state="closing"],
.wander-modal[data-state="closing"]::backdrop {
    animation: wander-out 0.22s ease-out both;
}

@keyframes wander-out {
    to { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .wander-cell,
    .wander-modal__head {
        transition: none;
    }

    .wander-modal::backdrop { animation: none; }
}

/* ── Writing list ────────────────────────────────────────── */

.lede {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 10px 0;
    border-radius: var(--r-sm);
}

.lede__title {
    min-width: 0;
    color: var(--text-primary);
    text-wrap: pretty;
}

.lede__rule {
    min-width: 32px;
    flex: 1;
    translate: 0 -4px;
    border-bottom: 1px dotted rgb(from var(--text-tertiary) r g b / 0.55);
    transition: border-color 0.2s;
}

.lede:hover .lede__rule {
    border-color: var(--text-secondary);
}

.lede__date {
    flex-shrink: 0;
    font-size: 14px;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
    transition: color 0.2s;
}

.lede:hover .lede__date {
    color: var(--text-primary);
}

/* ── Contact ─────────────────────────────────────────────── */

.contact {
    margin-top: 20px;
    padding: 20px;
    border-radius: var(--r-lg);
    background: var(--surface);
}

.contact__copy {
    font-size: 15px;
    color: var(--text-secondary);
    text-wrap: pretty;
}


.socials {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 16px;
}

.socials .chip {
    margin-inline: 0;
    translate: none;
}

/* ── Footer ──────────────────────────────────────────────── */

.site-footer {
    margin-top: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 96px 0 8px;
    font-size: 13px;
    color: var(--text-tertiary);
}

.site-footer__left {
    display: flex;
    align-items: center;
    gap: 8px;
    font-variant-numeric: tabular-nums;
}

.site-footer__mark {
    color: var(--text-primary);
}

.site-footer__hint {
    display: none;
}

@media (min-width: 640px) {
    .site-footer__hint {
        display: inline;
    }
}

kbd {
    padding: 2px 6px;
    border-radius: 6px;
    background: var(--control);
    font-family: var(--font-sans);
    font-size: 11px;
}

/* ── Command menu ────────────────────────────────────────── */
.dock-spacer { height: 96px; flex-shrink: 0; }
.header-actions { gap: 8px; }
.menu-trigger {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: 34px;
    padding: 5px 10px 5px 12px;
    border: 0;
    border-radius: 9px;
    background: var(--control);
    color: var(--text-secondary);
    font: 500 14px/1.4 var(--font-sans);
    cursor: pointer;
}
.menu-trigger__icon { display: none; }
@media (max-width: 639px) {
    .menu-trigger .menu-trigger__icon { display: block; }
    .menu-trigger__label { display: none; }
    .header-actions .menu-trigger { width: 34px; height: 34px; padding: 0; justify-content: center; }
}
.menu-trigger:hover { background: var(--control-hover); color: var(--text-primary); }
.menu-trigger kbd {
    padding: 1px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, currentColor 8%, transparent);
    font: inherit;
}
.command-menu {
    position: fixed;
    inset: auto 0 16px;
    margin: 0 auto;
    width: min(420px, calc(100vw - 24px));
    max-width: none;
    max-height: calc(100dvh - 48px);
    padding: 0;
    border: 0;
    border-radius: var(--r-xl);
    background: var(--raised);
    color: var(--text-primary);
    box-shadow: var(--popover-shadow);
    overflow: hidden;
    font: 15px/1.5 var(--font-sans);
}
.command-menu[open] { display: flex; flex-direction: column; }
.command-menu::backdrop { background: #0006; backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); }
.command-menu__bar { display: flex; align-items: center; gap: 12px; height: 48px; flex-shrink: 0; padding: 0 10px 0 16px; border-bottom: 1px solid var(--border); }
.command-menu__bar input { flex: 1; min-width: 0; border: 0; padding: 0; outline: none; background: transparent; color: inherit; font: inherit; /* 16px minimum — iOS zooms the viewport on focus below this */ font-size: 16px; }
.command-menu__bar input::placeholder { color: var(--text-tertiary); }
.command-menu__bar button { background: transparent; color: var(--text-secondary); border: 0; cursor: pointer; }
.command-menu kbd { padding: 2px 5px; border-radius: 4px; background: var(--control); font: 12px/1.4 var(--font-sans); }
.command-menu__results { max-height: 280px; overflow-y: auto; overscroll-behavior: contain; padding: 0 8px 8px; }

.command-menu__results a { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 4px; padding: 9px 10px; border-radius: 10px; }
.dock__kind { color: var(--text-tertiary); font-size: 12px; }
.command-menu__results a.is-selected, .command-menu__results a:hover { background: var(--control); }
.command-menu__empty { padding: 16px 8px; color: var(--text-secondary); }

@media (max-width: 639px) {
    .intro {
        margin-top: 60px;
    }
}

@media (max-width: 400px) {
    .case-page .site-header { gap: 8px; }
    .case-page .crumbs { gap: 5px; }
    .case-page .site-header--sticky.is-scrolled { padding-inline: 12px; }
    .case-page .header-actions { gap: 4px; }
    .case-page .menu-trigger { gap: 5px; padding-inline: 8px; }
}

/* ── Entrance ───────────────────────────────────────────────
   One short fade for the whole page. No stagger, no loading screen —
   the signature draw in the header is the only timed moment.
   ─────────────────────────────────────────────────────────── */

.shell {
    animation: fade-in 0.25s ease-out both;
}

@keyframes fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@media (prefers-reduced-motion: reduce) {

    .shell {
        animation: none;
    }
}

.dock__mark { width: 17px; height: 17px; flex-shrink: 0; color: var(--text-tertiary); }
.dock-launcher {
    position: fixed;
    inset: auto 0 max(var(--search-bottom, 16px), env(safe-area-inset-bottom));
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 12px;
    width: min(300px, calc(100vw - 24px));
    height: 48px;
    margin: 0 auto;
    padding: 0 10px 0 16px;
    border: 0;
    border-radius: var(--r-xl);
    background: var(--raised);
    color: var(--text-tertiary);
    box-shadow: var(--popover-shadow);
    font: 15px/1.5 var(--font-sans);
    cursor: pointer;
}
.dock-launcher[hidden] { display: none; }
.dock-launcher span { flex: 1; text-align: left; }
.dock-launcher kbd { font: 12px var(--font-sans); padding: 2px 5px; border-radius: 4px; background: var(--control); }
@media (min-width: 640px) {
    .command-menu { bottom: 24px; }
}

/* On a phone the software keyboard — and on iOS its accessory bar of
   up/down arrows and Done — covers anything pinned to the bottom, which
   buried the input and the results the moment the menu opened. Anchor it
   to the top instead, where the keyboard cannot reach it. */
@media (max-width: 639px) {
    .command-menu {
        inset: 12px 0 auto;
        max-height: calc(100dvh - 24px);
    }

    .command-menu__results {
        max-height: min(380px, calc(100dvh - 140px));
    }
}

@media (min-width: 840px) {
    .command-menu {
        inset: 0;
        margin: auto;
    }
}

/* Keep navigation fluid without moving the reading layout. */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
    body { transition: background-color 180ms ease, color 180ms ease; }
    ::view-transition-old(root) { animation: fade-in 160ms ease reverse both; }
    ::view-transition-new(root) { animation: fade-in 220ms ease both; }
    html:active-view-transition .shell { animation: none; }
    .contact-card[open] { animation: contact-enter 220ms ease-out both; }
    .contact-card[open]::backdrop { animation: fade-in 180ms ease-out both; }
}

@keyframes contact-enter {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
}

.card-audio-status { color: #fff; font-size: 12px; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; animation: none !important; }
    ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

.case-card__art .case-card__next { position: absolute; inset: 0; }

/* ── Skeletons ───────────────────────────────────────────────
   Held in the place the content will take, so nothing moves when the
   two swap. The base is --surface, the same grey the covers and the
   case hero already sit on while their image decodes, so a skeleton
   and a still-loading picture are the same shade.
   ─────────────────────────────────────────────────────────── */

.skeleton {
    position: relative;
    display: block;
    overflow: hidden;
    background: var(--surface);
    border-radius: var(--r-sm);
}

/* The sweep is a highlight passing over the shape, not the shape
   blinking: a pulse at this size reads as something being wrong. */
.skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    translate: -100% 0;
    background: linear-gradient(90deg, transparent, var(--surface-hover), transparent);
    animation: skeleton-sweep 1.5s ease-in-out infinite;
}

@keyframes skeleton-sweep {
    to { translate: 100% 0; }
}

.skeleton__line { height: 1em; border-radius: 4px; }
.skeleton__block { width: 100%; border-radius: var(--r-lg); }

/* The cards keep their own art ratio and radius; only the sheen is new. */
.skeleton-card { pointer-events: none; }
.skeleton-card .case-card__art { box-shadow: none; }

/* An archive plate waiting for its picture. The sheen sits inside the tile
   rather than on it, so the tile keeps its own corner and shadow. */
.skeleton-tile { pointer-events: none; }
.skeleton-tile > .skeleton { border-radius: inherit; }

/* Each plate already holds the proportion its picture will arrive at, so
   the shimmer fills that box instead of imposing a ratio of its own. */
.archive-tile > .image-skeleton { width: 100%; height: 100%; aspect-ratio: auto; }

/* A Wandering plate keeps filling its cell while it waits, rather than
   taking the skeleton's own flow position. */
.wander-frame.skeleton { position: absolute; border-radius: 4px; pointer-events: none; }

.skeleton-post,
.skeleton-para,
.skeleton-prose,
.skeleton-case,
.skeleton-case__intro,
.skeleton-case__body {
    display: flex;
    flex-direction: column;
}

.skeleton-para { gap: 8px; }
.skeleton-prose { gap: 24px; }
.skeleton-case { gap: 28px; }
.skeleton-case__intro { gap: 10px; margin-top: 28px; }
.skeleton-case__lede { margin-top: 10px; }
.skeleton-case__body { gap: 28px; }

/* Matches .case-hero's full-bleed run to the viewport edges. */
.skeleton__hero {
    aspect-ratio: 16 / 9;
    max-height: 72vh;
    border-radius: 0;
}

.skeleton-case__meta {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px;
}

.skeleton-case__cell { display: flex; flex-direction: column; }
.skeleton-head { display: flex; flex-direction: column; gap: 8px; }

@media (max-width: 719px) {
    .skeleton-case__meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Stillness, not a frozen animation: the shapes stay, the sweep stops. */
@media (prefers-reduced-motion: reduce) {
    .skeleton::after { animation: none; opacity: 0; }
}

/* A quiet signal while published content is arriving. */
.page-progress {
    position: fixed;
    inset: 0 0 auto;
    height: 2px;
    z-index: 100;
    overflow: hidden;
    pointer-events: none;
}
.page-progress::before {
    content: '';
    display: block;
    width: 35%;
    height: 100%;
    background: var(--text-tertiary);
    opacity: 0.5;
    animation: page-progress 1.4s ease-in-out infinite;
}
@keyframes page-progress {
    from { transform: translateX(-100%); }
    to { transform: translateX(286%); }
}
@media (prefers-reduced-motion: reduce) {
    .page-progress::before { width: 100%; animation: none; }
}

/* Reserve image space while lazy loading; captions remain outside the shimmer. */
.image-skeleton {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
}
.image-skeleton > .image-skeleton__pending {
    opacity: 0;
    width: 100%;
    height: 100%;
}
.case-card__art > .image-skeleton { height: 100%; aspect-ratio: auto; }
.case-hero > .image-skeleton { max-height: 72vh; }
.case-quote__who > .image-skeleton { width: 36px; height: 36px; border-radius: 50%; }

/* A slow wash of colour belongs to the end of the page. */
body {
    position: relative;
}

body::after {
    content: '';
    position: absolute;
    z-index: 0;
    pointer-events: none;
    inset: auto 0 0;
    height: 420px;
    opacity: 0.18;
    background-image:
        radial-gradient(ellipse at 50% 100%, #00b74a 0%, transparent 65%),
        radial-gradient(ellipse at 50% 100%, #393bfe 0%, transparent 65%),
        radial-gradient(ellipse at 50% 100%, #5270ff 0%, transparent 68%);
    background-size: 70% 100%, 65% 85%, 60% 75%;
    background-position: -45% 100%, 110% 100%, 45% 100%;
    background-repeat: no-repeat;
    animation: footer-glow 18s ease-in-out infinite alternate;
}

.shell { position: relative; z-index: 1; }

.dark body::after { opacity: 0.35; }

@keyframes footer-glow {
    50% { background-position: 30% 100%, 65% 100%, 120% 100%; }
    100% { background-position: 110% 100%, -40% 100%, 35% 100%; }
}

.team-card {
    flex-shrink: 0;
    margin-top: 48px;
    padding: 20px;
    border-radius: 14px;
    background: var(--surface);
    color: var(--text-primary);
}

.team-card h2 { font-size: 16px; line-height: 1.4; }
.team-card p { margin-top: 4px; color: var(--text-secondary); font-size: 15px; line-height: 1.5; }
.team-card__talk {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 20px;
    padding: 8px 24px;
    min-height: 40px;
    border-radius: 8px;
    background: #00b344;
    color: #fff;
    font-size: 15px;
    font-weight: 500;
    transition: background 160ms ease;
}
.team-card__talk:hover { background: #008f36; }
.page-search-slot + .site-footer {
    flex-shrink: 0;
    margin-top: 0;
    padding: 28px 0 24px;
    font-size: 12px;
    gap: 12px;
    flex-wrap: wrap;
}

.page-search-slot {
    flex-shrink: 0;
    margin-top: auto;
    min-height: 96px;
    padding: 48px 0 0;
}

.team-card + .page-search-slot { margin-top: 0; padding-top: 32px; min-height: 80px; }

.page-search-slot .dock-launcher {
    position: fixed;
    inset: auto 0 max(var(--search-bottom, 16px), env(safe-area-inset-bottom));
    z-index: 30;
    inset: auto;
    width: min(300px, 100%);
}


@media (prefers-reduced-motion: reduce) {
    body::after { animation: none; }
    .team-card__talk { transition: none; }
}
