/* ============================================
   PROJECT PAGE STYLES
   ============================================ */

html {
  max-width: 100%;
  overscroll-behavior: auto;   /* allow the OS's native elastic bounce at top/bottom */
  background-color: #F5F3F1;   /* fallback behind the gradient canvas */
}

body {
  max-width: 100%;
  overscroll-behavior: auto;
  background-color: transparent;   /* let the full-screen gradient show through */
}

.project-body {
  background-color: transparent;
  min-height: 100vh;
  padding: 24px;
  overflow-x: hidden;
}

/* ============================================
   CLOSE BUTTON (sticky)
   ============================================ */
.close-button {
  position: fixed;
  top: 40px;
  right: 40px;
  z-index: 1100;   /* above EVERYTHING on the page — the toggles sit at 1000 */
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(0,0,0,0.2);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  font-size: 32px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background 0.2s ease, transform 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  animation: fadeIn 0.6s ease 0.3s both;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.close-button:hover {
  background: rgba(0,0,0,0.2);
  transform: scale(1.08);
}

/* ============================================
   TABLE OF CONTENTS (fixed left sidebar)
   Shown only on wide screens where the centered
   1080px content leaves room in the left margin.
   ============================================ */
.project-toc {
  position: fixed;      /* fixed = smooth (compositor-handled), no scroll jitter */
  top: 0;               /* exact top set by JS to align with the description row */
  left: 40px;
  z-index: 900;
  display: flex;
  flex-direction: column;
  gap: 14px;
  font-family: 'ABCDiatype', sans-serif;
  opacity: 1;
  transition: opacity 0.4s ease;
}

/* Each item rises/fades in one after another once revealed (staggered by --i) */
.project-toc.toc-revealed .toc-item {
  animation: tocItemIn 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--i, 0) * 0.12s);
}

@keyframes tocItemIn {
  from { opacity: 0; transform: translateY(48px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Fades out once the Other Projects section is reached */
.project-toc.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.toc-item {
  font-size: 16px;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: #9a9a9a;
  text-decoration: none;
  width: fit-content;
  opacity: 0;                 /* hidden until the staggered reveal */
  transition: color 0.25s ease;
}

.toc-item:hover {
  color: #1e1e1e;
}

.toc-item.is-active {
  color: #1e1e1e;
}

/* Extra space below the "Home" link, separating it from the section list */
.toc-home {
  margin-bottom: 24px;
}

/* Extra space below an item to visually group the list (e.g. on the Mojo App
   page: Intro · gap · Sections · gap · Challenge & Impact) */
.toc-gap-after {
  margin-bottom: 16px;
}

/* Tablet: smaller TOC text */
@media (max-width: 1400px) {
  .toc-item { font-size: 14px; }
}

/* Width of the left gutter reserved for the TOC. The project content shrinks
   to leave this space, so the TOC stays visible on desktop AND tablet. */
.project-body { --side-gutter: 200px; }

/* Phones: drop the gutter (content goes full width) and hide the TOC */
@media (max-width: 860px) {
  .project-body { --side-gutter: 0px; }
  .project-toc { display: none; }
}

/* ============================================
   PROJECT PAGE LAYOUT
   ============================================ */
.project-page {
  max-width: 1080px;
  margin: 0 auto;
  width: 100%;
}


/* ============================================
   PREVIEW SECTION
   ============================================ */
.preview-section {
  background: #F2F2F2;
  border-radius: 16px;
  overflow: hidden;
  position: relative;
  height: calc(100svh - 48px - 70px);
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  isolation: isolate;
  opacity: 0;
  transform: translateY(30px);
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.preview-section.animate-in {
  animation: sectionFadeIn 1.5s ease both, sectionSlideIn 1.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes sectionFadeIn {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

@keyframes sectionSlideIn {
  0% { transform: translateY(30px); }
  100% { transform: translateY(0); }
}

/* Small caption under the main preview — same look/position as the Mojo App
   "Preview" caption (.feature-desc-below): 16px, left-aligned, aligned to the
   1080px content column, 28px below the preview. */
.preview-caption {
  max-width: 1080px;
  margin: 28px auto 0;
  font-family: 'ABCDiatype', sans-serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.35;
  color: #1e1e1e;
}
@media (max-width: 768px) {
  .preview-caption { font-size: 14px; }
}

.preview-section.process-mode {
  background: rgba(30, 30, 30, 0.1);   /* #1e1e1e at 10% opacity */
  /* Hairline drawn as an INSET SHADOW, not a border: a border occupies real
     space, so adding it only in process mode made the toggle, section content
     and the text below jump ~1px on every mode switch. The shadow paints the
     same 1px line (black, 3% opacity) with zero layout change. */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.03);
}

.preview-section.process-mode::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, #B8B6B3 1px, transparent 1px);
  background-size: 12px 12px;
  background-position: center;
  pointer-events: none;
  z-index: 0;
  opacity: 1;
  transition: opacity 0.4s ease;
}

/* Two layers for crossfade transition */
.preview-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}


.preview-layer--incoming {
  opacity: 0;
  pointer-events: none;
}

.preview-mode {
  width: 100%;
  height: 100%;
  position: relative;
}

.preview-mode.hidden {
  display: none;
}

/* ============================================
   PROTOTYPE MODE
   ============================================ */
.prototype-empty-state {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.prototype-empty-state.empty::after {
  content: 'Prototype coming soon';
  color: #aaa;
  font-family: 'ABCDiatype', sans-serif;
  font-size: 16px;
}

.prototype-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* ============================================
   PROCESS MODE — DRAGGABLE CANVAS
   ============================================ */
.drag-canvas {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  cursor: default;
  touch-action: pan-y pinch-zoom;   /* vertical swipe scrolls the page, two-finger pinch zooms it; we drive horizontal ourselves */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
  isolation: isolate;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ✨ DRAGGABLE IMAGES */
.draggable-image {
  position: absolute;
  cursor: grab;
  border-radius: 10px;
  user-select: none;
  -webkit-user-drag: none;
  touch-action: pan-y pinch-zoom;   /* vertical swipe scrolls, two-finger pinch zooms the page */
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.08);
}

.draggable-image.no-shadow {
  box-shadow: none;
}

.draggable-image.no-radius {
  border-radius: 0;
}

/* ============================================
   MODE TOGGLE — SLIDING PILL
   Structure only. All colors/textures are set
   in mode-toggle.js via TOGGLE_STYLE constants.
   ============================================ */
.mode-toggle {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 999px;
  padding: 8px;
  display: flex;
  gap: 0;
  z-index: 1000;
  height: 80px;   /* desktop capsule height (tablet/mobile shrink to 64 below) */
  align-items: center;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  max-width: calc(100% - 48px);
}

/* ✨ THE SLIDING PILL */
.toggle-pill {
  position: absolute;
  top: 7px;
  bottom: 7px;
  left: 0;
  border-radius: 999px;
  z-index: 0;
  pointer-events: none;
  will-change: transform;
  transition:
    transform 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94),
    width 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.toggle-btn {
  height: 64px;   /* desktop button height (tablet/mobile shrink to 50 below) */
  padding: 0 36px;
  border: none;
  border-radius: 999px;
  font-family: 'ABCDiatype', sans-serif;
  font-size: 20px;
  cursor: pointer;
  background: transparent;
  position: relative;
  z-index: 1;
  transition: color 0.35s ease;
  white-space: nowrap;
  flex-shrink: 1;
  min-width: 0;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* ============================================
   PROJECT INFO — moved to project-description.css
   ============================================ */

/* ============================================
   OTHER PROJECTS (simple card row)
   ============================================ */
.other-projects {
  height: 100svh;
  display: flex;
  align-items: center;       /* cards sit at the vertical centre of the screen */
  justify-content: center;
  padding: 0;
  /* Break out of the page's 24px body padding so this section is truly
     full-screen: full width (edge to edge) and bottom flush with the screen,
     so the cards land at the real vertical centre. */
  margin: 0 -24px -24px;
  overflow: hidden;          /* clip the looped row at the screen edges */
}

/* Full-width band the cards sit in: centres them when they fit, holds the
   draggable track when they don't. */
.op-viewport {
  width: 100%;
  display: flex;
  justify-content: center;   /* JS flips this to flex-start in carousel mode */
}

/* The row itself. Shrink-wraps so it can be centred, or translated when looped. */
.op-track {
  --op-gap: clamp(24px, 5vw, 90px);
  display: flex;
  align-items: center;
  gap: var(--op-gap);
  width: max-content;
  will-change: transform;
}

.op-track.op-draggable { cursor: grab; }
.op-track.op-dragging  { cursor: grabbing; }

/* Living silk-gradient — fixed full-viewport layer behind the whole page */
.op-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

/* Outer card = positioning slot that holds the scaling media + caption */
.op-card {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: clamp(160px, 22vw, 300px);
  aspect-ratio: 1 / 1;
  display: block;
  text-decoration: none;
  color: inherit;
}

/* Desktop: always fit 4 cards across the screen — shrink them below the 300px
   max if the window is too narrow, so it stays a centred row of 4 instead of
   tipping into a carousel. (24px breathing room each side.) */
@media (min-width: 1025px) {
  .op-card {
    width: min(300px, calc((100vw - 48px - 3 * var(--op-gap)) / 4));
  }
}

/* Inner = the square thumbnail that grows on hover */
.op-card-inner {
  position: relative;                               /* anchors the first-frame poster overlay */
  width: 100%;
  height: 100%;
  border-radius: clamp(16px, 2.4vw, 34px);
  overflow: hidden;
  background: #e9e6e3;                              /* tint before media loads */
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.10);      /* same as home-screen cards */
  transform: translateZ(0);                         /* own GPU layer → no rounded-corner hairline (Safari) */
  transition: transform 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94),
              box-shadow 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* First-frame poster laid over a video card: visible instantly, fades out the
   moment the (lazily-downloaded) video actually plays — the card is never an
   empty grey box. Same trick as the home cards and the mobile coverflow. */
.op-card-inner .op-poster {
  position: absolute;
  inset: -1px;                /* same 1px bleed as the video it covers */
  z-index: 1;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.op-card-inner img,
.op-card-inner video {
  /* Bleed 1px past the card on every side + round the media itself: Safari's
     rounded clipping otherwise blends the corner edge into the card's
     placeholder background — a faint light "border" around dark thumbnails
     over the coloured gradient. Same treatment as the mobile carousels. */
  position: absolute;
  inset: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  object-fit: cover;
  display: block;
  border-radius: inherit;
}

.op-card:hover .op-card-inner {
  transform: scale(1.15);                            /* same as home screen */
  box-shadow: 0 18px 45px rgba(0, 0, 0, 0.16);
}

/* Product Films is a dark thumbnail — dark placeholder so no light hairline of
   the card background peeks around the rounded corners. */
.op-card[data-project="product-films"] .op-card-inner { background: #1a1a1a; }

/* Project title that waves in under the card on hover (like home) */
.op-caption {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 56px;
  /* Wrap a long label to balanced 2 lines instead of one wide line — matches
     the home-screen caption (size to the text, then wrap once it hits 190px). */
  white-space: normal;
  width: max-content;
  max-width: 190px;
  line-height: 1.3;
  text-align: center;
  pointer-events: none;
  z-index: 2;
  font-family: 'ABCDiatype', sans-serif;
  font-size: 16px;
  color: #1a1a1a;
  /* Fuller look: use the browser's default (heavier) text rendering here,
     overriding the page-wide thin setting from style.css. */
  -webkit-font-smoothing: auto;
}

/* Each word is one inline-block unit that never splits, so the caption breaks
   at the spaces between words, not mid-word. */
.op-caption .op-word {
  display: inline-block;
  white-space: nowrap;
}

.op-caption .ch {
  display: inline-block;
  opacity: 0;
  transition: opacity 0.3s ease;          /* leaving = simple fade out */
}

.op-card:hover .op-caption .ch {
  animation: opch-rise 0.75s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: var(--d, 0s);          /* per-letter stagger = the wave */
  transform-origin: bottom center;
}

@keyframes opch-rise {
  from { opacity: 0; transform: translateY(20px) rotate(30deg); }
  to   { opacity: 1; transform: translateY(0)    rotate(0deg); }
}

/* ── Phones: Other Projects uses the SAME coverflow as the home screen ──
   One big centered card, a smaller card peeking each side, keywords beneath
   it, swipeable + looping. Built by other-projects.js on mobile instead of the
   draggable row. ── */
/* Hidden on desktop: once the phone coverflow is built (after the window has
   been at phone width), it must stay hidden when the window is widened back,
   so only the desktop row shows. */
.op-m-stage { display: none; }

@media (max-width: 640px) {
  .other-projects { display: block; }     /* stage fills it; no flex centering */
  .op-viewport { display: none; }         /* the desktop row isn't used on phones */

  .op-m-stage {
    display: block;
    position: relative;
    width: 100%;
    height: 100svh;
    overflow: hidden;
    /* The empty area around the cards still scrolls the page (pan-y); the cards
       themselves block vertical scroll (set on .op-m-card) so the swipe doesn't
       mix with scrolling. Two-finger pinch-zoom works everywhere here. */
    touch-action: pan-y pinch-zoom;
  }
  .op-m-card {
    position: absolute;
    left: 50%;
    top: 50%;
    border-radius: 24px;
    overflow: hidden;
    background: #e9e6e3;
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.16);
    will-change: transform, opacity;
    -webkit-tap-highlight-color: transparent;
    cursor: pointer;
    /* No vertical page scroll on a card — only the horizontal swipe (JS) runs
       here, so the gesture stays stable. The stage around it still scrolls. */
    touch-action: pinch-zoom;
  }
  .op-m-card img,
  .op-m-card video {
    position: absolute;
    /* Bleed 1px past the card on every side: scaled rounded cards on iPhones
       otherwise show a hairline of the card's grey background around dark
       thumbnails (a Safari rounding artifact). Matches the home carousel. */
    inset: -1px;
    width: calc(100% + 2px);
    height: calc(100% + 2px);
    object-fit: cover;
    display: block;
    pointer-events: none;
    /* Round the media itself as well: Safari softens a rounded CLIP by
       blending the edge into the card's grey background (a light fringe
       around dark thumbnails). With its own radius, the media's edge
       blends into whatever is BEHIND the card instead. */
    border-radius: inherit;
  }
  .op-m-card video { z-index: 0; }
  .op-m-card .m-poster { z-index: 1; transition: opacity 0.4s ease; }   /* first-frame poster over the video */

  .op-m-caption {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    max-width: 80vw;
    text-align: center;
    line-height: 1.3;
    font-family: 'ABCDiatype', sans-serif;
    font-size: 15px;
    color: #1a1a1a;
    pointer-events: none;
    z-index: 5;
    -webkit-font-smoothing: auto;   /* fuller look, same as the desktop caption */
  }
  /* Touch screens draw this font a touch skinnier than desktop — a hair-thin
     outline restores the desktop weight (same as the home nav + captions). */
  @media (hover: none) {
    .op-m-caption { -webkit-text-stroke: 0.3px currentColor; }
  }
  .op-m-caption .m-word { display: inline-block; white-space: nowrap; }
  .op-m-caption .ch {
    display: inline-block;
    transform-origin: bottom center;
    animation: opch-rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: var(--d, 0s);
  }
}

/* ============================================
   RESPONSIVE
   ============================================ */
@media (max-width: 560px) {
  .draggable-image {
    border-radius: 6px;
  }
}

/* Tablet: make the prototype landscape (height driven by its width via an
   aspect ratio) instead of stretching tall with the screen height. */
@media (max-width: 1400px) {
  .preview-section {
    height: auto;
    aspect-ratio: 4 / 3;
  }

  /* Tablet + mobile toggle: smaller capsule (64) + pill (50). */
  .mode-toggle { height: 64px; }
  .toggle-btn { height: 50px; font-size: 18px; padding: 0 28px; }
  .toggle-pill { top: 6px; bottom: 6px; height: auto; }
}

@media (max-width: 768px) {
  .project-body {
    padding: 16px;
  }

  /* Phone toggle: smaller text + padding so long labels
     (e.g. "Version 1.2.0 / Version 1.1.0") fit without clipping. */
  .toggle-btn { font-size: 16px; padding: 0 20px; }

  /* Match the smaller body padding so the section stays full-bleed on phones. */
  .other-projects {
    margin: 0 -16px -16px;
  }

  .close-button {
    top: 24px;
    right: 24px;
    width: 64px;
    height: 64px;
  }

  .preview-section {
    height: calc(100svh - 32px - 72px);
    aspect-ratio: auto;
    border-radius: 16px;
  }

  .preview-mode {
    height: 100%;
  }

  .drag-canvas {
    height: 100%;
  }

  .project-title {
    font-size: 26px;
  }

  /* Toggle sizing for tablet + mobile is handled in the max-width:1400 block. */
}


/* ============================================
   VIDEO PLAYER — NEW
   ============================================ */

/* "Speed" indicator — a small pill pinned to the top-left of a speed-adjustable
   video box (same look as the zoom "%" indicator in the grid viewers). Shows
   the current playback speed (1x / 2x); video-cta.js updates the text and
   fades it in only while a local (speed-toggle) video is active. */
.vc-speed-indicator {
  position: absolute;
  top: 24px;
  left: 24px;
  background: rgba(0,0,0,0.2);   /* same surface opacity as the Close button */
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: rgba(255,255,255,0.7);
  font-size: 15px;
  font-family: 'ABCDiatype', sans-serif;
  /* Fuller look: default (heavier) text rendering, like the zoom indicator */
  -webkit-font-smoothing: auto;
  padding: 8px 16px;
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 100;
}

/* Cursor-following play/pause button. The button is positioned/translated by JS
   (cursor-follow); the glassy circle + press-scale live on the inner element so
   the scale never fights the JS transform. */
.vc-center-btn {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 100;
  width: 130px;
  height: 130px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: none;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  will-change: transform, opacity;
}

/* The visible glassy circle. Scales up on press (same scale + ease as the
   Close button). */
.vc-center-btn-inner {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  backdrop-filter: blur(24px) saturate(1.2);
  -webkit-backdrop-filter: blur(24px) saturate(1.2);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  /* Keep the frosted circle on its own GPU layer so Safari renders the blur
     IN SYNC with the opacity fade-in. Without this it paints the flat tint
     first and the blur pops in a beat later (on load and after every pause). */
  transform: translateZ(0);
  will-change: backdrop-filter, transform;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

.vc-center-btn.is-pressed .vc-center-btn-inner {
  transform: scale(0.92) translateZ(0);
}

/* Progress bar — sits in the band below the toggle (vertical position set by JS).
   translateY(50%) anchors it by its CENTER, so it stays centered as the height
   grows on hover instead of expanding upward. */
.vc-progress-bar {
  position: absolute;
  bottom: 0;            /* overridden by positionProgressBar() */
  left: 10.5px;         /* overridden by positionProgressBar() to match bottom spacing */
  right: 10.5px;
  height: 2px;
  transform: translateY(50%);
  border-radius: 999px;
  transition: height 0.2s ease;
  background: rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 100;
  cursor: pointer;
}

.vc-progress-fill {
  height: 100%;
  width: 0%;
  background: #FFFFFF;
  opacity: 0.5;                       /* default: 50% */
  pointer-events: none;
  border-radius: 999px;
  transition: opacity 0.2s ease;
}

@media (pointer: fine) {
  .vc-progress-bar:hover {
    height: 10px;
  }
  .vc-progress-bar:hover .vc-progress-fill {
    opacity: 1;
  }
}

.vc-progress-bar.is-active {
  height: 10px;
}

.vc-progress-bar.is-active .vc-progress-fill {
  opacity: 1;
}

.vc-scrubber {
  position: absolute;
  inset: -8px 0;
  width: 100%;
  height: calc(100% + 16px);
  opacity: 0;
  cursor: pointer;
  margin: 0;
}

@media (pointer: coarse) {
  .vc-scrubber {
    pointer-events: none;
  }

  .vc-progress-bar::before {
    content: '';
    position: absolute;
    top: -16px;
    left: 0;
    right: 0;
    height: 16px;
  }
}

/* Parent-level click overlay for video — sits above iframe, catches mouse events */
.vc-parent-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  cursor: none;
}