* {
  box-sizing: border-box;
}

:root {
  --paper: #f0eee6;
  --ink: #0f172a;
  --muted: rgba(28, 25, 23, 0.62);
  --faint: rgba(28, 25, 23, 0.42);
  --line: rgba(15, 23, 42, 0.12);
  --surface: #f8f7f2;
  --surface-muted: #e7e4db;
  --button: #141413;
  --button-text: #f9f7f2;
  --electric: #4338ca;
  --electric-soft: #e0e7ff;
  --glass: rgba(248, 247, 242, 0.76);
  --glass-border: rgba(255, 255, 255, 0.58);
  --glass-shadow: rgba(15, 23, 42, 0.16);
  --font-sans: 'Manrope Variable', sans-serif;
  --font-serif: 'Playfair Display Variable', Georgia, serif;
  --ease-spring: cubic-bezier(0.32, 0.72, 0, 1);
  /* Pre-script fallback only. app.js measures the real viewport and overwrites this on
     the root element, because no CSS unit tracks a browser that leaves fullscreen with
     its address bar collapsed: svh is then too short and the next card peeks out. */
  --feed-viewport-height: 100vh;
}

@supports (height: 100svh) {
  :root {
    --feed-viewport-height: 100svh;
  }
}

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
}

body {
  overflow: hidden;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

body.theme-dark {
  --paper: #0a0a0a;
  --ink: #e7e5df;
  --muted: rgba(231, 229, 223, 0.66);
  --faint: rgba(231, 229, 223, 0.44);
  --line: rgba(255, 255, 255, 0.14);
  --surface: #151515;
  --surface-muted: #242424;
  --button: #e7e5df;
  --button-text: #0a0a0a;
  --electric-soft: #252248;
  --glass: rgba(18, 18, 18, 0.78);
  --glass-border: rgba(255, 255, 255, 0.12);
  --glass-shadow: rgba(0, 0, 0, 0.54);
}

button,
a {
  font: inherit;
  -webkit-tap-highlight-color: transparent;
}

button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--electric);
  outline-offset: 3px;
}

button svg,
a svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.feed-brand {
  display: none;
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  left: max(14px, env(safe-area-inset-left));
  z-index: 40;
  color: #fff;
  font-size: 1.4rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  text-decoration: none;
  text-transform: uppercase;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.72);
}

.feed-mobile-controls {
  position: fixed;
  top: max(8px, env(safe-area-inset-top));
  right: max(8px, env(safe-area-inset-right));
  left: max(8px, env(safe-area-inset-left));
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
}

.feed-mobile-controls button {
  --pixel-shadow: rgba(0, 0, 0, 0.82);
  display: flex;
  width: 48px;
  height: 48px;
  align-items: center;
  justify-content: center;
  appearance: none;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
  filter: drop-shadow(2px 2px 0 var(--pixel-shadow));
  pointer-events: auto;
  transition:
    filter 80ms ease-out,
    transform 80ms ease-out;
}

.feed-mobile-controls button:active {
  filter: drop-shadow(0 0 0 var(--pixel-shadow));
  transform: translate(2px, 2px);
}

.feed-mobile-controls .fullscreen-exit-icon,
.feed-mobile-controls button[data-fullscreen='true'] .fullscreen-enter-icon {
  display: none;
}

.feed-mobile-controls button[data-fullscreen='true'] .fullscreen-exit-icon {
  display: block;
}

/* Fullscreen is for the content, so the chrome gives its inset back and rides at the very
   top edge, level with the camera cutout. The state comes from a body class, not from
   `:fullscreen` — one unsupported member of such a selector list drops the whole rule.
   `top: 0`, not the safe-area inset: fullscreen already hid the status bar, yet Chrome
   keeps reporting the cutout height under `viewport-fit=cover`, so honouring it here
   pushed the row a camera's worth of pixels down onto the content for nothing. The punch
   hole is centred and these buttons hug the left and right edges, so they sit beside it.
   The horizontal insets stay — in landscape they are the ones that matter. */
body.is-fullscreen .feed-mobile-controls {
  top: 0;
  right: env(safe-area-inset-right);
  left: env(safe-area-inset-left);
}

body.is-fullscreen .feed-mobile-controls button {
  width: 40px;
  height: 40px;
}

.feed-sidebar {
  display: none;
}

.feed-shell,
.feed {
  width: 100%;
  height: var(--feed-viewport-height);
}

.feed-shell {
  position: relative;
}

.feed {
  position: relative;
  z-index: 1;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: none;
  overflow-anchor: none;
  scroll-behavior: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  touch-action: pan-y;
}

.feed::-webkit-scrollbar {
  display: none;
}

.feed.is-jumping,
.feed.is-viewport-realigning {
  scroll-behavior: auto;
  scroll-snap-type: none;
}

.feed-status {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  color: var(--muted);
  text-align: center;
}

.feed-status[hidden] {
  display: none;
}

.feed-status b {
  color: var(--ink);
  font-size: 1rem;
}

.feed-status span {
  max-width: 32ch;
  font-size: 0.86rem;
  line-height: 1.5;
}

.feed-spinner {
  width: 28px;
  height: 28px;
  border: 2px solid var(--line);
  border-top-color: var(--electric);
  border-radius: 50%;
  animation: feed-spin 0.8s linear infinite;
}

@keyframes feed-spin {
  to {
    transform: rotate(360deg);
  }
}

.feed-card {
  position: relative;
  width: 100%;
  height: var(--feed-viewport-height);
  overflow: hidden;
  scroll-snap-align: start;
  /* One gesture moves one card. With `normal` a single fling flew past the whole feed
     and skipped everything in between — the horizontal deck has always used `always`. */
  scroll-snap-stop: always;
  background: #111;
  content-visibility: auto;
  container-type: size;
  isolation: isolate;
}

.preview-deck,
.preview-track,
.preview-slide {
  width: 100%;
  height: 100%;
}

.preview-deck {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: none;
  scroll-behavior: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  touch-action: pan-x pan-y;
  background: var(--surface);
}

.feed-card:not(.is-pageable) .preview-deck {
  overflow-x: hidden;
  overscroll-behavior-x: none;
  scroll-snap-type: none;
  touch-action: pan-y;
}

.preview-deck::-webkit-scrollbar {
  display: none;
}

.preview-track {
  display: flex;
  transform: none;
  transition: none;
}

.preview-slide {
  position: relative;
  flex: 0 0 100%;
  min-width: 0;
  overflow: hidden;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

.preview-progress {
  position: absolute;
  top: auto;
  bottom: max(8px, calc(env(safe-area-inset-bottom) + 4px));
  left: 58px;
  right: 58px;
  z-index: 20;
  display: flex;
  gap: 5px;
  height: 3px;
  pointer-events: none;
}

.preview-progress i {
  flex: 1;
  overflow: hidden;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.32);
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.18);
}

.preview-progress em {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: left;
  background: #fff;
  transition: transform 0.28s var(--ease-spring);
}

/*
 * Tap-to-page zones. On mobile the deck scrolls natively, so these must not be hit
 * targets — an overlay button on top of the deck swallows the swipe. They stay in the
 * DOM with pointer-events off so assistive tech keeps a way to page, and app.js tells a
 * tap from a drag on the deck itself using the same band and the same 36/64 split.
 */
.preview-tap {
  position: absolute;
  top: 32px;
  bottom: 132px;
  z-index: 5;
  display: block;
  appearance: none;
  border: 0;
  background: transparent;
  pointer-events: none;
}

.preview-tap--previous {
  left: 0;
  width: 36%;
}

.preview-tap--next {
  right: 0;
  width: 64%;
}

.preview-frame {
  display: grid;
  place-items: center;
  background: var(--preview-frame-bg, #111);
}

.preview-frame-image {
  display: block;
  width: 100%;
  height: 100%;
  /* A frame is evidence of the package, not poster art: preserve the whole captured
     viewport at every card aspect. The matching surface paints any intentional bands
     and absorbs fractional-pixel rounding without buying a clean edge by cropping. */
  background: var(--preview-frame-bg, #111);
  object-fit: contain;
  object-position: center;
}

.preview-cover {
  color: #fff;
  background: #171717;
}

.preview-cover-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.preview-cover-animation {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.preview-terminal {
  overflow: hidden;
  background: #000;
  font-family: Consolas, 'Courier New', monospace;
}

.preview-terminal::after {
  position: absolute;
  z-index: 2;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(255, 255, 255, 0) 0,
    rgba(255, 255, 255, 0) 2px,
    rgba(255, 255, 255, 0.024) 3px,
    rgba(255, 255, 255, 0.01) 4px
  );
  content: '';
}

.preview-terminal-lines {
  position: absolute;
  top: 6cqh;
  right: 0;
  left: 0;
  height: 44cqh;
  overflow: hidden;
}

.preview-terminal-scroll {
  position: absolute;
  top: -61.2cqh;
  right: 0;
  left: 0;
  animation: preview-terminal-drop var(--terminal-duration, 14.4s) steps(12, end) infinite;
  will-change: transform;
}

.preview-terminal-sheet {
  height: 61.2cqh;
}

.preview-terminal-row {
  display: flex;
  align-items: center;
  height: 5.1cqh;
  padding-left: 3.67cqw;
  color: #00ff66;
  font-size: clamp(8px, 2.5cqw, 15px);
  line-height: 1;
  letter-spacing: 0.05em;
  opacity: 0.82;
  text-shadow: 0 0 8px rgba(0, 255, 102, 0.18);
  text-transform: uppercase;
  white-space: nowrap;
}

@keyframes preview-terminal-drop {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(61.2cqh);
  }
}

@media (prefers-reduced-motion: reduce) {
  .preview-terminal-scroll {
    animation: none;
    transform: translateY(61.2cqh);
  }
}

.preview-cover-collage {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 1px;
  padding: 8cqw 7cqw 56cqw;
  background: #e7e4db;
}

.preview-cover-collage img {
  width: 100%;
  height: 100%;
  min-height: 0;
  border-radius: 4px;
  object-fit: cover;
  box-shadow: 0 12px 24px -16px rgba(15, 23, 42, 0.58);
  transform: rotate(-1.5deg);
}

.preview-cover-collage img:nth-child(even) {
  transform: rotate(1.5deg);
}

.preview-cover-collage img:nth-child(3) {
  grid-column: 1 / -1;
  width: 58%;
  justify-self: center;
}

.preview-cover-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.03) 18%, rgba(0, 0, 0, 0.84) 82%);
}

.preview-cover-body {
  position: absolute;
  right: 76px;
  bottom: max(22px, calc(env(safe-area-inset-bottom) + 16px));
  left: 18px;
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2.4cqw;
}

.preview-eyebrow {
  display: block;
  font-size: 2.7cqw;
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: uppercase;
  opacity: 0.66;
}

.preview-cover h2 {
  margin: 0;
  overflow-wrap: anywhere;
  letter-spacing: 0;
}

.preview-cover h2 {
  max-width: 13ch;
  font-family: var(--font-serif);
  font-size: 8.8cqw;
  font-weight: 700;
  line-height: 1.02;
  text-wrap: balance;
}

.preview-cover p {
  margin: 0;
  font-size: 3.45cqw;
  line-height: 1.45;
  opacity: 0.82;
}

.preview-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.preview-facts span {
  padding: 6px 9px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.24);
  font-size: 2.8cqw;
  font-weight: 700;
  line-height: 1;
}

.preview-open {
  position: relative;
  z-index: 12;
  display: inline-flex;
  min-height: 46px;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 0 17px;
  appearance: none;
  border: 0;
  border-radius: 6px;
  background: var(--button);
  color: var(--button-text);
  box-shadow: 0 10px 28px -14px rgba(0, 0, 0, 0.62);
  font-size: 0.86rem;
  font-weight: 800;
  cursor: pointer;
  transition:
    transform 0.2s var(--ease-spring),
    opacity 0.2s;
}

.preview-open:hover {
  transform: translateY(-2px);
}

.preview-open:active {
  transform: scale(0.96);
}

.preview-open-icon,
.preview-open-icon svg {
  display: block;
  width: 17px;
  height: 17px;
}

.preview-open-icon svg {
  fill: currentColor;
  stroke: currentColor;
}

.feed-card-label {
  display: none;
}

.feed-actions {
  position: absolute;
  right: max(8px, env(safe-area-inset-right));
  bottom: max(20px, calc(env(safe-area-inset-bottom) + 14px));
  z-index: 14;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.feed-action {
  --pixel-shadow: rgba(0, 0, 0, 0.82);
  display: flex;
  width: 42px;
  height: 42px;
  align-items: center;
  justify-content: center;
  appearance: none;
  border: 0;
  background: transparent;
  color: #fff;
  cursor: pointer;
}

.feed-action-icon {
  display: flex;
  width: 42px;
  height: 42px;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(2px 2px 0 var(--pixel-shadow));
  transition:
    filter 80ms ease-out,
    transform 80ms ease-out;
}

.feed-action-icon svg {
  display: block;
  width: auto;
  height: 22px;
  fill: currentColor;
  stroke: none;
  shape-rendering: crispEdges;
}

.feed-action:hover .feed-action-icon {
  filter: drop-shadow(3px 3px 0 var(--pixel-shadow));
  transform: translate(-1px, -1px);
}

.feed-action:active .feed-action-icon {
  filter: drop-shadow(0 0 0 var(--pixel-shadow));
  transform: translate(2px, 2px);
}

.feed-action.is-active .feed-action-icon svg {
  fill: currentColor;
}

.feed-card:not(.is-cover) .feed-action {
  --pixel-shadow: rgba(15, 23, 42, 0.24);
  color: var(--ink);
}

.feed-card .feed-like.is-active {
  color: #f06d65;
}

.feed-card .feed-save.is-active {
  color: #e9c766;
}

.feed-card .feed-share.is-confirmed {
  color: #69b987;
}

.feed-navigation {
  display: none;
}

@media (min-width: 761px) {
  .feed-brand {
    display: block;
    top: 20px;
    left: 112px;
    color: var(--ink);
    font-size: 2rem;
    text-shadow: none;
  }

  .feed-mobile-controls {
    display: none;
  }

  .feed-sidebar {
    position: fixed;
    top: 50%;
    left: 12px;
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 8px 6px;
    border: 1px solid var(--glass-border);
    border-radius: 999px;
    background: var(--glass);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.26),
      0 12px 32px -14px var(--glass-shadow);
    backdrop-filter: blur(32px) saturate(160%);
    transform: translateY(-50%);
  }

  .feed-sidebar a {
    display: flex;
    width: 40px;
    height: 40px;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--muted);
    text-decoration: none;
  }

  .feed-sidebar a:hover {
    background: var(--surface-muted);
    color: var(--ink);
  }

  .feed-sidebar a.is-active {
    background: var(--button);
    color: var(--button-text);
  }

  .feed-sidebar svg {
    width: 18px;
    height: 18px;
  }

  .feed-shell {
    position: fixed;
    top: 0;
    left: 50%;
    width: min(62dvh, 606px);
    height: 100dvh;
    transform: translateX(-50%);
  }

  .feed {
    width: 100%;
    padding: max(4dvh, 24px) min(9dvh, 96px) max(4dvh, 24px) 0;
    scroll-snap-type: y mandatory;
  }

  .feed-card {
    width: min(51.75dvh, 506px);
    height: min(92dvh, 900px);
    margin: 0 0 16px;
    overflow: visible;
    border-radius: 8px;
    box-shadow: 0 20px 56px -24px rgba(15, 23, 42, 0.42);
    scroll-snap-align: center;
  }

  .theme-dark .feed-card {
    box-shadow: 0 24px 70px -26px rgba(0, 0, 0, 0.86);
  }

  .preview-deck {
    overflow: hidden;
    scroll-snap-type: none;
    touch-action: auto;
    border-radius: 8px;
  }

  .preview-track {
    transform: translateX(calc(var(--slide-index, 0) * -100%));
    transition: transform 0.24s var(--ease-spring);
  }

  .feed-card.is-active .preview-track {
    will-change: transform;
  }

  .preview-slide {
    position: relative;
    flex-basis: calc(100% + 2px);
    margin-left: -1px;
    margin-right: -1px;
    z-index: 0;
  }

  .preview-slide.is-active {
    z-index: 1;
  }

  /* The +2/-1/-1 combination preserves a 100% paging stride while the active layer
     overlaps both neighbouring rasters by one pixel. Desktop keeps its spatial
     transition without a contrasting seam at either fractional card edge. */
  .preview-tap {
    cursor: pointer;
    pointer-events: auto;
  }

  .feed-actions {
    right: -60px;
    bottom: 8px;
  }

  .feed-action {
    --pixel-shadow: rgba(15, 23, 42, 0.24);
    color: var(--ink);
  }

  .feed-navigation {
    position: fixed;
    top: 50%;
    right: 28px;
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: 22px;
    transform: translateY(-50%);
  }

  .feed-navigation button {
    --pixel-shadow: rgba(15, 23, 42, 0.22);
    display: flex;
    width: 48px;
    height: 48px;
    align-items: center;
    justify-content: center;
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    filter: drop-shadow(2px 2px 0 var(--pixel-shadow));
    transition:
      filter 80ms ease-out,
      transform 80ms ease-out,
      opacity 0.2s;
  }

  .theme-dark .feed-navigation button {
    --pixel-shadow: rgba(0, 0, 0, 0.8);
  }

  .feed-navigation button .pixel-svg {
    width: auto;
    height: 22px;
    fill: currentColor;
    stroke: none;
    shape-rendering: crispEdges;
  }

  .feed-navigation button:hover:not(:disabled) {
    filter: drop-shadow(3px 3px 0 var(--pixel-shadow));
    transform: translate(-1px, -1px);
  }

  .feed-navigation button:active:not(:disabled) {
    filter: drop-shadow(0 0 0 var(--pixel-shadow));
    transform: translate(2px, 2px);
  }

  .feed-navigation button:disabled {
    opacity: 0.28;
    cursor: default;
  }
}

@media (pointer: coarse) {
  .preview-open:active {
    transform: scale(0.94);
  }
}

@media (max-height: 620px) and (max-width: 760px) {
  .preview-cover-body {
    gap: 1.5cqw;
  }
}

@media (prefers-reduced-motion: reduce) {
  .preview-track,
  .preview-open,
  .feed-navigation button {
    transition-duration: 0.01ms;
  }

  .feed-spinner {
    animation: none;
  }
}
