/* ==========================================================================
   Tamara Jill Olson — Theater Portfolio
   ========================================================================== */

/* --------------------------------------------------------------------------
   Custom Properties
   -------------------------------------------------------------------------- */

:root {

    /* Colors */
    --black: #000;
    --white: #fff;

    --black-15: #00000026;
    --black-50: #00000080;
    --white-15: #FFFFFF26;
    --white-20: #FFFFFF33;
    --white-35: #FFFFFF59;
    --white-50: #FFFFFF80;

    --background: var(--black);
    --text: var(--white);

    /* Typography */
    --display-font: 'Anton', 'Haettenschweiler', 'Arial Narrow Bold', sans-serif;
    --body-font: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;

    /* Set against the blocky caps of a role, a show title wants the opposite
       voice — italic, serif, lighter on its feet. Enough stroke to hold its own
       against Anton, though: a true Didone's hairlines disappear on black. */
    --show-font: 'Newsreader', Georgia, 'Times New Roman', serif;

    /* Layout */
    --stage-padding: 40px;

    /* Vertical spacing */
    --text-spacing-xxsmall: 8px;
    --text-spacing-xsmall: 16px;
    --text-spacing-small: 24px;
    --text-spacing-medium: 32px;
    --text-spacing-large: 40px;
    --text-spacing-xlarge: 48px;
    --text-spacing-xxlarge: 56px;

    /* Motion */
    --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);
    --reveal-duration: 1.15s;

}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

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

html {
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
}

/* Jumping to the reels from the nav should travel, not cut. */
@media (prefers-reduced-motion: no-preference) {

    html {
        scroll-behavior: smooth;
    }

}

body {
    background: var(--background);
    color: var(--text);
    font-family: var(--body-font);
    margin: 0;
    min-height: 100vh;
    overflow-x: hidden;
}

/* --------------------------------------------------------------------------
   Stage
   -------------------------------------------------------------------------- */

/* The opening is a curtain: it stays pinned to the top and lifts away on scroll,
   uncovering the portrait fixed behind it.
   
   Multiplied over the page, the black panel stays black (black x anything is
   black) while the white type turns to clear glass (white x anything is the
   thing itself) — so the letters read as windows onto the photograph, and they
   slide up over it as the curtain lifts. */
.stage {
    align-items: center;
    background: var(--black);
    mix-blend-mode: multiply;
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    justify-content: center;
    padding: var(--stage-padding) var(--stage-padding)
             calc(var(--stage-padding) + var(--text-spacing-xxlarge));
    position: sticky;
    top: 0;
    width: 100%;
    z-index: 3;
}

/* --------------------------------------------------------------------------
   Portrait
   -------------------------------------------------------------------------- */

/* The section is only there to hold scroll — its height is how long the photo
   stays on screen alone, between the curtain clearing and the reels arriving. */
.portrait {
    height: 150vh;
    position: relative;
    z-index: 1;
}

/* The letters read as solid white first, then the photograph arrives through
   them: a white sheet over the portrait, fading away once the name has landed.
   Multiplying against white gives white, so nothing else has to change. */
.portrait-veil {
    background: var(--white);
    height: 100vh;
    height: 100dvh;
    left: 0;
    position: fixed;
    top: 0;
    width: 100%;
}

.is-ready .portrait-veil {
    animation: veil-clear 1.6s var(--ease-reveal) 1.85s forwards;
}

@keyframes veil-clear {
    from { opacity: 1; }
    to   { opacity: 0; }
}

/* Fixed, so it doesn't travel: the curtain moves, the reels move, this holds. */
.portrait-image {
    filter: grayscale(1);
    height: 100vh;
    height: 100dvh;
    left: 0;
    object-fit: cover;
    object-position: 50% 45%;
    position: fixed;
    top: 0;
    width: 100%;
}

/* --------------------------------------------------------------------------
   Fitted Type

   Lines scaled by JS to span their container exactly, so a stack of them reads
   as one solid block with flush left and right edges. Used by the name and by
   the email address on the contact page.
   -------------------------------------------------------------------------- */

.fit {
    width: 100%;
}

/* Each line is a mask its words travel out of. */
.fit-line {
    display: block;
    overflow: hidden;
}

/* Fallback only — JS sets an exact pixel margin once the lines are fitted. */
.fit-line + .fit-line {
    margin-top: -0.16em;
}

.fit-line-text {
    display: inline-block;
    font-family: var(--display-font);
    font-weight: 400;
    letter-spacing: -0.015em;
    line-height: 1;
    text-transform: uppercase;

    /* Fallback size — JS refits each line to its container on load. */
    font-size: 21vw;

    /* Guards against the mask clipping cap overshoot. */
    padding: 0.06em 0 0.04em;

    /* A line must never wrap — it's measured at a large reference size to work
       out its fitted size, and a wrapped line measures as the container width
       and gets cropped away by its own mask. */
    white-space: nowrap;
}

/* Inline-block, or a transform wouldn't touch it. */
.fit-word {
    display: inline-block;
}

.name {
    margin: 0;
    text-align: center;
}

/* --------------------------------------------------------------------------
   Tagline
   -------------------------------------------------------------------------- */

/* The tagline sits outside the curtain, pinned to the foot of the screen. Inside
   it, the multiply blend would turn it to glass too, and at this size that's
   illegible — the name can afford to be a window, three tracked words can't.
   JS gives it the curtain's transform so the two travel together. */
.tagline {
    bottom: var(--stage-padding);
    left: 0;
    overflow: hidden;
    pointer-events: none;
    position: fixed;
    text-align: center;
    width: 100%;
    z-index: 4;
}

.tagline-text {
    display: inline-block;
    font-size: clamp(11px, 1.15vw, 17px);
    font-weight: 500;
    letter-spacing: 0.42em;
    line-height: 1.4;

    /* Tracking pushes a trailing gap after the last letter — pull it back so the
       line stays optically centered. */
    text-indent: 0.42em;
    text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Reveal
   -------------------------------------------------------------------------- */

/* Hold everything until the fonts have loaded and the lines have been fitted,
   so nothing flashes at the wrong size. */
.fit-line-text,
.tagline-text {
    opacity: 0;
}

.is-ready .fit-line-text {
    opacity: 1;
}

/* Inline-block, or a transform wouldn't touch it. */
.fit-word {
    display: inline-block;
    opacity: 0;
}

/* Each word arrives on its own path, one after the next: TAMARA drops from
   above, then JILL and OLSON close in from either side. The line's mask is what
   hides them beforehand — each starts just past its own edge of it. */
.is-ready .fit-word {
    animation-delay: calc(var(--word-index, 0) * 0.26s);
    animation-duration: 1.15s;
    animation-fill-mode: forwards;
    animation-timing-function: var(--ease-reveal);
}

.is-ready .fit-word.from-top   { animation-name: word-from-top; }
.is-ready .fit-word.from-left  { animation-name: word-from-left; }
.is-ready .fit-word.from-right  { animation-name: word-from-right; }
.is-ready .fit-word.from-bottom { animation-name: word-from-bottom; }

.is-ready .tagline-text {
    animation: tagline-settle 1.4s var(--ease-reveal) forwards;
    animation-delay: 1.05s;
}

@keyframes word-from-top {
    from {
        opacity: 1;
        transform: translate3d(0, -115%, 0);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes word-from-left {
    from {
        opacity: 1;
        transform: translate3d(-115%, 0, 0);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes word-from-right {
    from {
        opacity: 1;
        transform: translate3d(115%, 0, 0);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes word-from-bottom {
    from {
        opacity: 1;
        transform: translate3d(0, 115%, 0);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes tagline-settle {
    from {
        letter-spacing: 0.18em;
        opacity: 0;
        transform: translate3d(0, 100%, 0);
    }
    to {
        letter-spacing: 0.42em;
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}


/* --------------------------------------------------------------------------
   Roles
   -------------------------------------------------------------------------- */

.roles {
    display: flex;
    flex-direction: column;
    justify-content: center;
    background: var(--black);
    min-height: 100vh;
    min-height: 100dvh;
    padding: 0 0 12vh;
    position: relative;
    width: 100%;
    z-index: 2;
}

/* Hierarchy without severance.

   Inverting the ground was the strongest separation the palette has, and that
   was the problem: it read as a different section rather than as this list's
   header. What signals a table head is a heavy rule over hairlines — the same
   device the resume page already uses — because the rule is literally the top
   edge of the list rather than a border between two zones.

   Distinction comes from three things instead: a lot of black above the header,
   the eyebrow set as a white chip, and the weight of that rule. */
.section-head {
    padding: calc(var(--text-spacing-xxlarge) * 1.75) var(--stage-padding)
             var(--text-spacing-medium);
    width: 100%;
}

.section-head .section-intro {
    margin-bottom: 0;
}

/* The pop of white, kept to the label instead of the whole zone. */
.section-head .section-eyebrow {
    background: var(--white);
    color: var(--black);
    display: inline-block;
    padding: 7px 10px 6px;
}

.section-body {
    padding: 0 var(--stage-padding);
    width: 100%;
}

/* Says what the section is, so the list underneath doesn't have to. */
.section-eyebrow {
    color: var(--white-50);
    font-size: clamp(11px, 1vw, 14px);
    font-weight: 600;
    letter-spacing: 0.28em;
    margin: 0 0 var(--text-spacing-small);
    text-transform: uppercase;
}

.section-intro {
    font-size: clamp(24px, 3.6vw, 54px);
    font-weight: 400;
    letter-spacing: -0.03em;
    line-height: 1.15;
    margin: 0 0 var(--text-spacing-xxlarge);
}

.section-ellipsis span {
    display: inline-block;
    margin-left: 0.06em;
}

/* --------------------------------------------------------------------------
   Role List
   -------------------------------------------------------------------------- */

.role-list {
    border-top: 2px solid var(--white);
    list-style: none;
    margin: 0;
    padding: 0;
}

.role-item {
    border-bottom: 1px solid var(--white-15);
}

.role {
    align-items: center;
    color: var(--white);
    display: flex;
    gap: var(--text-spacing-medium);
    margin: 0 calc(-1 * var(--text-spacing-small));
    padding: var(--text-spacing-medium) var(--text-spacing-small);
    position: relative;
    text-decoration: none;
    transition: color 0.45s var(--ease-reveal);
}

/* Hover brings the lights up on the row — a white fill wipes in from the
   bottom and the type inverts against it. */
.role::before {
    background: var(--white);
    content: '';
    inset: 0;
    position: absolute;
    transform: scaleY(0);
    transform-origin: 50% 100%;
    transition: transform 0.5s var(--ease-reveal);
}

.role > * {
    position: relative;
}

a.role:hover,
a.role:focus-visible {
    color: var(--black);
}

a.role:hover::before,
a.role:focus-visible::before {
    transform: scaleY(1);
}

/* Rows with no video yet are type, nothing more — no arrow, no hover, nothing
   that promises a click. */
.role-static::before {
    display: none;
}

/* The inverted row reads as the focus state; the inset rule keeps it explicit. */
a.role:focus-visible {
    outline: 2px solid var(--black);
    outline-offset: -8px;
}

.role-title {
    flex: 1 1 auto;
    font-family: var(--display-font);
    min-width: 0;
    font-size: clamp(26px, 4vw, 66px);
    letter-spacing: -0.01em;
    line-height: 1;
    text-transform: uppercase;
}

/* Set small, tracked and lifted onto the middle of the caps beside it. */
.role-in {
    display: inline-block;
    font-family: var(--body-font);
    font-size: 0.24em;
    font-weight: 500;
    letter-spacing: 0.16em;
    margin: 0 0.9em;
    opacity: 0.55;
    vertical-align: 1.1em;
}

/* Title case, not caps — italic is the convention for the title of a work, and
   the lowercase lets the serif's contrast show. Sized up to match the cap
   height of the Anton beside it. */
.role-show {
    font-family: var(--show-font);
    white-space: nowrap;
    font-size: 1.2em;
    font-style: italic;
    letter-spacing: 0;
    text-transform: none;

    /* Left alone, the browser picks the display cut at these sizes and thins the
       strokes right down. Pinning the optical size to a text cut keeps them
       sturdy, and white on black needs the extra weight to hold up. */
    font-optical-sizing: none;
    font-variation-settings: 'opsz' 14, 'wght' 420;
}

/* Held to the right edge. A row with no reel keeps an empty cell, or the song
   column would drift right on that row alone. */
.role-action {
    align-items: center;
    display: flex;
    flex: none;
    margin-left: auto;
}

.role-song + .role-action {
    justify-content: flex-end;
    margin-left: var(--text-spacing-large);
    width: clamp(44px, 8.5vw, 116px);
}

/* A role whose reel isn't shot yet says so, in the cell the arrow would take —
   so the one row that is ready reads as ready. */
.role-soon {
    color: var(--white-35);
    font-family: var(--body-font);
    font-size: clamp(10px, 0.85vw, 12px);
    font-weight: 600;
    letter-spacing: 0.2em;
    line-height: 1.3;
    text-align: right;
    text-transform: uppercase;
    white-space: nowrap;
}

.role-arrow {
    display: block;
    flex: none;
    height: clamp(24px, 3vw, 44px);
    overflow: hidden;
    position: relative;
    width: clamp(24px, 3vw, 44px);
}

.role-arrow svg {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    transition: transform 0.45s var(--ease-reveal);
    width: 100%;
}

/* Two arrows: on hover the first exits right as the second arrives from the
   left, so the arrow reads as travelling rather than sliding out of frame. */
.role-arrow svg + svg {
    transform: translate3d(-115%, 0, 0);
}

a.role:hover .role-arrow svg,
a.role:focus-visible .role-arrow svg {
    transform: translate3d(115%, 0, 0);
}

a.role:hover .role-arrow svg + svg,
a.role:focus-visible .role-arrow svg + svg {
    transform: translate3d(0, 0, 0);
}

/* --------------------------------------------------------------------------
   Roles Reveal
   -------------------------------------------------------------------------- */

.section-eyebrow,
.section-intro,
.role-item {
    opacity: 0;
}

.is-visible .section-eyebrow {
    animation: rise 1s var(--ease-reveal) forwards;
}

.is-visible .section-intro {
    animation: rise 1s var(--ease-reveal) forwards;
    animation-delay: 0.1s;
}

/* Each row reveals on its own as it's scrolled to, not with its section. */
.role-item.is-visible {
    animation: rise 1s var(--ease-reveal) forwards;
    animation-delay: calc(var(--row-index, 0) * 0.08s);
}

.section-ellipsis span {
    opacity: 0;
}

.is-visible .section-ellipsis span {
    animation: dot 0.45s ease forwards;
}

.is-visible .section-ellipsis span:nth-child(1) { animation-delay: 0.5s; }
.is-visible .section-ellipsis span:nth-child(2) { animation-delay: 0.64s; }
.is-visible .section-ellipsis span:nth-child(3) { animation-delay: 0.78s; }

@keyframes rise {
    from {
        opacity: 0;
        transform: translate3d(0, 30px, 0);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes dot {
    to { opacity: 1; }
}


/* --------------------------------------------------------------------------
   Lightbox
   -------------------------------------------------------------------------- */

.lightbox {
    background: transparent;
    border: 0;
    max-height: none;
    max-width: none;
    padding: 0;
    width: 100%;
}

.lightbox::backdrop {
    backdrop-filter: blur(6px);
    background: rgba(0, 0, 0, 0.96);
}

.lightbox-stage {
    align-items: center;
    display: flex;
    gap: clamp(16px, 2.4vw, 40px);
    justify-content: center;
    padding: 0 var(--stage-padding);
    width: 100%;
}

/* Capped by height rather than width, so the video keeps its ratio without
   letterboxing and the rails always sit flush against its edges. */
.lightbox-main {
    flex: 1 1 auto;
    max-width: calc(74vh * 16 / 9);
    min-width: 0;
}

.lightbox-frame {
    aspect-ratio: 16 / 9;
    background: var(--black);
    width: 100%;
}

.lightbox-frame iframe {
    border: 0;
    display: block;
    height: 100%;
    width: 100%;
}

/* A reel takes the whole screen — nothing beside it but the way out. */
.lightbox:not(.is-image) {
    height: 100vh;
    height: 100dvh;
    inset: 0;
    margin: 0;
    max-height: none;
    max-width: none;
    width: 100vw;
}

.lightbox:not(.is-image) .lightbox-stage {
    height: 100%;
    padding: 0;
}

.lightbox:not(.is-image) .lightbox-rail {
    display: none;
}

.lightbox:not(.is-image) .lightbox-main {
    align-items: center;
    display: flex;
    height: 100%;
    justify-content: center;
    max-width: none;
    width: 100%;
}

/* As large as the screen allows without cropping the performer. */
.lightbox:not(.is-image) .lightbox-frame {
    width: min(100vw, calc(100dvh * 16 / 9));
}

.lightbox:not(.is-image) .lightbox-close {
    background: rgba(0, 0, 0, 0.6);
    margin: 0;
    padding: 12px 16px;
    position: fixed;
    right: var(--stage-padding);
    top: var(--stage-padding);
    z-index: 2;
}

/* A photograph sets its own proportions rather than taking the video's. */
.lightbox.is-image .lightbox-main {
    flex: 0 1 auto;
    max-width: none;
}

.lightbox.is-image .lightbox-frame {
    aspect-ratio: auto;
    background: none;
}

.lightbox-frame img {
    display: block;
    height: auto;
    max-height: 74vh;
    max-width: 100%;
    width: auto;
}


/* --------------------------------------------------------------------------
   Lightbox Rails
   -------------------------------------------------------------------------- */

.lightbox-rail {
    flex: none;
    font-family: var(--display-font);
    font-size: clamp(15px, 1.9vw, 30px);
    letter-spacing: 0.02em;
    line-height: 1;
    margin: 0;
    text-transform: uppercase;
    white-space: nowrap;
    writing-mode: vertical-rl;
}

/* The role reads bottom to top on the left, the link top to bottom on the
   right — the pair frames the video like the sides of a proscenium. */
.lightbox-role {
    color: var(--white);
    rotate: 180deg;
}

.lightbox-contact {
    align-items: center;
    color: var(--white);
    display: flex;
    gap: 0.7em;
    text-decoration: none;
    transition: opacity 0.3s var(--ease-reveal);
}

.lightbox-contact:hover {
    opacity: 0.65;
}

.lightbox-contact:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 8px;
}

.lightbox-contact-arrow {
    display: block;
    flex: none;
    height: 1.15em;
    overflow: hidden;
    position: relative;
    rotate: 90deg;
    width: 1.15em;
}

.lightbox-contact-arrow svg {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    transition: transform 0.45s var(--ease-reveal);
    width: 100%;
}

.lightbox-contact-arrow svg + svg {
    transform: translate3d(-115%, 0, 0);
}

.lightbox-contact:hover .lightbox-contact-arrow svg,
.lightbox-contact:focus-visible .lightbox-contact-arrow svg {
    transform: translate3d(115%, 0, 0);
}

.lightbox-contact:hover .lightbox-contact-arrow svg + svg,
.lightbox-contact:focus-visible .lightbox-contact-arrow svg + svg {
    transform: translate3d(0, 0, 0);
}

/* The rail sets the pairing smaller, so the connector needs a little more air
   around it than it does in the list. */
.lightbox-rail .role-title {
    font-size: inherit;
}

.lightbox-rail .role-in {
    margin: 0 1.5em;
}

/* "I've seen enough" — the way out answers the line that got you here. */
.lightbox-close {
    align-items: center;
    background: none;
    border: 0;
    color: var(--white);
    cursor: pointer;
    display: flex;
    gap: var(--text-spacing-xsmall);
    margin: 0 0 var(--text-spacing-xsmall) auto;
    padding: 8px 0;
    transition: opacity 0.25s ease;
}

.lightbox-close-text {
    font-family: var(--display-font);
    font-size: clamp(13px, 1.05vw, 16px);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.lightbox-close svg {
    flex: none;
    height: 22px;
    width: 22px;
}

/* The wink belongs to a reel. A headshot just closes. */
.lightbox.is-image .lightbox-close-text {
    display: none;
}

.lightbox-close:hover {
    opacity: 0.6;
}

.lightbox-close:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 4px;
}

html.lightbox-open {
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   Utilities
   -------------------------------------------------------------------------- */

/* Read aloud, never seen — used to tell screen readers a link leaves the site. */
.visually-hidden {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}


/* --------------------------------------------------------------------------
   Nav
   -------------------------------------------------------------------------- */

/* Held above the top edge until the opening has been scrolled past. */
.nav {
    background: var(--white);
    color: var(--black);
    left: 0;
    position: fixed;
    top: 0;
    transform: translate3d(0, -100%, 0);
    transition: transform 0.6s var(--ease-reveal);
    width: 100%;
    z-index: 10;
}

.nav.is-down,
.subpage .nav {
    transform: translate3d(0, 0, 0);
}

.nav-inner {
    align-items: center;
    display: flex;
    gap: var(--text-spacing-medium);
    justify-content: space-between;
    padding: var(--text-spacing-xsmall) var(--stage-padding);
}

.nav a {
    color: var(--black);
    font-family: var(--display-font);
    font-size: clamp(13px, 1.05vw, 17px);
    letter-spacing: 0.03em;
    text-decoration: none;
    text-transform: uppercase;
}

.nav-home {
    flex: none;
}

.nav-list {
    display: flex;
    gap: clamp(16px, 2vw, 36px);
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Each link is underlined by a rule that wipes in from the left. */
.nav-list a {
    display: inline-block;
    padding-bottom: 3px;
    position: relative;
}

.nav-list a::after {
    background: var(--black);
    bottom: 0;
    content: '';
    height: 2px;
    left: 0;
    position: absolute;
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform 0.35s var(--ease-reveal);
    width: 100%;
}

.nav-list a:hover::after,
.nav-list a:focus-visible::after,
.nav-list a[aria-current="page"]::after {
    transform: scaleX(1);
}

.nav a:focus-visible {
    outline: 2px solid var(--black);
    outline-offset: 4px;
}

/* --------------------------------------------------------------------------
   Subpages
   -------------------------------------------------------------------------- */

.subpage {
    background: var(--black);
}

.page {
    padding: calc(var(--text-spacing-xxlarge) * 2) var(--stage-padding) var(--text-spacing-xxlarge);
}

.page-title {
    font-family: var(--display-font);
    font-size: clamp(44px, 11vw, 170px);
    letter-spacing: -0.015em;
    line-height: 0.92;
    margin: 0;
    text-transform: uppercase;
}


/* --------------------------------------------------------------------------
   Resume
   -------------------------------------------------------------------------- */

.page-title-compact {
    font-size: clamp(40px, 7vw, 104px);
    margin-bottom: var(--text-spacing-xxlarge);
}

.resume {
    display: grid;
    gap: var(--text-spacing-xxlarge) clamp(32px, 5vw, 80px);
    grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
}

/* The headshot rides along while the credits scroll past it. */
.resume-aside {
    align-self: start;
    position: sticky;
    top: calc(var(--text-spacing-xxlarge) + var(--text-spacing-medium));
}

.resume-headshot {
    display: block;
    height: auto;
    width: 100%;
}

/* The headshot opens at full size — the corner mark says so. */
.headshot {
    background: none;
    border: 0;
    cursor: pointer;
    display: block;
    padding: 0;
    position: relative;
    width: 100%;
}

.headshot img {
    transition: opacity 0.4s var(--ease-reveal);
}

.headshot:hover img,
.headshot:focus-visible img {
    opacity: 0.85;
}

.headshot-expand {
    align-items: center;
    background: var(--white);
    bottom: 0;
    color: var(--black);
    display: flex;
    height: 46px;
    justify-content: center;
    position: absolute;
    right: 0;
    transition: transform 0.35s var(--ease-reveal);
    width: 46px;
}

.headshot:hover .headshot-expand,
.headshot:focus-visible .headshot-expand {
    transform: scale(1.1);
}

.headshot-expand svg {
    height: 20px;
    width: 20px;
}

.headshot:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 4px;
}

.resume-union {
    color: var(--white-50);
    font-size: clamp(11px, 1vw, 13px);
    font-weight: 600;
    letter-spacing: 0.28em;
    margin: var(--text-spacing-small) 0 0;
    text-transform: uppercase;
}

.resume-vitals,
.resume-contact {
    list-style: none;
    margin: var(--text-spacing-small) 0 0;
    padding: 0;
}

.resume-vitals li {
    border-bottom: 1px solid var(--white-15);
    display: flex;
    font-size: clamp(13px, 1.1vw, 15px);
    gap: var(--text-spacing-xsmall);
    justify-content: space-between;
    padding: var(--text-spacing-xxsmall) 0;
}

/* The label side of each stat sits back so the value reads first. */
.resume-vitals span {
    color: var(--white-50);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.resume-contact li {
    font-size: clamp(13px, 1.1vw, 15px);
    margin-top: var(--text-spacing-xxsmall);
}

.resume-contact a {
    color: var(--white);
    text-decoration-color: var(--white-50);
    text-underline-offset: 4px;
    transition: text-decoration-color 0.3s var(--ease-reveal);
}

.resume-contact a:hover {
    text-decoration-color: var(--white);
}

/* --------------------------------------------------------------------------
   Resume Body
   -------------------------------------------------------------------------- */

.resume-block + .resume-block {
    margin-top: var(--text-spacing-xxlarge);
}

.resume-heading {
    border-bottom: 1px solid var(--white);
    font-family: var(--display-font);
    font-size: clamp(18px, 1.7vw, 24px);
    font-weight: 400;
    letter-spacing: 0.04em;
    margin: 0 0 var(--text-spacing-xsmall);
    padding-bottom: var(--text-spacing-xxsmall);
    text-transform: uppercase;
}

.credit-list,
.entry-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Credits carry the same pairing as the reels list — the show in italic serif,
   the role in blocky caps — so the two read as one system. */
.credit {
    align-items: baseline;
    border-bottom: 1px solid var(--white-15);
    display: grid;
    gap: var(--text-spacing-xsmall);
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1.6fr);
    padding: var(--text-spacing-xsmall) 0;
}

.credit p {
    margin: 0;
}

.credit-show {
    font-family: var(--show-font);
    font-size: clamp(19px, 1.9vw, 27px);
    font-style: italic;
    font-optical-sizing: none;
    font-variation-settings: 'opsz' 14, 'wght' 420;
    line-height: 1.15;
}

.credit-role {
    font-family: var(--display-font);
    font-size: clamp(13px, 1.15vw, 16px);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.credit-where {
    color: var(--white-50);
    font-size: clamp(12px, 1vw, 14px);
    line-height: 1.5;
}

.credit-year::before {
    content: ' — ';
}

.credit-group {
    color: var(--white-50);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.22em;
    margin: var(--text-spacing-medium) 0 var(--text-spacing-xsmall);
    text-transform: uppercase;
}

.entry {
    align-items: baseline;
    border-bottom: 1px solid var(--white-15);
    display: grid;
    gap: var(--text-spacing-xsmall);
    grid-template-columns: minmax(0, 1fr) minmax(0, 3.6fr);
    padding: var(--text-spacing-xsmall) 0;
}

.entry p {
    margin: 0;
}

.entry-label {
    font-family: var(--display-font);
    font-size: clamp(13px, 1.15vw, 16px);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.entry-detail {
    font-size: clamp(13px, 1.1vw, 15px);
    line-height: 1.5;
}

.resume-note {
    color: var(--white-50);
    font-size: clamp(13px, 1.1vw, 15px);
    font-style: italic;
    margin: var(--text-spacing-xlarge) 0 0;
}

/* Brighter than the line around it, so it reads as the one thing to click. */
.resume-note a {
    color: var(--white);
    text-decoration: underline;
    text-decoration-color: var(--white-50);
    text-underline-offset: 3px;
    transition: text-decoration-color 0.3s var(--ease-reveal);
}

.resume-note a:hover,
.resume-note a:focus-visible {
    text-decoration-color: var(--white);
}

.resume-note a:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Button
   -------------------------------------------------------------------------- */

.btn {
    align-items: center;
    background: var(--white);
    color: var(--black);
    display: inline-flex;
    font-family: var(--display-font);
    font-size: clamp(13px, 1.05vw, 16px);
    gap: var(--text-spacing-xsmall);
    letter-spacing: 0.04em;
    margin-top: var(--text-spacing-small);
    padding: var(--text-spacing-xsmall) var(--text-spacing-small);
    text-decoration: none;
    text-transform: uppercase;
    transition: opacity 0.3s var(--ease-reveal);
}

.btn:hover {
    opacity: 0.82;
}

.btn:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 3px;
}

/* Turned downward — it's a download, not a link onward. */
.btn-arrow {
    display: block;
    flex: none;
    height: 1.1em;
    overflow: hidden;
    position: relative;
    rotate: 90deg;
    width: 1.1em;
}

.btn-arrow svg {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    transition: transform 0.45s var(--ease-reveal);
    width: 100%;
}

.btn-arrow svg + svg {
    transform: translate3d(-115%, 0, 0);
}

.btn:hover .btn-arrow svg,
.btn:focus-visible .btn-arrow svg {
    transform: translate3d(115%, 0, 0);
}

.btn:hover .btn-arrow svg + svg,
.btn:focus-visible .btn-arrow svg + svg {
    transform: translate3d(0, 0, 0);
}

/* --------------------------------------------------------------------------
   Resume Reveal
   -------------------------------------------------------------------------- */

.resume-section {
    opacity: 0;
}

.is-visible.resume-section,
.is-visible .resume-section {
    animation: rise 1s var(--ease-reveal) forwards;
}


/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

/* White, so the site closes on the same ground the nav and the section heads
   sit on — the colour change is its own edge against the black above it. */
.footer {
    background: var(--white);
    color: var(--black);
    padding: calc(var(--text-spacing-xxlarge) * 1.2) var(--stage-padding)
             var(--text-spacing-large);
    position: relative;
    width: 100%;
    z-index: 2;
}

.footer-upper {
    display: flex;
    gap: clamp(32px, 6vw, 120px);
    justify-content: space-between;
}

/* --- Left: who she is and how to reach her ------------------------------- */

.footer-intro {
    max-width: 42ch;
}

/* The most interesting sentence on the site, so it leads the footer rather than
   sitting under a monogram that said nothing the ghosted name doesn't. */
.footer-note {
    font-size: clamp(18px, 1.7vw, 27px);
    letter-spacing: -0.01em;
    line-height: 1.35;
    margin: 0;
    max-width: 26ch;
}

.footer-email {
    margin: var(--text-spacing-medium) 0 0;
}

.footer-email a {
    color: var(--black);
    font-size: clamp(15px, 1.25vw, 19px);
    text-decoration: underline;
    text-decoration-color: var(--black-50);
    text-underline-offset: 4px;
    transition: text-decoration-color 0.3s var(--ease-reveal);
}

.footer-email a:hover,
.footer-email a:focus-visible {
    text-decoration-color: var(--black);
}

.footer-facts {
    color: var(--black-50);
    font-size: clamp(12px, 1vw, 14px);
    margin: var(--text-spacing-xxsmall) 0 0;
}

/* --- Middle: the same destinations as the top nav ------------------------ */

/* Two columns, filled across — site links land in the left one, everything
   that leaves the site in the right. */
.footer-nav ul {
    display: grid;
    gap: var(--text-spacing-small) clamp(24px, 3vw, 56px);
    grid-auto-flow: row;
    grid-template-columns: repeat(2, auto);
    list-style: none;
    margin: 0;
    padding: 0;
}

.footer-nav a {
    color: var(--black);
    display: inline-block;
    font-family: var(--display-font);
    font-size: clamp(13px, 1.05vw, 17px);
    letter-spacing: 0.03em;
    padding-bottom: 3px;
    position: relative;
    text-decoration: none;
    text-transform: uppercase;
    white-space: nowrap;
}

.footer-nav a::after {
    background: var(--black);
    bottom: 0;
    content: '';
    height: 1px;
    left: 0;
    position: absolute;
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform 0.35s var(--ease-reveal);
    width: 100%;
}

.footer-nav a:hover::after,
.footer-nav a:focus-visible::after {
    transform: scaleX(1);
}

/* --- Right: back to the top ---------------------------------------------- */

.footer-up {
    color: var(--black);
    flex: none;
}

.footer-up-arrow {
    display: block;
    height: clamp(30px, 2.6vw, 42px);
    overflow: hidden;
    position: relative;
    width: clamp(30px, 2.6vw, 42px);
}

.footer-up-arrow svg {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    transition: transform 0.45s var(--ease-reveal);
    width: 100%;
}

/* Travels upward on hover, the way the row arrows travel right. */
.footer-up-arrow svg + svg {
    transform: translate3d(0, 115%, 0);
}

.footer-up:hover .footer-up-arrow svg,
.footer-up:focus-visible .footer-up-arrow svg {
    transform: translate3d(0, -115%, 0);
}

.footer-up:hover .footer-up-arrow svg + svg,
.footer-up:focus-visible .footer-up-arrow svg + svg {
    transform: translate3d(0, 0, 0);
}

.footer-up:focus-visible {
    outline: 2px solid var(--black);
    outline-offset: 6px;
}

/* --- The name, once more, quietly ---------------------------------------- */

/* A reprise of the opening: the same lockup that arrived enormous and windowed
   now sits at the foot of the page, barely there. */
.footer-name {
    margin: clamp(48px, 7vw, 104px) 0 var(--text-spacing-medium);
}

.footer-name .fit-line-text {
    color: var(--black-15);
}

.footer-legal {
    color: var(--black-50);
    font-size: clamp(11px, 0.9vw, 13px);
    margin: 0;
}


/* --------------------------------------------------------------------------
   Gate

   A soft gate while the site is unfinished. It is NOT security — see the
   Gotchas in PROJECT.md. It keeps casual visitors out, nothing more.
   -------------------------------------------------------------------------- */

.gate {
    align-items: center;
    background: var(--black);
    display: none;
    inset: 0;
    justify-content: center;
    padding: var(--stage-padding);
    position: fixed;
    z-index: 100;
}

.is-locked .gate {
    display: flex;
}

/* Nothing behind the gate should scroll or be reachable by tab. */
.is-locked,
.is-locked body {
    overflow: hidden;
}

.gate-form {
    max-width: 520px;
    width: 100%;
}

.gate-label {
    color: var(--white-50);
    font-size: clamp(11px, 1vw, 14px);
    font-weight: 600;
    letter-spacing: 0.28em;
    margin: 0 0 var(--text-spacing-small);
    text-transform: uppercase;
}

.gate-field {
    align-items: center;
    border-bottom: 2px solid var(--white);
    display: flex;
    gap: var(--text-spacing-small);
}

.gate-input {
    background: none;
    border: 0;
    color: var(--white);
    flex: 1 1 auto;
    font-family: var(--display-font);
    font-size: clamp(28px, 5vw, 64px);
    letter-spacing: 0.06em;
    min-width: 0;
    padding: 0 0 var(--text-spacing-xxsmall);
    text-transform: uppercase;
}

.gate-input:focus {
    outline: none;
}

.gate-enter {
    background: none;
    border: 0;
    color: var(--white);
    cursor: pointer;
    flex: none;
    padding: 0 0 var(--text-spacing-xxsmall);
}

.gate-enter-arrow {
    display: block;
    height: clamp(26px, 3vw, 40px);
    overflow: hidden;
    position: relative;
    width: clamp(26px, 3vw, 40px);
}

.gate-enter-arrow svg {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    transition: transform 0.45s var(--ease-reveal);
    width: 100%;
}

.gate-enter-arrow svg + svg {
    transform: translate3d(-115%, 0, 0);
}

.gate-enter:hover .gate-enter-arrow svg,
.gate-enter:focus-visible .gate-enter-arrow svg {
    transform: translate3d(115%, 0, 0);
}

.gate-enter:hover .gate-enter-arrow svg + svg,
.gate-enter:focus-visible .gate-enter-arrow svg + svg {
    transform: translate3d(0, 0, 0);
}

.gate-enter:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 4px;
}

.gate-error {
    color: var(--white-50);
    font-size: clamp(12px, 1vw, 14px);
    margin: var(--text-spacing-small) 0 0;
    min-height: 1.4em;
}

.gate-form.is-wrong {
    animation: gate-refuse 0.45s ease;
}

@keyframes gate-refuse {
    10%, 50%, 90% { transform: translate3d(-7px, 0, 0); }
    30%, 70%      { transform: translate3d(7px, 0, 0); }
}

/* --------------------------------------------------------------------------
   Reduced Motion
   -------------------------------------------------------------------------- */

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

    .is-ready .fit-word,
    .is-ready .tagline-text,
    .is-visible.resume-section,
    .is-visible .resume-section,
    .is-visible .section-eyebrow,
    .is-visible .section-intro,
    .role-item.is-visible,
    .is-visible .section-ellipsis span {
        animation: none;
        opacity: 1;
        transform: none;
    }

    .is-ready .portrait-veil {
        animation: none;
        opacity: 0;
    }

    .gate-form.is-wrong {
        animation: none;
    }

    /* State changes still land, they just don't travel. */
    .role,
    .role::before,
    .role-arrow svg,
    .footer-up-arrow svg {
        transition-duration: 0.01ms;
    }

}

/* --------------------------------------------------------------------------
   Media Queries
   -------------------------------------------------------------------------- */

@media (max-width: 1200px) {

    /* The row runs out of room for a song column before it runs out of room for
       the title, so the title takes the first line on its own and the song and
       the action share the second. */
    .role {
        flex-wrap: wrap;
    }

    .role-title {
        flex: 1 1 100%;
    }

    .role-song {
        margin-left: 0;
        margin-top: var(--text-spacing-xxsmall);
        order: 2;
        text-align: left;
        width: auto;
    }

    .role-song + .role-action {
        margin-left: auto;
        margin-top: var(--text-spacing-xxsmall);
        order: 3;
        width: auto;
    }

}

@media (max-width: 950px) {

    :root {
        --stage-padding: 32px;
    }

    .roles {
        padding-bottom: 10vh;
    }

    .section-head {
        padding: calc(var(--text-spacing-xxlarge) * 1.25) var(--stage-padding)
                 var(--text-spacing-small);
    }

    .resume {
        grid-template-columns: minmax(0, 1fr);
    }

    .resume-aside {
        max-width: 420px;
        position: static;
    }

    .credit {
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    }

    .credit-where {
        grid-column: 1 / -1;
    }

    .role {
        padding-bottom: var(--text-spacing-medium);
        padding-top: var(--text-spacing-medium);
    }

    .tagline-text {
        letter-spacing: 0.34em;
        text-indent: 0.34em;
    }

    @keyframes tagline-settle {
        from {
            letter-spacing: 0.14em;
            opacity: 0;
            transform: translate3d(0, 100%, 0);
        }
        to {
            letter-spacing: 0.34em;
            opacity: 1;
            transform: translate3d(0, 0, 0);
        }
    }

}

@media (max-width: 650px) {

    :root {
        --stage-padding: 22px;
        --text-spacing-large: 32px;
    }

    .section-intro {
        margin-bottom: var(--text-spacing-large);
    }

    .role {
        gap: var(--text-spacing-xsmall);
    }



    .role-title {
        font-size: clamp(26px, 8.4vw, 40px);
        line-height: 1.05;
    }

    .role-in {
        margin: 0 0.7em;
    }

    .footer-upper {
        display: block;
    }

    .footer-nav {
        margin-top: var(--text-spacing-large);
    }

    .footer-up {
        display: inline-block;
        margin-top: var(--text-spacing-large);
    }

    .credit,
    .entry {
        gap: var(--text-spacing-xxsmall);
        grid-template-columns: minmax(0, 1fr);
    }

    .portrait-image {
        object-position: 32% 45%;
    }

    .resume-aside {
        max-width: none;
    }

    .lightbox-stage {
        display: block;
        padding: 0;
    }

    .lightbox-main {
        max-width: none;
    }

    .lightbox-rail {
        writing-mode: horizontal-tb;
    }

    .lightbox-role {
        margin-bottom: var(--text-spacing-xsmall);
        rotate: none;
    }

    .lightbox-contact {
        justify-content: flex-end;
        margin-top: var(--text-spacing-small);
    }

    .lightbox-contact-arrow {
        rotate: none;
    }

    .nav-home {
        display: none;
    }

    .nav-inner {
        padding: var(--text-spacing-xxsmall) var(--stage-padding);
    }

    .nav-list {
        gap: 14px;
        justify-content: space-between;
        width: 100%;
    }

    .nav a {
        font-size: 11px;
    }

    .tagline-text {
        font-size: 11px;
        letter-spacing: 0.26em;
        text-indent: 0.26em;
    }

    @keyframes tagline-settle {
        from {
            letter-spacing: 0.1em;
            opacity: 0;
            transform: translate3d(0, 100%, 0);
        }
        to {
            letter-spacing: 0.26em;
            opacity: 1;
            transform: translate3d(0, 0, 0);
        }
    }

}


