@import url('./tokens.css');

@font-face {
  font-family: 'Nuances Affection';
  src: url('../assets/fonts/NuancesAffection.woff2') format('woff2');
  font-weight: 100 900; /* one weight, declared as a range so browsers never fake-bold it at your 600/800 heading weights */
  font-style: normal;
  font-display: swap;
  size-adjust: 120%; /* the script runs small; this evens it out with Montserrat so heading sizes still feel right */
}

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

html.lenis,
html.lenis body {
  height: auto;
}

.lenis.lenis-smooth {
  scroll-behavior: auto !important;
}

.lenis.lenis-stopped {
  overflow: hidden;
}

body {
  font-family: var(--font-body);
  color: var(--color-primary-light);
  cursor: none;
}

/* ---- Site Header (reusable across pages) ---- */

.site-header {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: var(--space-6) var(--space-8);
  cursor: auto;
}

.site-header--transparent {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: transparent;
}

.site-header--solid {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--site-header-height);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  background-image: url('../assets/images/header-bg.png');
  background-size: cover;
  background-position: center;
}

/* About page: the header and footer use the same pinned oil pastel image as
   the left column, so the texture flows straight through all three. */
.page-about .site-header--solid,
.page-about .site-footer {
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-attachment: fixed;
}

.site-header__nav {
  list-style: none;
  display: flex;
  gap: var(--space-6);
}

.site-header__nav a {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.2s ease;
}

.site-header__nav a:hover {
  color: var(--color-accent-yellow);
}

.site-header__nav a[aria-current],
.site-footer__links a[aria-current] {
  color: var(--color-accent-yellow);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* ---- Site logo (top-left of the header, swaps to the starry version on hover) ---- */

.site-header__logo {
  position: relative;
  display: block;
  margin-right: auto;
  height: 56px;
  cursor: pointer;
}

/* Home page only: the transparent header gets the larger logo. */
.site-header--transparent .site-header__logo {
  height: 72px;
}

.site-header__logo-img {
  display: block;
  height: 100%;
  width: auto;
}

.site-header__logo-img--hover {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.site-header__logo:hover .site-header__logo-img--hover {
  opacity: 1;
}
/* ---- Site Footer (reusable across pages) ---- */

.site-footer {
  background-image: url('../assets/images/footer-bg.png');
  background-size: cover;
  background-position: center;
  padding: var(--space-8) var(--space-8);
}

.site-footer__cols {
  display: flex;
  justify-content: flex-start;
  gap: var(--space-10);
}

.site-footer__brand {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.site-footer__mascot {
  display: block;
  height: 160px;
  width: auto;
}

.site-footer__credit {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  margin-top: var(--space-1);
}

.site-footer__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
}

.site-footer__links {
  list-style: none;
  margin-top: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.site-footer__links a {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  text-decoration: none;
  transition: color 0.2s ease;
}

.site-footer__links a:hover {
  color: var(--color-accent-yellow);
}

/* Current page in the header nav and footer links: yellow + underlined.
   main.js sets aria-current="page" once the partials have loaded. */
.site-header__nav a[aria-current="page"],
.site-footer__links a[aria-current="page"] {
  color: var(--color-accent-yellow);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* ==========================================================================
   ABOUT PAGE
   ========================================================================== */

.about-layout {
  display: flex;
  align-items: stretch;
}

.about-left {
  width: 70%;
  background-color: var(--color-primary-dark);
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  color: var(--color-primary-light);
  padding: var(--space-6) var(--space-10) var(--space-10);
}

.about-left__heading {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-accent-yellow);
  margin-top: var(--space-6);
}

.about-left__heading:first-child {
  margin-top: 0;
}

.about-left .about-left__closing {
  font-style: italic;
  color: var(--color-accent-gold);
}

.about-left p {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
  line-height: 1.6;
  margin-top: var(--space-3);
}

.about-left__closing a,
.about-left__closing a:visited,
.about-left__closing a:hover,
.about-left__closing a:active {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
}

/* ---- Story/Experience Tabs (about page left column) ---- */

.story-tabs {
  display: inline-flex;
  border: 1px solid rgba(250, 249, 246, 0.25);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
}

.story-tabs__btn {
  min-width: 96px;
  text-align: center;
  border: none;
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-primary-light);
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  padding: var(--space-1) var(--space-3);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.story-tabs__btn.is-active {
  background-color: var(--color-accent-yellow);
  color: var(--color-primary-dark);
}

/* Sliding highlight: main.js adds .story-tabs--slide and a .story-tabs__indicator
   behind the buttons, and the active button's own background hands off to it. */
.story-tabs--slide {
  position: relative;
}

.story-tabs--slide .story-tabs__btn {
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.story-tabs--slide .story-tabs__btn.is-active {
  background-color: transparent;
}

.story-tabs__indicator {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  border-radius: 999px;
  background-color: var(--color-accent-yellow);
  pointer-events: none;
  transition: transform 0.3s ease, width 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
  .story-tabs__indicator {
    transition: none;
  }
}

.story-panel {
  margin-top: var(--space-4);
  transition: opacity 0.2s ease;
}

.story-panel[hidden] {
  display: none;
}

/* ---- Experience List (about page, "experience" tab) — open/editorial
   list, no cards, matching the divider rhythm of the about-right "also"
   section. That section's divider color (secondary-blue) is too close
   to this column's navy background to read here, so it's swapped for
   the same soft light divider already used for dark-background lists
   elsewhere (see .comp-block__list). ---- */

.experience-item {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid rgba(250, 249, 246, 0.2);
}

.experience-item:first-child {
  padding-top: 0;
}

.experience-item:last-child {
  border-bottom: none;
}

.experience-item__company {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
}

.experience-item__title {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  margin-top: var(--space-1);
}

.experience-item__dates {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  white-space: nowrap;
  text-align: right;
  flex-shrink: 0;
}

.experience-item__date-start {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-accent-yellow);
}

.experience-item__date-end {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-accent-yellow);
  margin-top: var(--space-1);
}

.about-right {
  width: 30%;
  position: sticky;
  top: var(--site-header-height);
  height: calc(100vh - var(--site-header-height));
  overflow-y: auto;
  overflow-x: hidden;
  background-color: var(--color-primary-light);
  padding: var(--space-6) var(--space-6) var(--space-10);
}

/* The scrollable content lives in this inner wrapper (not .about-right
   directly), so the decorative stars' percentage-based positions resolve
   against the full, natural content height rather than the fixed
   viewport-height of the sticky/scrolling outer panel — otherwise they'd
   only ever appear within the first "screen" of content. */
.about-right__inner {
  position: relative;
}

.about-right__hint {
  position: relative;
  z-index: 1;
  font-family: var(--font-body);
  font-style: italic;
  font-size: 10px;
  color: var(--color-primary-dark);
  margin-bottom: var(--space-1);
}

.about-right__label {
  position: relative;
  z-index: 1;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
}

.about-photo-grid {
  position: relative;
  z-index: 1;
  max-width: 88%;
  margin: var(--space-4) auto 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}

.about-photo {
  position: relative;
  z-index: 1;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  cursor: none;
}

.about-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Modifier: crops from the bottom of the source image instead of the
   center (default object-position). */
.about-photo--bottom img {
  object-position: bottom;
}

.about-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--color-primary-dark);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}

.about-photo:hover::after {
  opacity: 0.25;
}

/* ---- Background decorations (about page right column). Sit outside the text
   and photos, allowed to bleed past the sidebar's own edge and get softly
   clipped there, the same way the old stars did. Full opacity — not the
   faded .bg-star treatment. ---- */

.about-decor {
  position: absolute;
  z-index: 0;
  pointer-events: none;
}

.about-decor--constellation {
  top: 20%;
  right: 100%;
  transform: translateY(-50%);
  margin-right: -20px;
  width: 120px;
}

.about-decor--squiggles {
  top: auto;
  bottom: -120px;
  right: -80px;
  left: auto;
  margin: 0;
  width: 120px;
  z-index: 0;
}

.about-decor--unique {
  top: -80px;
  right: -50px;
  left: auto;
  margin: 0;
  width: 75px;
  transform: rotate(45deg);
  transform-origin: center;
  z-index: 0;
}

/* level with the list underneath the heading, not the heading itself */
.about-decor--squiggle {
  top: 50%;
  right: 100%;
  transform: translateY(-50%);
  margin-right: 6px;
  width: 95px;
}

.about-decor--squiggle-small {
  top: -50px;
  right: -50px;
  left: auto;
  width: 55px;
  margin: 0;
  transform: rotate(18deg);
  z-index: 0;
}

/* Anchored to the LIST's own bottom edge (not the section around it), so it
   sits a fixed, reliable gap below the last line no matter what else is going
   on in the section. Small, so it stays clear of the text above it. Pre-
   rotated as its own file (not a CSS transform), so its box is exactly this
   size and sizing is predictable. */
.about-decor--dots {
  top: 100%;
  margin-top: var(--space-2);
  right: -10px;
  width: 150px;
  transform: rotate(-180deg);
}

.about-decor--dots-orbit {
  top: 150px;
  left: -90px;
  width: 100px;
  transform: rotate(120deg);
  transform-origin: center;
  margin: 0;
  z-index: 0;
}

.about-media > *:not(.about-decor) {
  position: relative;
  z-index: 1;
}

/* ---- Currently Reading / Recently Watched (about page, sits directly
   below the photo preview grid) ---- */

.about-media {
  position: relative;
  z-index: 2;
  max-width: 88%;
  margin: var(--space-6) auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.about-media__subtitle {
  font-family: var(--font-body);
  font-size: 10px;
  color: var(--color-secondary-blue);
}

.media-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  text-decoration: none;
  color: inherit;
  cursor: none;
}

.media-item__cover-wrap {
  width: 64px;
  height: 96px;
  flex-shrink: 0;
  background-color: var(--color-secondary-blue);
  overflow: hidden;
}

.media-item__cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.media-item__label {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-secondary-blue);
}

.media-item__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
  transition: color 0.2s ease;
  max-width: 100px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.media-item:hover .media-item__title {
  color: var(--color-accent-gold);
}

.media-item__meta {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--small-size);
  color: var(--color-secondary-blue);
}

/* ---- "also..." section (about page) — a loose, editorial-scrapbook
   style personality collage. Deliberately NOT cards: no borders, no
   background fills, no shadows. Just type, offset slightly per block so
   it doesn't read as a rigid grid. ---- */

.also-section {
  position: relative;
  z-index: 1;
  max-width: 88%;
  margin: var(--space-6) auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.also-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.also-list__item {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-secondary-blue);
}

.also-list__item:first-child {
  padding-top: 0;
}

.also-list__item:last-child {
  border-bottom: none;
}

.also-list__item a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.2s ease;
}

.also-list__item a:hover {
  color: var(--color-accent-gold);
}

.also-list__secondary {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-style: italic;
  font-size: var(--small-size);
  color: var(--color-secondary-blue);
  margin-top: 2px;
}

/* ---- Hero ---- */

/* ---- Loading Screen (home page only, plays once before the site reveals) ---- */

.loading-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: default;
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-size: cover;
  background-position: center;
  transition: opacity 0.6s ease;
}

.loading-screen.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.loading-screen__text {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
  width: 100%;
  text-align: center;
  padding: 0 var(--space-6);
  transition: opacity 0.4s ease;
}

.loading-screen__text.is-hidden {
  opacity: 0;
}

.loading-screen__stars {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(140px, 16vw, 190px);
  height: auto;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.loading-screen__stars.is-visible {
  opacity: 1;
}

body.is-loading {
  overflow: hidden;
}

.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-size: cover;
  background-position: center;
  overflow: hidden;
  cursor: none;
}

.hero-cursor {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  background-color: var(--color-primary-dark);
  color: var(--color-primary-light);
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  z-index: 999;
  transition: opacity 0.2s ease;
}

.hero-cursor.is-visible {
  opacity: 1;
}

/* ---- Custom circle cursor: a real circle drawn in CSS (crisp at any
   screen resolution) that follows the mouse. Injected and positioned
   by main.js. Hidden wherever the site already uses a different cursor
   (hover pills, links, the header). ---- */

.cursor-dot {
  position: fixed;
  top: 0;
  left: 0;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background-color: var(--color-accent-yellow);
  pointer-events: none;
  z-index: 10001;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.cursor-dot.is-visible {
  opacity: 1;
}

/* ---- Star cursor: shown instead of the circle over links and buttons
   that don't have a hover pill. A small static cluster of CSS stars
   (clip-path, so they stay smooth at any size). Positioned by main.js. ---- */

.cursor-stars {
  position: fixed;
  top: 0;
  left: 0;
  width: 26px;
  height: 26px;
  pointer-events: none;
  z-index: 10001;
  display: none;
}

.cursor-stars.is-visible {
  display: block;
}

.cursor-stars__star {
  position: absolute;
  background-color: var(--color-accent-yellow);
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

.cursor-stars__star:nth-child(1) {
  top: 8px;
  left: 8px;
  width: 10px;
  height: 10px;
  background-color: var(--color-accent-gold);
}

.cursor-stars__star:nth-child(2) {
  top: 1px;
  left: 0;
  width: 6px;
  height: 6px;
}

.cursor-stars__star:nth-child(3) {
  top: 0;
  left: 20px;
  width: 5px;
  height: 5px;
  background-color: var(--color-accent-gold);
}

.cursor-stars__star:nth-child(4) {
  top: 19px;
  left: 20px;
  width: 6px;
  height: 6px;
}

.cursor-stars__star:nth-child(5) {
  top: 21px;
  left: 1px;
  width: 4px;
  height: 4px;
  background-color: var(--color-accent-gold);
}

/* The native hand pointer is hidden on links and buttons, since the star
   cursor replaces it. !important so it beats the individual
   "cursor: pointer" rules scattered through this file. */
a,
button,
summary,
[role="button"] {
  cursor: none !important;
}

/* ---- Hero layout: title top-left, framed dog stage centered, paragraph
   bottom-right. The header is absolutely positioned, so the top padding
   clears it (the logo's bottom edge sits ~120px down). ---- */

.hero__layout {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: calc(var(--space-16) + var(--space-3)) var(--space-8) var(--space-8);
}

/* ---- Hero stars: small decorations scattered behind the hero content.
   Positions are % of the hero so they spread across the whole section and
   stay clear of the title, paragraph, nav, and the dog square. ---- */

.hero-star--1  { top: 21%; left: 47%; width: 44px; transform: rotate(-15deg); }
.hero-star--2  { top: 15%; left: 70%; width: 36px; transform: rotate(22deg); }
.hero-star--3  { top: 27%; left: 91%; width: 52px; transform: rotate(-20deg); }
.hero-star--4  { top: 38%; left: 60%; width: 40px; transform: rotate(12deg); }
.hero-star--5  { top: 64%; left: 7%;  width: 48px; transform: rotate(18deg); }
.hero-star--6  { top: 72%; left: 30%; width: 38px; transform: rotate(-10deg); }
.hero-star--7  { top: 62%; left: 50%; width: 56px; transform: rotate(25deg); }
.hero-star--8  { top: 88%; left: 15%; width: 40px; transform: rotate(-25deg); }
.hero-star--9  { top: 84%; left: 62%; width: 36px; transform: rotate(8deg); }
.hero-star--10 { top: 47%; left: 84%; width: 34px; transform: rotate(-12deg); }

/* Full opacity in the hero (the shared .bg-star base is 0.7). Two classes so it
   wins no matter where .bg-star sits in the file, and it leaves the about
   page's stars untouched. */
.hero .bg-star {
  opacity: 1;
}

.hero__stage-wrap {
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding-top: var(--space-4);
}

/* Off-white square. Sized from the viewport height (width follows from
   the aspect ratio) so the whole hero fits one screen. */
.hero__stage {
  height: clamp(240px, 32vh, 360px);
  aspect-ratio: 1 / 1;
  max-width: 100%;
  background-color: var(--color-primary-light);
  padding: var(--space-4);
}

.hero__stage-inner {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-primary-dark);
}

.hero__mascot {
  display: block;
  height: 80%;
  width: auto;
  max-width: 90%;
  object-fit: contain;
}

.hero__title {
  font-family: var(--font-heading);
  font-weight: var(--h1-weight);
  font-size: var(--h1-size);
  color: var(--color-primary-light);
  line-height: 1.1;
  white-space: nowrap;
}

.hero__subtitle {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-accent-gold);
  line-height: 1.1;
  margin-top: var(--space-1);
  white-space: nowrap;
}

.hero__body {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  line-height: 1.6;
  margin-top: var(--space-3);
  max-width: 520px;
}

.hero__body a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.2s ease;
}

.hero__body a:hover {
  color: var(--color-accent-yellow);
}

/* ---- Explore My Different Worlds (project grid) ---- */

.projects {
  position: relative;
  background-color: var(--color-primary-light);
  color: var(--color-primary-dark);
  padding: var(--space-6) var(--space-8);
  overflow: hidden;
}

.bg-star {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  opacity: 0.7;
}

.bg-star--1 {
  top: -2%;
  left: -8%;
  width: 480px;
  transform: rotate(-18deg);
}

.bg-star--2 {
  top: 18%;
  right: -6%;
  width: 400px;
  transform: rotate(24deg);
}

.bg-star--3 {
  top: 36%;
  left: -10%;
  width: 440px;
  transform: rotate(12deg);
}

.bg-star--4 {
  top: 56%;
  right: -9%;
  width: 540px;
  transform: rotate(-10deg);
}

.bg-star--5 {
  top: 74%;
  left: -4%;
  width: 420px;
  transform: rotate(20deg);
}

.bg-star--6 {
  top: 92%;
  right: -2%;
  width: 470px;
  transform: rotate(-25deg);
}

.projects > *:not(.bg-star) {
  position: relative;
  z-index: 1;
}

.projects__intro {
  margin-bottom: 0;
}

.projects__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-dark);
  line-height: 1.1;
}

.projects__subtitle {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
  line-height: 1.1;
}

.projects__filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
  margin-bottom: var(--space-6);
}

.filter-btn {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-dark);
  background-color: transparent;
  border: 1px solid var(--color-primary-dark);
  padding: var(--space-1) var(--space-2);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.filter-btn:hover,
.filter-btn.is-active {
  background-color: var(--color-primary-dark);
  color: var(--color-primary-light);
}

.projects__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}

.project-card {
  display: block;
  text-decoration: none;
  color: inherit;
}

.project-card[hidden] {
  display: none;
}

.project-card__image {
  width: 100%;
  aspect-ratio: 1.878 / 1;
  display: block;
}

.project-card__image--placeholder {
  background-color: var(--color-secondary-blue);
}

/* ---- Inset card variants: dark navy frame peeking through on some
   edges of the cover media, with a hover overlay. Reusable for any
   project card with a real image or video cover. ---- */

.project-card__frame {
  position: relative;
  aspect-ratio: 1.878 / 1;
  background-color: var(--color-primary-dark);
  overflow: hidden;
  cursor: none;
}

/* Per-card background overrides (frame modifiers, since --inset is
   shared by multiple cards and can't be targeted alone). */
.project-card__frame.project-card__frame--gold {
  background-color: var(--color-accent-gold);
}

.project-card__frame.project-card__frame--blue {
  background-color: var(--color-secondary-blue);
}

.project-card--inset-lrb .project-card__frame {
  background-color: var(--color-accent-blue);
}

.project-card__frame img,
.project-card__frame video {
  display: block;
  position: absolute;
}

/* top + left visible (e.g. substack) — image crops to fill its inset area */
.project-card--inset .project-card__frame img,
.project-card--inset .project-card__frame video {
  top: var(--space-4);
  left: var(--space-4);
  width: calc(100% - var(--space-4));
  height: calc(100% - var(--space-4));
  object-fit: cover;
  object-position: top left;
}

/* Modifier: full-bleed, centered media instead of the top-left inset
   offset above — keeps the --inset card's hover/cursor-pill behavior
   (that's scoped to the parent .project-card--inset, not this frame
   modifier) while overriding just the media's own positioning. */
.project-card--inset .project-card__frame--centered img,
.project-card--inset .project-card__frame--centered video {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* left + right + bottom visible, top flush (e.g. medium) — image crops
   to fill its inset area, top edge flush with the frame */
.project-card--inset-lrb .project-card__frame img,
.project-card--inset-lrb .project-card__frame video {
  top: 0;
  left: var(--space-4);
  width: calc(100% - (var(--space-4) * 2));
  height: calc(100% - var(--space-4));
  object-fit: cover;
  object-position: center 55%;
}

.project-card__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--color-primary-light);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}

.project-card--inset:hover .project-card__frame::after,
.project-card--inset-lrb:hover .project-card__frame::after {
  opacity: 0.35;
}

.project-card__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-2);
}

.project-card__desc {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
}

.project-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-2);
}

.tag {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-secondary-blue);
  border: 1px solid var(--color-secondary-blue);
  padding: 0 var(--space-1);
}

/* ---- Section Divider (site-wide — used between major sections on the
   home page and between case study sections alike) ---- */

.section-divider {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
}

.section-divider img {
  width: 140px;
  height: auto;
  display: block;
}

/* ==========================================================================
   CASE STUDY SYSTEM (reusable across every case study — see
   DESIGN_SYSTEM.md section 11). Do not add case-study-specific styling
   inside individual project pages; extend these shared classes instead.
   ========================================================================== */

.case-study {
  background-color: var(--color-primary-light);
  color: var(--color-primary-dark);
}

.case-container {
  max-width: 900px;
  margin-left: max(240px, calc((100% - 900px) / 2));
  margin-right: auto;
  padding: 0 var(--space-8);
}

/* ---- Case Hero ---- */

.case-hero {
  padding-top: var(--space-6);
  padding-bottom: 0;
}

.case-hero__title {
  font-family: var(--font-heading);
  font-weight: var(--h1-weight);
  font-size: var(--h1-size);
  color: var(--color-primary-dark);
  line-height: 1.1;
}

.case-hero__subtitle {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
  line-height: 1.4;
}

.case-hero__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-2);
}

.case-media {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
}

.case-media--placeholder {
  background-color: var(--color-secondary-blue);
}

.case-media--real {
  aspect-ratio: auto;
  height: auto;
  border: 1px solid var(--color-secondary-blue);
}

/* Modifier: drops the border above, for a hero video/image that
   shouldn't have the outline. */
.case-media--no-border {
  border: none;
}

/* ---- Case Figure (required format for every real photo in a case
   study's body: a full-width blue box, containing the image at a
   slightly reduced width, with an all-caps caption underneath. Use this
   for any in-body photo/screenshot — do not place a bare <img> in a
   case study's body content.) ---- */

.case-figure {
  background-color: var(--color-primary-dark);
  padding: var(--space-4);
  margin-top: var(--space-4);
}

.case-figure__image {
  display: block;
  width: 100%;
  height: auto;
  margin: 0;
}

.case-figure__caption {
  display: block;
  text-align: left;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-light);
  margin-top: var(--space-2);
}

/* Modifier: a much smaller image, for use alongside other content (e.g.
   pros/cons text) rather than as a full-width standalone figure. */
.case-figure--small {
  max-width: 260px;
}

/* Modifier: a small image (with its caption still stacked underneath it,
   same as every other case-figure) on one side, and a block of body text
   to the other — for a figure that's illustrating a paragraph rather than
   standing alone full-width. */
.case-figure--horizontal {
  display: flex;
  align-items: flex-end;
  gap: var(--space-4);
}

.case-figure--horizontal .case-figure__media {
  flex-shrink: 0;
  max-width: 220px;
}

.case-figure--horizontal .case-figure__image {
  width: 100%;
}

/* .case-section p (0,1,1) beats a single-class rule for any overlapping
   property, so this needs a 2-class selector to actually win. */
.case-figure--horizontal .case-figure__text {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  line-height: 1.6;
  margin: 0;
}

.case-figure__attribution {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--small-size);
  font-style: normal;
  color: var(--color-accent-blue);
}

/* ---- Competitor comparison block: one cohesive dark-blue container per
   competitor (image + title on one side, pros/cons stacked on the
   other) rather than separate cards for each piece. ---- */

.comp-block {
  display: grid;
  grid-template-columns: 220px 1fr;
  align-items: center;
  gap: var(--space-6);
  background-color: var(--color-primary-dark);
  padding: var(--space-6);
  margin-top: var(--space-4);
}

.comp-block__media {
  width: 220px;
}

/* Fixed-size frame both images sit within, regardless of their native
   aspect ratio — this is what keeps a wide landscape screenshot and a
   tall portrait screenshot occupying the exact same visual footprint,
   so neither card ends up taller or more dominant than the other. */
.comp-block__media-frame {
  width: 100%;
  height: 280px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.comp-block__media-frame img,
.comp-block__media-frame video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  display: block;
}

.comp-block__media .comp-block__media-title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-light);
  margin-top: var(--space-2);
}

.comp-block__content {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.comp-block__group--divided {
  border-top: 1px solid rgba(250, 249, 246, 0.2);
  padding-top: var(--space-4);
}

.comp-block__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
}

.comp-block__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-light);
  margin-top: var(--space-1);
}

.comp-block .comp-block__content p {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  line-height: 1.6;
  margin-top: var(--space-2);
}

.comp-block__list {
  list-style: disc;
  padding-left: var(--space-3);
  margin-top: var(--space-2);
}

.comp-block__list li {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
}

.comp-block__list li + li {
  margin-top: var(--space-1);
}

/* Modifier: key-feature video blocks (phone recordings). Bigger fixed
   media frame so the video fills more of the box, plus extra bottom
   padding so the carousel dots have breathing room under the content. */
.comp-block--video {
  grid-template-columns: 240px 1fr;
  padding-bottom: var(--space-10);
}

.comp-block--video .comp-block__media {
  width: 240px;
}

.comp-block--video .comp-block__media-frame {
  height: 370px;
}

.comp-block--video .comp-block__media-frame video {
  height: 100%;
  width: auto;
  max-width: 100%;
}

/* Modifier: plain bullet points instead of divider lines between items. */
.comp-block__list--bullets {
  list-style: disc;
  padding-left: var(--space-3);
}

.comp-block__list--bullets li {
  padding: 0;
  border-bottom: none;
}

.comp-block__list--bullets li + li {
  margin-top: var(--space-1);
}

/* ---- Article Box: outlined (not filled) link box — dark border,
   white fill, dark text. Used for linking out to external sources
   (e.g. secondary research citations). ---- */

.article-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.article-box {
  display: block;
  border: 1px solid var(--color-primary-dark);
  padding: var(--space-3);
  text-decoration: none;
  color: var(--color-primary-dark);
  transition: color 0.2s ease, border-color 0.2s ease;
}

.article-box:hover {
  color: var(--color-accent-gold);
  border-color: var(--color-accent-gold);
}

a.article-box p {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: inherit;
  margin-top: 0;
  line-height: 1.5;
}

.article-box__source {
  display: block;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: var(--space-1);
}

/* ---- Dual Image Box: two images side by side in one dark container
   (e.g. a screening-questions table + a recruitment email shown
   together as one artifact). Images are bottom-aligned since they're
   often different heights, and an optional caption can span below
   both. ---- */

.dual-image-box {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  gap: var(--space-3);
  background-color: var(--color-primary-dark);
  padding: var(--space-4);
  margin-top: var(--space-4);
}

.dual-image-box img {
  width: 100%;
  height: auto;
  display: block;
  align-self: end;
}

.dual-image-box__caption {
  grid-column: 1 / -1;
  display: block;
  text-align: left;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-light);
  margin-top: var(--space-2);
}

/* ---- Reveal (collapsible content): native <details>/<summary>, styled
   to match the rest of the case-study system rather than the browser
   default triangle. Used for long supporting material (e.g. a full
   interview guide) that shouldn't dominate the page by default. The
   +/- indicator sits on the right, opposite the summary text. ---- */

.reveal {
  margin-top: var(--space-4);
  border: 1px solid var(--color-secondary-blue);
}

.reveal summary {
  cursor: pointer;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.reveal summary::-webkit-details-marker {
  display: none;
}

.reveal summary::after {
  content: '+';
  color: var(--color-secondary-blue);
  flex-shrink: 0;
}

.reveal[open] summary::after {
  content: '\2212';
}

.reveal[open] summary {
  border-bottom: 1px solid var(--color-secondary-blue);
}

.reveal__content {
  padding: var(--space-3);
}

.reveal__content p {
  margin-top: var(--space-2);
}

.reveal__content p:first-child {
  margin-top: 0;
}

/* ---- Affinity Map: a compact grid of clickable research-theme tiles;
   clicking one reveals its participant quotes in a shared panel below
   the grid (not per-tile expansion), keeping the default view skimmable
   regardless of how many quotes a theme has. ---- */

.affinity-map {
  margin-top: var(--space-4);
}

.case-container .affinity-map__instruction {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--small-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-2);
}

.affinity-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

.affinity-theme {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background-color: var(--color-primary-light);
  border: 1px solid var(--color-primary-dark);
  padding: var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  color: var(--color-primary-dark);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.affinity-theme:hover {
  border-color: var(--color-accent-gold);
}

.affinity-theme.is-active {
  background-color: var(--color-primary-dark);
  color: var(--color-primary-light);
}

.affinity-quotes {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background-color: var(--color-primary-dark);
  display: none;
}

.affinity-quotes.is-visible {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}

.affinity-quotes .affinity-quote {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-style: italic;
  font-size: var(--small-size);
  line-height: 1.4;
  color: var(--color-primary-light);
  background-color: var(--color-secondary-blue);
  padding: var(--space-2);
  margin-top: 0;
  max-width: 220px;
  opacity: 0;
  transform: translateY(4px);
  animation: affinity-quote-in 0.3s ease forwards;
}

@keyframes affinity-quote-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.case-hero__media {
  margin-top: var(--space-4);
}

.case-hero__media--inset {
  position: relative;
  background-color: var(--color-primary-dark);
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

/* Modifier: swaps the peek-through background from the default navy
   to secondary/medium blue — used on substack's hero. */
.case-hero__media--blue {
  background-color: var(--color-secondary-blue);
}

.case-hero__media--inset img {
  display: block;
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  width: calc(100% - var(--space-4));
  height: calc(100% - var(--space-4));
  object-fit: cover;
  object-position: top left;
}

.case-section__media {
  margin-top: var(--space-4);
}

.case-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-2);
}

.case-links a {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-dark);
  text-decoration: none;
  border-bottom: 1px solid var(--color-primary-dark);
  padding-bottom: 2px;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.case-links a:hover {
  color: var(--color-accent-yellow);
  border-color: var(--color-accent-yellow);
}

/* ---- Metadata + Metrics (flow directly out of the hero) ---- */

.case-meta {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
  border-top: 1px solid var(--color-secondary-blue);
  border-bottom: 1px solid var(--color-secondary-blue);
}

.case-meta__label {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-secondary-blue);
}

.case-meta__value {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

/* ---- Accent Box (shared background treatment for highlighted case
   study moments — currently used by the metrics box and the callout box.
   Reuse this class alongside a component's own layout class rather than
   redefining the background per component.) ---- */

.case-accent-box {
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-size: cover;
  background-position: center;
  background-color: var(--color-primary-dark);
  color: var(--color-primary-light);
}

.case-metrics {
  margin-top: var(--space-6);
  color: var(--color-primary-light);
  padding: var(--space-4);
}

.case-metrics__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-light);
}

.case-metrics__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin-top: var(--space-2);
}

.case-metrics__number {
  font-family: var(--font-heading);
  font-weight: var(--h1-weight);
  font-size: var(--h2-size);
  color: var(--color-accent-gold);
  line-height: 1.1;
}

.case-metrics__label {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
}

.case-metrics__note {
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--small-size);
  color: var(--color-primary-light);
  margin-top: var(--space-2);
}

/* ---- Sticky Case Study Nav (vertical sidebar) ---- */

.case-nav {
  position: fixed;
  top: calc(var(--site-header-height) + var(--space-6));
  left: var(--space-6);
  z-index: 15;
}

.case-nav__home {
  display: block;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-dark);
  text-decoration: none;
  margin-bottom: var(--space-3);
  transition: color 0.2s ease;
}

.case-nav__home:hover {
  color: var(--color-accent-yellow);
}

.case-nav__list {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  list-style: none;
}

/* The connecting line — an li itself (so it's valid inside the ul and
   ignores the list's flex gap via being taken out of flow), sized and
   positioned by main.js to run exactly from the first dot's center to
   the last dot's center. Sits behind the dots via z-index so it reads
   as one continuous timeline. */
.case-nav__line {
  position: absolute;
  width: 1px;
  background-color: rgba(25, 25, 57, 0.2);
  z-index: 0;
  pointer-events: none;
}

.case-nav__list a {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-primary-dark);
  text-decoration: none;
  transition: color 0.2s ease;
}

.case-nav__list a:hover,
.case-nav__list a.is-active {
  color: var(--color-accent-yellow);
}

/* Timeline dot — small hollow-looking circle (light fill + navy border)
   when inactive, filled navy and a touch larger when its section is the
   one currently in view. Kept deliberately subtle per spec: 9px/11px,
   not a dramatic size jump. */
.case-nav__dot {
  flex-shrink: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: var(--color-primary-light);
  border: 1px solid var(--color-primary-dark);
  transition: background-color 0.2s ease, border-color 0.2s ease, width 0.2s ease, height 0.2s ease;
}

.case-nav__list a.is-active .case-nav__dot {
  width: 11px;
  height: 11px;
  background-color: var(--color-accent-yellow);
  border-color: var(--color-accent-yellow);
}

/* Yellow progress fill on the timeline line. main.js grows it from the first
   dot down to the active section's dot, and shrinks it back when you scroll up. */
.case-nav__line-fill {
  position: absolute;
  top: 0;
  left: -0.5px;
  width: 2px;
  height: 0;
  background-color: var(--color-accent-yellow);
  transition: height 0.4s ease;
}

/* The dot lights up as the line arrives at it */
.case-nav__list a.is-active .case-nav__dot {
  transition-delay: 0.25s;
}

/* ---- Generic Case Section ---- */

.case-section {
  padding: var(--space-4) 0;
  scroll-margin-top: calc(var(--site-header-height) + var(--space-4));
}

.case-section__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-secondary-blue);
}

.case-section__lead {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
  line-height: 1.5;
}

.challenge-block + .case-section__lead {
  margin-top: var(--space-6);
}

.case-section p {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  line-height: 1.6;
  margin-top: var(--space-2);
}

.case-statement {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-dark);
  line-height: 1.25;
  margin-top: var(--space-4);
}

.case-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  align-items: stretch;
  margin-top: var(--space-4);
}

/* ---- Plain List (bulleted content — traits, constraints, tradeoffs,
   included/out-of-scope lists, etc.) ---- */

.case-list {
  list-style: disc;
  padding-left: var(--space-3);
  margin-top: var(--space-2);
}

.case-list li {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  line-height: 1.6;
  margin-top: var(--space-1);
}

.case-list__lead {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  color: var(--color-primary-dark);
}

.case-list__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-4);
}

/* ---- Box List (bordered box with a header and dividers between items —
   an alternative to .case-list for content better shown as two
   compared columns, e.g. included/out-of-scope) ---- */

.box-list {
  background-color: var(--color-secondary-blue);
  height: 100%;
  text-align: center;
}

/* The margin-top below is meant for two .box-list elements stacked
   vertically in normal document flow. When they sit side by side inside
   .case-split's grid instead, that margin would eat into the row's
   stretched height and make the second box look shorter/offset — so
   cancel it in that context. */
.box-list + .box-list {
  margin-top: var(--space-3);
}

.case-split .box-list + .box-list {
  margin-top: 0;
}

.box-list__heading {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
  padding: var(--space-3) var(--space-3) var(--space-1);
}

.box-list__items {
  list-style: none;
  padding: 0 var(--space-3);
}

.box-list__items li {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  line-height: 1.5;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-primary-light);
}

.box-list__items li:last-child {
  border-bottom: none;
}

/* ---- Quote Stack ---- */

.quote-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.quote-card {
  border-left: 2px solid var(--color-secondary-blue);
  padding-left: var(--space-3);
}

.quote-card p {
  font-family: var(--font-body);
  font-style: italic;
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-secondary-blue);
  line-height: 1.6;
  margin: 0;
}

.case-section .quote-stack__caption {
  font-family: var(--font-body);
  font-size: 10px;
  color: var(--color-secondary-blue);
  margin-top: var(--space-2);
}

/* ---- Callout Box (e.g. product hypothesis, key takeaway) ---- */

.case-callout {
  margin-top: var(--space-4);
  color: var(--color-primary-light);
  padding: var(--space-4) var(--space-6);
}

.case-callout__label {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
}

.case-callout .case-callout__text {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-light);
  line-height: 1.25;
  margin-top: var(--space-1);
}

/* ---- Insight Cards ---- */

.insight-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.insight-card {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-secondary-blue);
  color: var(--color-primary-light);
  padding: var(--space-2);
  text-align: center;
}

.insight-card__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  margin-bottom: var(--space-2);
}

.insight-card .insight-card__desc {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  margin-top: 0;
  line-height: 1.5;
}

/* Modifier: 4-across instead of the default 3, same pattern as
   .process-grid--four. */
.insight-grid--four {
  grid-template-columns: repeat(4, 1fr);
}

/* ---- Process Steps ---- */

.process-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
  text-align: center;
}

.process-grid--four {
  grid-template-columns: repeat(4, 1fr);
}

.process-step__number {
  font-family: var(--font-heading);
  font-weight: var(--h1-weight);
  font-size: var(--h4-size);
  color: var(--color-accent-gold);
}

.process-step__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

.process-step .process-step__desc {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
  line-height: 1.5;
}

.process-step__media {
  aspect-ratio: 9 / 16;
  margin-top: var(--space-3);
}

/* ---- Technical Stack ---- */

.tech-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
  text-align: center;
}

.tech-card {
  border: 1px solid var(--color-secondary-blue);
  padding: var(--space-3);
}

/* Modifier: for a tech-card used standalone (no title above it) — centers
   the single item both ways instead of the default top/left alignment. */
.tech-card--center {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.tech-card--center .tech-card__list,
.tech-card--center .tech-card__list li {
  margin-top: 0;
}

.tech-card__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-secondary-blue);
}

.tech-card__list {
  list-style: none;
  margin-top: var(--space-2);
}

.tech-card__list li {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

/* ---- Challenge / Solution Block ---- */

.challenge-block {
  margin-top: var(--space-6);
}

/* When a challenge-block is the very first thing after a section
   heading (no intro paragraph in between), it should get the same
   heading-to-content gap as a paragraph would (.case-section p's
   margin-top), not the larger spacing meant to separate multiple
   challenge-blocks from each other. */
.case-section__heading + .challenge-block {
  margin-top: var(--space-2);
}

.challenge-block__label {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
}

.challenge-block__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

.challenge-block__media {
  margin-top: var(--space-4);
}

/* ---- Reflection + Takeaways ---- */

.takeaway-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.takeaway-card {
  /* intentionally no border/padding — headings carry the emphasis instead of a boxed treatment */
}

.takeaway-card__number {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
}

.takeaway-card__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

.takeaway-card .takeaway-card__desc {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
  line-height: 1.5;
}

/* ---- Final CTA ---- */

.case-cta {
  background-color: var(--color-primary-dark);
  color: var(--color-primary-light);
  padding: var(--space-6) 0;
  text-align: center;
}

/* ---- Case study sign-off: injected by main.js at the bottom of every
   case study, just above "explore more". Centered in the content column. ---- */

.case-closing {
  padding: var(--space-6) 0;
  text-align: center;
}

.case-closing .case-closing__text {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-dark);
  line-height: 1.25;
}

.case-cta__heading {
  font-family: var(--font-heading);
  font-weight: var(--h2-weight);
  font-size: var(--h2-size);
  color: var(--color-primary-light);
}

.case-cta__text {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
  margin-top: var(--space-1);
}

.case-cta .case-links {
  justify-content: center;
}

.case-cta .case-links a {
  color: var(--color-primary-light);
  border-color: var(--color-primary-light);
}

.case-cta .case-links a:hover {
  color: var(--color-accent-yellow);
  border-color: var(--color-accent-yellow);
}

.case-cta__media {
  margin-top: var(--space-4);
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}

/* ---- Next Project ---- */

.next-project {
  display: block;
  position: relative;
  text-decoration: none;
  color: inherit;
}

.next-project__media {
  aspect-ratio: 21 / 9;
}

.next-project__overlay {
  position: absolute;
  left: var(--space-8);
  bottom: var(--space-6);
}

.next-project__label {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-yellow);
}

.next-project__title {
  font-family: var(--font-heading);
  font-weight: var(--h2-weight);
  font-size: var(--h2-size);
  color: var(--color-primary-light);
  margin-top: var(--space-1);
}

/* ---- Explore More (bottom of every case study — replaces the old
   single "next project" placeholder). A horizontally-scrolling rail of
   every OTHER project, built from main.js's shared PROJECTS list. Cards
   reuse .project-card/.project-card__frame and its modifiers as-is (see
   main.js for how each variant maps to a modifier) — .explore-card only
   adds the fixed width + no-shrink needed for the scroll rail, plus a
   smaller type scale so the cards genuinely read as smaller, secondary
   picks rather than a repeat of the home page grid. ---- */

.explore-more {
  padding: var(--space-6) 0;
  overflow: hidden;
}

.explore-more__heading {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-dark);
}

.explore-more__track {
  display: flex;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-bottom: var(--space-2);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}

.explore-card {
  flex: 0 0 220px;
  width: 220px;
  scroll-snap-align: start;
}

.explore-card .project-card__title {
  font-size: var(--body-size);
}

/* ---- Stat Grid (e.g. research percentages) ---- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
  text-align: center;
}

.stat-item__number {
  font-family: var(--font-heading);
  font-weight: var(--h1-weight);
  font-size: var(--h2-size);
  color: var(--color-accent-gold);
  line-height: 1.1;
}

.stat-item__label {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
}

/* ---- Empathy Map (2x2 quadrant grid, e.g. says/thinks/does/feels) ---- */

.empathy-map-header {
  margin-top: var(--space-6);
}

.empathy-map-header__heading {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
}

.empathy-map-header .empathy-map-header__instruction {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--small-size);
  color: var(--color-secondary-blue);
  margin-top: var(--space-1);
}

.empathy-map {
  display: grid;
  grid-template-columns: 1fr 1fr;
  background-color: var(--color-primary-dark);
  padding: var(--space-3);
  margin-top: var(--space-4);
}

.empathy-quad {
  position: relative;
  min-height: 240px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Clean full-bleed cross, meeting evenly in the center — a single
   vertical divider and a single horizontal divider, both edge to edge,
   inset from the outer edge only by the map's own padding above, so
   the grid reads as one cohesive frame rather than four loose
   corners. */
.empathy-quad--says,
.empathy-quad--does {
  border-right: 1px solid rgba(250, 249, 246, 0.2);
}

.empathy-quad--says,
.empathy-quad--thinks {
  border-bottom: 1px solid rgba(250, 249, 246, 0.2);
}

.empathy-quad__label-box {
  position: relative;
  z-index: 1;
  background-color: var(--color-primary-light);
  padding: var(--space-3) var(--space-6);
}

.empathy-quad__label {
  display: block;
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-align: center;
  color: var(--color-primary-dark);
  transition: color 0.3s ease;
}

.empathy-quad.is-active .empathy-quad__label {
  color: var(--color-secondary-blue);
}

/* A tight, layered cluster around the centered label box — not spread
   across the quadrant. All three notes are positioned relative to the
   exact center point (top/left: 50%), then nudged out from there with
   a small translate offset, so overlap amounts are precise and
   intentional: the two top cards sit close enough to slightly overlap
   the label box's upper corners, and the bottom card sits close enough
   to slightly overlap its lower edge. z-index keeps the label box
   in front where they meet. */
.empathy-quad__notes {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.empathy-quad__notes .empathy-note {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 50%;
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--small-size);
  line-height: 1.35;
  color: var(--color-primary-light);
  background-color: var(--color-secondary-blue);
  padding: var(--space-1) var(--space-2);
  margin-top: 0;
  width: max-content;
  max-width: 150px;
  opacity: 0;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.empathy-quad__notes .empathy-note:nth-child(1) {
  transform: translate(-50%, -50%) translate(-110px, -46px) scale(0.96);
}

.empathy-quad__notes .empathy-note:nth-child(2) {
  transform: translate(-50%, -50%) translate(110px, -46px) scale(0.96);
}

.empathy-quad__notes .empathy-note:nth-child(3) {
  max-width: 190px;
  transform: translate(-50%, -50%) translate(0, 56px) scale(0.96);
}

.empathy-quad.is-active .empathy-quad__notes .empathy-note {
  opacity: 1;
}

.empathy-quad.is-active .empathy-quad__notes .empathy-note:nth-child(1) {
  transform: translate(-50%, -50%) translate(-110px, -46px) scale(1);
}

.empathy-quad.is-active .empathy-quad__notes .empathy-note:nth-child(2) {
  transform: translate(-50%, -50%) translate(110px, -46px) scale(1);
}

.empathy-quad.is-active .empathy-quad__notes .empathy-note:nth-child(3) {
  transform: translate(-50%, -50%) translate(0, 56px) scale(1);
}

/* ---- User Goal Flow (reusable: eyebrow + heading + body + flow chart
   + image, used for each user goal in a case study) ---- */

.goal-flow {
  margin-top: var(--space-10);
}

.goal-flow:first-child {
  margin-top: 0;
}

.goal-flow__eyebrow {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-accent-gold);
}

.goal-flow__heading {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h4-size);
  color: var(--color-primary-dark);
  margin-top: var(--space-1);
}

.goal-flow__body {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  line-height: 1.6;
  margin-top: var(--space-2);
  max-width: 640px;
}

.flow-chart {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

.flow-step {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-secondary-blue);
  color: var(--color-primary-light);
  padding: var(--space-2);
  text-align: center;
  min-height: 96px;
}


.flow-step .flow-step__text {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-light);
  margin-top: 0;
  line-height: 1.5;
}

.flow-arrow {
  flex-shrink: 0;
  align-self: center;
  width: 24px;
  height: 12px;
  color: var(--color-primary-dark);
}

/* ---- Constraint Table: a real 3-column table (original idea /
   constraint / revised approach) for mapping early ideas to what
   shipped. Cells reuse the same dark-navy-box-on-light-page treatment
   as .flow-step; border-spacing (not gaps on flex children) is what
   creates the visual gap between cells here, since it's a table. ---- */

/* ---- Constraint Table: a real 3-column table (original idea /
   constraint / revised approach) for mapping early ideas to what
   shipped. Plain table on the page's own background — row dividers
   (not boxed cells) carry the structure, using the same thin
   secondary-blue line already used for the "also..." list dividers on
   the about page. border-collapse merges each row's divider into one
   continuous line across all three columns rather than three separate
   per-cell segments. */

.constraint-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--space-6);
  margin-bottom: var(--space-6);
  table-layout: fixed;
}

.constraint-table th {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--small-size);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-secondary-blue);
  text-align: left;
  padding: 0 var(--space-2) var(--space-2) 0;
  border-bottom: 1px solid var(--color-secondary-blue);
}

.constraint-table td {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-primary-dark);
  line-height: 1.5;
  text-align: left;
  vertical-align: middle;
  padding: var(--space-3) var(--space-2) var(--space-3) 0;
  border-bottom: 1px solid var(--color-secondary-blue);
}

.constraint-table tr:last-child td {
  border-bottom: none;
}

/* Every value in the "revised approach" column is bold — only its own
   column header stays at the normal label weight. */
.constraint-table td:last-child {
  font-weight: 700;
}

/* ---- Before/After Flows: two side-by-side vertical step sequences
   (e.g. old path to information vs. new path). Reuses .flow-step and
   .flow-arrow from the flow-chart component, just stacked instead of
   side-by-side — the same rotation trick already used to stack a
   regular .flow-chart on mobile. ---- */

.before-after-flows {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  margin-top: var(--space-4);
}

.before-after-flows__col {
  text-align: center;
}

.flow-chart--vertical {
  flex-direction: column;
  align-items: center;
  margin-top: var(--space-3);
}

.flow-chart--vertical .flow-step {
  flex: none;
  width: 100%;
  max-width: 280px;
  min-height: 0;
  padding: var(--space-1) var(--space-3);
}

.flow-chart--vertical .flow-arrow {
  transform: rotate(90deg);
}

.goal-flow__image {
  margin-top: var(--space-4);
}

.goal-flow__image img {
  width: 100%;
  height: auto;
  display: block;
}

/* Modifier: for a single narrow screen/widget crop (as opposed to a wide
   multi-screen flow), don't stretch to the full column width. */
.goal-flow__image--small img {
  width: auto;
  max-width: 260px;
}

/* ---- Iteration Row: a horizontal row of screens, each with its own
   hover-pill text for the specific critique, rather than a heading +
   paragraph under every single one. Only the final "why I chose this
   one" reasoning appears as visible text below the row. ---- */

.iteration-row {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.iteration-row__item {
  flex: 1;
  cursor: none;
}

.iteration-row__item img {
  width: 100%;
  height: auto;
  display: block;
}

/* ---- Media Carousel: reusable arrow+dot carousel for a repeated group
   of image/video blocks (case-figures or whole comp-blocks) — one slide
   visible at a time. Deliberately NOT absolutely positioned: the active
   slide stays in normal flow so it's visible from pure CSS alone (the
   first slide already has is-active in the markup) even if the sizing
   JS below never runs. JS only refines the height to match the tallest
   slide once images load — it's an enhancement, not a requirement for
   visibility. Dots are circular per explicit request — the only place
   on the site overriding the no-border-radius rule. ---- */

.media-carousel {
  position: relative;
  margin-top: var(--space-4);
}

.media-carousel__slide .case-figure {
  padding: var(--space-4) 64px 88px;
}

.media-carousel__viewport {
  position: relative;
  display: grid;
  overflow: hidden;
  background-color: var(--color-primary-dark);
}

.media-carousel__slide {
  grid-area: 1 / 1;
  width: 100%;

  display: flex;
  flex-direction: column;
  justify-content: flex-end;

  visibility: hidden;
  pointer-events: none;
}

.media-carousel__slide.is-active {
  visibility: visible;
  pointer-events: auto;
}

.media-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  background: none;
  border: none;
  padding: 0;
  color: var(--color-primary-light);
  font-family: var(--font-heading);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}

.media-carousel__arrow:hover {
  opacity: 1;
}

.media-carousel__arrow--prev {
  left: 16px;
}

.media-carousel__arrow--next {
  right: 16px;
}

.media-carousel__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 32px;
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: var(--space-2);
}

.media-carousel__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  padding: 0;
  border: 1px solid var(--color-primary-light);
  background-color: transparent;
  opacity: 0.6;
  cursor: pointer;
  transition: opacity 0.2s ease, background-color 0.2s ease;
}

.media-carousel__dot.is-active {
  opacity: 1;
  background-color: var(--color-accent-gold);
  border-color: var(--color-accent-gold);
}

/* Keep comp-block media bottom-aligned inside carousels */
.media-carousel .comp-block {
  align-items: end;
  padding-bottom: var(--space-10);
}

.media-carousel .comp-block__media-frame {
  align-items: flex-end;
}

/* ==========================================================================
   FUN PAGE
   ========================================================================== */

.site-header.site-header--navy {
  background-image: url('../assets/images/fun-bg.jpg');
  background-attachment: fixed;
}

.page-fun {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background-color: var(--color-primary-dark);
  background-image: url('../assets/images/fun-bg.jpg');
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
}

.page-fun .site-footer {
  background-image: url('../assets/images/fun-bg.jpg');
  background-attachment: fixed;
}

.fun {
  flex: 1 0 auto;
}

/* ---- Layout: content stage on the left (70%), title + drawing tabs in a
   sticky column on the right (30%), like the about page. The markup lists the
   right column first (so the h1 comes first for screen readers) and
   row-reverse puts it on the right. ---- */

.fun-layout {
  display: flex;
  flex-direction: row-reverse;
  align-items: flex-start;
}

.fun-side {
  width: 30%;
  position: sticky;
  top: var(--site-header-height);
  height: calc(100vh - var(--site-header-height));
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  padding: var(--space-4) var(--space-8) var(--space-4) var(--space-4);
}

.fun-stage {
  width: 70%;
  min-width: 0;
}

/* ---- Fun page title + subtitle (top of the right column) ---- */

.fun-hero__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-light);
  line-height: 1.1;
}

.fun-hero__subtitle {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  font-size: var(--body-size);
  color: var(--color-accent-yellow);
  line-height: 1.4;
  margin-top: var(--space-1);
}

/* ---- Drawing tabs (film / art / music blog). Each button stacks two
   images that share one canvas: the resting drawing underneath, and the
   colored "hover/clicked" drawing on top, faded in on hover or when active.
   When a tab is open, the other two fade back. ---- */

.fun-tabs {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* 640:580 = the shared crop of the drawings (hardcoded on purpose, like the
   comp-block frame: it's sized to the artwork, not the spacing scale). Width
   scales with screen height (max 170px) so all three fit in the right column
   on shorter screens too. */
.fun-tab {
  position: relative;
  flex: none;
  width: clamp(112px, 18vh, 170px);
  aspect-ratio: 640 / 580;
  padding: 0;
  border: none;
  background: none;
  transition: opacity 0.3s ease;
}

.fun-tab__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  transition: opacity 0.3s ease;
}

.fun-tab__img--active {
  opacity: 0;
}

.fun-tab:hover .fun-tab__img--active,
.fun-tab.is-active .fun-tab__img--active {
  opacity: 1;
}

.fun-tabs.has-selection .fun-tab:not(.is-active) {
  opacity: 0.4;
}

.fun-tabs.has-selection .fun-tab:not(.is-active):hover {
  opacity: 1;
}

/* ---- Content panel (opens in the left stage) ---- */

.fun-panel {
  min-height: calc(100vh - var(--site-header-height));
  background-color: var(--color-primary-light);
  color: var(--color-primary-dark);
  padding: var(--space-8);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.fun-panel.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.fun-panel[hidden] {
  display: none;
}

.fun-panel__inner {
  max-width: 1100px;
  margin: 0 auto;
}

.fun-panel__title {
  font-family: var(--font-heading);
  font-weight: var(--h3-weight);
  font-size: var(--h3-size);
  color: var(--color-primary-dark);
  line-height: 1.1;
}

.fun-panel__subtitle {
  font-family: var(--font-body);
  font-weight: var(--body-lg-weight);
  font-size: var(--body-lg-size);
  color: var(--color-secondary-blue);
  line-height: 1.4;
  margin-top: var(--space-1);
  max-width: 720px;
}

/* ---- Film photo grid: reuses .about-photo (hover overlay + cursor pill),
   just at the photos' native 3:2 instead of the about page's squares. ---- */

.fun-photo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.about-photo--landscape {
  aspect-ratio: 3 / 2;
}

/* ---- Music blog: the four newest posts in a row (wraps on narrower screens),
   reusing .article-box ---- */

.fun-posts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-6);
}

/* 3 classes so it beats `a.article-box p` (0,1,2) */
.fun-posts .article-box .article-box__title {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  line-height: 1.3;
}

/* ==========================================================================
   DISPLAY FONT (Nuances Affection)
   The script is thin, so it's only used for true headings at 28px and up.
   Anything smaller (20px card titles, nav, buttons, tags, uppercase labels,
   numbers) stays on --font-heading (Montserrat). One block, single-class
   selectors, placed after the rules it overrides so it wins on source order.
   To add or remove a heading, add or remove its selector here.
   ========================================================================== */

.hero__title,
.hero__subtitle,
.projects__title,
.about-left__heading,
.about-right__label,
.case-hero__title,
.case-cta__heading,
.explore-more__heading,
.case-closing__text,
.fun-hero__title,
.fun-panel__title {
  font-family: var(--font-display);
  line-height: 1.2;
}

/* The script reads small next to Montserrat, so these titles go up one step:
   fun page titles 28px -> 36px, about page right-column titles 20px -> 28px. */
.fun-hero__title,
.fun-panel__title {
  font-size: var(--h2-size);
  font-weight: var(--h2-weight);
}

.about-right__label {
  font-size: var(--h3-size);
}

/* ---- Desktop-only notice: shown instead of the site on small screens.
   The element itself is injected by main.js. ---- */

.screen-too-small {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10000;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-6);
  text-align: center;
  background-image: url('../assets/images/oil-pastel-space-bg.jpg');
  background-size: cover;
  background-position: center;
}

.screen-too-small__mascot {
  display: block;
  height: 180px;
  width: auto;
}

.screen-too-small__text {
  font-family: var(--font-heading);
  font-weight: var(--h4-weight);
  font-size: var(--body-lg-size);
  color: var(--color-primary-light);
  line-height: 1.4;
  max-width: 520px;
}

@media (max-width: 1023px) {
  .screen-too-small {
    display: flex;
  }

  html,
  body {
    overflow: hidden;
  }
}

/* ==========================================================================
   PAGE TRANSITION: project card -> case study hero (View Transitions API)
   Chrome/Edge 126+ and Safari 18.2+. Other browsers, and anyone with reduced
   motion on, just navigate normally. main.js skips the transition for every
   navigation except home page card -> case study.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

/* One unique name per project. main.js gives the clicked home page card the
   same name at click time (only that card), and each case study hero gets it
   here, keyed off the data-project-id already on .case-study. Add a line
   when you add a case study. */
.case-study[data-project-id="soundswap"] .case-hero__media { view-transition-name: soundswap-media; }
.case-study[data-project-id="substack"] .case-hero__media { view-transition-name: substack-media; }
.case-study[data-project-id="vvwh-redesign"] .case-hero__media { view-transition-name: vvwh-redesign-media; }
.case-study[data-project-id="venmo"] .case-hero__media { view-transition-name: venmo-media; }
.case-study[data-project-id="healthiest-self"] .case-hero__media { view-transition-name: healthiest-self-media; }
.case-study[data-project-id="travel-app-research"] .case-hero__media { view-transition-name: travel-app-research-media; }

/* Lets one rule below style every project's media transition */
.project-card__frame,
.case-hero__media {
  view-transition-class: project-media;
}

/* Off-white behind everything, so there's never a white flash between the
   home page fading out and the case study fading in */
::view-transition {
  background-color: var(--color-primary-light);
}

/* The shared media: 750ms ease-in-out. Both snapshots fill the moving box
   with cover, so the card (1.88:1) morphs into the hero (16:9) without
   stretching. */
::view-transition-group(*.project-media) {
  animation-duration: 750ms;
  animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
}

::view-transition-old(*.project-media),
::view-transition-new(*.project-media) {
  height: 100%;
  object-fit: cover;
}

/* Everything else: the home page fades out fast, then the case study content
   (title, subtitle, tags, sidebar, links, the rest) fades in with a slight
   rise, starting just after the media begins moving. */
::view-transition-old(root) {
  animation: 200ms ease-out both vt-page-out;
}

::view-transition-new(root) {
  animation: 560ms cubic-bezier(0.22, 1, 0.36, 1) 240ms both vt-page-in;
}

@keyframes vt-page-out {
  to { opacity: 0; }
}

@keyframes vt-page-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ==========================================================================
   PAGE TRANSITIONS: home / work / about me (View Transitions API)
   The new page is revealed with a wipe while the old page stays put, and its
   content rises into place while the wipe is still sweeping in. Which wipe
   plays is decided by what the new page is showing:
     about page            -> wipes in from the left
     home page             -> wipes down from the top
     home page at #work    -> wipes up from the bottom
   Clicks that stay on the home page (logo, work links) set data-vt on <html>
   in main.js instead.
   ========================================================================== */

/* Wipe directions. The URL checks (:target) only apply to transitions between
   pages; a same-page transition sets data-vt and always wins, so a leftover #work
   in the address bar can't flip the direction. */
:root:is([data-vt="home"], :not([data-vt]):active-view-transition:has(.hero):not(:has(#work:target)))::view-transition-new(root) {
  animation: 950ms linear both vt-wipe-down;
}

:root:is([data-vt="work"], :not([data-vt]):active-view-transition:has(.hero):has(#work:target))::view-transition-new(root) {
  animation: 950ms linear both vt-wipe-up;
}

:root:active-view-transition:has(.about-layout)::view-transition-new(root) {
  animation: 950ms linear both vt-wipe-right;
}

/* The page you're leaving stays put until it's covered (no fading, no blending) */
:root:is([data-vt], :active-view-transition:has(.hero, .about-layout, .fun-layout))::view-transition-old(root) {
  animation: none;
  mix-blend-mode: normal;
}

:root:is([data-vt], :active-view-transition:has(.hero, .about-layout, .fun-layout))::view-transition-new(root) {
  mix-blend-mode: normal;
}

/* Content (text, images, nav, footer columns) rises and fades in while the
   background is still wiping in, so the two read as one motion (the wipe edge
   reveals it region by region). The backgrounds themselves belong to the
   containers listed here, so only their children animate. .vt-in is set by the
   small script in the <head> of the home and about pages when they're revealed
   by a transition (the page being LEFT never gets it, so its content stays
   visible until covered); data-vt covers clicks that stay on the home page. */
:root:is([data-vt], .vt-in) :is(.site-header, .site-footer, .hero, .projects, .about-left, .about-right) > * {
  animation: vt-content-in 650ms cubic-bezier(0.22, 1, 0.36, 1) 120ms both;
}

/* Fun page: content (header, footer, title, drawings, panel) slides left in step with the wipe */
:root.vt-in:has(.fun-layout) :is(.site-header, .site-footer, .fun-side, .fun-stage) > * {
  animation: vt-content-in-left 650ms cubic-bezier(0.22, 1, 0.36, 1) 120ms both;
}

/* 650ms wipe (68% of 950ms), then hold until the content has landed */
@keyframes vt-wipe-down {
  0% { clip-path: inset(0 0 100% 0); animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  68%, 100% { clip-path: inset(0 0 0 0); }
}

@keyframes vt-wipe-up {
  0% { clip-path: inset(100% 0 0 0); animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  68%, 100% { clip-path: inset(0 0 0 0); }
}

@keyframes vt-wipe-right {
  0% { clip-path: inset(0 100% 0 0); animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  68%, 100% { clip-path: inset(0 0 0 0); }
}

/* Fun page: the opposite of the about page. It wipes in from the right edge,
   sliding left, and its content slides left with it. */
:root:active-view-transition:has(.fun-layout)::view-transition-new(root) {
  animation: 950ms linear both vt-wipe-left;
}

@keyframes vt-wipe-left {
  0% { clip-path: inset(0 0 0 100%); animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  68%, 100% { clip-path: inset(0 0 0 0); }
}

@keyframes vt-content-in-left {
  from { opacity: 0; transform: translateX(40px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes vt-content-in {
  from { opacity: 0; transform: translateY(28px); }
  to { opacity: 1; transform: translateY(0); }
}