.packs-screen { background: var(--color-neutral-50); padding-bottom: 8px; }
/* The sticky status bar and the list share one colour (#FAFAFA), but the cards throw a
   40px shadow that greys the background around them and the opaque bar cuts that gradient
   off with a straight line — which reads as the bar being a different, cleaner tone than
   the page. The tail lets the shadow run out under the bar instead of stopping at it. */
.packs-screen > .status-bar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 14px;
  background: linear-gradient(180deg, var(--color-neutral-50), rgba(250,250,250,0));
  pointer-events: none;
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 16px;
  overflow-x: auto;
}
.filter-row::-webkit-scrollbar { display: none; }
.filter-btn {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 80px;
  border: none;
  background: var(--color-white);
  box-shadow: 0 0 5px rgba(0,0,0,0.04);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.filter-btn img { width: 24px; height: 24px; }
.chips { display: flex; gap: 8px; }
.chip {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border-radius: 80px;
  background: var(--color-white);
  color: var(--color-neutral-600);
  font-family: var(--font-supreme);
  font-size: 14px;
  letter-spacing: -0.42px;
  white-space: nowrap;
  box-shadow: 0 0 2px rgba(0,0,0,0.12);
}
.chip img { width: 16px; height: 16px; }
.chip-selected {
  background: linear-gradient(180deg, #FA3030 54%, #FE6E3C 85%, #FEC389 105%, #D1FEFF 105%);
  color: var(--color-white);
  font-family: var(--font-supreme-medium);
}

/* Reels, not a feed. The list comes to rest with a card centred instead of sliding past
   a stack of them, and scroll-snap-stop: always is the part that makes it ONE card per
   flick rather than four — without it a hard swipe skims straight over the snap points.
   Only the cards are snap points; the app bar and the chip row scroll normally.
   .snap is added by filters.js once the arrival animation has finished — see there. */
.packs-screen.snap { scroll-snap-type: y mandatory; }
.pack-card { scroll-snap-align: center; scroll-snap-stop: always; }
@media (prefers-reduced-motion: reduce) { .packs-screen.snap { scroll-snap-type: none; } }

.pack-list { display: flex; flex-direction: column; gap: 12px; padding: 12px; }
.pack-card {
  text-decoration: none;
  color: inherit;
  display: block;
  background: #fff;
  border-radius: 40px;
  padding: 4px;
  box-shadow: 0 14px 40px 2px rgba(0,0,0,0.12);
}
.pack-thumb {
  position: relative;
  border-radius: 38px;
  background-size: cover;
  background-position: center;
  padding: 16px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;   /* joined-avatar stack sits bottom-right (Figma) */
  overflow: hidden;
}
/* iOS does not clip a composited child — every one of these holds a <video> — to a
   parent's border-radius. The radial-gradient mask is the WebKit-specific way to force
   the radius to be applied as a real mask. Safe here because the card, not the thumb,
   carries the shadow, so nothing outside the box is being clipped away. */
.pack-thumb { -webkit-mask-image: -webkit-radial-gradient(#fff, #000); }
.pack-thumb-top { width: 100%; }
.pack-thumb-dark::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 38px;
  background: rgba(0,0,0,0.35);
}
.pack-thumb-top { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; }
.pack-person { display: flex; align-items: center; gap: 6px; }
.pack-avatar-sm { width: 24px; height: 24px; border-radius: 80px; object-fit: cover; }
.badge {
  padding: 4px 8px;
  border-radius: 80px;
  /* base fill + a #000000 20% layer on top (Figma badge component) */
  background: linear-gradient(rgba(0,0,0,0.2), rgba(0,0,0,0.2)), rgba(207,224,234,0.9);
  color: var(--color-white);
  font-family: var(--font-supreme);
  font-size: 12px;
  letter-spacing: -0.5px;
}
.badge-dark { background: linear-gradient(rgba(0,0,0,0.2), rgba(0,0,0,0.2)), rgba(90,76,50,0.74); }
/* HOT badge: fire icon + HOT, same dark pill / size as the date badge it replaces */
.badge-hot { display: inline-flex; align-items: center; gap: 4px; }
.badge-hot img { width: 12px; height: 13.5px; display: block; }
.pack-stack { position: relative; z-index: 1; display: flex; }
.pack-stack img { width: 32px; height: 32px; border-radius: 999px; border: 1px solid #fff; object-fit: cover; margin-right: -16px; }
.pack-stack img:last-child { margin-right: 0; }
.pack-dots { position: absolute; right: 16px; bottom: 16px; z-index: 1; }
.pack-dots img { width: 10px; }
/* Info container: fixed 78px tall, 16 top / 20 bottom margins. Figma: title 18/22,
   meta 13/16, 4px between them — 22 + 4 + 16 = the 42px the padding leaves. The line
   heights are stated in px rather than left on the 1.2 token (21.6 / 15.6), because the
   4px gap only reads as 4 if the boxes above and below it are the designed height. */
.pack-info { height: 78px; box-sizing: border-box; padding: 16px 16px 20px; }
.pack-info .pack-name { margin: 0 0 4px; color: var(--color-black); line-height: 22px; }
.pack-meta { display: flex; align-items: center; gap: 4px; line-height: 16px; color: var(--color-neutral-500); }
.pack-meta .sep { width: 2px; height: 2px; }

/* "차라락" slide-in when Packs opens, and again on the way back in — see filters.js.
   18px over .42s on a curve that front-loads was more than half spent by the first frame
   and invisible against a card 275 to 600px tall; it read as no animation at all. Both
   the travel and the gap between cards have to be big enough to watch them arrive one
   after another.
   A card carries a 40px-blur shadow, and moving one without promoting it repaints that
   shadow every frame — that stutter is what read as the cards blinking rather than
   sliding. will-change puts each card on its own layer for the duration.
   Eight slots, not five: with only five the sixth card onward all started together, so
   the cascade stopped dead halfway down instead of running off the bottom of the screen. */
.pack-card {
  animation: pack-slide .55s cubic-bezier(.22,.8,.3,1) both;
  will-change: transform, opacity;
}
.pack-card:nth-child(2) { animation-delay: .07s; }
.pack-card:nth-child(3) { animation-delay: .14s; }
.pack-card:nth-child(4) { animation-delay: .21s; }
.pack-card:nth-child(5) { animation-delay: .28s; }
.pack-card:nth-child(6) { animation-delay: .35s; }
.pack-card:nth-child(7) { animation-delay: .42s; }
.pack-card:nth-child(8) { animation-delay: .49s; }
@keyframes pack-slide { from { opacity: 0; transform: translateY(44px); } }
/* Hand the layers back once the entrance has landed (.snap is added then, see
   filters.js). Eight cards held on their own composited layer for the whole session was
   memory the scroll had to drag around. */
.packs-screen.snap .pack-card { will-change: auto; }

/* Picked: the card settles back and goes flat — its shadow drops from 40px of lift to a
   thin contact shadow and a matte veil takes the gloss off the picture, so it reads as
   being pressed down onto the page rather than floating above it.
   The press has to LAND before the pages dissolve. It used to run 340ms while packs.html
   left at 170, so the outgoing snapshot froze the card exactly halfway down — a still
   frame of something mid-move, which is what read as the screen having broken rather
   than as a press. It now settles in 280ms and the navigation waits for it.
   The curve is front-loaded (.2,.8,.3,1): the card is most of the way down in the first
   third and spends the rest arriving, which is what makes it feel soft rather than stiff. */
/* No shrink at all: at .975 it still read as the card changing size rather than as
   being chosen. The selection is the colour now — the card just goes a little darker.
   The lift eases off rather than collapsing (40px of blur down to 26, not to 10), so it
   settles slightly instead of going flat. */
.pack-card { transition: box-shadow .28s cubic-bezier(.2,.8,.3,1); }
.pack-card.picked { box-shadow: 0 9px 26px 1px rgba(0,0,0,0.11); }
/* Black, not the grey it used to be: a light veil washed the picture out, which is the
   flattening. Darkening leaves the card looking like itself, only picked out.
   A pseudo-element rather than a filter — a filter on a box with a <video> in it makes
   iOS re-rasterise the card every frame. isolation gives it a stacking context to sit
   above that composited video in. */
.pack-card { position: relative; isolation: isolate; }
.pack-card::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: 40px; background: #000;
  opacity: 0; transition: opacity .28s cubic-bezier(.2,.8,.3,1);
}
.pack-card.picked::after { opacity: .10; }
/* The siblings used to dim to .86 over the same 340ms and were caught halfway too — a
   feed of half-faded cards on the outgoing frame. The chosen card says enough on its own. */

/* A clip fades up over its own poster instead of cutting in, and the outgoing one fades
   down over it — so handing playback from one card to the next is a dissolve, never a
   cut. The poster is also the thumb's background-image, so there is always a picture
   underneath and the fade has something to happen against. Held until 'playing' fires
   (clips.js), which is why a slow first frame shows the poster rather than black. */
.pack-thumb video { opacity: 0; transition: opacity .6s ease; }
.pack-thumb video.rolling { opacity: 1; }

/* Video thumbnails fill the same box as the background image and sit under the
   avatars/badges, which already carry z-index 1. */
.pack-thumb video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Filter drawer. Closed it has no height at all, so the list sits where it always did;
   open, it adds exactly one chip row and everything below moves down with it. Height is
   a fixed 48 rather than auto because a transition needs a number to animate to, and
   the row is only ever one line. */
.chip-drawer {
  height: 0;
  overflow: hidden;
  transition: height .28s cubic-bezier(.2,.85,.3,1);
}
.chip-drawer.open { height: 48px; }
.chip-drawer .chips {
  padding: 6px 16px 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.chip-drawer .chips::-webkit-scrollbar { display: none; }
.chip-drawer .chip { cursor: pointer; }
/* Filtered out — hidden alone is not enough against .pack-card's own display. */
.pack-card[hidden] { display: none; }
.filter-row .chip { cursor: pointer; }
/* Every combination is reachable and some of them are empty (there is no Pro basketball
   pack), so the list says so rather than going blank. */
.pack-empty { margin: 40px 12px; text-align: center; font-family: var(--font-supreme); font-size: 14px; letter-spacing: -0.42px; color: var(--color-neutral-400); }
.pack-empty[hidden] { display: none; }

/* ── The app bar's two actions ────────────────────────────────────────────────
   They were bare images; the magnifier now toggles something, so it is a button and the
   plus is a link. Both keep the 24px glyph and gain a thumb-sized hit box around it. */
.app-bar .actions .act {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; margin: -4px;
  padding: 0; border: 0; background: none; cursor: pointer;
  border-radius: 999px;
  -webkit-tap-highlight-color: transparent;
}
.app-bar .actions .act.on { background: rgba(0,0,0,0.06); }
.app-bar .actions .act:active { background: rgba(0,0,0,0.10); }

/* ── Search ───────────────────────────────────────────────────────────────────
   Height, not opacity — the row has to give its space back to the flow so the list sits
   where it did when the field is shut, the same way the chip drawer works. */
.pack-search {
  height: 0;
  overflow: hidden;
  transition: height .24s cubic-bezier(.2,.8,.25,1);
}
.pack-search.open { height: 56px; }
.ps-field {
  display: flex; align-items: center; gap: 8px;
  margin: 8px 16px 0;
  padding: 10px 12px;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 14px 54px 10px rgba(0,0,0,0.03);
}
.ps-field img { width: 18px; height: 18px; display: block; opacity: .45; flex: none; }
.ps-field input {
  flex: 1; min-width: 0;
  border: 0; outline: 0; background: none; padding: 0;
  font-family: var(--font-supreme); font-size: 15px; letter-spacing: -0.5px;
  color: var(--color-black);
}
.ps-field input::placeholder { color: var(--color-neutral-500); }
/* The browser's own clear affordance is a second, differently-shaped X next to ours. */
.ps-field input::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }
.ps-clear {
  flex: none; width: 22px; height: 22px; border: 0; border-radius: 999px;
  background: var(--color-neutral-100); color: var(--color-neutral-600);
  font: 16px/1 -apple-system, sans-serif; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.ps-clear:active { background: var(--color-neutral-200, #e5e5e5); }

/* ── Create a pack ────────────────────────────────────────────────────────────
   The one thing the setup flow has no component for is a place to type. It borrows the
   card's own shell — white, 16 radius, the same soft shadow — so it sits in the column
   as one more answer rather than as a form control. */
.np-field {
  background: #fff; border-radius: 16px; padding: 16px 20px;
  box-shadow: 0 14px 54px 10px rgba(0,0,0,0.03);
}
.np-field input {
  width: 100%; border: 0; outline: 0; background: none; padding: 0;
  font-family: var(--font-supreme-bold); font-size: 20px; line-height: 1.2;
  letter-spacing: -1px; color: var(--color-black);
}
.np-field input::placeholder { font-family: var(--font-supreme); color: var(--color-neutral-500); letter-spacing: -0.5px; }

.np-covers { display: flex; gap: 4px; }
.np-cover {
  flex: 1; height: 88px; min-width: 0; padding: 0; border: 0; cursor: pointer;
  border-radius: 16px; background: #fff center / cover no-repeat;
  box-shadow: 0 14px 54px 10px rgba(0,0,0,0.03);
  position: relative; overflow: hidden;
}
/* The tick this app already draws, at the size a thumbnail can carry. */
.np-cover .ob-check { top: -12px; right: -12px; transform: scale(.72); }
.np-cover.sel { box-shadow: 0 0 0 2px var(--color-red), 0 14px 54px 10px rgba(0,0,0,0.06); }

/* The preview is a real pack card, so it is styled by the list's own rules; it only has
   to stop behaving like a link and say when no cover has been picked yet. */
.np-preview { pointer-events: none; }
.np-preview .pack-thumb.np-empty { background: var(--color-neutral-200, #E5E5E5); }
