/* ==========================================================================
   COMPONENTS
   Each block matches a named component in the Figma file.
   ========================================================================== */

/* ---------- Navigation ----------
   Stronger chrome bands: solid surface shelf so header reads as app chrome
   against the ink/grid body. Compresses on first scroll (UI2).            */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  /* Height stays fixed so scroll-linked shrink never reflows the page
     (that reflow was causing the header to shake near the top). */
  height: var(--nav-height);
  /* Extra air under the tab cluster so it doesn’t sit on the header edge.
     Bottom padding also clears the reserved progress track. */
  padding-block: 18px 32px;
  padding-inline: var(--gutter-mobile);
  background: var(--color-bg-surface);
  border-bottom: 1px solid var(--color-border-default);
  /* Do not set overflow:hidden here — it breaks position:sticky in Chromium. */
  transition:
    padding var(--dur-nav-shrink) var(--ease-out),
    background-color var(--dur-nav-shrink) var(--ease-out),
    border-color var(--dur-nav-shrink) var(--ease-out);
}

@media (min-width: 768px)  { .nav { padding-inline: var(--gutter-tablet); } }

/* The nav bar itself stays full-bleed so its band spans the screen,
   but its contents have to line up with the content column, not the viewport
   edge, or the wordmark drifts away from the page on a wide monitor. */
@media (min-width: 1200px) {
  .nav {
    padding-inline: max(
      var(--gutter-desktop),
      calc((100% - var(--content-max)) / 2)
    );
  }
}

.nav.is-scrolled {
  padding-block: 14px 24px;
}

/* M6: scroll progress lives in a reserved 2px track on the nav’s bottom
   edge — never painted across tabs, wordmark, or status. */
.nav::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 0;
  height: 3px;
  width: var(--scroll-progress, 0%);
  background: var(--color-accent-primary);
  pointer-events: none;
  transition: width 80ms linear;
}

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

.nav__wordmark,
.nav__status,
.nav__tabs {
  position: relative;
  z-index: 1;
}

.nav__wordmark {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  /* Visual label stays label-sized; padding grows the hit area to ≥44px. */
  min-height: 44px;
  padding-block: 10px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-primary);
}

.nav__wordmark::before {
  content: '';
  width: 9px; height: 9px;
  background: var(--color-accent-primary);
}

/* Square sits after the label so it mirrors the wordmark square on the left. */
.nav__status::after {
  content: '';
  width: 9px; height: 9px;
  background: var(--color-accent-primary);
}

/* The "Nav Bar" frame in Figma: a bordered surface the items sit inside,
   separated by a hairline divider rather than spaced apart. */
.nav__tabs {
  display: flex;
  align-items: stretch;
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-default);
  border-radius: 2px;
  overflow: clip;
}

.nav__tab + .nav__tab { border-left: 1px solid var(--color-border-default); }

.nav__tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 18px var(--space-xl);
  border-top: 2px solid transparent;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent-primary);
  transition:
    background-color var(--dur-hover) var(--ease-out),
    color var(--dur-hover) var(--ease-out),
    text-shadow var(--dur-hover) var(--ease-out);
}

/* Corner brackets, 10px at 1.5px, same motif as .brackets but tighter. */
.nav__tab::before,
.nav__tab::after {
  content: '';
  position: absolute;
  width: 10px; height: 10px;
  border-color: var(--color-accent-primary);
  border-style: solid;
  pointer-events: none;
  transition:
    transform var(--dur-hover) var(--ease-out),
    border-color var(--dur-hover) var(--ease-out);
}

.nav__tab::before { top: 0; left: 0; border-width: 1.5px 0 0 1.5px; }
.nav__tab::after  { bottom: 0; right: 0; border-width: 0 1.5px 1.5px 0; }

/* Same language as .cta-glow / View Projects: white, soft glow, brackets veer. */
.nav__tab:hover,
.nav__tab:focus-visible {
  background: var(--color-bg-base);
  color: var(--color-text-primary);
  text-shadow:
    0 0 10px rgba(242, 242, 240, 0.55),
    0 0 24px rgba(242, 242, 240, 0.28);
}

.nav__tab:hover::before,
.nav__tab:focus-visible::before {
  transform: translate(-3px, -3px);
  border-color: var(--color-text-primary);
}

.nav__tab:hover::after,
.nav__tab:focus-visible::after {
  transform: translate(3px, 3px);
  border-color: var(--color-text-primary);
}

/* Active item: darker fill + accent rule across the top. No bottom underline. */
.nav__tab[aria-current='page'] {
  background-color: var(--color-bg-base);
  border-top-color: var(--color-accent-primary);
}

/* Figma hides the top-left bracket on the active item. */
.nav__tab[aria-current='page']::before { content: none; }

.nav__status {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-primary);
}

/* Mobile nav is two rows: wordmark and status on top, a full-bleed tab bar
   under it. The tab bar breaks out of the page gutter to sit edge to edge. */
@media (max-width: 767px) {
  .nav {
    display: grid;
    grid-template-columns: 1fr auto;
    /* Row 3 is the reserved progress track — tabs must never sit on it. */
    grid-template-rows: 66px 50px 4px;
    align-items: center;
    align-content: start;
    /* Kill the desktop flex gap — it was inserting 16px between grid rows
       and shoving the tab bar down over the progress track. */
    gap: 0;
    height: var(--nav-height-mobile);
    padding-block: 0;
    padding-inline: 0;
  }

  .nav.is-scrolled {
    height: var(--nav-height-mobile);
    padding-block: 0;
  }

  .nav__wordmark { grid-area: 1 / 1; padding-left: var(--gutter-mobile); }
  .nav__status   { grid-area: 1 / 2; padding-right: var(--gutter-mobile); }

  .nav__tabs {
    grid-area: 2 / 1 / 3 / -1;
    align-self: stretch;
    border-inline-width: 0;
    border-bottom-width: 0; /* progress track owns the bottom edge */
    border-radius: 0;
  }

  .nav__tab {
    flex: 1;
    justify-content: center;
    padding-block: 12px;
  }

  /* Keep corner brackets inside the tab row, above the progress track. */
  .nav__tab::after { bottom: 3px; }

  /* Figma shows both brackets on the active item on mobile. */
  .nav__tab[aria-current='page']::before { content: ''; }
}


/* ---------- Hero ----------
   Fills the first screen so the next section starts below the fold. */

.hero {
  display: grid;
  place-items: center;
  min-height: calc(100svh - var(--nav-height));
  text-align: center;
  padding-block: var(--space-3xl);
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-lg);
  /* 1020px is Figma's Intro Copy width at the 1440 artboard. It has to grow
     with the type or the headline picks up a third line on a wide monitor. */
  max-width: clamp(1020px, 70.8333vw, 1320px);
}

.hero__eyebrow {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(17px, 1.3889vw, 24px);   /* bigger than --text-small on purpose */
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-primary);
}

.hero__subline {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-sub);
  color: var(--color-accent-primary);
}

/* The block cursor after the cycling sub-line. Spec item 04. */
.hero__cursor {
  display: inline-block;
  width: 0.5em; height: 1em;
  margin-left: 0.15em;
  background: var(--color-accent-primary);
  vertical-align: text-bottom;
  animation: blink 1.1s steps(1) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .hero__cursor { animation: none; }
}

.hero__bio { max-width: 64ch; }


/* ---------- Shared CTA hover language ----------
   White + soft glow, with optional text brackets that ease outward.
   Used by .btn-bracket, phase chips, back links, draw-links, contact cubes. */

.text-brace {
  display: inline-block;
  transition: transform var(--dur-hover) var(--ease-out);
}

.cta-glow {
  transition:
    color var(--dur-hover) var(--ease-out),
    text-shadow var(--dur-hover) var(--ease-out),
    border-color var(--dur-hover) var(--ease-out),
    box-shadow var(--dur-hover) var(--ease-out);
}

.cta-glow:hover,
.cta-glow:focus-visible {
  color: var(--color-text-primary);
  text-shadow:
    0 0 10px rgba(242, 242, 240, 0.55),
    0 0 24px rgba(242, 242, 240, 0.28);
}

.cta-glow:hover .text-brace:first-child,
.cta-glow:focus-visible .text-brace:first-child {
  transform: translateX(-0.35em);
}

.cta-glow:hover .text-brace:last-child,
.cta-glow:focus-visible .text-brace:last-child {
  transform: translateX(0.35em);
}


/* ---------- Buttons ---------- */

.btn-bracket {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lead);
  color: var(--color-accent-primary);
  padding: var(--space-md) var(--space-lg);  /* generous hit area */
  margin: calc(var(--space-md) * -1) calc(var(--space-lg) * -1);
}

.btn-bracket:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}


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

.section-header {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  margin-bottom: var(--space-2xl);
}

.section-header__title {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-shrink: 0;
}

/* The cyan tick to the left of section titles. */
.section-header__title::before {
  content: '';
  width: 3px;
  align-self: stretch;
  background: var(--color-accent-primary);
}

.section-header__rule {
  flex: 1;
  height: 1px;
  background: var(--color-border-default);
}


/* ---------- Ghost numeral ---------- */

/* height: 0 + overflow: visible means the glyph paints downward behind the
   section header without the box itself taking up flow height, the same way
   it sits as a watermark behind the header in Figma. A flow-height numeral
   here was pushing every numbered section down by its own ~150-375px,
   which is where most of the dead space between About's sections came from. */
.ghost-numeral {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-ghost);
  line-height: 0.8;
  color: var(--color-text-ghost);
  pointer-events: none;
  user-select: none;
  height: 0;
  overflow: visible;
  margin: 0;
}


/* ---------- Project cards ---------- */

.project-grid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .project-grid { grid-template-columns: repeat(2, 1fr); }
}

.project-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 5px;
  background: var(--color-bg-base);
  border: 1px solid color-mix(in srgb, var(--color-accent-primary) 55%, var(--color-border-default));
  border-radius: 2px;
  overflow: visible;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 22%, transparent),
    0 0 36px var(--color-accent-tint);
  transition:
    transform var(--dur-hover) var(--ease-out),
    box-shadow var(--dur-hover) var(--ease-out);
}

/* Same oversized cyan brackets as the picture frames. */
.project-card.brackets::before,
.project-card.brackets::after {
  width: 36px;
  height: 36px;
  z-index: 4;
  border-color: var(--color-accent-primary);
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent-primary) 55%, transparent));
}

.project-card.brackets::before {
  top: -2px; left: -2px;
  border-width: 3px 0 0 3px;
}

.project-card.brackets::after {
  bottom: -2px; right: -2px;
  border-width: 0 3px 3px 0;
}

.project-card__viewport {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden;
  border-radius: 1px;
  border: 1px solid var(--color-border-default);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 12%, transparent);
  background: var(--color-bg-surface-glass);
  transition: background-color var(--dur-hover) var(--ease-out);
}

/* M3: lift, and let more grid show through the inner fill. */
.project-card:hover {
  transform: translateY(-4px);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 40%, transparent),
    0 0 48px var(--color-accent-tint);
}

.project-card:hover .project-card__viewport {
  background: color-mix(in srgb, var(--color-bg-surface) 30%, transparent);
}

a.project-card:hover::before,
a.project-card:focus-visible::before {
  transform: translate(-5px, -5px);
}

a.project-card:hover::after,
a.project-card:focus-visible::after {
  transform: translate(5px, 5px);
}

a.project-card:hover .cta-glow,
a.project-card:focus-visible .cta-glow {
  color: var(--color-text-primary);
  text-shadow:
    0 0 10px rgba(242, 242, 240, 0.45),
    0 0 22px rgba(242, 242, 240, 0.2);
}

a.project-card:hover .cta-glow .text-brace:first-child,
a.project-card:focus-visible .cta-glow .text-brace:first-child {
  transform: translateX(-0.35em);
}

a.project-card:hover .cta-glow .text-brace:last-child,
a.project-card:focus-visible .cta-glow .text-brace:last-child {
  transform: translateX(0.35em);
}

.project-card__cover {
  display: grid;
  place-items: center;
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  overflow: hidden;
  aspect-ratio: 1440 / 948;
  background: var(--color-bg-surface);
}

.project-card__thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 50%;
}

.lock-circle {
  display: grid;
  place-items: center;
  width: 152px; height: 152px;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-full);
  background: var(--color-bg-surface);
  box-shadow: 0 0 64px var(--color-accent-tint);
  color: var(--color-accent-primary);
}

.projects-method {
  margin-bottom: var(--space-2xl);
}

#projects .section-header {
  margin-bottom: var(--space-lg);
}

.project-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-md);
  padding: var(--space-xl);
  border-top: 1px solid var(--color-border-default);
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
}

.tag-row__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}

.tag {
  padding: 6px var(--space-md);
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent-primary);
}

/* The status chip is the inverse of a tag: outlined, no fill, and it sits
   opposite the tag group. */
.tag--status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--color-border-default);
}

.tag--status::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-accent-primary);
}

/* Narrow cards (phone + tablet 2-up): tags on one row, status on the next.
   Avoids margin-left:auto overlap when the row cannot fit all three. */
@media (max-width: 1199px) {
  .project-card .tag-row {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-sm);
  }

  .project-card .tag-row__tags {
    width: 100%;
  }

  .project-card .tag--status {
    margin-left: 0;
  }
}

.project-card__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-card);
  line-height: var(--leading-snug);
  color: var(--color-text-primary);
}

.project-card__note {
  display: inline;
  margin-left: var(--space-sm);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.project-card__desc { font-size: var(--text-small); }


/* ---------- Capability cards (About) ---------- */

.capability-grid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
}

@media (min-width: 768px)  { .capability-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .capability-grid { grid-template-columns: repeat(3, 1fr); } }

.capability-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-xl);
  background: var(--color-bg-surface-glass);
  border: 1px solid var(--color-border-default);
  transition: border-color var(--dur-hover) var(--ease-out);
}

.capability-card:hover { border-color: var(--color-accent-primary); }

.capability-card__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-card);
  line-height: var(--leading-snug);
  color: var(--color-text-primary);
}

.capability-card__desc { font-size: var(--text-small); }


/* ---------- Tech stack chips ---------- */

.stack-group { margin-bottom: var(--space-xl); }

.stack-group__label { margin-bottom: var(--space-md); }

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  background: var(--color-bg-surface-glass);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-secondary);
  transition:
    transform var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.chip:hover {
  transform: translateY(-2px);
  border-color: var(--color-accent-primary);
}

.chip__icon {
  width: 16px;
  height: 16px;
  object-fit: contain;
  flex-shrink: 0;
}


/* ---------- Experience rows ---------- */

.exp-row {
  display: grid;
  gap: var(--space-sm) var(--space-2xl);
  grid-template-columns: 1fr;
  padding-block: var(--space-xl);
  border-top: 1px solid var(--color-border-default);
}

@media (min-width: 768px) {
  .exp-row { grid-template-columns: 160px 1fr; }
}

.exp-row__org {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-sub);
  color: var(--color-text-primary);
}

.exp-row__logo {
  width: 28px;
  height: 28px;
  object-fit: contain;
  flex-shrink: 0;
}

.exp-row__role {
  font-size: var(--text-small);
  color: var(--color-accent-primary);
  margin-block: var(--space-xs) var(--space-sm);
}

.exp-row__desc { font-size: var(--text-small); }


/* ---------- Education ---------- */

.credential-grid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) { .credential-grid { grid-template-columns: repeat(2, 1fr); } }

.credential-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-xl);
  background: var(--color-bg-surface-glass);
  border: 1px solid var(--color-border-default);
  margin-bottom: var(--space-md);
}

.credential-card__crest {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--space-md);
}

.credential-card__crest img {
  width: 34px;
  height: 34px;
  object-fit: contain;
}

.credential-card__name {
  font-size: var(--text-small);
  color: var(--color-text-primary);
}

.credential-card__org { font-size: var(--text-meta); }


/* ---------- Photo carousel (About) ----------
   UI1: one photo at a time, cross-fade, arrows disable at the ends so the
   counter always tells the truth. No auto-advance.
   Frame language matches project/media cards: surface border + cyan
   corner brackets (kept outside the clipped viewport so they aren’t cut). */

.carousel { display: flex; flex-direction: column; gap: var(--space-md); }

.carousel__stack {
  position: relative;
  padding: 5px;
  background: var(--color-bg-base);
  border: 1px solid color-mix(in srgb, var(--color-accent-primary) 55%, var(--color-border-default));
  border-radius: 2px;
  /* visible so .brackets corners aren’t clipped by the photo crop */
  overflow: visible;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 22%, transparent),
    0 0 36px var(--color-accent-tint);
}

/* Stronger top scan-line — reads as framed hardware, not a plain card. */
.carousel__stack-edge {
  position: absolute;
  top: 5px; left: 5px; right: 5px;
  z-index: 3;
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--color-accent-primary) 20%,
    var(--color-accent-primary) 80%,
    transparent 100%);
  opacity: 0.85;
  pointer-events: none;
}

/* Oversized cyan brackets — the photo’s main brand signal. */
.carousel__stack.brackets::before,
.carousel__stack.brackets::after {
  width: 36px;
  height: 36px;
  z-index: 4;
  border-color: var(--color-accent-primary);
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent-primary) 55%, transparent));
}

.carousel__stack.brackets::before {
  top: -2px; left: -2px;
  border-width: 3px 0 0 3px;
}

.carousel__stack.brackets::after {
  bottom: -2px; right: -2px;
  border-width: 0 3px 3px 0;
}

/* Complete the set — all four corners, same weight as the pseudo brackets. */
.carousel__corner {
  position: absolute;
  width: 36px;
  height: 36px;
  z-index: 4;
  border-color: var(--color-accent-primary);
  border-style: solid;
  pointer-events: none;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent-primary) 55%, transparent));
}

.carousel__corner--tr {
  top: -2px; right: -2px;
  border-width: 3px 3px 0 0;
}

.carousel__corner--bl {
  bottom: -2px; left: -2px;
  border-width: 0 0 3px 3px;
}

.carousel__viewport {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: 1px;
  border: 1px solid var(--color-border-default);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 12%, transparent);
}

/* Tablet uses a taller crop so the photo holds its own beside the bio. */
@media (min-width: 768px) and (max-width: 1199px) {
  .carousel__viewport { aspect-ratio: 3 / 5; }
  .carousel--screen .carousel__viewport { aspect-ratio: 1440 / 948; }
  .carousel--sheet .carousel__viewport { aspect-ratio: 1024 / 544; }
  .carousel--board .carousel__viewport { aspect-ratio: 2932 / 4096; }
}

/* Case-study product screens: keep the real 1440×948 frame, no portrait crop.
   Light/dark dissolve is slower and even, not the snappy photo ease. */
.carousel--screen .carousel__viewport { aspect-ratio: 1440 / 948; }

.carousel--screen .carousel__slide {
  transition: opacity var(--dur-carousel-screen) var(--ease-crossfade);
}

.carousel--screen .carousel__slide img {
  object-fit: contain;
  transform: none;
}

/* Owner's notebook: same 3:4 frame as About, full photo visible. */
.carousel--notebook .carousel__viewport { aspect-ratio: 3 / 4; }

.carousel--notebook .carousel__slide img {
  object-fit: cover;
  transform: none;
}

/* Google Sheet screenshots: landscape, full UI visible. */
.carousel--sheet .carousel__viewport { aspect-ratio: 1024 / 544; }

.carousel--sheet .carousel__slide img {
  object-fit: contain;
  transform: none;
}

/* FigJam lo-fi boards: frame matches the active slide so no side bars. */
.carousel--board .carousel__viewport {
  aspect-ratio: 2932 / 4096;
  transition: aspect-ratio var(--dur-carousel) var(--ease-out);
}

.carousel--board .carousel__slide img {
  object-fit: contain;
  transform: none;
}

.carousel--screen .carousel__slide img,
.carousel--notebook .carousel__slide img,
.carousel--sheet .carousel__slide img,
.carousel--board .carousel__slide img {
  cursor: zoom-in;
}

.carousel__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none; /* inactive slides must not steal drag hits */
  transition: opacity var(--dur-carousel) var(--ease-out);
}

.carousel__slide.is-active {
  opacity: 1;
  pointer-events: auto;
  z-index: 1;
}

/*
  Zoom via --focal-zoom; aim via object-position + transform-origin
  from --focal-x / --focal-y (photo-app style crop).
*/
.carousel__slide img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focal-x, 50%) var(--focal-y, 50%);
  transform: scale(var(--focal-zoom, 1.12));
  transform-origin: var(--focal-x, 50%) var(--focal-y, 50%);
  will-change: transform, object-position;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-drag: none;
}

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

.carousel.is-editing .carousel__slide.is-active img {
  cursor: grab;
  touch-action: none;
}

.carousel.is-editing .carousel__slide.is-active img.is-dragging {
  cursor: grabbing;
}

.carousel__edit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-md);
  padding: var(--space-sm) 0;
}

.carousel__edit[hidden] { display: none; }

.carousel__edit-hint,
.carousel__readout {
  color: var(--color-accent-primary);
}

.carousel__zoom {
  display: flex;
  gap: var(--space-sm);
}

.carousel__save {
  padding: 10px 14px;
  border: 1px solid var(--color-accent-primary);
  background: transparent;
  color: var(--color-accent-primary);
  cursor: pointer;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  transition:
    color var(--dur-hover) var(--ease-out),
    background-color var(--dur-hover) var(--ease-out),
    text-shadow var(--dur-hover) var(--ease-out);
}

.carousel__save:hover,
.carousel__save:focus-visible {
  color: var(--color-text-primary);
  border-color: var(--color-text-primary);
  text-shadow:
    0 0 10px rgba(242, 242, 240, 0.45),
    0 0 22px rgba(242, 242, 240, 0.2);
}

.carousel__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}

.carousel__dots { display: flex; }

/* The dot reads as 8px but the button is 44×44 so it clears the
   minimum tap target on a phone. Visual size and hit area are separate. */
.carousel__dot {
  position: relative;
  width: 44px; height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.carousel__dot::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-border-default);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.carousel__dot.is-active::before { background: var(--color-accent-primary); }

.carousel__arrows { display: flex; gap: var(--space-sm); }

.carousel__arrow {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;      /* meets the 44px tap-target minimum */
  background: transparent;
  border: 1px solid var(--color-border-default);
  color: var(--color-text-primary);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.carousel__arrow:hover:not(:disabled) { border-color: var(--color-accent-primary); }

.carousel__arrow:disabled { opacity: 0.3; cursor: not-allowed; }


/* ---------- Case study ---------- */

.case-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-case);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  color: var(--color-text-primary);
  text-wrap: balance;
}

.meta-row {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
  padding-block: var(--space-xl);
  border-block: 1px solid var(--color-border-default);
}

@media (min-width: 640px) { .meta-row { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) { .meta-row { grid-template-columns: repeat(3, 1fr); } }

.meta-row__value {
  font-size: var(--text-small);
  color: var(--color-text-primary);
  margin-top: var(--space-xs);
}

.stat-row {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(3, 1fr);
  padding-block: var(--space-xl);
}

.stat__value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-card);
  color: var(--color-accent-primary);
}

.stat__label { margin-top: var(--space-xs); }

/* Phase jump nav, pins under the header once scrolled past.
   Must be a sibling of phase sections — not nested inside one —
   or sticky ends at that section’s bottom.
   Full-bleed band like .nav; chips stay centered in the bar. */
.phase-nav {
  position: sticky;
  top: var(--nav-height);
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
  width: 100%;
  margin-block: 0 var(--space-lg);
  padding-block: var(--space-md);
  padding-inline: var(--gutter-mobile);
  background: var(--color-bg-surface);
  border-block: 1px solid var(--color-border-default);
}

@media (min-width: 768px) {
  .phase-nav { padding-inline: var(--gutter-tablet); }
}

@media (min-width: 1200px) {
  .phase-nav {
    padding-inline: max(
      var(--gutter-desktop),
      calc((100% - var(--content-max)) / 2)
    );
  }
}

.case-hero-media {
  margin-bottom: var(--space-4xl);
}

/* Overview ends at the meta row; phase nav + hero sit just below. */
#overview.section {
  padding-bottom: var(--space-xl);
}

.phase-nav__chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-sm) var(--space-md);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  color: var(--color-text-muted);
}

.phase-nav__chip .text-brace:first-child { margin-inline-end: 1ch; }
.phase-nav__chip .text-brace:last-child { margin-inline-start: 1ch; }

.phase-nav__chip.is-active {
  color: var(--color-accent-primary);
}

/* Four chips in one row on a 390px phone. Grid, not flex, so they
   share the row evenly instead of overflowing Deliver off-screen.
   Brackets stay, but sit flush against the word so [Discover] fits. */
@media (max-width: 767px) {
  .phase-nav {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    justify-content: stretch;
    gap: var(--space-xs);
    padding-inline: var(--space-md);
    max-width: 100%;
  }

  .phase-nav__chip {
    justify-content: center;
    min-width: 0;
    width: 100%;
    padding-inline: 0;
    letter-spacing: 0;
    white-space: nowrap;
  }

  .phase-nav__chip .text-brace:first-child,
  .phase-nav__chip .text-brace:last-child {
    margin-inline: 0;
  }
}

.phase { padding-block: var(--space-4xl); }

.takeaway {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xl);
  margin-top: var(--space-2xl);
  padding-block: var(--space-3xl);
  border-top: 1px solid var(--color-border-default);
  text-align: center;
}

.takeaway__copy {
  max-width: none;
  color: var(--color-text-primary);
  text-wrap: pretty;
}

.takeaway .btn-bracket {
  margin: 0;
}

.phase__opener { margin-bottom: var(--space-2xl); }

.phase__title { margin-top: var(--space-sm); }

.phase__tools {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

.block { margin-bottom: var(--space-3xl); }

.block__label { margin-bottom: var(--space-md); }

/* Phone: copy above the frame, with room so it does not sit on the border. */
@media (max-width: 767px) {
  .block--split {
    display: flex;
    flex-direction: column;
    gap: var(--space-xl);
  }
}

/* Tablet: side by side, copy vertically centered on the frame. */
@media (min-width: 768px) and (max-width: 1199px) {
  .block--split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    align-items: center;
  }
  .block--split.is-flipped > .block__media,
  .block--split.is-flipped > .media-frame,
  .block--split.is-flipped > .carousel { order: -1; }
}

/* Desktop: wider picture column, same vertical centering. */
@media (min-width: 1200px) {
  .block--split {
    display: grid;
    grid-template-columns: 456px 1fr;
    gap: 56px;
    align-items: center;
  }
  .block--split.is-flipped {
    grid-template-columns: 1fr 456px;
  }
  .block--split.is-flipped > .block__media,
  .block--split.is-flipped > .media-frame,
  .block--split.is-flipped > .carousel { order: -1; }
  .block--split.is-wide-first { grid-template-columns: 1fr 456px; }
  /* Tall 3:4 notebook crop: equal columns so it fills its side without
     eating the whole leftover 1fr. */
  .block--split:has(> .carousel--notebook),
  .block--split:has(> .carousel--board) {
    grid-template-columns: 1fr 1fr;
  }
}

.block--split > .carousel { min-width: 0; }

.pain-points {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-default);
}

.pain-points__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--color-border-default);
}

.pain-points__row:last-child { border-bottom: 0; }

.pain-points__num {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.media-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .media-grid { grid-template-columns: 1fr 1fr; }
}

.block__media {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-default);
  display: grid;
  place-items: center;
  aspect-ratio: 2 / 1;
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-align: center;
  padding: var(--space-md);
}

/* Case-study media frames — same bezel language as About carousel photos. */
.media-frame {
  position: relative;
  display: block;
  width: 100%;
  padding: 5px;
  background: var(--color-bg-base);
  border: 1px solid color-mix(in srgb, var(--color-accent-primary) 55%, var(--color-border-default));
  border-radius: 2px;
  overflow: visible;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 22%, transparent),
    0 0 36px var(--color-accent-tint);
}

button.media-frame {
  cursor: zoom-in;
  text-align: inherit;
  font: inherit;
  color: inherit;
  appearance: none;
}

button.media-frame:focus-visible {
  outline: 2px solid var(--color-accent-primary);
  outline-offset: 4px;
}

.media-frame__edge {
  position: absolute;
  top: 5px; left: 5px; right: 5px;
  z-index: 3;
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--color-accent-primary) 20%,
    var(--color-accent-primary) 80%,
    transparent 100%);
  opacity: 0.85;
  pointer-events: none;
}

.media-frame.brackets::before,
.media-frame.brackets::after {
  width: 36px;
  height: 36px;
  z-index: 4;
  border-color: var(--color-accent-primary);
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent-primary) 55%, transparent));
}

.media-frame.brackets::before {
  top: -2px; left: -2px;
  border-width: 3px 0 0 3px;
}

.media-frame.brackets::after {
  bottom: -2px; right: -2px;
  border-width: 0 3px 3px 0;
}

.media-frame__corner {
  position: absolute;
  width: 36px;
  height: 36px;
  z-index: 4;
  border-color: var(--color-accent-primary);
  border-style: solid;
  pointer-events: none;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent-primary) 55%, transparent));
}

.media-frame__corner--tr {
  top: -2px; right: -2px;
  border-width: 3px 3px 0 0;
}

.media-frame__corner--bl {
  bottom: -2px; left: -2px;
  border-width: 0 0 3px 3px;
}

.media-frame__viewport {
  position: relative;
  aspect-ratio: 2 / 1;
  overflow: hidden;
  border-radius: 1px;
  border: 1px solid var(--color-border-default);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent-primary) 12%, transparent);
  background: var(--color-bg-surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
}

.media-frame__stack {
  display: flex;
  align-items: center;
  align-content: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-lg);
  width: 100%;
  box-sizing: border-box;
}

.media-frame__viewport.media-frame__viewport--icons {
  aspect-ratio: auto;
  height: auto;
  max-height: none;
  gap: 0;
  overflow: hidden;
  padding: var(--space-2xl) var(--space-lg);
  box-sizing: border-box;
  justify-content: center;
}

.media-frame__viewport .media-frame__stack img.chip__icon {
  width: 112px;
  height: 112px;
  flex: 0 0 auto;
  object-fit: contain;
}

.media-frame__viewport--icons .media-frame__stack {
  display: grid;
  grid-template-columns: repeat(4, 64px);
  justify-content: center;
  justify-items: center;
  align-items: center;
  gap: var(--space-lg);
}

.media-frame__viewport--icons .media-frame__stack img.chip__icon {
  width: 64px;
  height: 64px;
  max-width: none;
}

@media (max-width: 767px) {
  .media-frame__viewport.media-frame__viewport--icons {
    padding: var(--space-xl) var(--space-md);
  }

  .media-frame__viewport--icons .media-frame__stack {
    grid-template-columns: repeat(4, 48px);
    gap: var(--space-md);
  }

  .media-frame__viewport--icons .media-frame__stack img.chip__icon {
    width: 48px;
    height: 48px;
  }
}

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

.media-frame__viewport:has(> img) {
  aspect-ratio: auto;
}

.media-frame__viewport:has(> img) img {
  height: auto;
  object-fit: contain;
}

.media-frame__label {
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-align: center;
  padding: var(--space-md);
}

/* Fullscreen-ish image lightbox for case study media frames. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--space-xl);
}

.lightbox[hidden] { display: none; }

.lightbox__backdrop {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  cursor: zoom-out;
  background: color-mix(in srgb, var(--color-bg-base) 72%, transparent);
  backdrop-filter: grayscale(1) brightness(0.45);
  -webkit-backdrop-filter: grayscale(1) brightness(0.45);
}

.lightbox__dialog {
  position: relative;
  z-index: 1;
  /* Width follows the photo’s ratio so portrait shots don’t sit in a wide
     black box. --lb-ar is width/height, set when the lightbox opens. */
  width: min(92vw, 1400px, calc((88vh - 64px) * var(--lb-ar, 1.6)));
  max-height: 88vh;
  outline: none;
}

.lightbox__close {
  position: absolute;
  top: calc(-1 * var(--space-2xl));
  right: 0;
  z-index: 2;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border-default);
  background: var(--color-bg-surface);
  color: var(--color-text-primary);
  cursor: pointer;
}

.lightbox__close:hover,
.lightbox__close:focus-visible {
  border-color: var(--color-accent-primary);
  color: var(--color-accent-primary);
}

.lightbox__frame {
  width: 100%;
  max-height: 88vh;
}

.lightbox__viewport {
  aspect-ratio: var(--lb-ar, auto);
  width: 100%;
  max-height: calc(88vh - 12px);
  min-height: 0;
}

.lightbox__viewport img {
  width: 100%;
  height: auto;
  max-height: calc(88vh - 12px);
  object-fit: contain;
  display: block;
}

body.is-lightbox-open {
  overflow: hidden;
}

.insight-card {
  padding: var(--space-xl);
  background: var(--color-bg-surface-glass);
  border: 1px solid var(--color-border-default);
}

/* ---------- Back link ---------- */

.back-link {
  display: inline-block;
  padding: 11px 18px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Figma frames this one with 12px brackets, not the 18px used on cards. */
.back-link.brackets::before,
.back-link.brackets::after {
  width: 12px;
  height: 12px;
  border-width: 1.5px 0 0 1.5px;
}

.back-link.brackets::after { border-width: 0 1.5px 1.5px 0; }

.back-link.brackets:hover::before,
.back-link.brackets:focus-visible::before {
  transform: translate(-4px, -4px);
  border-color: var(--color-text-primary);
}

.back-link.brackets:hover::after,
.back-link.brackets:focus-visible::after {
  transform: translate(4px, 4px);
  border-color: var(--color-text-primary);
}


/* ---------- Footer ---------- */

.footer {
  position: relative;
  padding-top: var(--space-4xl);
  padding-bottom: var(--space-lg);
  margin-top: var(--space-3xl);
  border-top: 1px solid transparent;
  isolation: isolate;
}

/* Full-bleed solid surface band behind the container-aligned footer content. */
.footer::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  z-index: -1;
  background: var(--color-bg-surface);
  border-top: 1px solid var(--color-border-default);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-accent-primary) 18%, transparent);
  pointer-events: none;
}

.footer__copy { max-width: 60ch; margin-bottom: var(--space-2xl); }

.footer__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-title);
  color: var(--color-text-primary);
  margin-block: var(--space-sm) var(--space-md);
}

.contact-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}

@media (min-width: 640px)  { .contact-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .contact-grid { grid-template-columns: repeat(4, 1fr); } }

.contact-cube {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-lg);
  min-height: 190px;
  border-radius: var(--radius-sm);
  background: var(--color-bg-surface-glass);
  border: 1px solid var(--color-border-default);
  transition:
    transform var(--dur-hover) var(--ease-out),
    border-color var(--dur-hover) var(--ease-out),
    box-shadow var(--dur-hover) var(--ease-out);
}

.contact-cube:hover,
.contact-cube:focus-visible {
  transform: translateY(-3px);
  border-color: var(--color-text-primary);
  box-shadow:
    0 0 18px rgba(242, 242, 240, 0.12),
    0 0 36px rgba(242, 242, 240, 0.06);
}

.contact-cube:hover .label,
.contact-cube:focus-visible .label,
.contact-cube:hover .contact-cube__arrow,
.contact-cube:focus-visible .contact-cube__arrow,
.contact-cube:hover .contact-cube__sub,
.contact-cube:focus-visible .contact-cube__sub {
  color: var(--color-text-primary);
  text-shadow:
    0 0 10px rgba(242, 242, 240, 0.45),
    0 0 22px rgba(242, 242, 240, 0.2);
}

.contact-cube:hover .contact-cube__head,
.contact-cube:focus-visible .contact-cube__head {
  color: var(--color-text-primary);
  filter: drop-shadow(0 0 8px rgba(242, 242, 240, 0.35));
}

.contact-cube__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--color-accent-primary);
}

/* Figma exports crop each glyph to its own bounds, so the viewBoxes are all
   different shapes. Sizing one axis and letting contain do the rest keeps
   them from stretching. */
.contact-cube__icon {
  width: 24px;
  height: 24px;
  object-fit: contain;
  object-position: left center;
}

.contact-cube__label {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.contact-cube__arrow {
  font-family: var(--font-mono);
  transition: transform var(--dur-fast) var(--ease-out);
}

.contact-cube:hover .contact-cube__arrow { transform: translateX(4px); }

.contact-cube__sub {
  font-size: var(--text-small);
  color: var(--color-text-primary);
}

.colophon {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding-block: var(--space-2xl);
  margin-top: var(--space-2xl);
  border-top: 1px solid var(--color-border-default);
}


/* ---------- About hero layout ---------- */

.about-hero {
  display: grid;
  gap: var(--space-2xl);
  grid-template-columns: 1fr;
  padding-block: var(--space-4xl);
  align-items: start;
}

@media (min-width: 768px) {
  .about-hero {
    /* Keep the 3:5 crop and focal alignment. Shrink the photo column so
       height (width × 5/3) plus chrome still fits under the nav. Extra
       chrome covers the carousel bar so arrows are not clipped. */
    --about-hero-chrome: calc(var(--nav-height) + var(--space-lg) * 2 + 112px);
    grid-template-columns:
      minmax(0, 1fr)
      min(360px, calc((100svh - var(--about-hero-chrome)) * 3 / 5));
    gap: var(--space-lg);
    padding-block: var(--space-lg);
    align-items: start;
  }

  .about-hero__copy {
    gap: var(--space-sm);
  }

  .about-hero__headline {
    font-size: clamp(26px, 2.8vw, 40px);
  }

  .about-hero__copy .body-copy {
    font-size: clamp(14px, 1.1vw, 16px);
    max-width: none;
  }
}

@media (min-width: 1200px) {
  .about-hero {
    /* Same 3:4 crop. A little more chrome than tablet so the last bio
       line and carousel bar clear the fold on desktop. */
    --about-hero-chrome: calc(var(--nav-height) + var(--space-lg) * 2 + 136px);
    grid-template-columns:
      minmax(0, 1fr)
      min(620px, calc((100svh - var(--about-hero-chrome)) * 3 / 4));
    gap: 40px;
  }

  .about-hero__headline {
    font-size: clamp(26px, 2.4vw, 36px);
  }

  .about-hero__copy .body-copy {
    font-size: clamp(14px, 1vw, 15px);
    max-width: none;
  }
}

/* The carousel deliberately runs past the content column and into the
   gutter. In Figma this layer is literally named "breaks the grid". */
@media (min-width: 768px) {
  .about-hero > .carousel { margin-right: calc(var(--gutter-tablet) * -1); }
}

@media (min-width: 1200px) {
  .about-hero > .carousel { margin-right: calc(var(--gutter-desktop) * -1); }
}

.about-hero__copy { display: flex; flex-direction: column; gap: var(--space-lg); }

.about-hero__headline {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  color: var(--color-text-primary);
  text-wrap: balance;
}


/* ==========================================================================
   MOTION SPEC
   Behaviours from the Interaction Spec board (84:216) that need styling.
   The JS side lives in main.js and only ever sets custom properties.
   ========================================================================== */

/* ---------- M9 - Link hover states ----------
   Arrow slides 6px in the direction of travel, 1px accent underline draws
   from zero to full width. The underline is a background layer rather than a
   pseudo element so it can coexist with .brackets, which already owns both
   ::before and ::after on some of these.                                   */

.link-draw {
  background-image: linear-gradient(
    var(--color-accent-primary), var(--color-accent-primary));
  background-repeat: no-repeat;
  background-position: left 100%;
  background-size: 0% 1px;
  transition:
    background-size var(--dur-link) var(--ease-out),
    color var(--dur-hover) var(--ease-out),
    text-shadow var(--dur-hover) var(--ease-out);
}

.link-draw:hover,
.link-draw:focus-visible,
a:hover > .link-draw,
a:focus-visible > .link-draw,
a:hover .link-draw,
a:focus-visible .link-draw { background-size: 100% 1px; }

.link-draw.cta-glow:hover,
.link-draw.cta-glow:focus-visible,
.cta-glow:hover .link-draw,
.cta-glow:focus-visible .link-draw {
  background-image: linear-gradient(
    var(--color-text-primary), var(--color-text-primary));
}

.link-arrow {
  display: inline-block;
  transition: transform var(--dur-link) var(--ease-out);
}

.link-draw:hover .link-arrow,
a:hover .link-arrow,
a:focus-visible .link-arrow { transform: translateX(6px); }

/* Back link travels the other way. */
.back-link .link-arrow { transform: translateX(0); }
.back-link:hover .link-arrow { transform: translateX(-6px); }

/* The contact cube arrow was already sliding, just not the spec distance. */
.contact-cube:hover .contact-cube__arrow { transform: translateX(6px); }


/* ---------- Spec 07 - Magnetic chip hover ----------
   Lift and scale are driven per-chip from JS against live cursor distance,
   which is why the transform reads from variables instead of :hover.       */

.chip {
  transform: translateY(var(--lift, 0px)) scale(var(--scale, 1));
  transition:
    transform var(--dur-magnet) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.chip:hover { transform: translateY(var(--lift, 0px)) scale(var(--scale, 1)); }


/* ---------- M12 - Cover parallax inside the project card ----------
   Desktop only. There is no cursor on touch, so it is simply absent there. */

.project-card__cover .lock-circle {
  transform: translate3d(var(--px, 0px), var(--py, 0px), 0);
  transition: transform var(--dur-magnet) var(--ease-out);
}


/* ---------- Spec 06 - Image fallbacks ----------
   An image that fails to load renders as its labelled placeholder rather
   than a broken frame, so the site never looks unfinished while covers and
   photos are still missing.                                               */

.img-fallback {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  width: 100%;
  height: 100%;
  min-height: 180px;
  padding: var(--space-lg);
  background: var(--color-bg-surface);
  border: 1px dashed var(--color-border-default);
  text-align: center;
}

.img-fallback__name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent-primary);
}

.img-fallback__meta {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  color: var(--color-text-muted);
}


/* ---------- M5 - Decode on reveal ----------
   Runs once on load, then repeats occasionally while the element is idle
   in view or scrolls back into view; see main.js. The scrambling
   characters are monospace so the line does not reflow while it resolves.
   Colour eases in quickly for the scramble, then settles back to the
   normal text colour on a longer ease so the finish feels soft, not a snap. */

[data-decode] { transition: color 780ms var(--ease-out); }

.is-decoding {
  font-variant-ligatures: none;
  /* Soft cyan wash — full accent was too sharp against the white settle. */
  color: color-mix(in srgb, var(--color-accent-primary) 58%, var(--color-text-primary));
  transition: color 200ms var(--ease-out);
}


/* ---------- Home arrival — cinematic title card ----------
   Pure black hold → hairline → stage + nav fade → staggered hero rise.
   Home only, once per session. Without JS the page renders normally.
   html.arrival-pending is set by a tiny head script to prevent a flash. */

/* Full-viewport black from the first paint — covers nav, grid, everything. */
html.arrival-pending::before,
.page.is-arriving::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 200;
  background: #000;
  pointer-events: none;
}

html.arrival-pending .nav,
html.arrival-pending .grid-backdrop,
html.arrival-pending .hero__inner > *,
html.arrival-pending #projects,
html.arrival-pending .footer {
  opacity: 0;
}

html.arrival-pending .hero__inner > * {
  transform: translate3d(0, 18px, 0);
}

.page.is-arriving {
  position: relative;
}

.page.is-arriving .nav,
.page.is-arriving .grid-backdrop {
  opacity: 0;
}

.page.is-arriving .hero__inner > *,
.page.is-arriving #projects,
.page.is-arriving .footer {
  opacity: 0;
}

.page.is-arriving .hero__inner > * {
  transform: translate3d(0, 18px, 0);
  will-change: transform, opacity;
}

/* Black holds, then lifts after the hairline — title-card pacing. */
.page.is-arriving.is-arrival-play::after {
  animation: arrival-veil 1100ms var(--ease-arrival) 720ms forwards;
}

@keyframes arrival-veil {
  to { opacity: 0; }
}

/* Stage chrome fades in as the black clears. */
.page.is-arriving.is-arrival-play .nav {
  animation: arrival-fade var(--dur-arrival) var(--ease-arrival) 900ms forwards;
}

.page.is-arriving.is-arrival-play .grid-backdrop {
  animation: arrival-fade 1400ms var(--ease-arrival) 880ms forwards;
}

@keyframes arrival-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Hero lines rise after the black has started to clear. */
.page.is-arriving.is-arrival-play .hero__eyebrow {
  animation: arrival-rise var(--dur-arrival) var(--ease-arrival) 1100ms forwards;
}

.page.is-arriving.is-arrival-play .hero__inner > .display {
  animation: arrival-rise var(--dur-arrival) var(--ease-arrival) 1280ms forwards;
}

.page.is-arriving.is-arrival-play .hero__subline {
  animation: arrival-rise var(--dur-arrival) var(--ease-arrival) 1480ms forwards;
}

.page.is-arriving.is-arrival-play .hero__bio {
  animation: arrival-rise var(--dur-arrival) var(--ease-arrival) 1660ms forwards;
}

.page.is-arriving.is-arrival-play .btn-bracket {
  animation: arrival-rise var(--dur-arrival) var(--ease-arrival) 1840ms forwards;
}

/* Opacity leads slightly so type feels present while it finishes settling. */
@keyframes arrival-rise {
  0% {
    opacity: 0;
    transform: translate3d(0, 18px, 0);
  }
  55% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

.page.is-arriving.is-arrival-play #projects,
.page.is-arriving.is-arrival-play .footer {
  animation: m8-content-in 700ms var(--ease-arrival) 2200ms forwards;
}

/* Hairline draws on black first — the cue that the title card is opening. */
.page.is-arriving::before {
  content: '';
  position: fixed;
  top: 42%;
  left: 0;
  height: 1px;
  width: 0;
  z-index: 210;
  background: var(--color-accent-primary);
  box-shadow: 0 0 14px rgba(58, 214, 255, 0.4);
  pointer-events: none;
  opacity: 0;
}

.page.is-arriving.is-arrival-play::before {
  opacity: 1;
  animation:
    m8-hairline-draw 640ms var(--ease-arrival) 380ms forwards,
    m8-hairline-out  480ms linear 1100ms forwards;
}


/* ---------- M8 - Page transitions, "Hairline Draw" ----------
   The grid and the nav never move; only the content swaps. A cyan hairline
   draws left to right above the incoming content, then fades as the content
   settles underneath. Both halves are keyframe animations that END at the
   visible state and the class is only ever added by JS, so a page with no
   scripting still renders at full opacity.                                 */

.page.is-entering #main,
.page.is-entering .footer {
  animation: m8-content-in var(--dur-hover) var(--ease-out) 140ms backwards;
}

@keyframes m8-content-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.page.is-entering::before {
  content: '';
  position: absolute;
  top: var(--nav-height);
  left: 0;
  height: 1px;
  width: 0;
  z-index: 40;
  background: var(--color-accent-primary);
  pointer-events: none;
  animation:
    m8-hairline-draw 250ms var(--ease-out) forwards,
    m8-hairline-out  200ms linear 260ms forwards;
}

@keyframes m8-hairline-draw { to { width: 100%; } }
@keyframes m8-hairline-out  { to { opacity: 0; } }
