/* dsgn:ng — portfolio of brian ng
   Style guide: bg #1e1e1e · Montserrat · 16:9 imagery · 5px corner radius */

:root {
  --bg: #1e1e1e;
  --ink: #f4f4f4;
  --muted: #c9c9c9;
  --frame: #101010;
  --tile: #0c0c0c;
  --chip: #e6e7e8;
  --radius: 5px;
  --content: min(76vw, 1240px);
  --gap-section: clamp(3.5rem, 7vw, 6.5rem);
}

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

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: "Montserrat", "Avenir Next", "Helvetica Neue", Arial, sans-serif;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  overflow-x: hidden;
}

img, video { display: block; }

a { color: inherit; text-decoration: none; }

::selection { background: #e6e7e8; color: #1e1e1e; }

:focus-visible {
  outline: 2px solid #e6e7e8;
  outline-offset: 4px;
  border-radius: 4px;
}

.container {
  width: var(--content);
  margin-inline: auto;
}

@media (max-width: 720px) {
  :root { --content: min(88vw, 1240px); }
}

/* ---------- header ---------- */

.site-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding-top: clamp(1.75rem, 4vw, 3.5rem);
}

.brand img {
  width: clamp(138px, 15.525vw, 225.4px);
  height: auto;
}

.menu-btn {
  position: relative;
  border: 0;
  background: transparent;
  cursor: pointer;
  width: clamp(70px, 6vw, 88px);
  aspect-ratio: 1157.44 / 474.08;
  display: grid;
  place-items: center;
  padding: 0;
  font: inherit;
  z-index: 60;
}

.menu-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../assets/menu_button.svg") no-repeat center / contain;
  transition: transform 0.25s ease;
}

.menu-btn:hover::before { transform: scale(1.06); }

.menu-btn span {
  /* Heading 4: Montserrat Bold */
  position: relative;
  color: #1e1e1e;
  font-weight: 700;
  font-size: clamp(0.72rem, 0.9vw, 0.9rem);
  letter-spacing: 0.01em;
}

/* ---------- menu overlay ---------- */

.menu-overlay {
  position: fixed;
  inset: 0;
  background: rgba(18, 18, 18, 0.97);
  display: grid;
  place-items: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease, visibility 0.35s ease;
  z-index: 50;
}

.menu-overlay.is-open { opacity: 1; visibility: visible; }

.menu-overlay nav ul {
  list-style: none;
  display: grid;
  gap: clamp(0.9rem, 2.5vh, 1.6rem);
  text-align: center;
}

.menu-overlay nav a {
  font-weight: 700;
  font-size: clamp(1.5rem, 4vw, 2.5rem);
  line-height: 1.1;
  color: var(--ink);
  transition: color 0.2s ease, opacity 0.2s ease;
  opacity: 0;
  transform: translateY(14px);
}

.menu-overlay.is-open nav a {
  opacity: 1;
  transform: none;
  transition: color 0.2s ease, opacity 0.45s ease, transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1);
  transition-delay: calc(var(--i) * 55ms);
}

.menu-overlay nav a:hover { color: #8d8d8d; }

.menu-overlay nav a[aria-current="page"] { color: #8d8d8d; }

/* ---------- page heading (Heading 2) ---------- */

.tagline {
  text-align: center;
  font-weight: 600;
  font-size: clamp(1.1rem, 0.95rem + 0.95vw, 1.65rem);
  line-height: 1.45;
  margin: clamp(2.5rem, 6vw, 4.5rem) auto clamp(2rem, 4.5vw, 3rem);
}

/* one size smaller than the default Heading 2 tagline (page-specific) */
.tagline-sm {
  font-size: clamp(0.95rem, 0.8rem + 0.8vw, 1.4rem);
}

/* ---------- home: intro bio (Heading 2, multi-line, left-justified) ---------- */

.home-intro {
  text-align: center;
  font-weight: 600;
  font-size: clamp(1.1rem, 0.95rem + 0.95vw, 1.65rem);
  line-height: 1.45;
  max-width: 52ch;
  margin: clamp(2.5rem, 6vw, 4.5rem) auto clamp(2rem, 4.5vw, 3rem) auto;
}

@media (max-width: 720px) {
  .home-intro { max-width: 100%; }
}

/* smaller caption variant */
.caption-sm {
  font-size: clamp(0.7rem, 0.64rem + 0.28vw, 0.82rem);
}

/* ---------- media frames ---------- */

.frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--frame);
}

.frame img,
.frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.fit-contain img,
.frame video {
  object-fit: contain;
}

.caption {
  text-align: center;
  color: var(--muted);
  font-size: clamp(0.85rem, 0.78rem + 0.35vw, 1rem);
  line-height: 1.5;
  margin-top: clamp(0.9rem, 1.8vw, 1.4rem);
}

/* ---------- licensee logo marquee ---------- */

.licensee-marquee {
  --licensee-space: clamp(1rem, 3vw, 2rem);
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: transparent;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--licensee-space);
  padding: var(--licensee-space) 0;
}

.marquee-row { overflow: hidden; }

.marquee-track {
  --marquee-gap: clamp(1.25rem, 3vw, 2.25rem);
  display: flex;
  align-items: center;
  width: max-content;
  gap: var(--marquee-gap);
  will-change: transform;
}

.marquee-row--rtl .marquee-track { animation: marquee-rtl 34s linear infinite; }
.marquee-row--ltr .marquee-track { animation: marquee-ltr 34s linear infinite; }

.marquee-track img {
  height: clamp(45px, 6.75vw, 72px);
  width: auto;
  flex: 0 0 auto;
  opacity: 0.85;
}

/* extra breathing room after specific logos in the top row */
.marquee-track img.gap-boost { margin-right: calc(var(--marquee-gap) * 0.5); }

@keyframes marquee-rtl {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@keyframes marquee-ltr {
  from { transform: translateX(-50%); }
  to { transform: translateX(0); }
}

.media-block { margin-bottom: var(--gap-section); }

/* Invincible VS, Consumer Products, Product Concepts, Pride: half gap between cards */
.tight-gaps .media-block { margin-bottom: calc(var(--gap-section) * 0.5); }

main > .media-block:first-child { margin-top: clamp(2.5rem, 6vw, 4.5rem); }

main { padding-bottom: clamp(4rem, 9vw, 8rem); }

/* ---------- home: hero ---------- */

.hero-link { display: block; }

/* :not(.hero-dynamic) — this hover-zoom is for a single static hero
   image. The dynamic composite below replaces that markup with 9
   stacked .dbg-layer images, which are still `.frame img` and would
   otherwise match here too: same class count as `.dbg-layer`'s own
   rule, and an extra type selector (img) tips the specificity tie in
   THIS rule's favor, silently overriding the composite's own
   transform-origin/transition. Scoping it out avoids that collision
   instead of relying on out-specificity-ing it. */
.hero-link:not(.hero-dynamic) .frame img {
  transform-origin: 50% 0;
  transition: transform 1.1s cubic-bezier(0.3, 0.6, 0.3, 1);
}

.hero-link:not(.hero-dynamic):hover .frame img,
.hero-link:not(.hero-dynamic):focus-visible .frame img {
  transform: scale(1.2);
  transition: transform 6s cubic-bezier(0.16, 0.6, 0.3, 1);
}

/* ---------- home: hero (dynamic background, 9-layer parallax composite) ---------- */
/* Reconstructed from the Xbox Dynamic Background source
   (InvincibleVs_DynamicBG_V4): a base plate, five debris/atmosphere
   layers, an isolated cape, the hero art, and the title lockup —
   each on its own depth so cursor movement reads as parallax. */

.hero-dynamic .frame { perspective: 1400px; }

.hero-dynamic .dbg {
  position: absolute;
  inset: 0;
}

.hero-dynamic .dbg-layer {
  position: absolute;
  object-fit: contain;
  object-position: left top;
  transform-origin: 50% 50%;
  transition: translate 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
  will-change: transform, translate;
}

/* Every box carries an explicit height, not just width — letterboxed by
   object-fit: contain like the rest. That matters for the animation
   below: with a real height, the box's own center (the scale transform's
   origin) sits further from a layer's near-the-top content than an
   auto-height box would, so scaling from center pushes the box's edges
   safely past the frame before it pushes the art itself out of frame. */
#dbg-bg   { inset: 0; width: 100%; height: 100%; object-fit: cover; }
#dbg-img2 { left: 2%;  top: 1%;  width: 74%; height: 26%; }
#dbg-img3 { left: 66%; top: 3%;  width: 18%; height: 16%; }
#dbg-img7 { left: 56%; top: 22%; width: 6%;  height: 11%; }
#dbg-img4 { left: 20%; top: 33%; width: 46%; height: 13%; opacity: 0.85; }
#dbg-img6 { left: 38%; top: 20%; width: 26%; height: 22%; }
#dbg-cape { left: 2%;  top: 1%;  width: 34%; height: 26%; }
#dbg-hero { left: 9%;  top: 1.5%; width: 82%; height: 84%; }
#dbg-logo { left: 9%;  top: 1.5%; width: 82%; height: 84%; }

@media (prefers-reduced-motion: no-preference) {
  /* hero art and the title lockup share one animation — the logo is
     drawn on the exact same canvas as the hero art (same box, same
     content registration), so giving it its own independent scale
     curve makes it visibly drift off the character art it's supposed
     to sit on. Keeping it a separate <img>/layer, just not a separate
     curve. */
  #dbg-hero,
  #dbg-logo { animation: dbg-breathe-hero 60s ease-in-out infinite; }
  #dbg-img4 { animation: dbg-breathe-mid 60s ease-in-out infinite; }
  #dbg-img6 { animation: dbg-breathe-soft 60s ease-in-out infinite; }
  #dbg-cape { animation: dbg-sway-cape 14s ease-in-out infinite; }
  #dbg-img2 { animation: dbg-sway-wide 22s ease-in-out infinite; }
  #dbg-img3 { animation: dbg-sway-small 18s ease-in-out infinite; }
  #dbg-img7 { animation: dbg-sway-small 16s ease-in-out infinite reverse; }
  #dbg-bg   { animation: dbg-sway-bg 26s ease-in-out infinite; }
}

@keyframes dbg-breathe-hero { 0%, 100% { transform: scale(1.48); } 50% { transform: scale(1.54); } }
@keyframes dbg-breathe-mid  { 0%, 100% { transform: scale(1.2); } 50% { transform: scale(1.35); } }
@keyframes dbg-breathe-soft { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
@keyframes dbg-sway-cape { 0%, 100% { transform: rotate(-2.4deg) translateY(0); } 47% { transform: rotate(3deg) translateY(-4px); } }
@keyframes dbg-sway-wide { 0%, 100% { transform: skewX(-1.4deg); } 50% { transform: skewX(1.6deg); } }
@keyframes dbg-sway-small { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 50% { transform: translate(6px, -5px) rotate(4deg); } }
@keyframes dbg-sway-bg { 0%, 100% { transform: scale(1.015) translate(0, 0); } 50% { transform: scale(1.03) translate(-0.6%, 0.4%); } }

/* ---------- home: tiles ---------- */

.tile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: clamp(2rem, 5.5vw, 4.5rem);
  row-gap: clamp(2.5rem, 5.5vw, 4.5rem);
  margin-top: clamp(3rem, 7vw, 5.5rem);
}

.tile { display: block; }

.tile .frame img {
  opacity: 0.5;
  transition: opacity 0.45s ease;
}

/* 25% more transparent than the default tile opacity */
.tile-pride .frame img { opacity: 0.375; }

.tile:hover .frame img,
.tile:focus-visible .frame img {
  opacity: 0.75;
}

.tile-label {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  font-weight: 700;
  font-size: clamp(1.5rem, 1rem + 2.1vw, 2.9rem);
  line-height: 1.1;
  color: #fff;
  padding: 0 1rem;
  z-index: 2;
}

@media (max-width: 720px) {
  .tile-grid { grid-template-columns: 1fr; }
}

/* ---------- carousel ---------- */

.carousel {
  position: relative;
  touch-action: pan-y;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.car-track {
  display: flex;
  height: 100%;
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}

.car-slide {
  flex: 0 0 100%;
  min-width: 100%;
  height: 100%;
}

.car-slide img,
.car-slide video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.car-slide.fit-contain img { object-fit: contain; }
.car-slide video { object-fit: contain; }

.car-arrow {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: clamp(2.4rem, 3.4vw, 3rem);
  aspect-ratio: 1;
  border: 0;
  border-radius: 50%;
  background: rgba(15, 15, 15, 0.55);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  z-index: 3;
  opacity: 0.75;
  transition: opacity 0.25s ease, background 0.25s ease;
}

.car-arrow:hover { opacity: 1; background: rgba(10, 10, 10, 0.8); }

.car-arrow.prev { left: clamp(0.6rem, 1.4vw, 1.25rem); }
.car-arrow.next { right: clamp(0.6rem, 1.4vw, 1.25rem); }

.car-arrow svg { width: 42%; height: 42%; }

.car-play {
  position: absolute;
  left: clamp(0.6rem, 1.4vw, 1.25rem);
  bottom: clamp(0.6rem, 1.4vw, 1rem);
  width: clamp(2.1rem, 2.8vw, 2.6rem);
  aspect-ratio: 1;
  border: 0;
  border-radius: 50%;
  background: rgba(15, 15, 15, 0.55);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  z-index: 3;
  opacity: 0.75;
  transition: opacity 0.25s ease, background 0.25s ease;
}

.car-play:hover { opacity: 1; background: rgba(10, 10, 10, 0.8); }

.car-play svg { width: 48%; height: 48%; }

.car-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(0.6rem, 1.4vw, 1rem);
  display: flex;
  justify-content: center;
  gap: 0.45rem;
  z-index: 3;
}

.car-dots button {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: rgba(255, 255, 255, 0.35);
  cursor: pointer;
  transition: background 0.25s ease, transform 0.25s ease;
}

.car-dots button[aria-current="true"] {
  background: #fff;
  transform: scale(1.25);
}

/* ---------- logos grid ---------- */

.logo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1rem, 2vw, 1.6rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.logo-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--tile);
}

.logo-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.logo-tile .color {
  opacity: 0;
  transition: opacity 0.35s ease;
}

.logo-tile:hover .color,
.logo-tile:focus-visible .color { opacity: 1; }

@media (max-width: 720px) {
  .logo-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- more page ---------- */

.more-note {
  text-align: center;
  color: var(--muted);
  font-size: clamp(0.9rem, 0.85rem + 0.35vw, 1.05rem);
  margin-top: clamp(2rem, 4vw, 3rem);
}

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-link .frame img,
  .car-track,
  .tile .frame img,
  .logo-tile .color,
  .menu-overlay,
  .menu-overlay.is-open nav a,
  .dbg-layer {
    transition: none !important;
  }
  .marquee-track { animation: none !important; }
  .hero-link:hover .frame img,
  .hero-link:focus-visible .frame img { transform: none; }
  .dbg-layer { translate: none !important; }
}
