/* =============================================================
   Param Tanna — portfolio
   Poppins on white, neutral grey scale, centred hero, card sections.
   ============================================================= */

:root{
    --bg:    #ffffff;
    --ink:   #000000;
    --n800:  #262626;
    --n700:  #404040;
    --n600:  #525252;
    --n500:  #737373;
    --n400:  #a3a3a3;
    --n300:  #d4d4d4;
    --n200:  #e5e5e5;
    --n100:  #f5f5f5;
    --dark:  #353535;

    --wrap: 1120px;
    --pad: clamp(1.25rem, 4vw, 2.5rem);

    /* Height of the fixed header, plus a little breathing room. Anchor jumps
       land exactly here, and full-height sections are sized to fill the space
       below it, so a section never bleeds into the next one. */
    --snap: 88px;
}

*,
*::before,
*::after{
    box-sizing: border-box;
}

html{
    scroll-behavior: smooth;
    scroll-padding-top: var(--snap);
    -webkit-text-size-adjust: 100%;
}

body{
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: "Poppins", sans-serif;
    font-size: 16px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

img{
    display: block;
    max-width: 100%;
}

figure{
    margin: 0;
}

ul{
    list-style: none;
    margin: 0;
    padding: 0;
}

a{
    color: inherit;
    text-decoration: none;
}

h1, h2, h3{
    margin: 0;
    letter-spacing: -0.025em;
}

p{
    margin: 0;
}

:focus-visible{
    outline: 2px solid var(--ink);
    outline-offset: 3px;
    border-radius: 3px;
}

.wrap{
    width: 100%;
    max-width: var(--wrap);
    margin-inline: auto;
    padding-inline: var(--pad);
}

/* ---------- navigation ---------- */

header{
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 100;
    background: rgba(255, 255, 255, 0.88);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid transparent;
    transition: border-color .25s ease;
}

header.scrolled{
    border-bottom-color: var(--n200);
}

/* Wider than the content column, so the brand sits further out to the
   left and the nav further right than the body text. */
header .wrap{
    max-width: 1320px;
}

.nav{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    height: 74px;
}

.brand{
    display: flex;
    align-items: center;
    gap: 12px;
    font-weight: 600;
    font-size: 20px;
}

.brand .mark{
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: var(--ink);
    color: #fff;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 0;
}

.navlist{
    display: flex;
    align-items: center;
    /* Wider gaps make the nav block itself wider, so it reaches back toward
       the mark and closes the empty middle of the bar. */
    gap: clamp(1.1rem, 3vw, 3.1rem);
}

.navlist a{
    font-size: 21px;
    font-weight: 500;
    color: var(--n600);
    transition: color .18s ease;
}

.navlist a:hover,
.navlist a.active{
    color: var(--ink);
}

#menu-icon{
    display: none;
    width: 38px;
    height: 38px;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--ink);
}

#menu-icon span{
    display: block;
    width: 20px;
    height: 1.6px;
    margin: 5px auto;
    background: currentColor;
    transition: transform .3s ease, opacity .2s ease;
}

#menu-icon.open span:nth-child(1){ transform: translateY(6.6px) rotate(45deg); }
#menu-icon.open span:nth-child(2){ opacity: 0; }
#menu-icon.open span:nth-child(3){ transform: translateY(-6.6px) rotate(-45deg); }

@media (max-width: 900px){
    #menu-icon{ display: block; }

    /* A small panel hanging under the menu button rather than a full-width
       sheet. It is hidden with visibility as well as height, or its border
       and shadow would still draw while collapsed. */
    .navlist{
        position: fixed;
        top: 82px;
        right: var(--pad);
        left: auto;
        width: min(216px, calc(100vw - var(--pad) * 2));
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        padding: 0 16px;
        background: #fff;
        border: 1px solid var(--n200);
        border-radius: 14px;
        box-shadow: 0 18px 40px -22px rgba(0, 0, 0, .35);
        max-height: 0;
        opacity: 0;
        visibility: hidden;
        overflow: hidden;
        transition: max-height .3s ease, opacity .2s ease, visibility .3s;
    }

    .navlist.show{
        max-height: 70vh;
        opacity: 1;
        visibility: visible;
    }

    .navlist li + li{ border-top: 1px solid var(--n100); }

    .navlist a{
        display: block;
        padding: 12px 0;
        font-size: 15px;
    }
}

/* ---------- hero ---------- */

.hero{
    position: relative;
    min-height: 100svh;
    display: grid;
    place-items: center;
    text-align: left;
    padding: 124px 0 18px;
}

.hero-grid{
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: clamp(1.5rem, 5vw, 4.5rem);
}

.hero-text{
    position: relative;
    z-index: 60;
}

/* Reserves the circle's place in the grid. */
.photo-slot{
    width: clamp(250px, 31vw, 440px);
    aspect-ratio: 1;
    justify-self: end;
}

/* ---------- portrait, which docks on scroll ---------- */

/* z-index 50 keeps it under the hero text (60) and under the header (100),
   while still painting above the white section backgrounds once docked. */
/* A circle at both sizes, so the dock morph only changes scale and
   position. z-index 50 keeps it under the header (100). */
.avatar{
    position: fixed;
    z-index: 50;
    margin: 0;
    overflow: hidden;
    border-radius: 50%;
    background: var(--n100);
    /* Tight, low-contrast lift — reads as depth, not as a drop shadow. */
    box-shadow: 0 20px 45px -16px rgba(0, 0, 0, .3);
}

.avatar img{
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    transition: opacity .45s ease;
}

/* Only the box shadow deepens on docking; it is already a circle. */
.avatar.is-docked{
    box-shadow: 0 8px 26px rgba(0, 0, 0, .2);
}

/* Transitions run only during the dock morph. While undocked the portrait
   is repositioned every scroll frame to track its slot, and transitioning
   that would make it lag behind the scroll. */
.avatar.is-moving{
    transition:
        left   .8s cubic-bezier(.16, 1, .3, 1),
        top    .8s cubic-bezier(.16, 1, .3, 1),
        width  .8s cubic-bezier(.16, 1, .3, 1),
        height .8s cubic-bezier(.16, 1, .3, 1),
        border-radius .8s cubic-bezier(.16, 1, .3, 1),
        box-shadow .8s ease;
}

@media (prefers-reduced-motion: reduce){
    .avatar.is-moving{ transition: none; }
}

@media (max-width: 900px){
    .hero-grid{
        grid-template-columns: minmax(0, 1fr);
    }

    .photo-slot{
        order: -1;
        max-width: 260px;
        justify-self: start;
    }
}

.greeting{
    font-size: 16px;
    font-weight: 600;
    color: var(--n600);
    margin-bottom: 4px;
}

.hero h1{
    font-size: 34px;
    line-height: 1.15;
    font-weight: 700;
    color: var(--ink);
    margin-bottom: 12px;
}

.role{
    font-size: 20px;
    font-weight: 600;
    color: var(--n700);
    margin-bottom: 8px;
}

.role-sub{
    font-size: 15px;
    line-height: 1.625;
    font-weight: 400;
    color: var(--n600);
    margin-bottom: 26px;
}

/* Kept on one line only where there is room for it. This has to come after
   the rule above: an earlier override at the same specificity loses, which is
   what let 487px of text sit in a 335px box and scroll the whole page. */
@media (min-width: 1181px){
    .role-sub{ white-space: nowrap; }
}

@media (min-width: 640px){
    .hero h1{ font-size: 42px; }
    .role-sub{ font-size: 17.5px; }
}

@media (min-width: 768px){
    .greeting{ font-size: 18px; }
    .role{ font-size: 23px; }
}

@media (min-width: 1024px){
    .hero h1{ font-size: 58px; line-height: 1.04; }
}

.hero-actions{
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 12px;
    margin-bottom: 24px;
}

.btn{
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 13px 26px;
    border-radius: 16px;
    font-size: 16px;
    font-weight: 600;
    border: 1.5px solid var(--dark);
    background: var(--dark);
    color: #fff;
    transition: background .2s ease, color .2s ease, transform .2s ease;
}

/* Both fill on hover, but to different tones: this one goes to full black,
   the outlined one only to charcoal, so they never match. */
.btn:hover{
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
    transform: translateY(-2px);
}

.btn-ghost{
    background: transparent;
    color: var(--ink);
    border-color: var(--ink);
}

.btn-ghost:hover{
    background: var(--dark);
    border-color: var(--dark);
    color: #fff;
}

.socials{
    display: flex;
    justify-content: center;
    gap: 18px;
}

/* Hero socials follow the left-aligned text; the contact ones stay centred. */
.hero .socials{
    justify-content: flex-start;
}

/* No container — the mark sits on the page on its own, and the whole
   interaction is scale: up on hover, a small dip on press. */
.socials a{
    display: grid;
    place-items: center;
    color: var(--ink);
    transition: transform .2s ease;
}

.socials a:hover{
    transform: scale(1.1);
}

.socials a:active{
    transform: scale(0.95);
}

.socials svg{
    width: 32px;
    height: 32px;
    fill: currentColor;
}

/* Now a direct child of .hero rather than the text column, so it is pinned
   to the bottom of the viewport instead of sitting under the buttons. */
.scroll-cue{
    position: absolute;
    left: 50%;
    bottom: 34px;
    transform: translateX(-50%);
    display: grid;
    place-items: center;
    color: var(--n400);
    animation: bob 2.2s ease-in-out infinite;
}

.scroll-cue svg{
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
}

/* Carries the -50% centring through, or the animation would cancel it. */
@keyframes bob{
    0%, 100% { transform: translateX(-50%) translateY(0); }
    50%      { transform: translateX(-50%) translateY(7px); }
}

/* ---------- sections ---------- */

.section{
    padding-block: clamp(2rem, 3.6vw, 3.1rem);
}

.section h2,
#experience h2{
    font-size: clamp(34px, 5.2vw, 54px);
    font-weight: 700;
    letter-spacing: -0.03em;
    margin-bottom: 6px;
}

.section-note{
    color: var(--n600);
    font-size: 15.5px;
    margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

/* ---------- about ---------- */

.about-top{
    display: grid;
    gap: clamp(1.75rem, 4vw, 3.25rem);
    align-items: center;
    margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem);
}

@media (min-width: 860px){
    .about-top{ grid-template-columns: 350px minmax(0, 1fr); }
}

.about-photo img{
    width: 100%;
    /* Portrait, to match the shot — a landscape box would crop the head off. */
    aspect-ratio: 4 / 5;
    object-fit: cover;
    object-position: center top;
    border-radius: 14px;
    background: var(--n100);
}

.about-photo figcaption{
    margin-top: 10px;
    font-size: 13px;
    color: var(--n500);
}

.about-copy p{
    color: var(--n700);
    font-size: 17px;
    line-height: 1.82;
    /* Poppins' zero is far wider than its average letter, so a ch-based
       measure reads long: 64ch set 97 characters a line. This lands ~75. */
    max-width: 50ch;
}

.about-copy p + p{ margin-top: 16px; }

/* ---------- experience: scroll-driven fan ----------
   .xp-stage is tall and .xp-pin sticks inside it, so scrolling the page
   advances the cards; once the last card lands the stage ends and the page
   carries on normally. JS sets each card's transform from a fractional
   index, which is what gives the outward curve. */

#experience{
    position: relative;
    /* The fanned-out side cards sit beyond the viewport on a phone, which was
       adding ~130px of horizontal scroll to every page. `clip` trims them
       without creating a scroll container, so the sticky pin still works. */
    overflow-x: clip;
}

.xp-stage{
    position: relative;
    /* one viewport to hold, plus a stretch of scroll per extra card */
    height: calc(100vh + (var(--xp-count, 3) - 1) * 72vh);
}

.xp-pin{
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(1rem, 2vw, 1.6rem);
    /* clears the fixed header so the heading is never tucked under it */
    padding-top: 74px;
    overflow: hidden;
}

.xp-head{
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.xp-head .section-note{ margin-bottom: 0; }

/* The cards all stack at the centre; JS fans them out from there. */
.xp-fan{
    position: relative;
    /* Tall enough for the logo plus four or five lines of copy, but short
       enough that the whole pin still fits one viewport. */
    height: clamp(424px, 51vh, 470px);
    /* Large perspective: enough to feel the cards wrap around a centre that
       sits behind the screen, gentle enough not to distort them. */
    perspective: 2000px;
    transform-style: preserve-3d;
}

.xp-card{
    position: absolute;
    top: 0;
    left: 50%;
    width: min(660px, 88vw);
    height: 100%;
    margin-left: calc(min(660px, 88vw) / -2);
    display: flex;
    flex-direction: column;
    padding: clamp(20px, 2.2vw, 30px);
    text-align: center;
    align-items: center;
    justify-content: center;
    gap: 4px;
    overflow: hidden;
    border: 1px solid var(--n200);
    border-radius: 22px;
    background: #fff;
    box-shadow: 0 20px 50px -26px rgba(0, 0, 0, .34);
    transform-origin: 50% 50%;
    will-change: transform, opacity;
}

.xp-logo{
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 100%;
    height: 104px;
    margin-bottom: 12px;
}

.xp-logo img{
    /* Explicit px, not a percentage — a percentage max-height does not
       constrain a replaced element in this grid slot, so the SVG rendered
       at its own 1200x800 ratio and overlapped the heading. */
    max-width: 290px;
    max-height: 104px;
    width: auto;
    height: auto;
    object-fit: contain;
}

/* Outlined slab, for a client with no logo file. Uppercase and widely
   letterspaced so it reads as a mark rather than as another line of copy,
   and outlined rather than filled so it does not out-weigh the real logos
   on the other cards. */
.xp-wordmark{
    display: inline-flex;
    align-items: center;
    padding: 10px 19px;
    border: 1.5px solid var(--ink);
    border-radius: 8px;
    color: var(--ink);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    white-space: nowrap;
}

.xp-when{
    font-size: 14px;
    color: var(--n400);
    margin-top: 7px;
}

.xp-card h3{
    font-size: clamp(19px, 2.05vw, 24px);
    font-weight: 600;
    line-height: 1.3;
    color: var(--ink);
}

.xp-org{
    font-size: 15px;
    color: var(--n600);
    margin-top: 4px;
}

.xp-desc{
    font-size: 15px;
    line-height: 1.62;
    color: var(--n600);
    margin-top: 12px;
    max-width: 52ch;
}

.xp-dots{
    display: flex;
    justify-content: center;
    gap: 10px;
    /* The cards' drop shadow reaches about 19px below them, and all three
       overlap across the width, so dots sitting right under the fan landed
       in a grey band. This drops them clear of it. */
    margin-top: 16px;
}

.xp-dots button{
    position: relative;
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--n300);
    cursor: pointer;
    transition: background .25s ease, transform .25s ease;
}

/* An 8px dot is unhittable with a thumb; this widens the target to 40px
   without changing anything visible. */
.xp-dots button::after{
    content: "";
    position: absolute;
    inset: -16px;
    border-radius: 50%;
}

.xp-dots button.is-active{
    background: var(--ink);
    transform: scale(1.4);
}

/* No pinning when motion is reduced — the cards simply stack down the page. */
@media (prefers-reduced-motion: reduce){
    .xp-stage{ height: auto; }

    .xp-pin{
        position: static;
        height: auto;
        overflow: visible;
        padding-block: clamp(2.5rem, 5vw, 4rem);
    }

    .xp-fan{
        height: auto;
        display: grid;
        gap: 20px;
    }

    .xp-card{
        position: static;
        height: auto;
        margin-left: 0;
        width: 100%;
        transform: none !important;
        opacity: 1 !important;
    }

    .xp-dots{ display: none; }
}

/* ---------- cards (projects / research) ----------
   Centred: framed image, a large heading, plain unbolded copy, and an
   outlined link button at the foot. No advisor, date or stack line — the
   title and the description carry it. */

.cards{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    gap: clamp(1.25rem, 2.5vw, 2rem);
    align-items: stretch;
}

.card{
    display: flex;
    flex-direction: column;
    padding: clamp(11px, 1.2vw, 15px);
    border: 1px solid var(--n200);
    border-radius: 18px;
    background: #fff;
    text-align: center;

    /* Entry state: dealt in — sitting low, rotated and slightly small. */
    opacity: 0;
    transform: translateY(50px) rotate(-4deg) scale(.95);
    transition: opacity .75s cubic-bezier(.2, .9, .25, 1),
                transform .75s cubic-bezier(.2, .9, .25, 1);
}

/* Settles flat when the row scrolls into view, each card a beat later. */
.cards.in-view .card{
    opacity: 1;
    transform: none;
}

.cards.in-view .card:nth-child(2){ transition-delay: .12s; }
.cards.in-view .card:nth-child(3){ transition-delay: .24s; }

/* Once dealt, hand the transition over to the hover timing. A `forwards`
   animation would have pinned transform and killed the hover lift. */
.cards.settled .card{
    transition: box-shadow .25s ease, transform .25s ease;
    transition-delay: 0s;
}

.card:hover{
    transform: translateY(-3px);
    box-shadow: 0 14px 34px -16px rgba(0, 0, 0, .2);
}

@media (prefers-reduced-motion: reduce){
    .card{
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* The image sits in its own framed panel inside the card. */
.card .thumb{
    /* 16:9, matching the source artwork and keeping the card short. */
    aspect-ratio: 16 / 9;
    border: 1px solid var(--n200);
    border-radius: 12px;
    background: var(--n100);
    overflow: hidden;
    display: grid;
    place-items: center;
    color: var(--n400);
    font-size: 13px;
}

.card .thumb img{
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card-body{
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 18px 10px 4px;
    flex: 1;
}

.card h3{
    font-size: clamp(21px, 2.1vw, 26px);
    font-weight: 700;
    line-height: 1.22;
    letter-spacing: -0.02em;
    color: var(--ink);
    max-width: 18ch;
}

.card p{
    font-size: 12.5px;
    line-height: 1.62;
    color: var(--n600);
    /* Wider measure: the copy is dense and 34ch broke it into slivers. */
    max-width: 42ch;
}

/* Emphasis inside a description, for the one word that carries the point. */
.card p em{
    font-style: normal;
    font-weight: 600;
    color: var(--n800);
}

/* Link as an outlined button, pinned to the foot so the three cards line up
   however long their copy runs. */
.card .link{
    margin-top: auto;
    padding: 9px 19px;
    border: 1.3px solid var(--n300);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
    transition: border-color .2s ease, background .2s ease;
}

.card .link:hover{
    border-color: var(--ink);
    background: var(--n100);
}

/* ---------- positions ----------
   One soft panel holding every role, with the items in an auto-flowing grid
   so adding a fifth or a tenth just fills another row. The space inside the
   panel is doing the work rather than any ruling. */

.por-panel{
    background: var(--n100);
    border-radius: 22px;
    padding: clamp(26px, 3.6vw, 46px);
}

.pors{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr));
    gap: clamp(24px, 3vw, 38px) clamp(28px, 4vw, 54px);
}

.por{
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 14px;
    align-items: start;
}

/* A solid square rather than a tick: the site is rectilinear everywhere
   else, and a check implies these were completed rather than held. */
.por-mark{
    width: 9px;
    height: 9px;
    margin-top: 8px;
    background: var(--ink);
}

.por h3{
    display: flex;
    align-items: baseline;
    gap: 16px;
    font-size: 21px;
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--ink);
    line-height: 1.28;
}

/* Tenure sits beside the role, lighter and at body weight so it reads as an
   aside rather than part of the title. */
/* Pushed to the right edge of the entry, so the dates line up down the
   column instead of trailing each title at a different place. */
.por-when{
    margin-left: auto;
    font-size: 14px;
    font-weight: 400;
    color: var(--n400);
    white-space: nowrap;
}

.por p{
    font-size: 15px;
    line-height: 1.55;
    color: var(--n700);
    margin-top: 6px;
    max-width: 42ch;
}

/* ---------- contact ----------
   A wide field for the address and a square tile for LinkedIn beside it.
   Nothing shares a border, so there is no divider anywhere. */

/* Sections that hold a screen of their own, with their content centred in
   it rather than stacked at the top. */
#news,
#positions,
.contact{
    /* Exactly the screen below the header, so landing on one fills it. */
    min-height: calc(100vh - var(--snap));
    display: grid;
    align-content: center;
}

.contact{
    text-align: center;
}

.contact .eyebrow{
    font-size: 15px;
    font-weight: 600;
    color: var(--n600);
    /* Negative: the heading's own line-height leaves a gap above its
       cap-height, so 4px of margin read as much more than 4px. */
    margin-bottom: -6px;
}

.contact h2{
    margin-bottom: 0;
}

.field-row{
    display: flex;
    justify-content: center;
    align-items: stretch;
    gap: 10px;
    margin-top: 26px;
    flex-wrap: wrap;
}

.field{
    display: inline-flex;
    align-items: center;
    gap: 12px;
    padding: 14px 22px;
    border: 1.5px solid var(--n200);
    border-radius: 12px;
    background: #fff;
    font-size: 15px;
    color: var(--ink);
    transition: border-color .2s ease;
}

.field:hover{
    border-color: var(--ink);
}

.field svg{
    width: 18px;
    height: 18px;
    fill: currentColor;
    flex: 0 0 auto;
}

.tile{
    display: grid;
    place-items: center;
    width: 50px;
    border: 1.5px solid var(--n200);
    border-radius: 12px;
    color: var(--ink);
    transition: background .2s ease, border-color .2s ease, color .2s ease;
}

.tile:hover{
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
}

.tile svg{
    width: 18px;
    height: 18px;
    fill: currentColor;
}

/* ---------- footer ---------- */

footer{
    border-top: 1px solid var(--n200);
    padding-block: 28px;
}

.foot{
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    font-size: 13px;
    color: var(--n500);
}

.foot ul{
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
}

.foot a:hover{ color: var(--ink); }

@media (prefers-reduced-motion: reduce){
    html{ scroll-behavior: auto; }

    *,
    *::before,
    *::after{
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}

/* Placeholder for the About photo until a second image is added. */
.photo-placeholder{
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 4 / 3;
    border: 1px dashed var(--n300);
    border-radius: 14px;
    background: var(--n100);
    color: var(--n500);
    font-size: 14px;
}

/* IITGN card: label and value per row. The pair is left-aligned so the
   values line up with each other, and the whole block is centred in the
   card so it still sits under the centred heading. */
.xp-rows{
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    gap: 8px 22px;
    margin: 15px 0 0;
    text-align: left;
}

.xp-rows dt{
    font-size: 13px;
    color: var(--n400);
    white-space: nowrap;
}

.xp-rows dd{
    margin: 0;
    font-size: 14.5px;
    line-height: 1.45;
    color: var(--n700);
}

/* Figures inside a card description, so a number is noticed without
   needing its own block. */
.xp-desc strong{
    font-weight: 600;
    color: var(--n800);
}

/* Engagement type, under the job title on a card. */
.xp-kind{
    font-size: 13px;
    color: var(--n400);
    margin-top: 3px;
}

/* Figures inside a project card, same emphasis as the experience cards. */
.card p strong{
    font-weight: 600;
    color: var(--n800);
}

/* ---------- news ----------
   A rail that fades from ink at the top to nothing at the bottom, with each
   dot lighter than the one above it. The fade carries the ordering, so the
   sequence reads before any date does. Dot colours are set from JS so this
   scales to any number of entries. */

.news-rail{
    position: relative;
    padding-left: 150px;
    max-width: 1000px;
}

.news-rail::before{
    content: "";
    position: absolute;
    left: 118px;
    top: 10px;
    bottom: 16px;
    width: 1.5px;
    /* Holds a visible tone most of the way down, so the line still reaches the
       final dot when there are only two entries. */
    background: linear-gradient(var(--ink), var(--n300) 40%, var(--n200) 88%, transparent);
}

.news-row{
    position: relative;
    padding-bottom: 26px;
}

.news-row:last-child{
    padding-bottom: 0;
}

.news-row::before{
    content: "";
    position: absolute;
    left: -35px;
    top: 5px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--dot, var(--n300));
    outline: 3px solid #fff;
}

/* Newest: larger, black, with a soft halo instead of a label. */
.news-row:first-child::before{
    left: -36px;
    top: 4px;
    width: 11px;
    height: 11px;
    background: var(--ink);
    box-shadow: 0 0 0 5px rgba(0, 0, 0, .09);
}

.news-when{
    position: absolute;
    left: -150px;
    top: 0;
    width: 102px;
    text-align: right;
    font-size: 13px;
    font-weight: 600;
    color: var(--n500);
    white-space: nowrap;
}

.news-row:first-child .news-when{
    color: var(--ink);
}

.news-what{
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--n600);
    /* No measure cap: the rail width governs, so entries run the full column
       rather than breaking early and leaving the right side empty. */
    max-width: none;
}

.news-what b{
    font-weight: 600;
    color: var(--ink);
}

/* The one colour on the page: an entry that opens something elsewhere. */
.news-what a{
    color: #1d4ed8;
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
    transition: color .2s ease;
}

.news-what a:hover,
.news-what a:focus-visible{
    color: #1e3a8a;
}

/* The arrow sits outside the underline, so it reads as a mark, not a letter. */
.news-what a span{
    display: inline-block;
    margin-left: 2px;
    text-decoration: none;
    font-size: .92em;
}

@media (max-width: 760px){
    .news-rail{ padding-left: 28px; }
    .news-rail::before{ left: 4px; }

    .news-when{
        position: static;
        width: auto;
        text-align: left;
        display: block;
        margin-bottom: 3px;
    }

    .news-row::before{ left: -28px; top: 6px; }
    .news-row:first-child::before{ left: -29px; top: 5px; }
}

/* ---------- phones ----------
   The experience card is a fixed height (it fills the pinned stage), so on a
   narrow screen the text wraps onto more lines than fit and `overflow: hidden`
   clipped it. Everything inside steps down a size rather than the card growing,
   which would break the scroll maths the fan depends on. */

@media (max-width: 760px){
    .xp-card{
        padding: 18px 16px;
        border-radius: 18px;
    }

    .xp-logo{
        height: 74px;
        margin-bottom: 8px;
    }

    .xp-logo img{ max-height: 70px; }

    .xp-wordmark{
        padding: 8px 14px;
        font-size: 13px;
        letter-spacing: .1em;
    }

    .xp-card h3{ font-size: 18px; }
    .xp-kind{ font-size: 12px; }
    .xp-when{ font-size: 13px; margin-top: 5px; }

    .xp-desc{
        font-size: 13.5px;
        line-height: 1.55;
        margin-top: 9px;
    }

    .xp-rows{
        font-size: 13px;
        gap: 6px 14px;
        margin-top: 11px;
    }
}

/* The narrowest phones need one more notch down: the longest card still ran a
   few pixels past its box at 360px. */
@media (max-width: 400px){
    .xp-card{ padding: 16px 13px; }
    .xp-card h3{ font-size: 17px; }

    .xp-desc{
        font-size: 13px;
        line-height: 1.5;
        margin-top: 8px;
    }

    .xp-rows{
        font-size: 12.5px;
        gap: 5px 12px;
        margin-top: 9px;
    }
}

/* Smallest screens still in use (iPhone SE, 320px). */
@media (max-width: 340px){
    .xp-logo{ height: 62px; margin-bottom: 6px; }
    .xp-logo img{ max-height: 58px; }
    .xp-card h3{ font-size: 16px; }
    .xp-desc{ font-size: 12.5px; margin-top: 7px; }
    .xp-rows{ font-size: 12px; gap: 4px 10px; margin-top: 8px; }
}

/* ---------- phone layout ----------
   No header at all: the brand mark and the menu button both go, which also
   removes the stray hairline the collapsed nav panel's border was drawing
   across the page. The hero centres, and everything steps down a size. */

@media (max-width: 760px){
    /* The bar stays, just shorter: mark on the left, menu button on the right. */
    .nav{ height: 58px; }

    .brand .mark{
        width: 34px;
        height: 34px;
        font-size: 13px;
    }

    .navlist{ top: 66px; }

    :root{ --snap: 72px; }

    .xp-pin{ padding-top: 58px; }

    .hero{
        text-align: center;
        padding: 84px 0 18px;
    }

    .hero .photo-slot{
        justify-self: center;
        max-width: 196px;
    }

    .hero-actions,
    .hero .socials{ justify-content: center; }

    .greeting{ font-size: 14px; }

    .hero h1{
        font-size: 28px;
        margin-bottom: 9px;
    }

    .role{
        font-size: 16px;
        margin-bottom: 6px;
    }

    .role-sub{
        font-size: 13.5px;
        margin-bottom: 20px;
    }

    .hero-actions{
        gap: 10px;
        margin-bottom: 18px;
    }

    .btn{
        padding: 11px 20px;
        font-size: 14px;
    }

    .socials a svg{ width: 26px; height: 26px; }

    .section h2,
    #experience h2{ font-size: 28px; }

    .section-note{ font-size: 13.5px; }

    .about-copy p{ font-size: 15px; line-height: 1.7; }

    .card h3{ font-size: 17px; }
    .card p{ font-size: 12px; }

    .por h3{ font-size: 17px; }
    .por-when{ font-size: 12.5px; }
    .por p{ font-size: 13px; }

    .news-what{ font-size: 13.5px; }
    .news-when{ font-size: 12.5px; }
}

/* The desktop footer splits copyright left, links right. On a phone that
   leaves both halves wrapping raggedly against the edges, so they stack and
   centre, links first. */

@media (max-width: 760px){
    footer{ padding-block: 22px; }

    .foot{
        flex-direction: column-reverse;
        justify-content: center;
        text-align: center;
        gap: 14px;
    }

    .foot ul{
        justify-content: center;
        gap: 12px 18px;
    }

    .foot span{ font-size: 12.5px; }
}

/* ---------- experience on phones ----------
   The fan itself is unchanged: same perspective, same curve. What changes is
   what drives it. The pinned stage goes, so scrolling the page past the
   section no longer drags every card across, and an invisible horizontal
   scroller laid over the cards takes the finger instead. */

.xp-deck{ position: relative; }

.xp-swipe{ display: none; }

@media (max-width: 760px){
    .xp-stage{ height: auto; }

    .xp-pin{
        position: static;
        height: auto;
        padding-top: 0;
        /* It is no longer a clipping stage here, so nothing should be cut. */
        overflow: visible;
        gap: 18px;
    }

    .xp-swipe{
        display: flex;
        position: absolute;
        inset: 0;
        z-index: 200;
        overflow-x: auto;
        overscroll-behavior-x: contain;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
    }

    .xp-swipe::-webkit-scrollbar{ display: none; }

    /* One full-width stop per card; nothing is drawn, it is just something
       to scroll. */
    .xp-swipe i{
        flex: 0 0 100%;
        scroll-snap-align: center;
    }

    .xp-dots{ margin-top: 14px; }
}
