/* ABHISHEK THOMAS — portfolio v4
   A Cross Media Bar wearing the PlayStation 5's skin.

   Horizontal = categories, vertical = the items inside one. Everything on
   screen derives from --key, the selected item's colour. Registering it with
   @property is what lets the background crossfade instead of snapping. */

@property --key {
  syntax: "<color>";
  inherits: true;
  initial-value: #FFC800;
}

/* The crosspoint: the one fixed x where the active category parks and the
   item column hangs. Registered so JS can read it back as resolved pixels. */
@property --crossx {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* Registered for the same reason: the column engine anchors the selected
   item exactly one row down, and needs this resolved, not as a calc(). */
@property --row {
  syntax: "<length>";
  inherits: true;
  initial-value: 46px;
}

:root {
  --key: #FFC800;

  --void:       #08090B;
  --chrome:     #EDEFF3;
  --chrome-dim: #EDEFF3;   /* no gray text: one ink per mode (user rule) */
  --yellow:     #FFC800;

  /* card mixing anchor: CONSTANT, so cards look the same in light
     and dark mode (user rule) — never tied to the page background */
  --ink: #08090B;
  --glass:      rgba(255, 255, 255, .06);
  --glass-line: #EDEFF3;   /* no gray lines: lines are drawn in the ink */
  /* Cards are plain opaque SURFACES, one neutral for every card (user
     rule: no colours). Dark mode follows the Material dark theme: a card
     is the page colour with a white overlay, 9% at rest, 14% selected, so
     height reads as a lighter step, not a shadow. Icons are off-white, not
     pure white (glare). */
  /* ICON PALETTE: Apple's system colours (HIG), the dark variants here,
     the light variants under body.light. Made for tinted icons on neutral
     surfaces, and each hue is tuned per mode. */
  --sys-red:    #FF453A;
  --sys-orange: #FF9F0A;
  --sys-yellow: #FFD60A;
  --sys-green:  #30D158;
  --sys-blue:   #0A84FF;
  --sys-purple: #BF5AF2;
  --sys-pink:   #FF375F;
  --surface:        #1E1F22;   /* tiles                       */
  --surface-on:     #2E3035;   /* the selected card, highest  */
  --surface-1:      #1E1F22;   /* its two neighbours          */
  --surface-2:      #131416;   /* the tucked slivers, lowest  */
  /* a 1px dark seam around every card, so stacked cards separate */
  --card-shadow:    0 0 0 1px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .07),
                    0 10px 24px -14px rgba(0, 0, 0, .7);
  --card-shadow-on: 0 0 0 1px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .09),
                    0 18px 40px -18px rgba(0, 0, 0, .8);

  /* One fluid base size; every other dimension of the cross derives from it,
     so the whole thing scales continuously with the viewport — no per-
     breakpoint re-declarations. */
  --cat:  clamp(46px, 5.6vw, 124px);    /* base size unit — keeps scaling
                                           on big screens (175% reference) */
  --catf: calc(var(--cat) * 1.28);      /* category card height        */
  --catw: var(--catf);                  /* category card width: SQUARE, the
                                           vertical menu box grown up   */
  /* ONE width for every image in the middle third (user rule): a logo,
     a school, a badge, a portrait, a video, a photo board. The square
     side is the same number, capped so a short screen never clips it. */
  --mediaw: min(clamp(320px, 30vw, 520px), calc(100vh - 470px));
  --imark: calc(var(--cat) * .74);      /* item card height            */
  --imarkw: calc(var(--imark) * 1.6);   /* item card width — same landscape
                                           ratio as the category cards  */
  /* rows are sized for the square icon plus air, so they can never overlap */
  --row:  calc(var(--imark) + 17px);
  /* Coverflow deck: each side card shows a .30 sliver, tucked directly
     against the selection — no clearance. The crosspoint equals one sliver,
     the exact space the faded neighbour occupies left of the selection.
     The deck is drawn purely with transforms over a constant layout pitch
     (one card width), so alignment can never drift: animating layout
     margins was what broke it. */
  --crossx: calc(var(--catw) * 1.18);   /* one FULL previous card, plus air */

  --ease:      cubic-bezier(.22, .61, .36, 1);
  --ease-back: cubic-bezier(.34, 1.28, .64, 1);

  transition: --key .45s var(--ease);
  color-scheme: dark;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Selecting rebuilds the hero, the cards and the key art from scratch, so
   those nodes have no previous state for a transition to interpolate from.
   They get keyframes instead, staggered by --i, which run on creation. */
@keyframes rise  { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes bloom { from { opacity: 0; transform: translateX(26px) scale(.96); } to { opacity: var(--ka-op, .07); transform: none; } }

html, body { height: 100%; }

/* The reference size is what 175% zoom showed on a ~2000px window: type
   scales with the viewport and the caps sit far higher, so big screens get
   a big interface instead of a laptop layout floating in space. */
html { font-size: clamp(15px, 1.4vw, 30px); }

body {
  background: var(--void);
  color: var(--chrome);
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 300;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

/* ---------- background ---------- */

/* ONE background for the whole site (user rule): a single deep blue
   gradient, PS4 royal-blue family. It never recolours per item. */
.field {
  position: fixed;
  inset: 0;
  z-index: -2;
  /* royal blue, the default: a deep field with light pooling low left,
     the way the console's own home screen sat */
  background:
    radial-gradient(70% 55% at 22% 58%, #15337a 0%, transparent 66%),
    radial-gradient(120% 80% at 50% 116%, #0c2161 0%, transparent 70%),
    linear-gradient(#030614, #07143c 58%, #040b20);
}


/* NORTHERN LIGHTS (js/aurora.js): a band low across the sky with rays
   climbing out of it, painted on a transparent canvas and blended by
   adding light, so the blue field underneath is untouched. */
.aurora {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  mix-blend-mode: screen;
  transition: opacity .6s var(--ease);
}
/* The lights belong to the royal blue field and to nothing else. Naming the
   three shader themes here was the wrong way round: the light mode rule that
   followed re-lit them, so the aurora bled over beach, lava and space. Turn
   it off everywhere and switch it on for default only. */
.aurora { opacity: 0; }
body[data-theme="default"] .aurora { opacity: .95; }
/* pale sky: the same light, washed out and multiplied instead of added,
   so it tints the white instead of glowing on it */
body.light[data-theme="default"] .aurora { opacity: .70; mix-blend-mode: multiply; }

/* The right-side panel. Text marks are ghosted type; a real image (logo,
   photo) shows in FULL colour as its own box — never a transparent slab. */
.keyart {
  --ka-op: .07;
  position: fixed;
  z-index: -2;
  right: 5vw;
  top: 44%;
  transform: translateY(-50%);
  font-family: 'Anton', sans-serif;
  font-size: clamp(8rem, 30vh, 22rem);
  line-height: .78;
  letter-spacing: -.05em;
  color: #fff;
  opacity: var(--ka-op);
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
  animation: bloom .7s var(--ease) both;
}
.keyart img { width: clamp(9rem, 32vh, 24rem); display: block; }

/* full-colour treatments sit above the grain, so they stay crisp */
.keyart.has-img,
.keyart--media { --ka-op: 1; z-index: 0; }
.keyart.has-img img {
  width: clamp(8rem, 26vh, 19rem);
  border-radius: 18px;
  box-shadow: 0 26px 70px -24px rgba(0, 0, 0, .65);
}

/* the event's media, stacked on the middle right: the demo video is the
   main element when there is one, photos follow with a casual fan */
.keyart--media {
  display: flex;
  flex-direction: column;
  gap: 14px;
  white-space: normal;
}
.keyart--media iframe {
  width: clamp(260px, 27vw, 400px);
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: 14px;
  box-shadow: 0 26px 70px -24px rgba(0, 0, 0, .65);
  pointer-events: auto;   /* the panel itself is pointer-transparent */
}
/* The photo board: a full-height container on the right, photos arranged
   neatly in ordered masonry columns — straight, no angles, Pinterest style.
   They sit blended into the field; hovering one lifts it out clear. */
.keyart--media {
  pointer-events: auto;
  top: clamp(90px, 12vh, 150px);
  bottom: auto;
  right: 3vw;
  transform: none;
  /* big enough that a hover enlarge is actually visible */
  width: clamp(340px, 30vw, 800px);
  max-height: calc(100vh - 240px);
  display: block;
  columns: 2;
  column-gap: 16px;
  /* NO container box, no clipping: the board floats straight on the field */
  overflow: visible;
}
/* the description spans the panel above the photo columns; it must reset
   the keyart's display styling (Anton, nowrap) or it clips to one letter */
.side__desc {
  column-span: all;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 300;
  white-space: normal;
  letter-spacing: 0;
  font-size: 1.14rem;   /* the written stuff takes real space */
  line-height: 1.6;
  color: var(--chrome);
  margin-bottom: 14px;
  /* middle aligned as a block, justified as text (user rule) */
  text-align: justify;
  hyphens: auto;
  /* NO height cap: the text uses the full right third — a capped box with a
     hidden scrollbar read as text cut off mid-sentence */
}

.keyart--media img {
  width: 100%;
  height: auto;
  display: block;
  margin-bottom: 18px;   /* real gutters, so an enlarged photo has air */
  break-inside: avoid;
  border-radius: 12px;
  position: relative;
  z-index: 1;
  /* photos are always FULL solid (user rule): no see-through anywhere */
  transition: transform .28s var(--ease-back);
}
.keyart--media img:hover {
  transform: scale(1.06);
  /* the hovered photo rises ABOVE its neighbours, never tangles with them */
  z-index: 10;
  box-shadow: 0 22px 60px -20px rgba(0, 0, 0, .7);
}

.grain {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .1;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- top bar ---------- */

.topbar {
  position: relative;   /* anchors the centred pill */
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: clamp(14px, 2.2vh, 22px) clamp(16px, 4vw, 52px);
}
.brand { display: flex; align-items: center; gap: 9px; }
.brand img { width: 24px; height: 24px; }
.brand span { font-size: .68rem; letter-spacing: .22em; color: var(--chrome-dim); }

/* the whole top-right cluster: one consistent height, generous padding */
.sys {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.4vw, 40px);
  padding: 6px 0;
  min-height: clamp(56px, 5vw, 92px);
}
/* the avatar card: afaicon, ABHISHEK stacked over @dcrey7, PS5-profile style */
/* the avatar card, icon plus name, lives in the bottom right corner
   (the icon is the future dark/light toggle) */
.who {
  position: fixed;
  right: clamp(16px, 4vw, 52px);
  bottom: clamp(14px, 2.4vh, 24px);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
}
.who img { width: clamp(28px, 2.2vw, 44px); height: auto; }
.who__names { display: flex; flex-direction: column; line-height: 1.25; }
.who__names b {
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .14em;
  color: var(--chrome);
}
.who__names i {
  font-style: normal;
  font-size: .64rem;
  letter-spacing: .1em;
  color: var(--chrome-dim);
}

/* ---------- the disc player pill ---------- */

/* NO pill, no box: the disc and clean elements float, pinned to the
   bottom right corner of the window */
.pill {
  position: fixed;
  right: clamp(16px, 4vw, 52px);
  /* the player rides at the same level as the category deck */
  top: clamp(110px, 14vh, 190px);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 14px;
}
.pill__disc {
  background: #101623;   /* neutral dark vinyl until the track art loads */
  width: clamp(52px, 4vw, 84px);
  height: clamp(52px, 4vw, 84px);
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--chrome);   /* the vinyl rim */
  box-shadow: 0 6px 18px -6px rgba(0, 0, 0, .6);
}
/* the animation NEVER restarts: it is always applied and only its
   play-state toggles, so the disc pauses at its current angle and
   resumes from it, across pauses and song changes alike */
.pill__disc { animation: discspin 6s linear infinite; animation-play-state: paused; }
.pill__disc.spin { animation-play-state: running; }
@keyframes discspin { to { transform: rotate(360deg); } }
.pill__meta {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  /* FIXED width: the block never resizes with the song name's length */
  width: clamp(200px, 17vw, 380px);
  /* the whole stack NEVER grows taller than the disc */
  max-height: clamp(52px, 4vw, 84px);
}
/* one line: elapsed left, the song name in the middle (a slow train
   when it does not fit), total right */
.pill__line {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .56rem;
  color: var(--chrome-dim);
  font-variant-numeric: tabular-nums;
}
.pill__title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .6rem;
  font-weight: 600;
  letter-spacing: .05em;
  color: var(--chrome);
}
.pill__title > span { display: inline-block; }
.pill__title.scrolls { text-align: left; }
.pill__title.scrolls > span {
  animation: traintitle 14s linear infinite;
}
@keyframes traintitle { to { transform: translateX(-50%); } }
.pill__bar {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(var(--chrome), var(--chrome)) center / 100% 1px no-repeat;
  overflow: hidden;
}
.pill__bar i { display: block; height: 100%; width: 0; background: var(--chrome); }
/* transport UNDER the bar: previous left, play centre, next right */
.pill__ctl {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.pill__btn {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--chrome-dim);
  font-size: .66rem;
  padding: 2px 0;
  line-height: 1;
  /* FIXED width so glyph swaps (▶/⏸) can never shift the row */
  width: 1.6em;
  text-align: center;
  transition: color .18s var(--ease);
}
.pill__btn:hover { color: var(--chrome); }
/* bare icons only — no button chrome anywhere; same fixed footprint */
.pill__btn--main {
  font-size: .8rem;
  color: var(--chrome);
  width: 1.6em;
}
.pill__btn--main:hover { color: var(--chrome); }

/* The embed that actually plays the audio: parked OFF-SCREEN at real size,
   never display:none or 1px — browsers throttle invisible media iframes,
   which cut playback after a few seconds. */
#radio-host {
  position: fixed;
  left: -9999px;
  bottom: 0;
  width: 320px;
  height: 152px;
  pointer-events: none;
}
#radio-host iframe { width: 320px; height: 152px; }
.sysbtn {
  background: none; border: 0; cursor: pointer;
  color: var(--chrome-dim); font-size: 1.05rem; line-height: 1; padding: 6px;
  transition: color .2s var(--ease);
}
.sysbtn:hover { color: var(--chrome); }
.sysbtn[aria-pressed="true"] { color: var(--void); background: var(--chrome); border-radius: 8px; }
/* ---------- settings: the theme menu, top left ---------- */

.settings { position: relative; z-index: 45; }
.gear {
  background: none; border: 0; cursor: pointer;
  color: var(--chrome-dim); padding: 8px; line-height: 0;
  transition: color .2s var(--ease);
}
.gear:hover { color: var(--chrome); }
.gear[aria-expanded="true"] { color: var(--void); background: var(--chrome); border-radius: 8px; }
.themenu[hidden] { display: none; }
.themenu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  display: flex;
  flex-direction: column;
  min-width: 176px;
  padding: 10px;
  border-radius: 12px;
  background: rgba(6, 10, 20, .88);
  border: 1px solid var(--chrome);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, .8);
}
.themenu__head {
  font-size: .58rem; letter-spacing: .3em; color: var(--chrome-dim);
  padding: 4px 10px 8px;
}
.themenu button {
  background: none; border: 0; cursor: pointer;
  text-align: left;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .72rem; letter-spacing: .12em;
  color: var(--chrome);
  padding: 9px 10px; border-radius: 8px;
  white-space: nowrap;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.themenu button:hover { background: var(--chrome); color: var(--void); }
.themenu button[aria-current="true"] { text-decoration: underline; text-underline-offset: 4px; font-weight: 700; }

/* the beach is bright: give the chrome a soft shadow so words survive */
body[data-theme="beach"]:not(.light) .clock,
body[data-theme="beach"]:not(.light) .who__names b,
body[data-theme="beach"]:not(.light) .who__names i,
body[data-theme="beach"]:not(.light) .cat__label,
body[data-theme="beach"]:not(.light) .item__name,
body[data-theme="beach"]:not(.light) .pill__meta,
body[data-theme="beach"]:not(.light) .keyart-rail,
body[data-theme="beach"]:not(.light) .hero {
  text-shadow: 0 1px 10px rgba(4, 8, 16, .8), 0 0 2px rgba(4, 8, 16, .6);
}

/* ---------- search ---------- */

.themenu--search { min-width: clamp(280px, 24vw, 420px); }
.themenu--search input {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--chrome);
  outline: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .78rem;
  letter-spacing: .12em;
  color: var(--chrome);
  padding: 8px 10px 10px;
  margin-bottom: 6px;
}
.themenu--search input::placeholder { color: var(--chrome-dim); }
#searchResults { display: flex; flex-direction: column; }
#searchResults .hit {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
}
#searchResults .hit i {
  font-style: normal;
  font-size: .56rem;
  letter-spacing: .22em;
  color: var(--chrome-dim);
}
#searchResults .none {
  font-size: .66rem;
  color: var(--chrome-dim);
  padding: 8px 10px;
}

/* leaving for a reload (language switch): a soft fade out */
body.leaving { opacity: 0; transition: opacity .26s var(--ease); }

/* ---------- light mode (acting toggle; final art comes later) ---------- */

body.light {
  --void:       #E9EDF4;
  --chrome:     #15181D;
  --chrome-dim: #15181D;
  --glass:      rgba(10, 20, 40, .05);
  --glass-line: #15181D;
  /* light mode: white cards that sit on the page with a soft shadow;
     icons are near black at 87% (Material high emphasis), not pure black */
  --sys-red:    #FF3B30;
  --sys-orange: #FF9500;
  --sys-yellow: #FFCC00;
  --sys-green:  #34C759;
  --sys-blue:   #007AFF;
  --sys-purple: #AF52DE;
  --sys-pink:   #FF2D55;
  --surface:        #FFFFFF;
  --surface-on:     #FFFFFF;
  --surface-1:      #F2F4F7;
  --surface-2:      #E6E9EE;
  /* a soft ring plus a short shadow, so a card's edge shows on the card
     behind it */
  --card-shadow:    0 0 0 1px rgba(20, 30, 50, .07), 0 3px 8px -2px rgba(20, 30, 50, .16),
                    0 10px 24px -14px rgba(20, 30, 50, .35);
  --card-shadow-on: 0 0 0 1px rgba(20, 30, 50, .07), 0 6px 14px -4px rgba(20, 30, 50, .18),
                    0 18px 40px -18px rgba(20, 30, 50, .45);
}
/* the light field CROSSFADES over the dark one: an overlay whose opacity
   animates, because background gradients cannot transition smoothly */
.field::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(80% 62% at 26% 62%, #AFC6F2 0%, transparent 68%),
    radial-gradient(120% 80% at 50% 118%, #CBDCF8 0%, transparent 70%),
    #E6EEFB;
  opacity: 0;
  transition: opacity .6s var(--ease);
}
body.light .field::after { opacity: 1; }
body { transition: color .5s var(--ease); }
body.light .grain { opacity: .04; }
.grain { transition: opacity .6s var(--ease); }
body.light .side__desc { color: var(--chrome); }
body.light .themenu {
  background: rgba(250, 251, 253, .92);
  border-color: var(--chrome);
}
body.light .keyart { color: var(--chrome); }

/* a shader theme runs full screen behind everything, untouchable */
.themeframe {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  z-index: -1;
  pointer-events: none;
}

/* day, date and time: dead centre of the top of the window */
.clock {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-family: 'Archivo', sans-serif;
  font-weight: 300;
  font-size: clamp(.9rem, 1.05vw, 1.3rem);
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--chrome);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}

/* ---------- layout ---------- */

.xmb {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0 clamp(16px, 4vw, 52px) clamp(14px, 2.4vh, 24px);
}

/* ---------- horizontal axis: categories ---------- */

/* Categories sliding past the edges fade out instead of being cut by the
   overflow — a hard clip reads as a line across the icon mid-slide.

   The vertical padding is glow room: the active icon's shadow reaches ~45px
   below it, and clipping it at the box edge drew a straight line right above
   the item column, exposing the container. The padding shows the glow inside
   the overflow box; the negative margins give the space back to the layout. */
.barwrap {
  flex: 0 0 auto;
  overflow: hidden;
  padding: 20px 0 76px;
  margin: -20px 0 -30px;   /* 46px effective — clear air between the category
                              label and the vertical menu below it */
  position: relative;
  z-index: 1;           /* glow may bleed over the column area, never under a box edge */
  pointer-events: none;
  /* The left fade must be NARROWER than the reserved slot (--crossx), or the
     faded neighbour dissolves entirely inside it and the slot reads empty. */
  mask-image: linear-gradient(to right,
    transparent 0, #000 calc(var(--crossx) * .5),
    #000 calc(100% - clamp(40px, 6vw, 90px)), transparent 100%);
  -webkit-mask-image: linear-gradient(to right,
    transparent 0, #000 calc(var(--crossx) * .5),
    #000 calc(100% - clamp(40px, 6vw, 90px)), transparent 100%);
}
.barwrap .bar { pointer-events: auto; }

.bar {
  display: flex;
  align-items: flex-start;
  width: max-content;
  position: relative;
  will-change: transform;
  transition: transform .46s var(--ease);
}

.cat {
  flex: 0 0 auto;
  width: var(--catw);
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* the title sits tight under the card, ON the reflection band */
  gap: clamp(18px, 1.6vw, 30px);
  padding: 0;
  position: relative;   /* stacking: JS sets z-index by distance from the
                           selection, so cards tuck under toward it */
}
/* The deck is drawn with transforms only — JS sets a translateX per card
   (sliver + clearance geometry) and layout never moves. NO margins here:
   an animating margin shifts offsetLeft mid-read and breaks the crosspoint. */
.cat { transition: transform .42s var(--ease); will-change: transform; }

/* NO CARDS (user rule): the deck is the icons alone. Each slot keeps the
   size a card had, so the layout, the crosspoint and the tests do not
   move. The selected icon is a 3D extrusion revolving on Y; the previous
   and the next stand still at the cover flow slant (drawn by icon3d.js,
   not by a CSS tilt); the ones further out are hidden, since with no card
   in front nothing would cover them. */
.cat__icon {
  width: var(--catw);
  height: var(--catf);
  display: grid;
  place-items: center;
  position: relative;
  transition: opacity .3s var(--ease), transform .38s var(--ease-back);
}
/* one previous, every next: only the far BEFORE icons hide */
.cat--before.cat--far .cat__icon { opacity: 0; }
.cat.is-on .cat__icon { transform: scale(1.2); }
.cat__icon img { width: 66%; height: 66%; object-fit: contain; }
/* the icon carries the category colour */
.cat__icon .glyph { display: none; width: 46%; height: 46%; color: var(--key); }
/* the canvas is taller than the slot: the icon takes the top square, its
   faded reflection hangs below it, the label sits on the reflection */
.cat__icon .glyph3d { display: block; position: absolute; top: 0; left: 0; width: 100%; height: 175%; color: var(--key); pointer-events: none; }
/* no motion: the flat icons */
@media (prefers-reduced-motion: reduce) {
  .cat__icon .glyph { display: block; }
  .cat__icon .glyph3d { display: none; }
}
.item__mark .glyph { width: 74%; height: 74%; color: var(--key); display: block; }

.cat__mark {
  font-family: 'Anton', sans-serif;
  font-size: calc(var(--catf) * .5);
  line-height: 1;
  color: var(--chrome);
}

/* Only the selected card is titled — the sides are anonymous thumbnails,
   exactly as the PS5 rail and an alt-tab switcher both do it. */
/* The glossy floor, done in LAYERS (user spec): every card's reflection
   is painted SOLID inside one group, stacked with the same z order as
   the deck, so an upper reflection fully hides a lower one where they
   overlap. The group is then faded as a whole: same .38 strength, same
   88% falloff as the approved look, but occlusion instead of blending. */
.reflbar {
  position: absolute;
  top: 20px;               /* the barwrap's top padding: same box as .bar */
  /* 120px of slack each side: the scaled first card must never overflow
     this box, or the mask clips it. The engine adds 120 to every left. */
  left: -120px;
  right: -120px;
  height: calc(var(--catf) * 2);
  pointer-events: none;
  opacity: .38;
  /* the same slide timing as .bar: the floor moves in the same frame */
  will-change: transform;
  transition: transform .46s var(--ease);
  -webkit-mask-image: linear-gradient(to bottom,
    rgb(0 0 0) calc(var(--catf) + 10px),
    transparent calc(var(--catf) + 10px + var(--catf) * .39));
  mask-image: linear-gradient(to bottom,
    rgb(0 0 0) calc(var(--catf) + 10px),
    transparent calc(var(--catf) + 10px + var(--catf) * .39));
}
.rcard {
  position: absolute;
  top: 0;
  width: var(--catw);
  height: var(--catf);
  transition: transform .42s var(--ease);
  will-change: transform;
}
.rcard__icon {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  /* same tilt timing as .cat__icon */
  transition: transform .38s var(--ease-back);
}
.rcard--before .rcard__icon {
  transform-origin: right center;
  transform: perspective(700px) rotateY(38deg) translateZ(-14px);
}
.rcard--after .rcard__icon {
  transform-origin: left center;
  transform: perspective(700px) rotateY(-38deg) translateZ(-14px);
}
.rcard.is-on .rcard__icon {
  transform: perspective(700px) translateZ(14px) scale(1.16);
}
.rcard__icon::after {
  content: '';
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  right: 0;
  height: 44%;
  border-radius: inherit;
  background: var(--surface);
  transition: filter .28s var(--ease), box-shadow .28s var(--ease),
              background-color .28s var(--ease);
}
.rcard--before .rcard__icon::after, .rcard--after .rcard__icon::after { background: var(--surface-1); }
.rcard--far .rcard__icon::after { background: var(--surface-2); }
.rcard.is-on .rcard__icon::after { background: var(--surface-on); }

.cat__label {
  font-size: .6rem;
  letter-spacing: .18em;
  color: var(--chrome);
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transition: opacity .25s var(--ease);
}
.cat.is-on .cat__label { opacity: 1; }
/* NO focus rings on the console chrome (user rule): selection reads
   from scale and colour, not boxes. Form fields keep theirs. */
.cat:focus-visible, .item:focus-visible, .btn:focus-visible,
a.card:focus-visible, .gear:focus-visible, .pill__btn:focus-visible,
.themenu button:focus-visible, .boot__start:focus-visible,
.cat:focus, .item:focus, .gear:focus, .pill__btn:focus,
.themenu button:focus { outline: none; }

/* ---------- the cross ---------- */

.cross {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  gap: clamp(20px, 4vw, 56px);
  align-items: stretch;
}

/* ---------- vertical axis: items ---------- */

/* The column hangs from --crossx — the same fixed x the active category parks
   on. Both are constants, so the column never moves horizontally at all;
   only the bar slides. That is what makes the two axes a cross.

   Aligned by CENTRE, not left edge: the item icon's centre line sits on the
   category icon's centre line, so the margin is crossx plus half the size
   difference. The extra 4px of item padding (ring room) is subtracted back. */
.column {
  flex: 0 0 auto;
  width: clamp(230px, 22vw, 430px);  /* names fit, video gets the rest */
  /* the selected icon's SHADOW spans 22px past the box (blur 34 minus
     spread 12) plus 6px of scale: 28px of clearance, or the shadow is
     guillotined at the left edge; margin compensates, crosspoint exact */
  margin-left: calc(var(--crossx) + (var(--catw) - var(--imark)) / 2 - 32px);
  padding-left: 28px;
  overflow: hidden;
  position: relative;
  padding-top: 6px;
  /* hangs from the top, directly below the active category — never centred */
  align-self: flex-start;
  max-height: 100%;
  /* The top fade spans the whole previous row: invisible at its top, full
     only where the selected row starts — so no fade edge ever crosses the
     selected icon. On the first item there IS no previous row, so the fade
     collapses to zero via .column--athead and the selection sits flush. */
  --topfade: calc(var(--row) + 6px);
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--topfade), #000 66%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--topfade), #000 66%, transparent 100%);
}

.column--athead { --topfade: 0px; }
/* when every item fits, there is nothing to fade INTO: the bottom fade
   would just smear the last rows like a bad shadow (visible on bright
   themes). The engine sets this class whenever the list fits. */
.column--fits {
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--topfade), #000 100%);
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--topfade), #000 100%);
}

.column__track {
  position: relative;
  display: flex;
  flex-direction: column;
  will-change: transform;
  transition: transform .4s var(--ease);
}

.item {
  flex: 0 0 auto;
  min-height: var(--row);
  display: flex;
  align-items: center;
  gap: 13px;
  /* 4px of left padding is ring room — without it the column's overflow
     clipped the selected icon's yellow ring into a hard vertical line */
  padding: 6px 10px 6px 4px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  color: var(--chrome);
  opacity: .72;   /* the other rows step back; the selected one is full */
  transition: opacity .24s var(--ease), color .24s var(--ease), transform .24s var(--ease);
}
.item:hover { opacity: .9; }
/* No nudge on the selected row: the column must sit exactly under the active
   category, and a 6px shift breaks that. The ring and weight carry the state. */
.item.is-on {
  opacity: 1;
  color: var(--chrome);
}


/* every vertical icon box is SQUARE, the same dimension as the logos */
.item__mark {
  flex: 0 0 auto;
  width: var(--imark);
  height: var(--imark);
  border-radius: 10px;
  transition: transform .3s var(--ease-back);
  display: grid;
  place-items: center;
  position: relative;
  overflow: visible;   /* the halo reaches past the box */
  /* NO BOX (user rule): a letter or an icon sits flat on nothing; only an
     image is a square, because the image itself is one */
  background: none;
  font-family: 'Anton', sans-serif;
  font-size: calc(var(--imark) * .52);
  color: var(--key);   /* letter marks are coloured like the icons */
}
.item__mark img { width: 72%; height: 72%; object-fit: contain; }
/* the selected row's icon GROWS (no glow, user rule): with the bold
   name and the other rows stepped back, it is the one row that reads as
   selected. 1.18, not more: bigger swallowed the neighbouring rows. */
.item.is-on .item__mark { transform: scale(1.22); z-index: 2; }

/* A real image is its OWN box: the coloured slab vanishes and the image
   fills the full row height, rounded like a proper icon. Square logos and
   portraits read as app icons, not stickers on a rectangle. */
.item__mark.has-img { background: none; overflow: visible; }
.item__mark.has-img img {
  /* exactly the square box, explicit sizes (a % height cannot resolve
     inside an auto-row grid and blows up to natural size) */
  width: var(--imark);
  height: var(--imark);
  object-fit: cover;
  border-radius: 10px;
}
/* No shadows on the tiles (user rule): the soft dark halo read as a
   smudge down the left side of the list. A tile sits flat on the field. */
.item .item__mark.has-img,
.item.is-on .item__mark.has-img { box-shadow: none; }
/* A badge with a transparent background is its OWN shape (the AWS hexagon):
   no box, no rounded rectangle. */
.item__mark.has-img.item__mark--shape img {
  object-fit: contain;
  border-radius: 0;
}

.item__name {
  font-size: .84rem;
  letter-spacing: .04em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.item.is-on .item__name { font-weight: 700; font-size: 1.18rem; }


/* ---------- detail ---------- */

/* Heading at the TOP of the detail (right of the vertical menu); the shelf
   stays pinned to the bottom; the photos live on the middle right. */
/* The screen is three thirds: menu | heading + media | words. The middle
   column ends where the right rail begins and NEVER extends into it. */
/* Two rows: everything above the shelf, then the shelf. The heading and the
   3D character are both placed in that first row, one on top of the other, so
   the character owns the full height of the column instead of starting below
   the heading. That is what lets him be big without the words cropping him. */
.detail {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  gap: clamp(12px, 2.4vh, 22px);
  padding: 6px 0 4px;
  margin-right: calc(clamp(300px, 23vw, 620px) + 3.5vw);
}
.detail > .hero { grid-area: 1 / 1; z-index: 1; }
.detail > .shelf { grid-area: 2 / 1; }

.hero {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;   /* the middle third is always LEFT aligned */
  gap: 8px;
  min-height: 0;
}
.hero > * {
  animation: rise .44s var(--ease) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}

.hero__badge {
  font-size: .66rem; letter-spacing: .2em; color: var(--chrome);
  display: flex; align-items: center; gap: 10px;
}
.hero__sub {
  font-size: .72rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--chrome-dim);
}
.hero__title {
  font-family: 'Anton', sans-serif;
  font-weight: 400;
  font-size: clamp(1.8rem, 3vw, 4.2rem);   /* small: the media is the star */
  margin-left: -.045em;   /* optical: Anton's side bearing pushed the huge
                             glyphs visually right of the eyebrow above */
  line-height: .88;
  letter-spacing: -.02em;
  text-transform: uppercase;
  max-width: 16ch;
  text-wrap: balance;
}
.hero__meta { font-family: 'Archivo', system-ui, sans-serif; font-size: .72rem; color: var(--chrome-dim); }

/* The action is embedded IN the title itself — no separate link text.
   A small arrow marks it; the whole heading (and the logo) opens it. */
.hero__title a {
  color: inherit;
  text-decoration: none;
  transition: color .18s var(--ease);
}
.hero__title a::after {
  content: ' ↗';
  font-size: .45em;
  vertical-align: super;
  color: var(--chrome-dim);
}
.hero__title a:hover { text-decoration: underline; text-underline-offset: .12em; }
.hero > a { align-self: flex-start; }   /* a linked logo stays left */
.hero__body {
  font-size: .88rem; line-height: 1.55; color: var(--chrome);
  max-width: 56ch;
  /* full recommendation quotes are long — contain them quietly */
  max-height: 40vh;
  overflow-y: auto;
  scrollbar-width: none;
}
.hero__body::-webkit-scrollbar { display: none; }

/* experience bullets: a real bulleted list, one line each */
.hero__bullets {
  list-style: none;
  display: grid;
  gap: 10px;
  max-width: 60ch;
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--chrome);
}
.hero__bullets li { padding-left: 1.1em; position: relative; }
/* a domain that is the link: bold, underlined, in the ink */
.bullet-link {
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: text-decoration-thickness .15s var(--ease);
}
.bullet-link:hover { text-decoration-thickness: 2px; }
.hero__bullets li::before {
  content: '▸';
  position: absolute;
  left: 0;
  color: var(--chrome);
}

.hero__stack {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .74rem;
  color: var(--chrome-dim);
  max-width: 60ch;
}

/* the demo video owns the main middle area */
/* a PROPER video frame: exact 16:9 always — the width is capped by the
   height budget, so the player is never letterboxed or squashed */
.hero__video {
  /* the video is the exception: as big as the middle third allows, it
     was right before (user rule) */
  width: min(100%, calc((100vh - clamp(260px, 30vh, 380px)) * 16 / 9));
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: 16px;
  margin-top: 8px;
  box-shadow: 0 30px 80px -28px rgba(0, 0, 0, .7);
}

.btn {
  align-self: flex-start;
  margin-top: 4px;
  padding: 10px 20px;
  border-radius: 999px;
  background: var(--chrome);
  color: var(--void);
  font-size: .76rem; font-weight: 600; letter-spacing: .08em;
  text-decoration: none;
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.btn:hover { transform: translateY(-2px); }


/* ---------- shelf ---------- */

.shelf {
  flex: 0 0 auto;
  display: flex;
  gap: clamp(18px, 2.2vw, 40px); align-items: center;
  overflow-x: auto;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  /* cards run off the right softly, not against a hard cut */
  mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent 100%);
}
.shelf::-webkit-scrollbar { display: none; }

.card {
  flex: 0 0 auto;
  width: clamp(200px, 23vw, 265px);
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  display: flex;
  flex-direction: column;
  gap: 5px;
  text-decoration: none;
  color: inherit;
  transition: background .18s var(--ease), transform .18s var(--ease);
  animation: rise .42s var(--ease) both;
  animation-delay: calc(180ms + var(--i, 0) * 45ms);
}
a.card:hover { box-shadow: inset 0 0 0 2px var(--chrome); transform: translateY(-3px); }


/* media cards: the embedded demo player and event photos — images keep their
   own aspect (the photos are what they are), sized by the shelf's height */
.card--media {
  width: clamp(250px, 27vw, 340px);
  aspect-ratio: 16 / 9;
  padding: 0;
  overflow: hidden;
}
.card--media iframe { width: 100%; height: 100%; border: 0; display: block; }

.card--photo { width: auto; padding: 0; overflow: hidden; }
.card--photo img {
  height: clamp(120px, 17vh, 165px);
  width: auto;
  display: block;
}

/* plain text links, no boxes */
.plink {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  color: var(--chrome);
  font-size: .84rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-color: var(--glass-line);
  transition: color .18s var(--ease);
  animation: rise .42s var(--ease) both;
  animation-delay: calc(180ms + var(--i, 0) * 45ms);
}
.plink:hover { text-decoration-thickness: 2px; }
.plink__host {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: .68rem;
  font-weight: 400;
  color: var(--chrome-dim);
}

.card__kicker { font-size: .56rem; letter-spacing: .2em; text-transform: uppercase; color: var(--chrome); }
.card__title  { font-size: .86rem; font-weight: 600; }
.card__body   { font-size: .76rem; line-height: 1.5; color: var(--chrome); }

/* ---------- the uniform item layout ---------- */

/* right rail: links on top, description or bullets below */
.keyart-rail {
  position: fixed;
  z-index: 1;
  right: 3vw;
  /* ONE top line for the whole cross (user rule): the rail starts where
     the column and the heading start. xmb.js measures the cross and sets
     --cross-top; the clamp is only the fallback before it runs. */
  top: var(--cross-top, clamp(210px, 24vh, 330px));
  bottom: clamp(120px, 14vh, 190px); /* and the avatar owns the corner below */
  width: clamp(300px, 23vw, 620px);
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
  overscroll-behavior: contain;
  pointer-events: auto;
  font-family: 'Archivo', system-ui, sans-serif;
  /* a slim scroller in the site's own chrome: long lists of bullets are
     common here, and a hidden scrollbar hides that there is more */
  padding-right: 6px;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--chrome) 45%, transparent) transparent;
}
.keyart-rail::-webkit-scrollbar { width: 6px; }
.keyart-rail::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--chrome) 45%, transparent);
  border-radius: 3px;
}
.keyart-rail::-webkit-scrollbar-track { background: transparent; }
/* the right third is centre aligned; the links right aligned above the
   words; everything starts at the TOP of the rail, level with the column
   and the heading */
.keyart-rail { text-align: center; align-items: center; }
.keyart-rail > .rail-links {
  align-self: flex-end;
  text-align: right;
  justify-content: flex-end;
}
.keyart-rail > .side__desc,
.keyart-rail > .hero__bullets { margin-bottom: auto; }
.rail-links { justify-content: flex-end; }

/* long descriptions scroll in place, with a slim scroller in the site's
   chrome: thin, rounded, quiet until hovered */
.keyart-rail .side__desc {
  max-height: 44vh;
  overflow-y: auto;
  padding-right: 10px;
  scrollbar-width: thin;
  scrollbar-color: var(--chrome) transparent;
}
.keyart-rail .side__desc::-webkit-scrollbar { display: block; width: 6px; }
.keyart-rail .side__desc::-webkit-scrollbar-track { background: transparent; }
.keyart-rail .side__desc::-webkit-scrollbar-thumb {
  background: var(--chrome);
  border-radius: 999px;
}
.keyart-rail .side__desc::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, .4);
}
.keyart-rail .hero__bullets { text-align: left; }   /* bullets read left */
.keyart-rail::-webkit-scrollbar { display: none; }
.keyart-rail > * {
  animation: rise .44s var(--ease) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
.rail-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px clamp(16px, 1.8vw, 32px);
  align-items: baseline;
}

/* The middle image fills the space under the heading. NO shadow and no
   backing: the image IS the card — a shadow around the letterboxed element
   box painted a phantom panel behind portraits. */
/* ONE square for every middle image (user rule): logo, school, badge or
   portrait, the box is identical. Logos and badges FIT inside it,
   portraits FILL it. */
.hero__logo {
  width: var(--mediaw);
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: contain;
  border-radius: 12px;
  margin-top: 10px;
}
/* a badge shape in the middle: the symbol itself, no rounded card */
.hero__logo--shape { border-radius: 0; filter: drop-shadow(0 18px 30px rgba(0, 0, 0, .35)); }
/* PEOPLE photos fill the same square, anchored to the face */
.hero__logo--person {
  object-fit: cover;
  object-position: top;
}

/* the middle photo board: ordered masonry, hover air, click = lightbox.
   Every photo must be reachable: the board SCROLLS inside the middle
   third instead of running off the bottom of the screen, with a slim
   scroller in the site's own chrome so it is obvious there is more. */
/* THE PHOTO WALL. A fixed mosaic, never a scroller: at most five pictures,
   laid into a box of a set height so it fits any screen with nothing to
   scroll. One picture leads and the rest tile around it. The cells crop
   (object-fit: cover) because that is the price of a shape that always
   fits; the lead picture is large enough to carry the story. */
/* THE PHOTO WALL. A pinboard, never a scroller: at most five pictures laid
   into quadrants of a box with a set height, so it fits any screen with
   nothing to scroll. The tiles are deliberately not all the same shape, two
   of them portrait, so it reads as a wall of pictures rather than a hero
   shot with thumbnails under it. Cells crop, which is the price of a shape
   that always fits. */
.mboard {
  width: 100%;
  display: grid;
  gap: 10px;
  margin-top: 10px;
  height: clamp(190px, 38vh, 460px);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: repeat(4, minmax(0, 1fr));
}
.mboard img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 12px;
  position: relative;
  z-index: 1;
  cursor: zoom-in;
  /* photos are always FULL solid (user rule): no see-through anywhere */
  transition: transform .28s var(--ease-back);
}
.mboard img:hover {
  transform: scale(1.05);
  z-index: 10;
  box-shadow: 0 22px 60px -20px rgba(0, 0, 0, .7);
}

/* One arrangement per count. No layout is a grid of equal squares and none
   line up into quadrants: the tiles in each column have different heights so
   the seams between them never meet across the wall. That offset is what
   makes a pinboard read as a pinboard.
   grid-area is row-start / column-start / row-end / column-end. */

/* two: a wide panel and a narrow one */
.mboard--2 {
  grid-template-columns: 3fr 2fr;
  grid-template-rows: minmax(0, 1fr);
}

/* three: a stacked column beside one full height picture */
.mboard--3 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(7, minmax(0, 1fr));
}
.mboard--3 img:nth-child(1) { grid-area: 1 / 1 / 4 / 2; }
.mboard--3 img:nth-child(2) { grid-area: 4 / 1 / 8 / 2; }
.mboard--3 img:nth-child(3) { grid-area: 1 / 2 / 8 / 3; }

/* four: two columns whose seams are out of step, 3+4 against 4+3 */
.mboard--4 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(7, minmax(0, 1fr));
}
.mboard--4 img:nth-child(1) { grid-area: 1 / 1 / 4 / 2; }
.mboard--4 img:nth-child(2) { grid-area: 4 / 1 / 8 / 2; }
.mboard--4 img:nth-child(3) { grid-area: 1 / 2 / 5 / 3; }
.mboard--4 img:nth-child(4) { grid-area: 5 / 2 / 8 / 3; }

/* five: three columns, every tile a different height, and the last column
   stops short so the bottom edge is ragged the way a real board is */
.mboard--5 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(6, minmax(0, 1fr));
}
.mboard--5 img:nth-child(1) { grid-area: 1 / 1 / 4 / 2; }
.mboard--5 img:nth-child(2) { grid-area: 4 / 1 / 7 / 2; }
.mboard--5 img:nth-child(3) { grid-area: 1 / 2 / 5 / 3; }
.mboard--5 img:nth-child(4) { grid-area: 5 / 2 / 7 / 3; }
.mboard--5 img:nth-child(5) { grid-area: 1 / 3 / 6 / 4; }

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

/* a single photo board (PEOPLE) is uniform: same height for every person,
   and shown whole, because cropping a face is not a design choice */
.mboard--one {
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  height: clamp(280px, 52vh, 620px);
}
.mboard--one img {
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin: 0 auto;
}

@media (max-width: 759px), (max-width: 1024px) and (orientation: portrait) {
  .mboard { height: clamp(170px, 26vh, 260px); gap: 8px; }
}

/* ---------- the ADD YOURS form ---------- */

.recform {
  width: min(100%, 640px);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-top: 12px;
}
.recform input,
.recform textarea {
  font: inherit;
  font-size: .92rem;
  color: var(--chrome);
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: 10px;
  padding: 12px 14px;
  outline: none;
  transition: border-color .18s var(--ease);
}
.recform input:focus,
.recform textarea:focus { border-color: var(--chrome); box-shadow: inset 0 0 0 1px var(--chrome); }
.recform input::placeholder,
.recform textarea::placeholder { color: var(--chrome-dim); }
.recform textarea { grid-column: 1 / -1; resize: vertical; }
.recform__photo {
  grid-column: 1 / -1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 14px;
  border: 1px dashed var(--glass-line);
  border-radius: 10px;
  color: var(--chrome-dim);
  cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.recform__photo:hover { border-style: solid; }
.recform button {
  grid-column: 1 / -1;
  justify-self: start;
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--void);
  background: var(--chrome);
  border: 0;
  border-radius: 999px;
  padding: 12px 26px;
  cursor: pointer;
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.recform button:hover { transform: translateY(-2px); }
.recform__note { grid-column: 1 / -1; font-size: .88rem; color: var(--chrome); font-weight: 600; }

/* ---------- lightbox ---------- */

#lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  background: rgba(4, 7, 14, .55);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s var(--ease);
  cursor: zoom-out;
}
#lightbox.on { opacity: 1; pointer-events: auto; }
#lightbox img {
  max-width: 86vw;
  max-height: 86vh;
  border-radius: 16px;
  box-shadow: 0 40px 120px -30px rgba(0, 0, 0, .9);
}
.keyart--media img { cursor: zoom-in; }

/* ---------- boot ---------- */

.boot {
  position: fixed; inset: 0; z-index: 10;
  /* a scrim, not a wall: the picked theme shows through on the landing */
  background: color-mix(in oklab, var(--void) 42%, transparent);
  transition: background .6s var(--ease);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; text-align: center; padding: 24px;
  perspective: 900px;   /* the coin turns in real 3D */
  transition: opacity .5s var(--ease), visibility .5s;
}
body:not(.booting) .boot { opacity: 0; visibility: hidden; }

/* The afaicon on the gate: its black outline, traced to a vector, drawn
   as a 3D extrusion turning on the Y axis by icon3d.js (the same renderer
   as the category icons), in silver chrome. */
.boot__mark3d {
  width: clamp(200px, 34vw, 380px);
  aspect-ratio: 1;
  color: var(--chrome);
  transition: opacity .45s var(--ease);
}
.boot.leaving .boot__mark3d,
.boot.leaving .boot__title, .boot.leaving .boot__sub,
.boot.leaving .loadbar, .boot.leaving .boot__start { opacity: 0; transition: opacity .45s var(--ease); }

.boot__title { font-family: 'Anton', sans-serif; font-weight: 400; font-size: clamp(1.7rem, 6vw, 3rem); }
.boot__sub { font-size: .66rem; letter-spacing: .26em; color: var(--chrome-dim); }

.loadbar { width: min(240px, 60vw); height: 2px; background: linear-gradient(var(--chrome), var(--chrome)) center / 100% 1px no-repeat; overflow: hidden; }
.loadbar i { display: block; height: 100%; width: 0; background: var(--chrome); transition: width .1s linear; }

.boot__start {
  margin-top: 6px; background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: .78rem; font-weight: 600; letter-spacing: .24em;
  color: var(--chrome); opacity: 0; padding: 10px 18px;
  transition: opacity .3s var(--ease);
}
/* PRESS ANY KEY breathes, it does not blink */
.boot__start.ready { opacity: 1; animation: breathe 2.6s ease-in-out infinite; }
@keyframes breathe { 50% { opacity: .35; } }

.boot__hint { font-family: 'Archivo', system-ui, sans-serif; font-size: .62rem; color: var(--chrome-dim); }

/* the landing page shows ONLY the boot screen: the player and the
   avatar wait for the home screen, then everything fades in together */
/* the landing page keeps the TOP BAR alive (theme, mode, language,
   search all work before PRESS START); only the bar, the player and
   the avatar wait for the home screen */
body.booting .xmb,
body.booting .pill, body.booting .who {
  opacity: 0;
  pointer-events: none;
}
/* the content rail and key art are fixed elements outside .xmb: they
   must not shine through the landing scrim (visibility, because the
   key art's own animation pins its opacity) */
body.booting .keyart-rail,
body.booting .keyart { visibility: hidden; }
/* the landing offers theme, mode and language; search waits for home */
body.booting .settings:has(#searchBtn),
body.booting .settings:has(#sfxBtn) { display: none; }
.xmb, .topbar, .pill, .who { transition: opacity .6s var(--ease) .1s; }
.topbar { z-index: 60; }   /* above the boot layer AND the player */
.pill[hidden] { display: none; }

/* ---------- breakpoints ---------- */

@media (max-width: 1023px) {
  .cross { gap: clamp(16px, 3vw, 32px); }
}

/* Phones: the cross stacks. Categories across the top, items in a row of
   their own beneath, detail below that — the same two axes, folded. */
@media (max-width: 759px), (max-width: 1024px) and (orientation: portrait) {
  .brand span, .clock { display: none; }
  .topbar { padding-block: 12px; }

  .cross {
    display: flex;
    flex-direction: column;
    gap: 12px;
    align-items: stretch;
  }

  .column {
    flex: 0 0 auto;
    align-self: flex-start;
    width: auto;
    min-width: 0;
    max-width: calc(100% - var(--crossx, 0px));
    max-height: calc(var(--row) * 4);
    mask-image: linear-gradient(to bottom,
      transparent 0, #000 var(--topfade), #000 56%, transparent 100%);
    -webkit-mask-image: linear-gradient(to bottom,
      transparent 0, #000 var(--topfade), #000 56%, transparent 100%);
  }

  /* Must grow, or the detail sits directly under the list and leaves the
     bottom half of the phone empty with the key art crashing into the text. */
  .detail { flex: 1 1 auto; min-height: 0; justify-content: flex-end; gap: 10px; }

  .hero__title { font-size: clamp(1.9rem, 8.6vw, 3rem); max-width: 100%; }
  .hero__body { font-size: .82rem; }
  .card { width: min(76vw, 290px); }
  /* Small enough that the glyphs fit the viewport — cropped, a big letter
     just reads as a grey rectangle. */
  .keyart { opacity: .06; right: 4vw; top: 40%; font-size: clamp(5rem, 17vh, 9rem); }
  .keyart img { width: clamp(6rem, 20vh, 10rem); }
}

@media (max-height: 560px) {
  .hero__title { font-size: clamp(1.5rem, 4.6vw, 2.4rem); }
  .hero { gap: 5px; }
}

/* ---------- motion ---------- */

@media (prefers-reduced-motion: reduce) {
  :root { transition: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* no cards, so no card reflection */
.reflbar { display: none; }

/* ---------- the phone version (js/mobile.js) ----------
   Under 760 px the desktop cross is hidden and three screens take over:
   the shared gate, a vertical wheel of the category icons, and one clean
   scroll per category. Not a shrunken desktop. */
@media (max-width: 759px), (max-width: 1024px) and (orientation: portrait) {
  body.mobile .xmb, body.mobile .reflbar,
  body.mobile #keyart, body.mobile .who { display: none !important; }

  /* the top bar: icons top left, the date and time in the middle */
  body.mobile .clock { display: block; font-size: .6rem; letter-spacing: .1em; white-space: nowrap; }
  /* the top bar is one 58px row shared by every screen: the clock (home)
     or the back button (page) sit in it, the hamburger at its right */
  body.mobile .topbar { position: fixed; top: 0; left: 0; right: 0; height: 58px; min-height: 0; padding: 0; pointer-events: none; }
  body.mobile.mob-menu-open .sys { pointer-events: auto; }
  body.mobile .sys { margin-left: 0; gap: 8px; min-height: 0; padding: 0; }
  body.mobile .gear { padding: 6px; }
  body.mobile .clock { left: auto; right: 12px; transform: translateY(-50%); }

  .mob { position: fixed; inset: 0; z-index: 5; display: none; }
  body.mobile:not(.booting) .mob { display: block; }

  /* 2. home: a launcher. Big clock, status line, two widgets, rows. */
  body.mobile .topbar .clock { display: none; }   /* the big one replaces it */
  .mob-menu { display: none; }
  body.mobile .mob-menu {
    display: block; background: none; border: 0; color: var(--chrome);
    padding: 8px; line-height: 0; cursor: pointer; z-index: 61;
    position: absolute; top: 9px; right: 10px;   /* top RIGHT, on the row */
    pointer-events: auto; margin: 0;
  }
  body.mobile .sys { display: none; }
  body.mobile.mob-menu-open .sys {
    display: flex; flex-wrap: wrap; gap: 4px;
    position: absolute; top: 50px; right: 10px; left: auto; z-index: 61;
    padding: 8px; border-radius: 12px;
    background: color-mix(in oklab, var(--void) 88%, transparent);
    border: 1px solid var(--chrome);
  }
  .mob-home {
    position: absolute; inset: 0;
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
    padding: 0 14px 40px;
    display: flex; flex-direction: column; gap: 12px;
  }
  /* the clock fills the top row, centred, level with the hamburger */
  .mob-clock { height: 58px; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 0 44px; text-align: center; }
  /* the player pill, in the flow under the clock, like a lock screen widget */
  body.mobile .pill {
    position: static; z-index: auto;
    display: flex; align-items: center; gap: 12px;
    padding: 6px 4px;
    background: none; border: 0;   /* no box (user rule) */
  }
  body.mobile .pill[hidden] { display: none; }
  /* a small widget: the disc turns only while a song plays, as on the
     desktop, and the whole thing sits low under the clock */
  body.mobile .pill { gap: 10px; padding: 2px 4px; }
  body.mobile .pill__disc { width: 44px; height: 44px; }
  body.mobile .pill__meta { width: auto; flex: 1 1 auto; min-width: 0; max-height: 44px; gap: 2px; }
  body.mobile .pill__line { font-size: .5rem; gap: 8px; }
  body.mobile .pill__title { font-size: .54rem; }
  body.mobile .pill__bar { height: 2px; }
  body.mobile .pill__btn { font-size: .58rem; padding: 1px 0; }
  .mob-clock__time { font-family: 'Archivo', system-ui, sans-serif; font-weight: 300; font-size: 2.4rem; line-height: 1; letter-spacing: .02em; }
  .mob-clock__date { margin-top: 3px; font-size: .6rem; letter-spacing: .18em; }
  .mob-rows { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
  /* rows: no box, no border (user rule). The icon left, the name in the
     middle, the chevron right; the selected row is the one whose icon
     revolves, with a bold name and a subtitle. */
  .mob-row {
    display: flex; align-items: center; gap: 12px;
    padding: 0 8px; min-height: 64px;
    background: none; border: 0; color: var(--chrome);
    font: inherit; cursor: pointer;
    transition: min-height .2s var(--ease);
  }
  .mob-row .glyph3d { flex: 0 0 auto; width: 52px; height: 52px; color: var(--key); display: block; }
  .mob-row__text { flex: 1 1 auto; min-width: 0; text-align: center; }
  .mob-row__name { font-size: 1.1rem; letter-spacing: .1em; font-weight: 500; }
  .mob-row__sub { font-size: .72rem; display: none; margin-top: 3px; }
  .mob-row__go { flex: 0 0 auto; width: 22px; opacity: 0; transition: opacity .2s var(--ease); }
  .mob-row.is-on { min-height: 84px; }
  .mob-row.is-on .glyph3d { width: 60px; height: 60px; }
  .mob-row.is-on .mob-row__name { font-weight: 700; }
  .mob-row.is-on .mob-row__sub { display: block; }
  .mob-row.is-on .mob-row__go { opacity: 1; }

  /* 3. the page: one scroll, sticky header with the back button */
  .mob-page {
    position: absolute; inset: 0; display: none;
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
    padding: 0 18px 80px;
  }
  .mob--page .mob-page { display: block; }
  .mob--page .mob-home { display: none; }
  .mob-head {
    position: sticky; top: 0; z-index: 2;
    display: flex; align-items: center; gap: 10px;
    min-height: 58px; padding: 0 56px 0 10px; margin: 0 -18px 14px;
    background: color-mix(in oklab, var(--void) 78%, transparent);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  }
  .mob-back { background: none; border: 0; color: var(--chrome); padding: 6px; line-height: 0; cursor: pointer; }
  .mob-head__title { font-family: 'Anton', sans-serif; font-weight: 400; font-size: 1.4rem; letter-spacing: .04em; margin: 0; }
  /* an item is a row in the home's style that DROPS DOWN: media left,
     the name centred, a chevron; the rest opens under it */
  .mob-item { display: flex; flex-direction: column; margin: 0 0 6px; animation: none; }
  .mob-item__head {
    display: grid; grid-template-columns: 64px minmax(0, 1fr) 24px; gap: 12px;
    align-items: center; cursor: pointer; padding: 8px 0; min-height: 72px;
  }
  .mob-item--nomedia .mob-item__head { grid-template-columns: minmax(0, 1fr) 24px; }
  .mob-item--nomedia .mob-item__media { display: none; }
  .mob-item__media { min-width: 0; display: grid; place-items: center; }
  .mob-item__media .hero__logo { width: 64px; height: 64px; object-fit: cover; max-height: none; margin: 0; border-radius: 12px; }
  .mob-item__media .hero__logo--shape { object-fit: contain; border-radius: 0; }
  .mob-item__media .hero__logo--person { object-position: top; }
  .mob-item__media .glyph { width: 40px; height: 40px; color: var(--key); }
  .mob-item__mark { font-family: 'Anton', sans-serif; font-size: 1.6rem; color: var(--key); }
  .mob-item__words { display: flex; flex-direction: column; gap: 6px; min-width: 0; text-align: center; align-items: center; }
  .mob-item__words .hero__title { font-size: clamp(1.15rem, 5.6vw, 1.5rem); line-height: 1.05; margin: 0; max-width: 100%; text-wrap: balance; }
  .mob-item__words .hero__sub { font-size: .6rem; }
  .mob-item__chev { transition: transform .25s var(--ease); line-height: 0; }
  .mob-item.is-open .mob-item__chev { transform: rotate(180deg); }
  .mob-item__more { display: none; flex-direction: column; gap: 10px; padding: 2px 0 16px; }
  .mob-item.is-open .mob-item__more { display: flex; }
  .mob-item__more .hero__video { width: 100%; height: auto; aspect-ratio: 16 / 9; border-radius: 10px; }
  .mob-item__more .side__desc { font-size: .95rem; text-align: left; hyphens: none; max-height: none; margin: 0; }
  .mob-item__more .hero__bullets { font-size: .92rem; gap: 6px; max-width: 100%; }
  .mob-item__more .mboard { columns: 2 140px; column-gap: 8px; margin: 0; }
  .mob-item__more .mboard img { margin-bottom: 8px; border-radius: 8px; }
  .mob-item__more .rail-links { display: flex; flex-wrap: wrap; gap: 12px 18px; justify-content: flex-start; animation: none; }
  .mob-item .hero__title a { text-decoration: none; }
  /* media: full width, natural height; the desktop's viewport maths do not apply */
  .mob-item .hero__logo { width: 100%; height: auto; max-height: none; max-width: 100%; margin-top: 4px; }
  .mob-item .hero__logo--person { aspect-ratio: 1 / 1; object-fit: cover; object-position: top; }
  .mob-item .hero__logo--shape { width: 70%; align-self: center; }
  .mob-item .hero__video { width: 100%; height: auto; aspect-ratio: 16 / 9; }
  .mob-item .mboard { columns: 2 140px; column-gap: 10px; margin-top: 4px; }
  .mob-item .mboard img { margin-bottom: 10px; border-radius: 10px; }
  .mob-item .side__desc { text-align: left; font-size: 1rem; max-height: none; margin: 0; hyphens: none; }
  .mob-item .hero__bullets { font-size: .98rem; max-width: 100%; }
  .mob-item .hero__stack { max-width: 100%; }
  .mob-item .rail-links { display: flex; flex-wrap: wrap; gap: 12px 18px; align-items: center; justify-content: flex-start; animation: none; }
  .mob-item .rail-links .plink { animation: none; }
  .mob-item .btn { margin-top: 0; }
  .mob-item .recform { width: 100%; grid-template-columns: 1fr; }
  .mob-item .recform input, .mob-item .recform textarea { width: 100%; min-width: 0; }
}

/* The live character sits between the title and the shelf. It is a frame,
   so every control inside it belongs to the playground page. */
/* The character takes the whole middle: everything left between the heading
   and the shelf, at any screen size. It grows instead of holding a set
   height, and min-height: 0 lets it shrink on a short screen rather than
   pushing the shelf off the bottom. The viewer sizes its canvas from this
   box every frame, so no other change is needed to make it fit. */
.avatar {
  /* The same cell as the heading, and on top of it. His canvas is clear
     glass everywhere he is not, so the words still read around him, and he
     is free to stand right through where they sit. */
  grid-area: 1 / 1;
  z-index: 3;
  width: 100%;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.avatar[hidden] { display: none; }
.avatar__stage {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  border: 0;
  background: transparent;
  color-scheme: normal;
}
/* No box round him when he is clicked. */
.avatar__stage:focus, .avatar__stage:focus-visible { outline: none; }
/* The phone has no fixed middle to fill: the character sits inside an open
   drop-down, so give it a share of the screen instead. */
@media (max-width: 759px), (max-width: 1024px) and (orientation: portrait) {
  .avatar { flex: 0 0 auto; margin-bottom: 14px; }
  /* The frame takes no touches on the phone. Its canvas asks for every
     touch to orbit him, so a swipe that started on the character did not
     scroll the page, and with a frame this tall the links under it were
     out of reach. The camera drifts on its own; it needs no finger. */
  .avatar__stage { height: 42vh; min-height: 260px; pointer-events: none; }
}
