/* 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: #8A9099;
  --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: rgba(255, 255, 255, .12);

  /* 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: calc(var(--catf) * 1.6);      /* category card width — landscape,
                                           like the window thumbnails of a
                                           cover-switch alt-tab         */
  --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) * .36);

  --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;
  background:
    radial-gradient(80% 62% at 26% 62%, #17346f 0%, transparent 68%),
    radial-gradient(120% 80% at 50% 118%, #0f2554 0%, transparent 70%),
    #070d1d;
}

/* 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: #C9CDD4;
  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: 3px solid rgba(255, 255, 255, .14);   /* 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: 'Space Mono', monospace;
  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: rgba(255, 255, 255, .16);
  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(--yellow); }

/* 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(--yellow); }
/* ---------- 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(--yellow); }
.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 rgba(255, 255, 255, .08);
  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: 'Space Mono', monospace;
  font-size: .72rem; letter-spacing: .12em;
  color: var(--chrome);
  padding: 9px 10px; border-radius: 8px;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.themenu button:hover { background: rgba(255, 255, 255, .08); }
.themenu button[aria-current="true"] { color: var(--yellow); }

/* 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 rgba(255, 255, 255, .18);
  outline: none;
  font-family: 'Space Mono', monospace;
  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: #5A6270;
  --glass:      rgba(10, 20, 40, .05);
  --glass-line: rgba(10, 20, 40, .14);
}
/* 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%, #C8D6F2 0%, transparent 68%),
    radial-gradient(120% 80% at 50% 118%, #DEE7F7 0%, transparent 70%),
    #EDF1F8;
  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: #3A414C; }
body.light .themenu {
  background: rgba(250, 251, 253, .92);
  border-color: rgba(10, 20, 40, .1);
}
body.light .themenu button:hover { background: rgba(10, 20, 40, .07); }
body.light .keyart { color: #10131A; }

/* 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; }

/* Side cards are dimmed by BRIGHTNESS, never by opacity: a transparent card
   lets the one behind show through, which dissolves the tucked-under
   stacking into mush. Opaque + dark is how a real switcher does it. */
/* ONE dimming treatment for BOTH modes (user rule): the deep saturated
   dark-mode colours are the good ones, light mode uses them too. */
.cat:not(.is-on) .cat__icon { filter: brightness(.42) saturate(.8); }
.cat:not(.is-on):hover .cat__icon { filter: brightness(.68) saturate(.9); }
/* NEVER opacity on hover: a see-through card exposes the deck behind it */

/* Cover-switch cards (Compiz Shift "Cover" / CoverflowAltTab grammar): every
   card the SAME size. The selected one faces the camera flat and slightly
   forward; cards before it tilt toward the centre, cards after it mirror.
   Selection reads from the tilt, the ring and the glow — never from scale. */
.cat__icon {
  width: var(--catw);
  height: var(--catf);
  border-radius: 12px;
  display: grid;
  place-items: center;
  overflow: visible;  /* the reflection hangs BELOW the box */
  background: color-mix(in oklab, var(--key) 55%, var(--ink));
  transition: box-shadow .28s var(--ease), transform .38s var(--ease-back),
              filter .28s var(--ease);
  position: relative;
}

/* Every card stands on the same glossy floor. The reflection is a pseudo
   element INSIDE the card's paint layer, so it scales, tilts, dims and
   fades in the same frame as the card itself — box-reflect could not:
   Chrome repaints it only after a composited animation ends (the lag). */
/* Per-card perspective, not a shared one: a shared vanishing point sits at
   the bar's centre, so distant cards barely foreshorten — every card gets
   the identical camera, which is what keeps a strip looking uniform.

   The pivot is the edge FACING the selection: the near edge stays planted
   next to its neighbour while the far edge swings back, so the tilted cards
   stay a packed deck instead of splaying apart. */
.cat--before .cat__icon {
  transform-origin: right center;
  transform: perspective(700px) rotateY(38deg) translateZ(-14px);
}
.cat--after .cat__icon {
  transform-origin: left center;
  transform: perspective(700px) rotateY(-38deg) translateZ(-14px);
}
/* Bigger AND middle-aligned in one move: scale grows the card symmetrically
   around its centre, so its centre line stays on the deck's centre line.
   (A layout size change would shift the grid — transforms only, as ever.) */
/* Selection: full colour, forward, and a soft floor REFLECTION under the
   card (classic cover-switch cap) — no grow, no ring (user rules). */
.cat.is-on .cat__icon {
  /* clearly bigger than the side cards; the reflection rides along in the
     same frame because it is part of the card's own layer */
  transform: perspective(700px) translateZ(14px) scale(1.16);
}
.cat__icon img { width: 66%; height: 66%; object-fit: contain; }
.cat__mark {
  font-family: 'Anton', sans-serif;
  font-size: calc(var(--catf) * .5);
  line-height: 1;
  color: rgba(255, 255, 255, .93);
}

/* 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: color-mix(in oklab, var(--key) 55%, var(--ink));
  transition: filter .28s var(--ease);
}
.rcard:not(.is-on) .rcard__icon::after { filter: brightness(.42) saturate(.8); }

.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-dim);
  opacity: .5;
  transition: opacity .24s var(--ease), color .24s var(--ease), transform .24s var(--ease);
}
.item:hover { opacity: .85; }
/* 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;
  overflow: hidden;
  background: color-mix(in oklab, var(--key) 62%, var(--ink));
  font-family: 'Anton', sans-serif;
  font-size: calc(var(--imark) * .36);
  color: #fff;
}
.item__mark img { width: 72%; height: 72%; object-fit: contain; }
/* the selected row's icon grows, centre-symmetric, no ring (user rule) */
.item.is-on .item__mark { transform: scale(1.12); }

/* 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;
}
/* EVERY box in the vertical menu casts the same soft shadow (letter
   tiles included, not only photos); the selected one casts deeper.
   The shadow lives on the BOX, so images and slabs are identical. */
.item .item__mark {
  box-shadow: 0 8px 20px -10px rgba(0, 0, 0, .45);
}
.item.is-on .item__mark {
  box-shadow: 0 14px 34px -12px rgba(0, 0, 0, .6);
}

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


/* ---------- 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. */
.detail {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(12px, 2.4vh, 22px);
  padding: 6px 0 4px;
  margin-right: calc(clamp(300px, 23vw, 620px) + 3.5vw);
}

.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(--yellow);
  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: 'Space Mono', monospace; 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 { color: var(--yellow); }
.hero > a { align-self: flex-start; }   /* a linked logo stays left */
.hero__body {
  font-size: .88rem; line-height: 1.55; color: #C9CDD4;
  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: #C9CDD4;
}
.hero__bullets li { padding-left: 1.1em; position: relative; }
.hero__bullets li::before {
  content: '▸';
  position: absolute;
  left: 0;
  color: var(--yellow);
}

.hero__stack {
  font-family: 'Space Mono', monospace;
  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 {
  /* as big as the middle third allows */
  /* fill the middle third: whichever of width or height runs out first */
  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); background: var(--yellow); }


/* ---------- 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 { background: rgba(255, 255, 255, .11); 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 { color: var(--yellow); }
.plink__host {
  font-family: 'Space Mono', monospace;
  font-size: .68rem;
  font-weight: 400;
  color: var(--chrome-dim);
}

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

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

/* right rail: links on top, description or bullets below */
.keyart-rail {
  position: fixed;
  z-index: 1;
  right: 3vw;
  top: clamp(210px, 24vh, 330px);    /* the player owns the deck level above */
  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;
  scrollbar-width: none;
  pointer-events: auto;
  font-family: 'Archivo', system-ui, sans-serif;
}
/* the right third is centre aligned; the links and the words cluster
   together in its vertical MIDDLE — links right aligned above the text */
.keyart-rail { text-align: center; align-items: center; }
.keyart-rail > .rail-links {
  margin-top: auto;
  align-self: flex-end;
  text-align: right;
  justify-content: flex-end;
}
.keyart-rail > .side__desc,
.keyart-rail > .hero__bullets { margin-bottom: auto; }
.keyart-rail > .side__desc:first-child,
.keyart-rail > .hero__bullets:first-child { margin-top: 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: rgba(255, 255, 255, .28) 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: rgba(255, 255, 255, .24);
  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. */
.hero__logo {
  height: min(clamp(240px, 56vh, 780px), calc(100vh - 470px));
  width: auto;
  max-width: 100%;
  object-fit: contain;
  border-radius: 12px;
  margin-top: 10px;
}
/* PEOPLE photos: one identical 4:5 portrait card for every person,
   cover-cropped, anchored to the face. Uniform by definition. */
.hero__logo--person {
  height: auto;
  width: min(100%, clamp(320px, 26vw, 540px));
  aspect-ratio: 1 / 1;
  max-height: calc(100vh - 470px);
  object-fit: cover;
  object-position: top;
}

/* the middle photo board: ordered masonry, hover air, click = lightbox */
.mboard {
  width: 100%;   /* the middle third, never past it */
  columns: 3 260px;
  column-gap: 16px;
  margin-top: 10px;
}
.mboard img {
  width: 100%;
  height: auto;
  display: block;
  margin-bottom: 16px;
  break-inside: avoid;
  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);
}

/* a single photo board (PEOPLE) is uniform: same height for every
   person, never touching the bottom of the screen */
.mboard--one { column-count: 1; }
.mboard--one img {
  width: auto;
  max-width: 100%;
  height: clamp(280px, 52vh, 620px);
  display: block;
  margin: 0;
}

/* ---------- 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(--yellow); }
.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-color: var(--yellow); color: var(--chrome); }
.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 { background: var(--yellow); transform: translateY(-2px); }
.recform__note { grid-column: 1 / -1; font-size: .88rem; color: var(--yellow); }

/* ---------- 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;
  transition: opacity .5s var(--ease), visibility .5s;
}
body:not(.booting) .boot { opacity: 0; visibility: hidden; }

.boot__mark { width: clamp(76px, 16vw, 120px); animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 50% { transform: scale(1.05); } }

.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: var(--glass-line); overflow: hidden; }
.loadbar i { display: block; height: 100%; width: 0; background: var(--yellow); 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);
}
.boot__start.ready { opacity: 1; animation: blink 1.5s steps(2, start) infinite; }
@keyframes blink { 50% { opacity: .25; } }

.boot__hint { font-family: 'Space Mono', monospace; 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) { visibility: hidden; }
.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) {
  .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;
  }
}
