/**
 * Otto Björkland Portfolio Engine v3 — 3D Jukebox Conveyor Architecture
 * Base 3D conveyor viewport, card dimensions, front/back face structure,
 * 4:3 media hero, motion bypass acrylic, exact 14px mobile margins, NO HUD SCRUBBER.
 */

/* ---------------------------------------------------- */
/* 3D ISOMETRIC CONVEYOR STAGE                          */
/* ---------------------------------------------------- */
.isometric-stage-viewport,
.conveyor-viewport,
#conveyor-viewport,
#stage-viewport {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  z-index: 10;
  touch-action: none;
  cursor: grab;
}

.isometric-stage-viewport:active,
.conveyor-viewport:active,
#conveyor-viewport:active,
#stage-viewport:active {
  cursor: grabbing;
}

/* The track is a FLAT stacking context that owns the perspective. Each card is
   its own 3D context, so cards are ordered purely by z-index and can never
   slice through each other (with a shared preserve-3d space, tilted neighbours
   intersected on narrow screens where the z-spacing is tighter). */
.conveyor-world-3d,
#conveyor-track,
#conveyor-world {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  transform-style: flat;
  perspective: 1600px;
  perspective-origin: 50% 50%;
}

/* ---------------------------------------------------- */
/* HIGH-FIDELITY OPTIC GLASS SLAB (WITH 3D CARD FLIP)   */
/* ---------------------------------------------------- */
.glass-slab-card {
  position: absolute;
  inset: 0;
  border-radius: 20px;
  transform-style: preserve-3d;
  will-change: transform;
  cursor: pointer;
  color: inherit;
}

/* The 3D Flippable Shell - pointer-events: none allows direct clicks/scrolls to pass into card faces */
.card-flipper-3d {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
  border-radius: 20px;
  pointer-events: none;
}

/* Card Faces Stacking & Ghosting Elimination */
.card-face-back {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  display: none !important;
}

/* Pre-render the back while it still faces away (first half of the flight),
   so its layout, image decode and raster don't land on the flip frame. */
.glass-slab-card.is-sweeping .card-face-back {
  display: flex !important;
  visibility: visible;
  opacity: 1;
}

.glass-slab-card.is-flipped .card-face-back {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
  display: flex !important;
}

.glass-slab-card.is-flipped .card-face-front {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  display: none !important;
}

.card-face-front {
  pointer-events: auto;
}

/* Front and Back Faces - 2px Z Separation Eliminates All Seams/Z-Fighting */
.card-face-front {
  position: absolute;
  inset: 0;
  border-radius: 20px;
  transform: translateZ(2px);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;

  /* Unified frost material (tokens in arctic-tokens.css). Cards move every
     frame, so they never use a live backdrop blur. */
  background: var(--glass-fill);
  border: var(--glass-border);
  box-shadow: var(--glass-shadow), var(--glass-rim);
}

/* Physical glass edge — hidden during rotation to prevent streak */
.glass-slab-card.is-active:not(.is-sweeping):not(.is-flipped):not(.is-inspected) .card-flipper-3d::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 20px;
  pointer-events: none;
  z-index: 10;
  box-shadow:
    inset  1px  1.5px 0 rgba(255, 255, 255, 0.9),
    inset -1px -1.5px 0 rgba(150, 180, 210, 0.25);
}

/* Micro-Refraction Inner Bevel */
.card-face-front::before,
.card-face-back::before {
  content: "";
  position: absolute;
  inset: 2.5px;
  border-radius: 17.5px;
  border: 1px solid rgba(255, 255, 255, 0.6);
  pointer-events: none;
  z-index: 8;
}

/* Caustic Specular Glint (plain alpha, no blend mode: blend modes force an
   extra offscreen pass per card on mobile GPUs) */
.specular-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 9;
  border-radius: inherit;
  background: radial-gradient(
    circle 260px at var(--sheen-x, 30%) var(--sheen-y, 0%),
    rgba(255, 255, 255, 0.34) 0%,
    rgba(255, 255, 255, 0.08) 45%,
    transparent 70%
  );
}

/* Atmospheric depth: fades cards into the arctic haze with distance from focus
   (opacity driven per frame by the compositor; compositor-only, no blur). */
.card-depth-haze {
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(240, 245, 250, 0.97), rgba(232, 240, 248, 0.97));
  opacity: 0;
  pointer-events: none;
  z-index: 20;
  will-change: opacity;
}

/* Front Face Content Layout */
.slab-hud-content {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  z-index: 3;
}

/* 1. Top Clean Header */
.card-header-clean {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 6px;
}

.card-title-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}

.card-project-title {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.025em;
  color: #0b1320;
  line-height: 1.18;
}

.card-meta-column {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-top: 2px;
}

.card-type-badge {
  font-family: 'JetBrains Mono', monospace;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  color: #ff4d15;
  background: rgba(255, 77, 21, 0.08);
  border: 1px solid rgba(255, 77, 21, 0.25);
  padding: 2px 8px;
  border-radius: 999px;
  letter-spacing: 0.03em;
  display: inline-block;
  white-space: nowrap;
  flex-shrink: 0;
}

.card-focus-text {
  font-size: 11px;
  color: #64748b;
  font-weight: 500;
  line-height: 1.3;
}

/* 2. Top Header Right & Wireframe Badge */
.card-header-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  flex-shrink: 0;
}

.card-wireframe-badge {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  /* Solid frosted tint instead of live backdrop-filter: the badge moves with
     every card every frame, and a filtered surface that small buys no visible
     refraction but costs a full GPU backdrop sample per frame. */
  background: rgba(255, 255, 255, 0.86);
  border: 1.5px solid rgba(255, 255, 255, 0.95);
  box-shadow:
    0 8px 20px -4px rgba(11, 19, 32, 0.08),
    inset 0 1.5px 0 rgba(255, 255, 255, 1),
    inset 0 -1px 2px rgba(200, 220, 240, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5px;
  position: relative;
  overflow: hidden;
  flex-shrink: 0;
}

.project-wireframe-svg {
  width: 28px;
  height: 28px;
  shape-rendering: geometricPrecision;
  filter: drop-shadow(0 0 6px rgba(255, 77, 21, 0.35));
  position: relative;
  z-index: 2;
}

/* 3. Center Stage: Strict 4:3 Hero Image Window */
.card-media-hero,
.card-media-hero.ratio-4-3 {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  height: auto;
  border-radius: 14px;
  overflow: hidden;
  background: #e2e8f0;
  border: 1px solid rgba(255, 255, 255, 0.88);
  box-shadow: 0 10px 28px rgba(11, 19, 32, 0.1);
  margin: 6px 0 10px 0;
  flex-shrink: 0;
}

.card-media-hero img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.94;
  filter: contrast(102%) saturate(104%);
  transition: transform 0.5s ease, opacity 0.3s ease;
  pointer-events: none;
  -webkit-user-drag: none;
  user-drag: none;
}

.glass-slab-card:hover .card-media-hero img {
  transform: scale(1.03);
  opacity: 1;
}

/* Optical frost veil overlay on image */
.media-frost-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.18) 0%,
    rgba(215, 235, 255, 0.05) 45%,
    rgba(255, 255, 255, 0.12) 100%
  );
  border-radius: 14px;
}

/* 4. Bottom Details & Left-Aligned Cues */
.hud-bottom-details {
  border-top: 1px solid rgba(226, 232, 240, 0.65);
  padding-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  text-align: left;
  margin-top: auto;
}

.hud-desc-text {
  font-size: 11.5px;
  line-height: 1.45;
  color: #475569;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hud-bottom-meta {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  margin-top: 2px;
}

.card-date-stamp {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  font-weight: 600;
  color: #475569;
  background: rgba(255, 255, 255, 0.45);
  padding: 3px 9px;
  border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, 0.75);
  box-shadow: 0 1px 2px rgba(11, 19, 32, 0.04);
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.hud-inspect-cue {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  font-weight: 700;
  color: #ff4d15;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  transition: transform 0.2s ease;
}

.glass-slab-card:hover .hud-inspect-cue {
  transform: translateX(2px);
}

/* ---------------------------------------------------- */
/* CARD EXPANSION DURING INSPECTION                     */
/* ---------------------------------------------------- */
/* The back face is always laid out at its final inspected size, centred on the
   card, and grows during the flight by a uniform GPU scale (compositor.js).
   Animating width/height instead re-rasterised the whole card every frame.
   Once the flight lands, the card box itself snaps to the same size (no
   visual change) so hit-testing and measurements match the panel. */
#conveyor-track {
  --inspect-w: min(1080px, 92vw);
  /* 48px header + 24px gap above and below; the panel is shifted down by half
     the header (compositor endY) so both gaps are equal. */
  --inspect-h: min(840px, calc(100vh - 96px));
}

.glass-slab-card.is-inspected {
  z-index: 3500 !important;
}

.glass-slab-card.is-expanded {
  /* Drop the pinned raster scale from the flight so text re-rasterises crisp. */
  will-change: auto;
  width: var(--inspect-w);
  height: var(--inspect-h);
  margin-left: calc((var(--card-w) - var(--inspect-w)) / 2);
  margin-top: calc((var(--card-h) - var(--inspect-h)) / 2);
}

.inspect-probe {
  position: absolute;
  width: var(--inspect-w);
  height: var(--inspect-h);
  visibility: hidden;
  pointer-events: none;
}

/* ---------------------------------------------------- */
/* BACK FACE: DEEP PROJECT SPECIFICATIONS & MEDIA       */
/* ---------------------------------------------------- */
.card-face-back {
  transform: rotateY(180deg) translateZ(2px);
  padding: 26px 34px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--inspect-w);
  height: var(--inspect-h);
  margin-left: calc(var(--inspect-w) / -2);
  margin-top: calc(var(--inspect-h) / -2);
  border-radius: 20px;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;

  /* True Optical Translucent Frosted Glass - Unified Recipe (identical to .unified-material) */
  /* Unified frost material; the live backdrop blur (desktop only) is applied
     once the card has settled open, see body.card-settled below. */
  background: var(--glass-fill);
  border: var(--glass-border);
  box-shadow: var(--glass-shadow), var(--glass-rim);
  contain: paint;
  isolation: isolate;
  -webkit-font-smoothing: antialiased;
}

body.card-settled .glass-slab-card.is-inspected .card-face-back {
  backdrop-filter: var(--glass-backdrop);
  -webkit-backdrop-filter: var(--glass-backdrop);
}

.back-header {
  position: relative;
  z-index: 3;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  border-bottom: 1px solid rgba(226, 232, 240, 0.7);
  padding-bottom: 14px;
  gap: 16px;
}

.back-title-group {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
}

.back-meta-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.back-title {
  font-size: 24px;
  font-weight: 800;
  color: #0b1320;
  letter-spacing: -0.025em;
  line-height: 1.2;
}

.back-category {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  color: #ff4d15;
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.back-close-btn {
  background: rgba(255, 255, 255, 0.72);
    border: 1.5px solid rgba(255, 255, 255, 0.95);
  box-shadow:
    0 6px 16px -2px rgba(11, 19, 32, 0.08),
    inset 0 1.5px 0 rgba(255, 255, 255, 1),
    inset 0 -1px 2px rgba(200, 220, 240, 0.25);
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #334155;
  transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
}

.back-close-btn:hover {
  background: rgba(255, 255, 255, 0.92);
  color: #ff4d15;
  border-color: rgba(255, 77, 21, 0.55);
  transform: scale(1.08);
  box-shadow:
    0 8px 24px -2px rgba(255, 77, 21, 0.32),
    0 0 16px rgba(255, 77, 21, 0.25),
    inset 0 1.5px 0 rgba(255, 255, 255, 1),
    inset 0 0 10px rgba(255, 77, 21, 0.15);
}

.back-body {
  position: relative;
  z-index: 3;
  margin: 14px 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex-grow: 1;
  overflow-y: auto;
  padding-top: 10px;
  padding-bottom: 10px;
  padding-right: 22px;
  overscroll-behavior: contain;

  /* Soft gradient fade masks for top & bottom edges so scrolling feels natural */
  mask-image: linear-gradient(
    to bottom,
    transparent 0px,
    black 16px,
    black calc(100% - 16px),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0px,
    black 16px,
    black calc(100% - 16px),
    transparent 100%
  );
}

.back-body > * {
  flex-shrink: 0;
}

.back-desc,
.back-text-block {
  font-size: 15.5px;
  line-height: 1.68;
  color: #334155;
}

.back-text-block p {
  margin: 0 0 12px 0;
  font-size: 15.5px;
  line-height: 1.68;
  color: #334155;
}

.back-text-block p:last-child {
  margin-bottom: 0;
}

.module-heading {
  font-size: 16.5px;
  font-weight: 700;
  color: #0b1320;
  margin: 6px 0 8px 0;
}

/* Standalone High-Res Project Images */
.back-media-single {
  width: 100%;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 10px 25px rgba(11, 19, 32, 0.08);
  background: #f8fafc;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 4px 0;
}

.back-media-single img,
.back-media-single video.back-clip {
  width: 100%;
  height: auto;
  max-height: 520px;
  object-fit: contain;
  display: block;
}

/* Transparent art on a solid backdrop (projects.json "bg"): padded and
   capped lower, so a wide logo reads as a banner, not a wall. */
.back-media-single.has-bg img {
  box-sizing: border-box;
  padding: clamp(18px, 4%, 40px) clamp(20px, 6%, 64px);
  max-height: 240px;
}

/* Responsive Video Player in Back Face */
.back-video-wrap {
  width: 100%;
  border-radius: 14px;
  overflow: hidden;
  margin: 10px 0;
  background: #0b1320;
  box-shadow: 0 10px 24px rgba(11, 19, 32, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.85);
  position: relative;
}

.back-video {
  width: 100%;
  display: block;
  border-radius: 14px;
}

.back-video-iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  display: block;
}

/* Smart Video Player: Interactive Audio Badge */
.video-audio-badge {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(11, 19, 32, 0.76);

  color: #ffffff;
  padding: 6px 14px;
  border-radius: 999px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid rgba(255, 255, 255, 0.35);
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  transition: opacity 0.35s ease, transform 0.35s ease, background 0.2s ease, border-color 0.2s ease;
  pointer-events: auto;
}

.video-audio-badge:hover {
  background: rgba(255, 77, 21, 0.9);
  border-color: rgba(255, 255, 255, 0.65);
  transform: scale(1.04);
}

.video-audio-badge.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px) scale(0.95);
}

/* 3D Model Viewer Container */
.back-model-wrap {
  width: 100%;
  margin: 8px 0 16px 0;
  display: flex;
  justify-content: center;
  position: relative;
}

.back-model-viewer {
  width: 100%;
  height: 420px;
  border-radius: 14px;
  background: transparent !important;
  background-color: transparent !important;
  --poster-color: transparent !important;
  border: none !important;
  box-shadow: none !important;
  /* No CSS filter here: once interaction repaints the WebGL canvas, a
     drop-shadow outlined the canvas rectangle as a dark border. The model's
     own ground shadow (shadow-intensity) provides the depth. */
  overflow: visible;
  outline: none;
  position: relative;
}

/* Frosted Glass Loading Indicator for 3D-Model */
.model-progress-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(280px, 82%);
  padding: 18px 24px;
  background: rgba(245, 248, 252, 0.85);
  backdrop-filter: var(--glass-backdrop);
  -webkit-backdrop-filter: var(--glass-backdrop);
  border: 1px solid rgba(255, 255, 255, 0.95);
  border-radius: 16px;
  box-shadow: 0 12px 36px rgba(11, 19, 32, 0.15), 0 2px 8px rgba(0, 0, 0, 0.04);
  z-index: 15;
  transition: opacity 0.4s ease, visibility 0.4s ease;
  pointer-events: none;
}

.model-progress-bar.hide {
  opacity: 0;
  visibility: hidden;
}

/* Pre-rendered picture of the model, in the 3D viewer's place and size. */
.model-still {
  display: block;
  width: 100%;
  height: 380px;
  object-fit: contain;
}

/* Shown on the poster when 3D can't run here (no WebGL, viewer failed). */
.model-fallback-note {
  position: absolute;
  bottom: 12px;
  right: 12px;
  max-width: calc(100% - 24px);
  padding: 6px 14px;
  border-radius: 20px;
  background: rgba(13, 20, 32, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  pointer-events: none;
}

.model-loading-text {
  color: #0b1320;
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-align: center;
}

.model-progress-track {
  width: 100%;
  height: 5px;
  background: rgba(11, 19, 32, 0.08);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}

.model-update-bar {
  background: linear-gradient(90deg, #ff4d15 0%, #ff8c28 100%);
  width: 0%;
  height: 100%;
  border-radius: 999px;
  transition: width 0.2s ease-out;
}

/* Crisp Progressive Image */
.progressive-load-img {
  opacity: 0;
  transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.progressive-load-img.is-loaded {
  opacity: 1;
}

/* LQIP blur-up: 20px placeholder, blurred + scaled to hide soft edges, sits under the image */
.lqip-host {
  position: relative;
  overflow: hidden;
}

.lqip-host::before {
  content: '';
  position: absolute;
  inset: 0;
  /* The 20px placeholder upscaled is already soft; a blur filter here was
     costly to rasterise at 3x on phones every time a card first appeared. */
  background: var(--lqip, rgba(0, 0, 0, 0.06)) center / cover no-repeat;
  pointer-events: none;
}

.lqip-host:has(> .progressive-load-img.is-loaded)::before {
  display: none;
}

.lqip-host > .progressive-load-img {
  position: relative;
}

/* Gallery & Specifications */
.back-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  margin: 8px 0;
}

.back-gallery-item {
  width: 100%;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 8px 20px rgba(11, 19, 32, 0.06);
  background: #f1f5f9;
  aspect-ratio: 16 / 10;
}

.back-gallery-item img,
.back-gallery-item video.back-clip {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Animation clips (former GIFs): positioned so they paint above the LQIP
   placeholder (.lqip-host::before), which stays as the backdrop until the
   poster arrives. No controls: they behave like the GIFs they replace. */
video.back-clip {
  position: relative;
  pointer-events: none;
}

.back-specs-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.88);
  border-radius: 14px;
  padding: 18px 22px;
}

.bullet-item {
  font-size: 15px;
  line-height: 1.65;
  color: #334155;
}

.bullet-item strong {
  color: #0b1320;
}

.bullet-dot {
  color: #ff4d15;
  margin-right: 6px;
  font-weight: 800;
}

.back-tags-row {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  border-top: 1px solid rgba(226, 232, 240, 0.7);
  padding-top: 14px;
  margin-top: 0;
}

.back-tag {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  padding: 4px 11px;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.92);
  border-radius: 999px;
  color: #475569;
  font-weight: 600;
}

/* Custom Frosted Glass Scrollbars - Material Unification */
.card-face-back::-webkit-scrollbar,
.back-body::-webkit-scrollbar,
.modal-body-scroll::-webkit-scrollbar {
  width: 9px;
}

.card-face-back::-webkit-scrollbar-track,
.back-body::-webkit-scrollbar-track,
.modal-body-scroll::-webkit-scrollbar-track {
  background: rgba(226, 232, 240, 0.35);
  border-radius: 999px;
  margin: 8px 0;
}

.card-face-back::-webkit-scrollbar-thumb,
.back-body::-webkit-scrollbar-thumb,
.modal-body-scroll::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.85);
  border: 1.5px solid rgba(255, 255, 255, 0.95);
  border-radius: 999px;
  box-shadow:
    0 2px 6px rgba(11, 19, 32, 0.12),
    inset 0 1px 1px #ffffff,
    inset 0 -1px 2px rgba(200, 220, 240, 0.3);
  transition: background 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.card-face-back::-webkit-scrollbar-thumb:hover,
.back-body::-webkit-scrollbar-thumb:hover,
.modal-body-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.98);
  border-color: #ffffff;
  box-shadow:
    0 3px 10px rgba(11, 19, 32, 0.18),
    inset 0 1.5px 2px #ffffff;
}

/* ---------------------------------------------------- */
/* INSPECTION STACKING CONTEXT & BACKDROP               */
/* ---------------------------------------------------- */
body.card-inspected .isometric-stage-viewport,
body.card-inspected #conveyor-viewport {
  z-index: 2500;
}

body.card-inspected .conveyor-world-3d,
body.card-inspected #conveyor-track {
  z-index: 2600;
  pointer-events: none;
}

body.card-inspected .glass-slab-card.is-inspected {
  pointer-events: auto !important;
}

body.card-inspected .glass-slab-card:not(.is-inspected) {
  pointer-events: none !important;
}

body.card-inspected .glass-slab-card.is-inspected {
  opacity: 1 !important;
  filter: none !important;
}

.inspection-backdrop {
  position: fixed;
  inset: 0;
  /* Click-catcher only: the scene behind an open card stays exactly as it was. */
  background: transparent;
  z-index: 2500;
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
  transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

body.card-inspected .inspection-backdrop {
  opacity: 1;
  pointer-events: auto;
}

body.modal-open .conveyor-world-3d,
body.modal-open #conveyor-track {
  pointer-events: none !important;
}

/* ---------------------------------------------------- */
/* MOBILE RESPONSIVENESS & 14px GEOMETRY (<= 768px)     */
/* ---------------------------------------------------- */
@media (max-width: 768px) {
  /* Inspected Back Face - Mathematically Balanced Equal Margins (Top & Bottom = 14px) */
  #conveyor-track {
    --inspect-w: calc(100vw - (var(--panel-margin-mobile, 14px) * 2));
    --inspect-h: calc(100vh - var(--header-h-mobile, 48px) - (var(--panel-margin-mobile, 14px) * 2));
    --inspect-h: calc(100dvh - var(--header-h-mobile, 48px) - (var(--panel-margin-mobile, 14px) * 2));
  }

  .glass-slab-card.is-expanded {
    border-radius: 18px;
  }

  .card-media-hero,
  .card-media-hero.ratio-4-3 {
    width: 100%;
    aspect-ratio: 4 / 3;
    height: auto;
    margin: 4px 0 8px 0;
  }

  .card-face-back {
    padding: 16px 18px;
    border-radius: 18px;
  }

  .back-header {
    padding-bottom: 12px;
    gap: 10px;
  }

  .back-title {
    font-size: 17px;
    line-height: 1.22;
    margin-bottom: 2px;
  }

  .back-close-btn {
    width: 36px;
    height: 36px;
  }

  .back-meta-row {
    flex-wrap: nowrap;
    gap: 6px;
    margin-bottom: 4px;
  }

  .card-type-badge {
    font-size: 8.5px;
    padding: 2px 6px;
    white-space: nowrap;
  }

  .card-date-stamp {
    font-size: 9px;
    padding: 2.5px 7px;
    border-radius: 5px;
    white-space: nowrap;
  }

  .back-body {
    margin: 10px 0;
    padding-top: 8px;
    padding-bottom: 8px;
    padding-right: 8px;
    gap: 12px;
  }

  .back-desc {
    font-size: 12.5px;
    line-height: 1.55;
  }

  .back-tags-row {
    gap: 5px;
    padding-top: 12px;
    margin-top: 0 !important;
  }

  .back-tag {
    font-size: 9px;
    padding: 2px 8px;
  }
}

/* Responsive Hero Stage Fade */
body.scrolled .hero-stage,
body.card-inspected .hero-stage,
body.modal-open .hero-stage {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
  transition: opacity 0.08s ease-out !important;
}

/* ---------------------------------------------------- */
/* PANEL TYPE SCALE & HIERARCHY (front + back)          */
/* One scale (~1.25 ratio). Order of reading:           */
/*   title -> image -> section labels -> body -> meta   */
/* Weight and colour separate levels, not size alone.   */
/* ---------------------------------------------------- */
.card-face-back {
  --t-title: 30px;
  --t-sub: 14px;
  --t-label: 11.5px;
  --t-body: 16px;
  --t-meta: 10.5px;
  --t-tag: 12.5px;
}

/* Header: title block takes the free width; the close button is pinned to
   the panel's top-right corner so no badge or date can ever push it out. */
.back-header {
  padding-right: 56px;
}

.back-title-group {
  min-width: 0;
}

.back-meta-row {
  flex-wrap: wrap;
  row-gap: 6px;
}

.back-meta-row .card-type-badge {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--t-meta);
}

.back-meta-row .card-date-stamp {
  font-size: var(--t-meta);
}

.back-close-btn {
  position: absolute;
  top: 0;
  right: 0;
}

.card-face-back .back-title {
  font-size: var(--t-title);
  line-height: 1.12;
  letter-spacing: -0.03em;
  margin-top: 2px;
}

.back-title-group .card-focus-text {
  font-size: var(--t-sub);
  color: #64748b;
}

/* Section headings become quiet labels: small, spaced, uppercase. */
.back-body .module-heading {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ff4d15;
  margin: 10px 0 2px 0;
}

.back-body .back-text-block,
.back-body .back-text-block p,
.back-body .bullet-item,
.back-body .back-desc,
.back-body .back-lead-text {
  font-size: var(--t-body);
  line-height: 1.62;
  color: #475569;
  max-width: 68ch;
}

.back-body .bullet-item {
  margin-bottom: 6px;
}

.back-body .bullet-item strong,
.back-body .back-text-block strong {
  color: #0b1320;
  font-weight: 650;
}

.back-tag {
  font-size: var(--t-tag);
  padding: 5px 12px;
}

/* Front face: same hierarchy at card scale. */
.card-project-title {
  font-size: 21px;
  line-height: 1.15;
}

@media (max-width: 768px) {
  .card-face-back {
    --t-title: 23px;
    --t-sub: 12.5px;
    --t-label: 10.5px;
    --t-body: 14.5px;
    --t-meta: 9.5px;
    --t-tag: 11.5px;
  }

  .back-header {
    padding-right: 46px;
  }

  .back-body .back-text-block,
  .back-body .back-text-block p,
  .back-body .bullet-item {
    line-height: 1.55;
  }

  .card-project-title {
    font-size: 19px;
  }
}

/* model-viewer draws a dark focus ring once the model has been touched;
   the frame should stay borderless like the images around it. */
model-viewer,
model-viewer:focus,
model-viewer:focus-visible,
.back-model-wrap {
  outline: none !important;
  border: none !important;
}

/* Pre-blurred twin of the card (js/fx/blur-impostor.js). Depth of field is a
   cross-fade between the sharp card and this, instead of a live filter. It
   sits a hair in front of the face (the card is a 3D context) and spills
   past the edges like a real defocused silhouette. */
.card-blur-impostor {
  position: absolute;
  left: calc(-1 * var(--imp-pad, 64px));
  top: calc(-1 * var(--imp-pad, 64px));
  width: calc(100% + 2 * var(--imp-pad, 64px));
  height: calc(100% + 2 * var(--imp-pad, 64px));
  transform: translateZ(3px);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  pointer-events: none;
  opacity: 0;
  will-change: opacity;
  display: none; /* in layout only when the conveyor/showcase is on screen */
}

/* Display is driven per twin by the compositor (only the needed ones). */
body:not(.stage-intro):not(.stage-conveyor) .card-blur-impostor {
  display: none !important;
}

/* Retired: the misty look now lives inside the heavy twin (an edge-less
   silhouette); a separate haze rectangle showed as a box on phones. */
.card-depth-haze {
  display: none;
}

/* Front header: the icon badge always sits in the card's top-right corner.
   The title group takes only the width left of it. */
.card-title-group {
  min-width: 0;
}

/* Pills are always ONE row. Category names in data/projects.json are kept
   short enough to fit; anything longer ends in "..." instead of wrapping. */
.card-header-clean .card-type-badge,
.back-meta-row .card-type-badge,
.back-tag {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: top;
}

.back-meta-row {
  min-width: 0;
}

/* Back face: ONE scroller for the whole panel. Header pills, title, subtitle,
   content and keyword pills all scroll together (more room for content on
   small screens); only the close button stays pinned in the corner. The
   scroller spans the panel's full height so its soft fade edges sit in the
   panel padding, not over the first pill. */
.card-face-back {
  --back-pad-y: 26px;
  --back-pad-x: 34px;
}

.card-face-back > .back-body {
  margin: calc(-1 * var(--back-pad-y)) 0;
  padding-top: var(--back-pad-y);
  padding-bottom: var(--back-pad-y);
  /* The scroller also reaches out to 6px from the panel's right edge, so its
     scrollbar runs in the margin to the RIGHT of the close button instead of
     under it. The extra padding keeps the content exactly where it was. */
  margin-right: calc(6px - var(--back-pad-x));
  padding-right: calc(var(--back-pad-x) - 6px + var(--back-body-pr, 22px));
}

.card-face-back > .back-body::-webkit-scrollbar-track {
  margin: 22px 0; /* clear the panel's rounded corners */
}

.card-face-back > .back-close-btn {
  position: absolute;
  top: var(--back-pad-y);
  right: var(--back-pad-x);
  z-index: 6;
}

.back-body > .back-header {
  padding-right: 36px;
}

@media (max-width: 768px) {
  .card-face-back {
    --back-pad-y: 16px;
    --back-pad-x: 18px;
    --back-body-pr: 8px;
  }

  .back-body > .back-header {
    padding-right: 40px;
  }
}

/* Rubber-band overscroll (js/fx/rubber-band.js). Touch devices get Chrome's
   native stretch from overscroll-behavior: contain; wheel input gets this. */
.modal-body-scroll {
  overscroll-behavior: contain;
}

/* The scroller moves as one layer (not every child: toggling a transform on
   dozens of children re-layered them all and stuttered). */
.rb-active,
.rb-release {
  transform: translate3d(0, var(--rb, 0px), 0);
}

.rb-release {
  transition: transform 0.55s cubic-bezier(0.22, 1.25, 0.36, 1);
}

/* Text links inside panels use the site's ember orange, not browser blue.
   (Class-less anchors only: styled link-buttons keep their own colours.) */
.card-face-back a:not([class]),
.modal-card-glass a:not([class]) {
  color: var(--ember-primary, #ff4d15);
  text-decoration: underline;
  text-decoration-color: rgba(255, 77, 21, 0.4);
  text-underline-offset: 2px;
  transition: text-decoration-color 0.2s ease;
}

.card-face-back a:not([class]):hover,
.modal-card-glass a:not([class]):hover {
  text-decoration-color: currentColor;
}

/* ---------------------------------------------------- */
/* INSPECT HINT (js/fx/inspect-hint.js)                 */
/* A pointing hand (touch) or cursor (mouse), in the    */
/* hero cue's style, taps the focused panel twice.      */
/* ---------------------------------------------------- */
.inspect-hint {
  position: absolute;
  left: 50%;
  top: 46%;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 30;
  /* In front of the face, and part of the panel, so it moves with it. */
  transform: translateZ(8px);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transition: opacity 0.25s ease;
}

/* The life-cycle fade runs on an inner layer: an animation would override the
   outer layer's "swiped out of focus" fade below. */
.inspect-hint-body {
  position: absolute;
  animation: inspect-hint-life 3.6s linear forwards;
}

/* Swiped out of focus: fade out while travelling away with the panel. */
.glass-slab-card:not(.is-active) .inspect-hint {
  opacity: 0;
}

.inspect-hint-icon {
  position: absolute;
  width: 46px;
  height: 46px;
  color: var(--ember-primary, #ff4d15);
  /* Orange core, white sticker edge (in the SVG), then a soft white glow and
     a dark lift shadow: it separates from dark, bright and orange photos. */
  filter:
    drop-shadow(0 0 6px rgba(255, 255, 255, 0.85))
    drop-shadow(0 3px 7px rgba(11, 19, 32, 0.35));
  animation: inspect-hint-move 3.6s cubic-bezier(0.45, 0, 0.25, 1) forwards;
}

.inspect-hint-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* The tip of each icon sits on the tap point: the hand's fingertip is at
   (8, 2) of its 24-unit box, the cursor's tip at (4, 4). */
.inspect-hint.is-touch .inspect-hint-icon { left: -15px; top: -4px; transform-origin: 15px 4px; }
.inspect-hint.is-mouse .inspect-hint-icon { left: -8px; top: -8px; transform-origin: 8px 8px; width: 38px; height: 38px; }

.inspect-hint-ripple {
  position: absolute;
  left: -28px;
  top: -28px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 2.5px solid rgba(255, 77, 21, 0.9);
  background: rgba(255, 77, 21, 0.14);
  /* White rim inside and out, so the ring shows on any photo too. */
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85), inset 0 0 0 2px rgba(255, 255, 255, 0.6),
    0 2px 10px rgba(11, 19, 32, 0.25);
  opacity: 0;
  transform: scale(0.2);
  animation: inspect-hint-ripple 3.6s ease-out forwards;
}

@keyframes inspect-hint-life {
  0%   { opacity: 0; }
  10%  { opacity: 1; }
  86%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Glide in from the lower right, tap, tap, drift off. */
@keyframes inspect-hint-move {
  0%   { transform: translate(46px, 52px) scale(1); }
  24%  { transform: translate(0, 0) scale(1); }
  30%  { transform: translate(0, 0) scale(0.82); }
  38%  { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(0, 0) scale(1); }
  56%  { transform: translate(0, 0) scale(0.82); }
  64%  { transform: translate(0, 0) scale(1); }
  86%  { transform: translate(8px, 10px) scale(1); }
  100% { transform: translate(14px, 18px) scale(1); }
}

@keyframes inspect-hint-ripple {
  0%, 29%  { opacity: 0; transform: scale(0.2); }
  31%      { opacity: 1; transform: scale(0.35); }
  46%      { opacity: 0; transform: scale(1.25); }
  55%      { opacity: 0; transform: scale(0.2); }
  57%      { opacity: 1; transform: scale(0.35); }
  72%      { opacity: 0; transform: scale(1.25); }
  100%     { opacity: 0; transform: scale(1.25); }
}

@media (prefers-reduced-motion: reduce) {
  .inspect-hint-icon { animation: none; }
  .inspect-hint-ripple { animation: none; }
}

/* Hidden while the panel is flipped open. */
.glass-slab-card.is-flipped .inspect-hint,
.glass-slab-card.is-sweeping .inspect-hint {
  display: none;
}
