.app-layout {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    height: 100dvh;
}

.header-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 2rem var(--content-inset);
    transition: padding 0.25s ease-out;
    border-bottom: 1px solid var(--text-color);
}

body.category-open .header-content {
    padding: 0.5rem var(--content-inset);
}

.app-layout h1 {
    font-size: calc((clamp(1.5rem, 5vw, 5rem)) * var(--font-scale));
    font-family: var(--font-display);
    font-weight: 900;
    transition: font-size 0.25s ease-out;
}

.app-layout h1 a {
    text-decoration: none;
    color: inherit;
}

body.category-open .app-layout h1 {
    font-size: calc((clamp(1rem, 3vw, 2.5rem)) * var(--font-scale));
}

body.category-open .app-layout h1 {
    cursor: pointer;
}

main#nav-grid {
    display: grid;
    /* No gap: the dividing hairlines are drawn as cell borders instead of the
       grid background bleeding through a 1px gap. Browsers snap borders to whole
       device pixels, so the lines stay a crisp 1px at any DPR — unlike a gap at
       a sub-pixel track boundary, which rendered ~2px on fractional mobile
       viewport heights. Tracks therefore sum to exactly 100%. */
    grid-template-columns: 50% 50%;
    grid-template-rows: 50% 50%;
    margin: 0;
    list-style: none;
    position: relative;
    width: auto;
    height: auto;
    align-self: stretch;
    justify-self: stretch;
    background-color: var(--text-color);
    transition: grid-template-columns 0.25s ease-out, grid-template-rows 0.25s ease-out;
}

main#nav-grid.has-active.active-work {
    grid-template-columns: calc(100% - 2rem) 2rem;
    grid-template-rows: calc(100% - 2rem) 2rem;
}

main#nav-grid.has-active.active-play {
    grid-template-columns: 2rem calc(100% - 2rem);
    grid-template-rows: calc(100% - 2rem) 2rem;
}

main#nav-grid.has-active.active-thoughts {
    grid-template-columns: calc(100% - 2rem) 2rem;
    grid-template-rows: 2rem calc(100% - 2rem);
}

main#nav-grid.has-active.active-contact {
    grid-template-columns: 2rem calc(100% - 2rem);
    grid-template-rows: 2rem calc(100% - 2rem);
}

main#nav-grid > article {
    display: block;
    position: relative;
    background-color: var(--bg-color);
    overflow: hidden;
    transition: background-color 0.25s ease-out, border-color 0.2s ease;
    /* Internal dividers: each cell paints its right + bottom hairline. Edge
       cells' borders coincide with the outer frame (body::after), so nothing
       doubles. box-sizing:border-box keeps them inside the track. */
    border-right: 1px solid var(--text-color);
    border-bottom: 1px solid var(--text-color);
}

.category-bg {
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity 0.25s ease-out;
    z-index: 0;
}

.category-bg.bg-work {
    background-color: var(--color-work);
}

.category-bg.bg-play {
    background-color: var(--color-play);
}

.category-bg.bg-thoughts {
    background-color: var(--color-thoughts);
}

.category-bg.bg-contact {
    background-color: var(--color-contact);
}

main#nav-grid.has-active.active-work article:nth-child(1) .category-bg,
main#nav-grid.has-active.active-play article:nth-child(2) .category-bg,
main#nav-grid.has-active.active-thoughts article:nth-child(3) .category-bg,
main#nav-grid.has-active.active-contact article:nth-child(4) .category-bg {
    opacity: 1;
}

main#nav-grid.has-active.active-work article:nth-child(1) .category-trigger,
main#nav-grid.has-active.active-play article:nth-child(2) .category-trigger,
main#nav-grid.has-active.active-thoughts article:nth-child(3) .category-trigger,
main#nav-grid.has-active.active-contact article:nth-child(4) .category-trigger {
    pointer-events: none;
}

main#nav-grid.has-active.active-work article:nth-child(1) a.category-trigger .label,
main#nav-grid.has-active.active-play article:nth-child(2) a.category-trigger .label,
main#nav-grid.has-active.active-thoughts article:nth-child(3) a.category-trigger .label,
main#nav-grid.has-active.active-contact article:nth-child(4) a.category-trigger .label {
    pointer-events: auto;
    cursor: pointer;
}

/* On wide screens the active label rides out to the content gutter. Each active
   "big cell" is already inset 2rem on its hugging side, so adding the gutter
   shift lands the label on the same content-inset line as the page content.
   Work/Thoughts hug the right; Play/Contact hug the left. */
main#nav-grid.has-active.active-work article:nth-child(1) .category-trigger,
main#nav-grid.has-active.active-thoughts article:nth-child(3) .category-trigger {
    padding-right: calc(2rem + var(--label-gutter-shift));
}

main#nav-grid.has-active.active-play article:nth-child(2) .category-trigger,
main#nav-grid.has-active.active-contact article:nth-child(4) .category-trigger {
    padding-left: calc(2rem + var(--label-gutter-shift));
}

@media (hover: hover) and (pointer: fine) {
    main#nav-grid:not(.has-active) article:nth-child(1):hover {
        background-color: var(--color-work);
    }

    main#nav-grid:not(.has-active) article:nth-child(2):hover {
        background-color: var(--color-play);
    }

    main#nav-grid:not(.has-active) article:nth-child(3):hover {
        background-color: var(--color-thoughts);
    }

    main#nav-grid:not(.has-active) article:nth-child(4):hover {
        background-color: var(--color-contact);
    }
}

main#nav-grid > article .category-trigger {
    display: flex;
    width: 100%;
    height: 100%;
    padding: 2rem;
    text-decoration: none;
    font-size: var(--category-label-size);
    font-weight: 600;
    /* Animate padding so the active label slides out to the gutter as part of
       the open/close animation (and chases the gutter gracefully on resize). */
    transition: padding 0.25s ease-out, var(--page-color-transition);
    font-family: var(--font-heading);
    position: absolute;
    inset: 0;
    box-sizing: border-box;
}

main#nav-grid > article .category-trigger.bottom-layer {
    z-index: 4;
    color: var(--label-bg-color);
    pointer-events: none;
}

main#nav-grid > article a.category-trigger.top-layer {
    z-index: 10;
    color: white;
    mix-blend-mode: difference;
}

/* Keyboard focus: a frame around the category label itself (not the whole
   cell), held 1rem off the text on every side. It rides on the difference-blend
   top layer like the label, so it stays legible over any category colour. */
main#nav-grid > article a.category-trigger:focus-visible {
    outline: none;
}

main#nav-grid > article a.category-trigger:focus-visible .label {
    outline: 2px solid currentColor;
    outline-offset: 1rem;
}

main#nav-grid > article .category-trigger .label {
    overflow: hidden;
    white-space: nowrap;
}


main#nav-grid.has-active.active-work article#cell-work .category-content,
main#nav-grid.has-active.active-play article#cell-play .category-content,
main#nav-grid.has-active.active-thoughts article#cell-thoughts .category-content,
main#nav-grid.has-active.active-contact article#cell-contact .category-content {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: scale(1);
    /* Stay at z-index 5 (from global.css): below the difference-blend
       top-layer label (z10) so content scrolls "sandwiched" between the
       solid bottom-layer fallback label (z4) and the blended top label. */
}

main#nav-grid .category-content {
    transform: scale(0.95);
    transition: opacity 0.25s ease-out, visibility 0.25s ease-out, transform 0.25s ease-out;
}


main#nav-grid.has-active.active-work article:not(:nth-child(1)) .category-trigger .label,
main#nav-grid.has-active.active-play article:not(:nth-child(2)) .category-trigger .label,
main#nav-grid.has-active.active-thoughts article:not(:nth-child(3)) .category-trigger .label,
main#nav-grid.has-active.active-contact article:not(:nth-child(4)) .category-trigger .label {
    opacity: 0;
    font-size: 0;
}


main#nav-grid > article .category-trigger .label {
    position: relative;
    transition: opacity 0.25s ease-out, font-size 0.25s ease-out;
}

/* The corner back-arrow is the Material Symbols `arrow_insert` glyph (base ↖),
   drawn via CSS mask so it themes through currentColor and can be rotated to
   point at each back corner. Only ::before is used now; ::after is kept inert so
   the existing visibility group-selectors that still name it stay harmless. */
:root {
    --arrow-insert: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%20-960%20960%20960'%3E%3Cpath%20d='M704-240%20320-624v344h-80v-480h480v80H376l384%20384-56%2056Z'/%3E%3C/svg%3E");
}

main#nav-grid > article .category-trigger::before,
main#nav-grid > article .category-trigger::after {
    content: '';
    position: absolute;
    opacity: 0;
    transition: opacity 0.25s ease-out, transform 0.25s ease-out;
    pointer-events: none;
}

main#nav-grid > article .category-trigger::before {
    /* Smaller than the 2rem collapsed corner cell so it can sit centred with
       clear breathing room from the edges it points toward. */
    width: 1.2rem;
    height: 1.2rem;
    background-color: currentColor;
    -webkit-mask: var(--arrow-insert) center / contain no-repeat;
    mask: var(--arrow-insert) center / contain no-repeat;
}

main#nav-grid > article .category-trigger::after {
    display: none;
}

main#nav-grid.active-work article:nth-child(4) .category-trigger::before,
main#nav-grid.active-work article:nth-child(4) .category-trigger::after,
main#nav-grid.active-play article:nth-child(3) .category-trigger::before,
main#nav-grid.active-play article:nth-child(3) .category-trigger::after,
main#nav-grid.active-thoughts article:nth-child(2) .category-trigger::before,
main#nav-grid.active-thoughts article:nth-child(2) .category-trigger::after,
main#nav-grid.active-contact article:nth-child(1) .category-trigger::before,
main#nav-grid.active-contact article:nth-child(1) .category-trigger::after {
    opacity: 1;
}

/* Back labels */
main#nav-grid .back-label {
    position: absolute;
    opacity: 0;
    font-size: calc((1.15rem) * var(--font-scale));
    line-height: 1;
    pointer-events: none;
    transition: opacity 0.25s ease-out, right 0.25s ease-out, left 0.25s ease-out;
    font-family: var(--font-heading);
    font-weight: 600;
}

/* When Work is active: Arrow is in Contact (4). Horizontally adjacent is Thoughts (3).
   Thoughts (3) is the wide strip at the bottom. Position label at the right edge. */
main#nav-grid.active-work article:nth-child(3) .back-label {
    opacity: 1;
    right: 7px;
    top: 50%;
    transform: translateY(-50%);
    bottom: auto;
    left: auto;
}

/* When Play is active: Arrow is in Thoughts (3). Horizontally adjacent is Contact (4).
   Contact (4) is the wide strip at the bottom. Position label at the left edge. */
main#nav-grid.active-play article:nth-child(4) .back-label {
    opacity: 1;
    left: 7px;
    top: 50%;
    transform: translateY(-50%);
    bottom: auto;
    right: auto;
}

/* When Thoughts is active: Arrow is in Play (2). Horizontally adjacent is Work (1).
   Work (1) is the wide strip at the top. Position label at the right edge. */
main#nav-grid.active-thoughts article:nth-child(1) .back-label {
    opacity: 1;
    right: 7px;
    top: 50%;
    transform: translateY(-50%);
    bottom: auto;
    left: auto;
}

/* When Contact is active: Arrow is in Work (1). Horizontally adjacent is Play (2).
   Play (2) is the wide strip at the top. Position label at the left edge. */
main#nav-grid.active-contact article:nth-child(2) .back-label {
    opacity: 1;
    left: 7px;
    top: 50%;
    transform: translateY(-50%);
    bottom: auto;
    right: auto;
}

/* --- Second back-arrow beside the active label -------------------------------
   On extra-wide screens the active label rides out into the content gutter, far
   enough from the corner close-arrow that the two stop reading as one control. A
   full diagonal arrow (the same two primitives as the corner arrows: ::before =
   the arrowhead bracket, ::after = the diagonal shaft) is drawn just beyond the
   label, in the gutter it moved into, mirroring that corner arrow's direction —
   re-establishing that pressing the label takes you back.

   Everything is sized in `em`, so the arrow scales with the label's own
   font-size: the shaft is ~1em tall (rotated √2·em ≈ 1em vertical extent), i.e.
   about the height of the label beside it. It sits at 50% opacity, rising to
   full when the close target (the centre hitbox, which carries .hover-close) is
   hovered. Hidden until the breakpoint below, where the gutter has grown enough
   to break the label↔corner-arrow coherence. */
main#nav-grid > article .category-trigger .label::before,
main#nav-grid > article .category-trigger .label::after {
    content: '';
    position: absolute;
    top: 50%;
    opacity: 0;
    transition: opacity 0.25s ease-out;
    pointer-events: none;
}

main#nav-grid > article .category-trigger .label::before { /* arrowhead */
    width: 0.5em;
    height: 0.5em;
    border: 0 solid currentColor;
    margin: -0.25em;
}

main#nav-grid > article .category-trigger .label::after { /* shaft */
    width: 0.09em;
    height: 1.4em;
    background: currentColor;
    margin: -0.7em -0.045em;
}

/* The active label must not clip the arrow that sits outside its box. */
main#nav-grid.has-active.active-work article:nth-child(1) .label,
main#nav-grid.has-active.active-play article:nth-child(2) .label,
main#nav-grid.has-active.active-thoughts article:nth-child(3) .label,
main#nav-grid.has-active.active-contact article:nth-child(4) .label {
    overflow: visible;
}

/* Per category: anchor on the gutter side of the label (left:100% / right:100%),
   push the shaft ~0.8em into the gutter, and seat the arrowhead on the shaft's
   far tip (±0.5em along the diagonal). Directions mirror each corner arrow. */
/* Work — right gutter, ↘ (as cell 4). */
main#nav-grid.active-work article:nth-child(1) .label::before {
    left: 100%;
    transform: translate(calc(0.8em + 0.25em), 0.25em);
    border-width: 0 0.14em 0.14em 0;
}
main#nav-grid.active-work article:nth-child(1) .label::after {
    left: 100%;
    transform: translate(0.8em, 0) rotate(-45deg);
}

/* Thoughts — right gutter, ↗ (as cell 2). */
main#nav-grid.active-thoughts article:nth-child(3) .label::before {
    left: 100%;
    transform: translate(calc(0.8em + 0.25em), -0.25em);
    border-width: 0.14em 0.14em 0 0;
}
main#nav-grid.active-thoughts article:nth-child(3) .label::after {
    left: 100%;
    transform: translate(0.8em, 0) rotate(45deg);
}

/* Play — left gutter, ↙ (as cell 3). */
main#nav-grid.active-play article:nth-child(2) .label::before {
    right: 100%;
    transform: translate(calc(-0.8em - 0.25em), 0.25em);
    border-width: 0 0 0.14em 0.14em;
}
main#nav-grid.active-play article:nth-child(2) .label::after {
    right: 100%;
    transform: translate(-0.8em, 0) rotate(45deg);
}

/* Contact — left gutter, ↖ (as cell 1). */
main#nav-grid.active-contact article:nth-child(4) .label::before {
    right: 100%;
    transform: translate(calc(-0.8em - 0.25em), -0.25em);
    border-width: 0.14em 0 0 0.14em;
}
main#nav-grid.active-contact article:nth-child(4) .label::after {
    right: 100%;
    transform: translate(-0.8em, 0) rotate(-45deg);
}

/* --- Extra-wide "Back" pill --------------------------------------------------
   On extra-wide screens the active label rides far out into the gutter, so the
   bare corner arrow + "Back" text read as disconnected from the title. Replace
   that whole wide-screen affordance with a single explicit "Back" pill carrying
   the diagonally-opposite category's arrow, seated in the diagonally-opposite
   corner over the centre close hitbox (which still handles hover + click — the
   pill is decorative, pointer-events:none). The plain back-label, the corner
   close-arrow, and the old label-adjacent diagonal arrow are all hidden here. */
.wide-back-pill {
    display: none;
    position: absolute;
    z-index: 510;
    align-items: center;
    gap: 0.45ch;
    padding: 0.4em 0.85em;
    border: 1px solid var(--text-color);
    border-radius: 999px;
    /* Solid surface that follows the theme even when the close target isn't
       hovered (hover then fills it with the back cell's colour, below). */
    background: var(--bg-color);
    color: var(--text-color);
    font-family: var(--font-body);
    font-size: calc((1.4rem) * var(--font-scale));
    line-height: 1;
    pointer-events: none;
    transition: background-color 0.25s ease-out, var(--page-color-transition);
}

.wbp-arrow {
    display: block;
    flex-shrink: 0;
    font-size: 17px; /* arrow_insert (↖ base); rotated to point at the corner per category below */
}

.wbp-label {
    display: block;
}

@media (min-width: 1700px) {
    main#nav-grid.has-active .wide-back-pill {
        display: inline-flex;
    }

    /* Hide the corner close-arrow (diagonally-opposite cell) in this mode. */
    main#nav-grid.active-work article:nth-child(4) .category-trigger::before,
    main#nav-grid.active-work article:nth-child(4) .category-trigger::after,
    main#nav-grid.active-play article:nth-child(3) .category-trigger::before,
    main#nav-grid.active-play article:nth-child(3) .category-trigger::after,
    main#nav-grid.active-thoughts article:nth-child(2) .category-trigger::before,
    main#nav-grid.active-thoughts article:nth-child(2) .category-trigger::after,
    main#nav-grid.active-contact article:nth-child(1) .category-trigger::before,
    main#nav-grid.active-contact article:nth-child(1) .category-trigger::after {
        opacity: 0;
    }

    /* Hide the plain "Back" label (horizontally-adjacent cell) in this mode. */
    main#nav-grid.active-work article:nth-child(3) .back-label,
    main#nav-grid.active-play article:nth-child(4) .back-label,
    main#nav-grid.active-thoughts article:nth-child(1) .back-label,
    main#nav-grid.active-contact article:nth-child(2) .back-label {
        opacity: 0;
    }

    /* Seat the pill beside the active label, out in the gutter it rides into.
       The label's gutter-side edge always lands on the --content-inset line
       (independent of the label's width), so anchor there and translate the
       pill fully into the gutter, vertically centred on the label. That centre
       sits 4rem from the cell's outer edge — the 2rem grid strip plus the
       trigger's 2rem padding — plus half the label's height (the translate's
       ±50% then centres the pill box on that line); anchoring at only 2rem
       pulled the pill a strip-width toward the centre cross. The arrow points
       toward the back corner (the diagonally-opposite arrow direction). */
    main#nav-grid.active-work .wide-back-pill {
        right: var(--content-inset);
        bottom: calc(4rem + var(--category-label-size) * 0.6);
        transform: translate(calc(100% + 2rem), 50%);
    }
    main#nav-grid.active-work .wbp-arrow { transform: rotate(180deg); }     /* ↘ Contact */

    main#nav-grid.active-play .wide-back-pill {
        left: var(--content-inset);
        bottom: calc(4rem + var(--category-label-size) * 0.6);
        transform: translate(calc(-100% - 2rem), 50%);
    }
    main#nav-grid.active-play .wbp-arrow { transform: rotate(270deg); }     /* ↙ Thoughts */

    main#nav-grid.active-thoughts .wide-back-pill {
        right: var(--content-inset);
        top: calc(4rem + var(--category-label-size) * 0.6);
        transform: translate(calc(100% + 2rem), -50%);
    }
    main#nav-grid.active-thoughts .wbp-arrow { transform: rotate(90deg); }  /* ↗ Play */

    main#nav-grid.active-contact .wide-back-pill {
        left: var(--content-inset);
        top: calc(4rem + var(--category-label-size) * 0.6);
        transform: translate(calc(-100% - 2rem), -50%);
    }
    main#nav-grid.active-contact .wbp-arrow { transform: rotate(0deg); }    /* ↖ Work */

    /* Hovering the centre close target fills the pill with the diagonally
       opposite category's background colour. */
    main#nav-grid.hover-close.active-work .wide-back-pill { background-color: var(--color-contact); }
    main#nav-grid.hover-close.active-play .wide-back-pill { background-color: var(--color-thoughts); }
    main#nav-grid.hover-close.active-thoughts .wide-back-pill { background-color: var(--color-play); }
    main#nav-grid.hover-close.active-contact .wide-back-pill { background-color: var(--color-work); }
}

/* Each cell's corner glyph sits centred in its 2rem collapsed corner cell (so it
   doesn't crowd the edges) and is rotated from the arrow_insert base (↖) to
   point at that corner. */
/* Cell 4: ↘ (Work active) */
main#nav-grid > article:nth-child(4) .category-trigger::before {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(180deg);
}

/* Cell 3: ↙ (Play active) */
main#nav-grid > article:nth-child(3) .category-trigger::before {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(270deg);
}

/* Cell 2: ↗ (Thoughts active) */
main#nav-grid > article:nth-child(2) .category-trigger::before {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(90deg);
}

/* Cell 1: ↖ (Contact active) */
main#nav-grid > article:nth-child(1) .category-trigger::before {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(0deg);
}

/* Hovering the close target tints the diagonally-opposite corner square. Capped
   below 1700px: on wider screens the explicit Back pill carries the hover fill
   instead, and the little corner square must stay neutral. */
@media (hover: hover) and (pointer: fine) and (max-width: 1699px) {
    main#nav-grid.hover-close.active-work > article:nth-child(4) {
        background-color: var(--color-contact) !important;
    }

    main#nav-grid.hover-close.active-play > article:nth-child(3) {
        background-color: var(--color-thoughts) !important;
    }

    main#nav-grid.hover-close.active-thoughts > article:nth-child(2) {
        background-color: var(--color-play) !important;
    }

    main#nav-grid.hover-close.active-contact > article:nth-child(1) {
        background-color: var(--color-work) !important;
    }
}

#center-close-hitbox {
    position: absolute;
    z-index: 500;
    cursor: pointer;
    border: none;
    background: transparent;
    display: none;
    font-family: var(--font-body);
    /* Match the label size so the ch-based width below tracks the real label. */
    font-size: var(--category-label-size);
    font-weight: 600;
}

main#nav-grid.has-active #center-close-hitbox {
    display: block;
}

main#nav-grid.active-work #center-close-hitbox {
    bottom: 0;
    right: 0;
    width: calc(3.5rem + (var(--hitbox-text-chars) * 1ch) + 0.5ch + var(--label-gutter-shift));
    height: calc(4rem + 1.2em);
}

main#nav-grid.active-play #center-close-hitbox {
    bottom: 0;
    left: 0;
    width: calc(3.5rem + (var(--hitbox-text-chars) * 1ch) + 0.5ch + var(--label-gutter-shift));
    height: calc(4rem + 1.2em);
}

main#nav-grid.active-thoughts #center-close-hitbox {
    top: 0;
    right: 0;
    width: calc(3.5rem + (var(--hitbox-text-chars) * 1ch) + 0.5ch + var(--label-gutter-shift));
    height: calc(4rem + 1.2em);
}

main#nav-grid.active-contact #center-close-hitbox {
    top: 0;
    left: 0;
    width: calc(3.5rem + (var(--hitbox-text-chars) * 1ch) + 0.5ch + var(--label-gutter-shift));
    height: calc(4rem + 1.2em);
}

main#nav-grid.active-work article:nth-child(4) .category-trigger,
main#nav-grid.active-play article:nth-child(3) .category-trigger,
main#nav-grid.active-thoughts article:nth-child(2) .category-trigger,
main#nav-grid.active-contact article:nth-child(1) .category-trigger {
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
}

/* Collapse padded space around inactive SVGs */
main#nav-grid.has-active.active-work > article:not(:nth-child(1)) .category-trigger,
main#nav-grid.has-active.active-play > article:not(:nth-child(2)) .category-trigger,
main#nav-grid.has-active.active-thoughts > article:not(:nth-child(3)) .category-trigger,
main#nav-grid.has-active.active-contact > article:not(:nth-child(4)) .category-trigger {
    pointer-events: none;
    padding: 0 !important;
}

main#nav-grid > article:nth-child(1) .category-trigger {
    align-items: flex-end;
    justify-content: flex-end;
}

main#nav-grid > article:nth-child(2) .category-trigger {
    align-items: flex-end;
    justify-content: flex-start;
}

main#nav-grid > article:nth-child(3) .category-trigger {
    align-items: flex-start;
    justify-content: flex-end;
}

main#nav-grid > article:nth-child(4) .category-trigger {
    align-items: flex-start;
    justify-content: flex-start;
}

#center-close-hitbox:focus-visible {
    outline: none !important;
}

#center-close-hitbox:focus-visible::after {
    content: '';
    position: absolute;
    border: 2px dashed var(--text-color);
    height: 2.2rem;
    width: calc(2rem + (var(--hitbox-text-chars) * 1ch) + 1rem);
    pointer-events: none;
}

/* Custom focus ring placement for each active state */
main#nav-grid.active-work #center-close-hitbox:focus-visible::after {
    bottom: -0.1rem;
    right: -0.1rem;
}

main#nav-grid.active-play #center-close-hitbox:focus-visible::after {
    bottom: -0.1rem;
    left: -0.1rem;
}

main#nav-grid.active-thoughts #center-close-hitbox:focus-visible::after {
    top: -0.1rem;
    right: -0.1rem;
}

main#nav-grid.active-contact #center-close-hitbox:focus-visible::after {
    top: -0.1rem;
    left: -0.1rem;
}

/* On touch devices, disable pointer events on the overlay labels so dragging naturally scrolls the content underneath. 
   Taps are handled via coordinate checking in JS. */
@media (hover: none) and (pointer: coarse) {
    main#nav-grid.has-active.active-work article:nth-child(1) a.category-trigger .label,
    main#nav-grid.has-active.active-play article:nth-child(2) a.category-trigger .label,
    main#nav-grid.has-active.active-thoughts article:nth-child(3) a.category-trigger .label,
    main#nav-grid.has-active.active-contact article:nth-child(4) a.category-trigger .label {
        pointer-events: none !important;
    }
    /* The hitbox stays a real button on touch (it sits above the content), so
       taps over the label/arrow region close the category directly instead of
       falling through to e.g. the Play canvas — which previously hijacked the
       press, panned, and flickered the close animation. */
}

