/* ==========================================================================
   stories.css — admin stories: the brand poster and the full-screen viewer

   Always loaded: PHP emits this markup, so it must not ride on the modern
   layer's kill flag. App chrome uses the --cs-* tokens with literal fallbacks
   like the other component sheets.

   The poster itself does NOT. It is brand artwork built to
   /globetrotters-posters/design-guide (designguide.txt + tokens.json), so it
   carries the brand palette and does not change between light and dark. Every
   number below is a design-guide token at a 1080px canvas, expressed as a
   percentage so one stylesheet serves the preview and the full-screen view.
   ========================================================================== */

/* --- brand tokens, from the design guide --------------------------------- */

.cs-poster {
  --bg-navy: #363e5e;
  --bg-navy-deep: #262c45;
  --bg-cream: #f8f0d8;
  --bg-sand: #e0b878;

  /* canvas-margin 86/1080 = 7.96%; logo-width 367/1080 = 33.98%.
     The guide's safe-top (270px) and safe-bottom (340px) exist to clear
     Instagram's progress bar and reply bar. In the app there is neither — our
     own progress bar is a 3px hairline — so the poster uses its own margin top
     and bottom and gets the whole canvas. Exported to Instagram it would need
     the safe zones back. */
  --margin: 7.96%;
  --safe-top: 4.5%;
  /* Was 11% to leave room for the footer URL under the text. The URL is gone
     in the app — everyone here knows whose camp it is — so the text block sits
     lower, clear of the phone's home indicator. */
  --safe-bottom: calc(6% + env(safe-area-inset-bottom, 0px));
  --logo-w: 33.98%;

  position: relative;
  aspect-ratio: 9 / 16;
  width: 100%;
  overflow: hidden;
  background: var(--bg-navy);
  /* Poster corners are square (radius-none); the preview rounds its own frame */
  container-type: inline-size;
  font-family: "Libre Franklin", "Helvetica Neue", Arial, sans-serif;
  color: var(--bg-cream);
  -webkit-font-smoothing: antialiased;
}

/* The poster is brand artwork that happens to live inside an app page, and the
   app styles paragraphs globally — `body.modern-ui p` paints every one of them
   --cs-muted, which put grey text on navy. Everything in here inherits the
   poster's own ink instead. The `body` prefix is what gets past that rule. */
body .cs-poster p,
body .cs-poster span,
body .cs-poster div { color: inherit; }

/* --- the photo and its fade (CampPhoto) ---------------------------------- */

.cs-poster-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* "cropped so faces sit in the upper 60%" */
  object-position: center 35%;
}

/* The guide's fade, stop for stop: navy-deep at 50% over the top 20%, clear
   from 20% to 38%, rising to 85% at 64% and 97% at the bottom. The top band
   is what keeps the logo legible. */
.cs-poster-fade {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(38, 44, 69, 0.5) 0%,
    rgba(38, 44, 69, 0.5) 20%,
    rgba(38, 44, 69, 0) 38%,
    rgba(38, 44, 69, 0.85) 64%,
    rgba(38, 44, 69, 0.97) 100%
  );
}

/* --- the logo ------------------------------------------------------------ */

.cs-poster-logo {
  position: absolute;
  top: var(--safe-top);
  left: var(--margin);
  width: var(--logo-w);
  height: auto;
}
/* shadow-logo, only when it sits on a photo */
.cs-poster.has-photo .cs-poster-logo { filter: drop-shadow(0 0.6cqw 1.6cqw rgba(0, 0, 0, 0.35)); }

/* --- the text block ------------------------------------------------------ */

/* CampPhoto puts the text block bottom-left, under the fade. */
.cs-poster-body {
  position: absolute;
  left: var(--margin);
  right: var(--margin);
  bottom: var(--safe-bottom);
}

/* RetroLine centres it vertically — the guide is explicit, and without it a
   9:16 poster is a band of text under a lot of empty navy. */
.cs-poster.is-retro .cs-poster-body {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

/* The retro line: 151/1080 wide, 15/1080 thick, 3px ends */
.cs-poster-line {
  width: 13.98%;
  height: 1.39cqw;
  min-height: 2px;
  border-radius: 0.28cqw;
  background: var(--bg-sand);
  margin-bottom: 3.98%;
}

/* Two styles, and they are not interchangeable.
   On a photo: statement-photo, Libre Franklin 800 at 86/1080 = 7.96cqw, no
   shadow — the guide puts shadow-stack on Archivo Black headlines and the quote
   mark, nowhere else.
   On navy: the RetroLine headline, Archivo Black with the stacked shadow, at
   headline (144/1080 = 13.33cqw) or headline-long (105/1080 = 9.72cqw). The
   shadow is sized for those; at body-text size it turns the words to mush. */
.cs-poster-text {
  margin: 0;
  color: var(--bg-cream);
  font-size: 7.96cqw;
  line-height: 1.04;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.cs-poster.is-retro .cs-poster-text {
  font-family: "Archivo Black", "Arial Black", system-ui, sans-serif;
  font-weight: 400;
  font-size: 13.33cqw;
  line-height: 1;
  letter-spacing: 0;
  /* shadow-stack: four steps of 7.5/1080 in the four-colour sequence */
  text-shadow:
    0.69cqw 0.69cqw 0 #e05038,
    1.39cqw 1.39cqw 0 #ec8448,
    2.08cqw 2.08cqw 0 #e0b878,
    2.78cqw 2.78cqw 0 #88a8a0;
}

/* headline-long, for anything past a few words */
.cs-poster.is-retro.is-long .cs-poster-text { font-size: 9.72cqw; }

/* Past a sentence or so, Archivo Black with a four-step shadow stops being a
   headline and starts being a wall. Fall back to the statement style the guide
   uses for sentences: Libre Franklin 500 at 60/1080. */
.cs-poster.is-retro.is-sentence .cs-poster-text {
  font-family: "Libre Franklin", "Helvetica Neue", Arial, sans-serif;
  font-weight: 500;
  font-size: 5.56cqw;
  line-height: 1.22;
  text-shadow: none;
}

/* "the highlighted words keep the sentence's own font and only change colour" */
body .cs-poster .cs-poster-text mark {
  background: none;
  color: var(--bg-sand);
  font: inherit;
  /* Bootstrap pads every mark by 0.1875em, which opened a gap around the
     highlighted phrase. The guide changes the colour and nothing else. */
  padding: 0;
}

/* The eyebrow label: 32/1080 = 2.96cqw, uppercase, 0.16em, sand on navy */
.cs-poster-label {
  margin: 0 0 2.96%;
  font-size: 2.96cqw;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bg-sand);
}

/* ==========================================================================
   ShareSheet — the paper alternative

   The guide's third poster style, built stop for stop from its own preview:
   paper ground, three soft glows, the four-colour bar across the top, a
   gradient number and the statement in navy with the highlight in red.
   ========================================================================== */

.cs-poster.is-share {
  background: #fbf6ea;
  color: #363e5e;
}

/* Three radial glows: red top-right at 13%, sage bottom-left at 26%,
   sand mid-right at 20% */
.cs-poster.is-share::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(70cqw 70cqw at 100% 0%, rgba(224, 80, 56, 0.13), transparent 70%),
    radial-gradient(90cqw 90cqw at 0% 100%, rgba(136, 168, 160, 0.26), transparent 70%),
    radial-gradient(45cqw 45cqw at 85% 62%, rgba(224, 184, 120, 0.2), transparent 70%);
}

/* The four-colour bar, 15/1080 thick, in the logo's order */
.cs-poster-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1.39cqw;
  min-height: 2px;
  background: linear-gradient(90deg, #e05038 0 25%, #ec8448 25% 50%, #e0b878 50% 75%, #88a8a0 75%);
}

/* Everything else sits above the glows */
.cs-poster.is-share .cs-poster-logo,
.cs-poster.is-share .cs-poster-body,
.cs-poster.is-share .cs-poster-footer { z-index: 1; }

/* Centred vertically, like RetroLine */
.cs-poster.is-share .cs-poster-body {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

.cs-poster.is-share .cs-poster-label { color: #6b6f7f; }

/* share-number: 367/1080 = 34cqw, Libre Franklin 800, filled with the
   four-colour gradient */
.cs-poster-number {
  margin: 0 0 1.5% -1.2cqw;
  font-size: 34cqw;
  line-height: 0.82;
  font-weight: 800;
  letter-spacing: -0.04em;
  background: linear-gradient(90deg, #e05038, #ec8448 40%, #e0b878 70%, #88a8a0);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* share-statement: 82/1080 = 7.59cqw, Libre Franklin 700, navy */
.cs-poster.is-share .cs-poster-text {
  font-family: "Libre Franklin", "Helvetica Neue", Arial, sans-serif;
  color: #363e5e;
  font-size: 7.59cqw;
  line-height: 1.08;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-shadow: none;
}

/* "red for highlighted words on paper at 24px+ only" — share-statement is
   82px on the 1080 canvas, so it clears that comfortably */
body .cs-poster.is-share .cs-poster-text mark { color: #e05038; }

body .cs-poster.is-share .cs-poster-footer {
  color: #6b6f7f;
  opacity: 1;
  letter-spacing: 0;
  text-transform: none;
  font-weight: 500;
}

/* The gradient number cannot inherit — the colour: inherit insulation rule
   above would paint it over the clipped background. */
body .cs-poster .cs-poster-number { color: transparent; }

/* footer: 28/1080 = 2.59cqw, cream at 70% */
body .cs-poster .cs-poster-footer {
  position: absolute;
  left: var(--margin);
  bottom: 4.2%;
  margin: 0;
  color: var(--bg-cream);
  opacity: 0.7;
  font-size: 2.59cqw;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ==========================================================================
   The viewer
   ========================================================================== */

.cs-stories {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1050;
  background: #0b0b10;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  -webkit-user-select: none;
}
.cs-stories.is-open { display: flex; }

.cs-stories-stage {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: calc(100vh * 9 / 16);
  margin: 0 auto;
}

.cs-stories-slide { display: none; width: 100%; height: 100%; }
.cs-stories-slide.is-current { display: block; }
.cs-stories-slide .cs-poster { height: 100%; aspect-ratio: auto; }

/* --- progress bar --------------------------------------------------------- */

.cs-stories-bars {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top, 0px));
  left: 10px;
  right: 10px;
  z-index: 2;
  display: flex;
  gap: 4px;
}

.cs-stories-bar {
  flex: 1 1 0;
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.3);
  overflow: hidden;
}

.cs-stories-bar span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: #fff;
}
.cs-stories-bar.is-done span { width: 100%; }

/* --- close and tap zones --------------------------------------------------- */

/* When this story went up. Sits in the viewer chrome on the same line as the
   close button, so it never lands on the poster itself. */
.cs-stories-ago {
  position: absolute;
  top: calc(32px + env(safe-area-inset-top, 0px));
  left: 16px;
  z-index: 3;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.75);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}

/* Sound toggle, next to the close button. Only on a slide that has a clip. */
.cs-stories-sound {
  position: absolute;
  top: calc(22px + env(safe-area-inset-top, 0px));
  right: 58px;
  z-index: 3;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.cs-stories-sound[hidden] { display: none; }

/* A wordless clip: nothing but the video and the logo. */
.cs-poster.is-bare .cs-poster-fade { display: none; }

.cs-stories-close {
  position: absolute;
  top: calc(22px + env(safe-area-inset-top, 0px));
  right: 10px;
  z-index: 3;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

.cs-stories-tap {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  width: 32%;
  cursor: pointer;
}
.cs-stories-tap.is-prev { left: 0; }
.cs-stories-tap.is-next { right: 0; width: 68%; }

/* --- the ring on the avatar ------------------------------------------------ */

/* Sits on the top bar's own avatar rather than taking another 45px slot: the
   bar is already full on a phone for staff. */
/* ==========================================================================
   The cue — "there is camp info waiting for you"

   A ring around the camper's own avatar was the first attempt and it was the
   wrong grammar: a story is the camp talking to everybody, not something the
   camper posted. This is one line at the top of the front page instead, in the
   poster's own navy so it is recognisably the same thing it opens, and it is
   only on the page while something is unseen.
   ========================================================================== */

.cs-stories-cue {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  padding: 14px 15px;
  border: 0;
  text-align: left;
  border-radius: var(--cs-r, 14px);
  background: #363e5e;
  color: #f8f0d8;
  box-shadow: var(--cs-shadow-2, 0 8px 24px -6px rgba(16, 16, 30, 0.22));
  -webkit-tap-highlight-color: transparent;
}

.cs-stories-cue:active { transform: scale(0.994); }

/* Instagram's grammar, in the brand's colours: a gradient ring around a
   thumbnail of the poster itself. The line of text this replaced was read as
   one more notice on a page full of cards — three quarters of the camp opened
   the app and scrolled straight past it. */
.cs-stories-cue-ring {
  flex: none;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 58px;
  height: 58px;
  padding: 2.5px;
  border-radius: 50%;
  background: conic-gradient(from 210deg, #e05038, #ec8448, #e0b878, #88a8a0, #e05038);
}

.cs-stories-cue-thumb {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  box-sizing: border-box;
  /* a ring of the card's own navy, so the gradient reads as a ring and not a halo */
  border: 2.5px solid #363e5e;
  background-color: #262c45;
  background-size: cover;
  background-position: center 35%;
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* A text-only story has no picture to show, so it gets the retro line instead */
.cs-stories-cue-mark {
  width: 22px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, #e05038 0 25%, #ec8448 25% 50%, #e0b878 50% 75%, #88a8a0 75%);
}

/* The whole bar glows, so the eye lands on it on the way down the page. It
   breathes rather than blinks — this sits above a camp schedule, not a casino. */
.cs-stories-cue {
  position: relative;
  animation: cs-cue-glow 2.8s ease-in-out infinite;
}

@keyframes cs-cue-glow {
  0%, 100% { box-shadow: 0 8px 24px -6px rgba(16, 16, 30, 0.22), 0 0 0 0 rgba(224, 184, 120, 0); }
  50%      { box-shadow: 0 8px 24px -6px rgba(16, 16, 30, 0.22), 0 0 0 4px rgba(224, 184, 120, 0.22); }
}

.cs-stories-cue-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* The four brand colours run through the letters, left to right, over and over.
   A glow around the card was too quiet — people read this as a static notice
   and scrolled on. Movement inside the words is what catches an eye that is
   already on its way past. */
.cs-stories-cue-label {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: linear-gradient(90deg, #e05038, #ec8448, #e0b878, #88a8a0, #e0b878, #ec8448, #e05038);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  /* -webkit-text-fill-color, not color: the app paints this element through a
     more specific rule and would win the colour, leaving solid text over the
     gradient. The fill wins regardless. */
  -webkit-text-fill-color: transparent;
  animation: cs-cue-flow 3.2s linear infinite;
}

@keyframes cs-cue-flow {
  from { background-position: 0% 0; }
  to   { background-position: -220% 0; }
}

.cs-stories-cue-text {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
  color: #f8f0d8;
  /* one line, because the poster itself is two taps away */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Admin only: how many have watched. Quiet, because it is a note to the
   organiser sitting inside a card aimed at everyone else. */
.cs-stories-cue-views {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(248, 240, 216, 0.55);
  margin-top: 2px;
}
body .cs-stories-cue .cs-stories-cue-views { color: rgba(248, 240, 216, 0.55); }

.cs-stories-cue-go {
  flex: none;
  font-size: 18px;
  color: #f8f0d8;
  opacity: 0.55;
}

/* The app paints text globally, and it paints it muted grey, which on this
   navy is unreadable. Each part is named explicitly with a `body` prefix so it
   outweighs those rules — an `inherit` here would just inherit the grey. */
body .cs-stories-cue { color: #f8f0d8; }
body .cs-stories-cue .cs-stories-cue-text { color: #f8f0d8; }
/* Deliberately not setting `color` here: the label is painted by the flowing
   gradient above, and a colour would sit on top of it in browsers that ignore
   background-clip. The fallback for those is the sand below. */
body .cs-stories-cue .cs-stories-cue-label { color: #e0b878; }

/* No background-clip support: plain sand text rather than invisible letters */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .cs-stories-cue-label {
    background: none;
    -webkit-text-fill-color: #e0b878;
    animation: none;
  }
}
body .cs-stories-cue .cs-stories-cue-go { color: #f8f0d8; }

/* Seen it: the cue stays so it can be watched again, but it stops competing
   with everything else on the page. No glow, no colours running through the
   letters, a grey ring rather than the brand one, and a smaller thumbnail. */
.cs-stories-cue.is-seen {
  animation: none;
  /* Read: it stops being a navy block altogether. A dark slab, even a small
     one, still reads as something demanding attention; this sits in the app's
     own quiet surface so it belongs among the other rows on the page. The app
     tokens carry it across light and dark without a second rule. */
  background: var(--cs-surface-2, rgba(15, 15, 25, 0.04));
  border: 1px solid var(--cs-line, rgba(15, 15, 25, 0.08));
  box-shadow: none;
}
.cs-stories-cue.is-seen {
  padding: 9px 13px;
  gap: 10px;
}
.cs-stories-cue.is-seen .cs-stories-cue-ring {
  width: 32px;
  height: 32px;
  padding: 1.5px;
  background: var(--cs-line-strong, rgba(15, 15, 25, 0.16));
}
.cs-stories-cue.is-seen .cs-stories-cue-thumb {
  border-width: 1.5px;
  border-color: var(--cs-surface-2, #f1f1f4);
}
/* One line, not two: once it is read the teaser has done its job and the row
   only needs to be a door back in. */
.cs-stories-cue.is-seen .cs-stories-cue-text { display: none; }
.cs-stories-cue.is-seen .cs-stories-cue-label { font-size: 11px; }
/* One row, even for an admin: the view count joins the label rather than
   adding a second line to a card whose whole job now is to be small. */
.cs-stories-cue.is-seen .cs-stories-cue-body {
  flex-direction: row;
  align-items: baseline;
  gap: 8px;
}
.cs-stories-cue.is-seen .cs-stories-cue-views { margin-top: 0; font-size: 10.5px; }
.cs-stories-cue.is-seen .cs-stories-cue-label {
  animation: none;
  background: none;
  -webkit-text-fill-color: var(--cs-muted, rgba(15, 15, 25, 0.55));
  letter-spacing: 0.1em;
}

.cs-stories-cue.is-seen .cs-stories-cue-go { color: var(--cs-muted, rgba(15, 15, 25, 0.55)); opacity: 1; }
body .cs-stories-cue.is-seen .cs-stories-cue-views { color: var(--cs-muted, rgba(15, 15, 25, 0.55)); }

@media (prefers-reduced-motion: reduce) {
  .cs-stories-cue:active { transform: none; }
  /* No pulse, but keep a permanent ring so it still stands out */
  .cs-stories-cue {
    animation: none;
    box-shadow: 0 8px 24px -6px rgba(16, 16, 30, 0.22), 0 0 0 3px rgba(224, 184, 120, 0.3);
  }
  /* And the colours stop running through the letters */
  .cs-stories-cue-label { animation: none; background-position: 0 0; }
}

/* --- admin preview --------------------------------------------------------- */

.cs-poster-preview {
  max-width: 340px;
  border-radius: var(--cs-r, 14px);
  overflow: hidden;
  box-shadow: var(--cs-shadow-2, 0 8px 24px -6px rgba(16, 16, 30, 0.18));
}

.cs-story-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 2px;
  border-top: 1px solid var(--cs-line, rgba(15, 15, 25, 0.07));
}
.cs-story-row:first-child { border-top: 0; }
.cs-story-thumb {
  flex: 0 0 44px;
  width: 44px;
  height: 78px;
  border-radius: 6px;
  overflow: hidden;
  background: #363e5e;
}
.cs-story-thumb .cs-poster { height: 100%; aspect-ratio: auto; }
.cs-story-row-body { flex: 1 1 auto; min-width: 0; }
.cs-story-row-text {
  font-size: 14px;
  font-weight: 600;
  color: var(--cs-ink, #0b0b10);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.cs-story-row-meta { margin-top: 2px; font-size: 12px; color: var(--cs-faint, #9a9aa5); }
.cs-story-row-meta.is-expired { color: var(--cs-red, #e11d48); }

body.theme-dark .cs-story-row { border-top-color: rgba(255, 255, 255, 0.08); }
body.theme-dark .cs-story-row-text { color: #f4f5f7; }

/* A timer bar is pure motion. Under reduced motion it steps rather than runs. */
@media (prefers-reduced-motion: reduce) {
  .cs-stories-bar span { transition: none !important; }
}

/* --- admin: picking a photo from the camp gallery -------------------------- */

/* A scroll box rather than a growing grid: a camp has hundreds of photos and
   this sits above the preview he is comparing against. */
.cs-photopick {
  max-height: 240px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: 6px;
  padding: 7px;
  border: 1px solid var(--cs-line, rgba(15, 15, 25, 0.07));
  border-radius: var(--cs-r, 14px);
  background: var(--cs-surface-2, rgba(15, 15, 25, 0.03));
}

.cs-photopick-item {
  position: relative;
  display: block;
  margin: 0;
  cursor: pointer;
  border-radius: 8px;
  overflow: hidden;
  aspect-ratio: 1;
}

.cs-photopick-item input { position: absolute; opacity: 0; pointer-events: none; }

.cs-photopick-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 140ms ease;
}

.cs-photopick-item.is-chosen img { transform: scale(1.06); }

/* The chosen photo gets a ring drawn inside the tile, so the grid does not
   reflow when the selection moves. */
.cs-photopick-item.is-chosen::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 8px;
  box-shadow: inset 0 0 0 3px var(--cs-accent, #2d6cdf);
}

.cs-photopick-empty {
  grid-column: 1 / -1;
  padding: 18px 8px;
  text-align: center;
  font-size: 13px;
  color: var(--cs-muted, rgba(15, 15, 25, 0.55));
}

/* --- admin: framing the picture ------------------------------------------- */

/* Dragging the poster pans the crop, so the poster must not scroll the page
   with the finger. Only on while a picture is actually in there. */
.cs-poster-preview.is-draggable .cs-poster {
  cursor: grab;
  touch-action: none;
}
.cs-poster-preview.is-dragging .cs-poster { cursor: grabbing; }

/* No transition on the photo while framing: the crop has to follow the finger */
.cs-poster-preview .cs-poster-photo { transition: none; }

.cs-frame-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 340px;
  margin-top: 9px;
}

.cs-frame-controls input[type="range"] { flex: 1 1 auto; margin: 0; }

.cs-frame-hint {
  max-width: 340px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--cs-muted, rgba(15, 15, 25, 0.55));
}

body.theme-dark .cs-photopick {
  border-color: var(--cs-line, rgba(255, 255, 255, 0.1));
  background: var(--cs-surface-2, rgba(255, 255, 255, 0.04));
}

/* The app sets -webkit-appearance: none on every input and never restores a
   range's track or thumb, so an unstyled slider shows as a lone red square.
   Draw the whole control here. */
.cs-frame-controls input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  height: 22px;
  padding: 0;
  background: transparent;
  border: 0;
}

.cs-frame-controls input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--cs-line-strong, rgba(15, 15, 25, 0.16));
}
.cs-frame-controls input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--cs-line-strong, rgba(15, 15, 25, 0.16));
}

.cs-frame-controls input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -8px;
  border-radius: 50%;
  border: 0;
  background: var(--cs-accent, #2d6cdf);
  box-shadow: 0 1px 4px rgba(16, 16, 30, 0.3);
  cursor: pointer;
}
.cs-frame-controls input[type="range"]::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 0;
  background: var(--cs-accent, #2d6cdf);
  box-shadow: 0 1px 4px rgba(16, 16, 30, 0.3);
  cursor: pointer;
}

body.theme-dark .cs-frame-controls input[type="range"]::-webkit-slider-runnable-track {
  background: var(--cs-line-strong, rgba(255, 255, 255, 0.2));
}
body.theme-dark .cs-frame-controls input[type="range"]::-moz-range-track {
  background: var(--cs-line-strong, rgba(255, 255, 255, 0.2));
}

/* --- admin: picking the poster style -------------------------------------- */

.cs-stylepick { display: flex; gap: 8px; }

.cs-stylepick-item {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  padding: 9px 11px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--cs-line, rgba(15, 15, 25, 0.1));
  border-radius: var(--cs-r, 14px);
  background: var(--cs-surface, #fff);
}

.cs-stylepick-item input { position: absolute; opacity: 0; pointer-events: none; }

.cs-stylepick-item.is-chosen {
  border-color: var(--cs-accent, #2d6cdf);
  box-shadow: inset 0 0 0 1px var(--cs-accent, #2d6cdf);
}

.cs-stylepick-swatch {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  flex: none;
  border: 1px solid rgba(15, 15, 25, 0.12);
}

/* The two grounds, so the choice reads without having to imagine it */
.cs-stylepick-swatch.is-navy { background: #363e5e; }
.cs-stylepick-swatch.is-paper {
  background: #fbf6ea;
  background-image: linear-gradient(135deg, rgba(224, 80, 56, 0.25), rgba(136, 168, 160, 0.3));
}

body.theme-dark .cs-stylepick-item {
  background: var(--cs-surface, #171821);
  border-color: var(--cs-line, rgba(255, 255, 255, 0.1));
}

/* The viewer's chrome is white, which disappears on a paper poster. When the
   slide on screen is a ShareSheet, the progress bar turns navy instead. */
.cs-stories-stage:has(.cs-stories-slide.is-current .cs-poster.is-share) .cs-stories-bars {
  /* clear of the poster's own four-colour bar, so the two do not stack */
  top: calc(16px + env(safe-area-inset-top, 0px));
}
.cs-stories-stage:has(.cs-stories-slide.is-current .cs-poster.is-share) .cs-stories-bar {
  background: rgba(54, 62, 94, 0.22);
}
.cs-stories-stage:has(.cs-stories-slide.is-current .cs-poster.is-share) .cs-stories-bar span {
  background: #363e5e;
}

/* --- the ninja game's winner poster (special-purpose, style 'ninja') ------
   A one-off like the quiz poster, but in the brand's own colours: navy ground,
   the winner's photo in a sand ring with the ninja mascot, "WINNER!" in cream
   Archivo Black with the logo's stacked shadow, name in cream, kills in sand.
   Centred, because it is a stamp. */
.cs-poster.is-ninja {
  background: var(--bg-navy);
}
.cs-ninja-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(85cqw 85cqw at 50% 40%, rgba(224, 184, 120, 0.22), transparent 65%);
}
.cs-poster.is-ninja .cs-poster-logo {
  position: absolute;
  top: var(--safe-top);
  left: var(--margin);
  width: var(--logo-w);
  z-index: 2;
}
.cs-ninja-stage {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0 var(--margin) var(--safe-bottom);
  text-align: center;
  z-index: 1;
}
.cs-ninja-portrait {
  position: relative;
  width: 58cqw;
  height: 58cqw;
  margin-bottom: 8cqw;
}
.cs-ninja-photo {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 30%;
  border: 1.6cqw solid var(--bg-sand);
  box-shadow: 1.39cqw 1.39cqw 0 var(--bg-navy-deep);
}
.cs-ninja-photo-empty { background: var(--bg-navy-deep); }
.cs-ninja-mascot {
  position: absolute;
  right: -12cqw;
  bottom: -9cqw;
  width: 36cqw;
  height: 36cqw;
}
.cs-ninja-mascot svg { width: 100%; height: 100%; display: block; }
.cs-ninja-mascot .nj-glow { display: none; }
body .cs-poster .cs-ninja-winner {
  font-family: "Archivo Black", "Libre Franklin", Arial, sans-serif;
  font-size: 16cqw;
  line-height: 1;
  color: var(--bg-cream);
  text-shadow:
    0.69cqw 0.69cqw 0 #e05038,
    1.39cqw 1.39cqw 0 #ec8448,
    2.08cqw 2.08cqw 0 #e0b878,
    2.78cqw 2.78cqw 0 #88a8a0;
  margin-bottom: 4.5cqw;
}
.cs-ninja-name {
  font-size: 7.4cqw;
  font-weight: 800;
  line-height: 1.1;
  color: var(--bg-cream);
  margin-bottom: 2.5cqw;
}
body .cs-poster .cs-ninja-kills {
  font-size: 4.4cqw;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bg-sand);
}
.cs-ninja-kills strong { font-weight: 900; }
/* The mascot's "won" gold is not a brand colour: repaint it sand (CSS beats SVG presentation attributes). */
.cs-ninja-mascot [fill="#f1c40f"] { fill: #e0b878; }
.cs-ninja-mascot [stroke="#f1c40f"] { stroke: #e0b878; }
.cs-ninja-mascot [fill="#fff6c8"] { fill: #f8f0d8; }
