/* ========== Team page (#team) ==========
   Shared tokens, type, buttons, chips and the shell live in css/tokens.css.
   Cubes on 2:1 plinths, like blocks in a district. The cubes are drawn by js/pages/team.js
   (IsoCube.avatar, relit for Dusk); their lit tiles are windows that come on one by one when a
   member scrolls into view. Then the cubes come alive (see "Idle motion"). A member may also have
   a photo (.tm-photo), which takes turns with their cube in the same place (see "The switch"). */

.tm-head .lede { max-width: 34em; }

/* ---------- Groups ----------
   Each row has one size token, --tm-av: the side of a member's avatar box. The cube, its rings,
   the photo, the plinth and its shadow are all drawn in proportion to that box, so they scale with
   it. The cubes are drawn at 148 and 104 px (js/pages/team.js: SIZE_LEAD, SIZE_MEMBER, which set
   their level of detail) and shown at this size. */
.tm-label { margin-bottom: 44px; }
.tm-label--crew { margin-top: clamp(64px, 7vw, 88px); }

.tm-leads, .tm-grid {
    --tm-av: 200px;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 48px 24px;
    margin: 0;
    padding: 0;
    list-style: none;
}
/* The crew wraps like the leads' grid, but a short last row sits in the middle rather than
   hanging off the left: 4 + 3 on wide screens, 2 + 2 + 2 + 1 on phones */
.tm-grid {
    --tm-av: 140px;
    --tm-cols: 4;
    --tm-gx: 24px;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 40px var(--tm-gx);
}
.tm-grid > .tm-member { flex: 0 0 calc((100% - (var(--tm-cols) - 1) * var(--tm-gx)) / var(--tm-cols)); }

/* ---------- A member: the cube on its plinth, the name, the role chip, a short bio for leads ---------- */
.tm-member { min-width: 0; text-align: center; }

.tm-av {
    position: relative;
    isolation: isolate;
    width: var(--tm-av);
    height: var(--tm-av);
    margin: 0 auto;
}
.tm-av > .isocube, .tm-cube > .isocube { position: relative; width: 100%; height: 100%; overflow: visible; }

/* the plinth: one 2:1 tile under the cube */
.tm-plinth {
    position: absolute;
    left: 50%;
    bottom: 7%;
    z-index: -1;
    width: 96%;
    height: auto;
    aspect-ratio: 2 / 1;
    overflow: visible;
    transform: translateX(-50%);
}
.tm-plinth .tm-plate {
    fill: var(--c-raised);
    stroke: var(--c-line-2);
    stroke-width: 1.5;
    vector-effect: non-scaling-stroke;
}

/* a member's photo over their own cube (js/pages/team.js: photoHTML): as tall as the cube, in its
   place (76%, js/pages/team.js: PHOTO_FIT), clipped to the ring's rounded hexagon (inline clip-path),
   with the cube's rings taken out of its drawing and drawn over both. The cube itself sits in
   .tm-cube, a plain box in the drawing's place (see "The switch"). Until the script turns it, and
   always under reduced motion, only the photo shows. The plinth's colour fills the hexagon until
   the picture loads. */
.tm-cube { position: relative; display: block; width: 100%; height: 100%; }
.tm-photo { position: absolute; inset: 0; }
.tm-photo > img {
    position: absolute;
    inset: 12%;
    width: 76%;
    height: 76%;
    object-fit: cover;
    background: var(--c-raised);
}
.tm-photo > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.tm-av--photo > .tm-cube { opacity: 0; scale: 0.96; }

/* without the cube script: the plinth and the member's initials */
.tm-initials {
    position: absolute;
    left: 0;
    right: 0;
    top: 36%;
    display: block;
    color: var(--c-muted);
    font: 600 calc(var(--tm-av) * 0.19)/1 var(--f-display);
    letter-spacing: var(--track-caps);
}

.tm-name {
    margin: 14px 0 0;
    font: 600 1.625rem/1.1 var(--f-display);
    color: var(--c-text);
    overflow-wrap: anywhere;
}

.tm-role {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    max-width: 100%;
    min-height: 26px;
    margin: 10px 0 0;
    padding: 3px 10px;
    border-radius: var(--r-chip);
    border: 1px solid var(--c-line-2);
    color: var(--c-body);
    font: 600 0.8125rem/1.2 var(--f-display);
    letter-spacing: var(--track-caps);
    text-transform: uppercase;
}

.tm-bio {
    margin: 12px auto 0;
    max-width: 24ch;
    font-size: 0.9375rem;
    line-height: 1.55;
    color: var(--c-body);
}

.tm-grid .tm-name { margin-top: 10px; font-size: 1.3125rem; }
.tm-grid .tm-initials { font-size: calc(var(--tm-av) * 0.21); }

/* ---------- Windows: the lit tiles come on one by one, stepped, after the member fades in.
   Only inside a .reveal that has not come in yet are they off; anywhere else they are simply on. ---------- */
.tm-win {
    transition: opacity 240ms steps(3, end);
    transition-delay: calc(var(--rd, 0ms) + 380ms + var(--k, 0) * 150ms);
}
.js .reveal:not(.is-in) .tm-win { opacity: 0; }

/* ---------- Idle motion: the cubes float and think, each to its own seeded rhythm.
   js/pages/team.js sets .tm-motion on the roster (never under reduced motion), the timings as
   custom properties on each .tm-av (--tm-fp/fd/fh: float period, phase, height; --tm-tp/td/ts:
   thought period, phase, step from light to light; --tm-dip: how far a thinking light dims),
   .is-away on a lamp whose light hops next door, .is-blink on a blinking cube, .is-ripple on a
   cube a pointer has just come to, and .tm-hold on a group that is off screen or in a hidden
   tab. Only transform and opacity move. ---------- */
.tm-glow, .tm-ghost { opacity: 0; pointer-events: none; }
.tm-shade { transform-box: fill-box; transform-origin: 50% 58%; }

/* floating: the cube and its rings rise 5-8% of their size above the plinth (8-12 px on a desktop)
   and settle back; the shadow shrinks and fades as the cube rises */
.tm-motion :is(.tm-av, .tm-cube) > .isocube {
    animation: tm-float var(--tm-fp, 4s) cubic-bezier(0.45, 0, 0.55, 1) var(--tm-fd, 0s) infinite;
    transition: translate 520ms var(--ease-out);
}
.tm-motion .tm-shade {
    animation: tm-shade var(--tm-fp, 4s) cubic-bezier(0.45, 0, 0.55, 1) var(--tm-fd, 0s) infinite;
    transition: scale 520ms var(--ease-out);
}
@keyframes tm-float {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50% { transform: translate3d(0, calc(-1 * var(--tm-fh, 5%)), 0); }
}
@keyframes tm-shade {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.6; transform: scale(0.86); }
}
/* a photo stands still on its plinth, and so do its rings and the shadow, whichever face shows
   (the cube behind it floats inside the rings, only 4% of its size); it only lifts when pointed at */
.tm-motion .tm-av > .tm-photo { transition: translate 520ms var(--ease-out); }
.tm-motion .tm-av--photo .tm-shade { animation: none; }

/* thinking: the member's own lights dim to about half and come back, one after another, a wave
   through the pattern every 2.5-4 s */
.tm-motion .tm-lamp {
    animation: tm-think var(--tm-tp, 3s) ease-in-out calc(var(--tm-td, 0s) + var(--o, 0) * var(--tm-ts, 0.2s)) infinite;
}
@keyframes tm-think {
    0%, 24%, 100% { opacity: 1; }
    8%, 14% { opacity: var(--tm-dip, 0.5); }
}

/* a hop: one light dims while its unlit neighbour on the same face glows at full warmth for
   about a second, then they swap back (js/pages/team.js: HOP) */
.tm-motion .tm-lamp.is-away > [data-tile] { animation: tm-away 1.5s ease-in-out 1; }
.tm-motion .tm-ghost { animation: tm-ghost 1.5s ease-in-out 1; }
@keyframes tm-away {
    0%, 100% { opacity: 1; }
    17%, 83% { opacity: 0.14; }
}
@keyframes tm-ghost {
    0%, 100% { opacity: 0; }
    17%, 83% { opacity: 1; }
}

/* a blink: every 8-14 s all the member's lights dip together (js/pages/team.js: BLINK) */
.tm-motion .is-blink .tm-lamp > [data-tile] { animation: tm-blink 280ms ease-in-out 1; }
@keyframes tm-blink {
    0%, 100% { opacity: 1; }
    40%, 55% { opacity: 0.18; }
}

/* pointing at a member: the cube lifts a little more (translate and scale add to the running
   float) and one quick ripple of light runs through its pattern. The ripple is .is-ripple, not
   :hover, so it plays to its end when the pointer moves on (js/pages/team.js: RIPPLE) */
@media (hover: hover) {
    .tm-motion .tm-member:hover .tm-av > :is(.isocube, .tm-cube, .tm-photo) { translate: 0 -8px; }
    .tm-motion .tm-member:hover .tm-shade { scale: 0.9; }
    .tm-motion .is-ripple .tm-glow { animation: tm-ripple 0.7s ease-out calc(var(--o, 0) * 70ms) 1 both; }
}
@keyframes tm-ripple {
    0%, 100% { opacity: 0; }
    35% { opacity: 0.6; }
}

/* held: off screen or in a hidden tab, and a cube while its photo shows */
.tm-motion :is(.tm-hold .tm-member, .tm-av--photo:not(.is-lit)) :is(.isocube, .tm-shade, .tm-lamp, .tm-lamp > [data-tile], .tm-glow, .tm-ghost) { animation-play-state: paused; }

/* ---------- The switch: a member's photo and their cube take turns in the same place.
   js/pages/team.js (turn) sets .is-cube on the .tm-av while the cube shows and .is-lit while its
   lights are on and it moves: from the turn to the cube until it has faded out again. The photo
   fades out and settles to 96% as the cube grows in from 96%, calmly, in 800 ms (SWITCH); the
   face underneath, the cube, arrives early and leaves late, so nothing behind them shows through
   halfway. The cube's lights then come on one by one as they do when a member comes in (LIT,
   LIGHT_STEP), it floats and thinks like the others, and the reverse takes it back to the photo.
   While it is away its lights are off and its motion is paused, so it comes back as it left.
   The cube fades, grows and lifts on .tm-cube, the plain box around its drawing, and the drawing
   only floats: Chrome can hand scale and translate to the GPU on an HTML box, but on an <svg> it
   runs them on the main thread, restyling the page every frame. ---------- */
.tm-motion .tm-photo > img {
    transition: opacity 800ms cubic-bezier(0.45, 0, 0.55, 1), scale 800ms cubic-bezier(0.45, 0, 0.55, 1);
}
.tm-motion .tm-av--photo.is-cube > .tm-photo > img { opacity: 0; scale: 0.96; }
.tm-motion .tm-av--photo > .tm-cube {
    transition: opacity 500ms cubic-bezier(0.45, 0, 0.55, 1) 300ms, scale 800ms cubic-bezier(0.45, 0, 0.55, 1), translate 520ms var(--ease-out);
}
.tm-motion .tm-av--photo.is-cube > .tm-cube {
    opacity: 1;
    scale: 1;
    transition: opacity 500ms cubic-bezier(0.45, 0, 0.55, 1), scale 800ms cubic-bezier(0.45, 0, 0.55, 1), translate 520ms var(--ease-out);
}
/* the turn starts the light-up, so the card's place in its row (--rd) plays no part */
.tm-av--photo .tm-win { --rd: 0ms; }
.tm-av--photo:not(.is-lit) .tm-win { opacity: 0; transition: none; }

/* ---------- Responsive: on phones the avatars follow the screen's width, up to their desktop size ---------- */
@media (max-width: 1080px) {
    .tm-leads { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
    .tm-grid { --tm-cols: 2; }
}

@media (max-width: 680px) {
    .tm-label { margin-bottom: 32px; }
    .tm-leads { --tm-av: clamp(124px, 40vw, 200px); gap: 36px 12px; }
    .tm-name { font-size: 1.375rem; }
    .tm-bio { font-size: 0.875rem; }
    .tm-grid { --tm-av: clamp(92px, 30vw, 140px); --tm-gx: 12px; row-gap: 32px; }
    .tm-grid .tm-name { font-size: 1.1875rem; }
    .tm-role { font-size: 0.75rem; }
}

/* ---------- Reduced motion: the lights are simply on, nothing idles, and a photo never turns ---------- */
@media (prefers-reduced-motion: reduce) {
    .tm-win, .js .reveal:not(.is-in) .tm-win { opacity: 1; transition: none; }
    .tm-av .isocube, .tm-shade, .tm-lamp, .tm-lamp > [data-tile], .tm-glow, .tm-win, .tm-ghost { animation: none !important; }
    .tm-av .isocube, .tm-av > :is(.tm-cube, .tm-photo), .tm-photo > img, .tm-shade { translate: none !important; scale: none !important; transition: none !important; }
}
