/**
 * Date Night.
 *
 * EXPERIMENTAL — loaded only by /date-night/[id]. Nothing else links it, so
 * deleting this file removes the whole surface's styling and touches no other
 * page.
 *
 * Date Night owns no layout. This file styles two things the chat page makes
 * room for: the deck of curated replies directly above the composer, and her
 * video over the profile panel. Everything else on the page is the chat page's
 * own CSS, untouched.
 */

/* ── The deck, above the composer ───────────────────────────────────────── */
/*
 * Part of the composer column, not a floating layer. It sits between the
 * transcript and the input, so reading her reply and answering it are the same
 * eye movement — and the ordinary composer stays right there for anyone who
 * would rather type their own line.
 *
 * Capped and scrollable because five choices plus a hint plus a purchase card
 * is taller than the transcript can afford to lose on a laptop.
 */
.dn-slot:empty { display: none; }

.dn-deck {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 16px 0;
  max-height: 46vh;
  overflow-y: auto;
}
/* Flex children shrink below their content by default, and in a column that is
   vertical — her line was being squeezed to half a row of text. */
.dn-deck > * { flex-shrink: 0; }

/* ── Chrome this page does not use ──────────────────────────────────────── */
/*
 * The chat list is not rendered here, so the two controls that show and hide
 * it have nothing to act on. Hidden rather than removed from chatHeader.astro,
 * which is shared with every other chat page.
 */
#chatToggleBtn,
.mobile-chat-toggle { display: none !important; }

/* Without the list beside it, the conversation takes the width. */
.chat-layout > .chat-center-panel { flex: 1 1 auto; }

/* ── The status strip ───────────────────────────────────────────────────── */
.dn-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 11px;
}
.dn-bar-venue {
  color: var(--neutral-400);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* The only part that gives up space; everything to its right is fixed and
     none of it can wrap. */
  flex: 1 1 auto;
}

.dn-mood {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(204, 149, 255, 0.12);
  color: #cc95ff;
  font-weight: 600;
}

/*
 * The AI disclosure, on screen for the whole date rather than only in the
 * modal — somebody arriving from a shared link never saw the modal.
 */
.dn-ai-label {
  flex-shrink: 0;
  padding: 2px 8px;
  border: 1px solid var(--neutral-700);
  border-radius: 999px;
  color: var(--neutral-400);
  white-space: nowrap;
}

.dn-leave {
  flex-shrink: 0;
  padding: 3px 10px;
  border: 1px solid var(--neutral-700);
  border-radius: 8px;
  background: none;
  color: var(--neutral-400);
  font-family: 'Heebo', sans-serif;
  font-size: 11px;
  cursor: pointer;
}
.dn-leave:hover { color: #fff; border-color: var(--neutral-600); }

/* ── Her video, over the profile panel ──────────────────────────────────── */
/*
 * Out of flow, so it neither adds to the panel's flex gap nor pushes the
 * carousel around — it simply covers it. Unmounting restores the profile with
 * no restore code, which is why leaving a date cannot strand a half state.
 *
 * This is the same slot and the same treatment Spicy Mode uses. The two never
 * collide because Spicy is not rendered on this page at all.
 */
/*
 * The panel has to carry its own height now.
 *
 * On the chat page the carousel gives it one; here the panel holds only the
 * video, and the video is absolutely positioned — an out-of-flow child cannot
 * give its parent a height, so without this the panel collapses to nothing
 * and the "cover" covers a zero-height box.
 */
.chat-profile-panel { height: calc(100vh - 80px); }

#spicyVideoSlot {
  position: absolute;
  inset: 0;
  z-index: 6;
  border-radius: 12px;
  overflow: hidden;
  background: #000;
}
/* Must collapse when empty or it would cover the profile permanently. */
#spicyVideoSlot:empty { display: none; }

/*
 * The panel scrolls, and an absolutely positioned cover scrolls with it —
 * without this, scrolling the profile slides the video away and reveals what
 * it is covering.
 */
body.date-night-on .chat-profile-panel {
  overflow: hidden;
  scroll-behavior: auto;
}
/* Not just covered — inert. Nothing under a full-panel video should be
   clickable or focusable through it. */
body.date-night-on .chat-profile-panel > *:not(#spicyVideoSlot) {
  pointer-events: none;
}

.dnv-frame.is-panel { width: 100%; height: 100%; }
.dnv-frame.is-panel .dn-stage,
.dnv-frame.is-panel .dn-stage-frame {
  width: 100%;
  height: 100%;
  max-height: none;
  border-radius: 0;
  /* The panel is not 9:16, and letterboxing her inside it looks like a
     mistake. object-fit on the video does the cropping instead. */
  aspect-ratio: auto;
}

/*
 * Phone: a small floating frame.
 *
 * There is no visible profile panel below 768px. It sits above the composer
 * and the deck, out of the way of both, and is deliberately not draggable —
 * one more thing to fiddle with mid-sentence is not worth the code.
 */
.dnv-frame.is-floating {
  position: fixed;
  right: 10px;
  top: calc(56px + env(safe-area-inset-top));
  z-index: 40;
  width: 130px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(156, 80, 232, 0.5);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
}
.dnv-frame.is-floating .dn-stage,
.dnv-frame.is-floating .dn-stage-frame {
  /*
   * An explicit width, because nothing inside the frame is in flow any more.
   *
   * Both <video> elements and the poster are absolutely positioned, so the
   * frame has no content to take its size from. As a flex item with `width:
   * auto` that made it 0 wide, and `aspect-ratio` then resolved the height to
   * 0 as well: a 130px box with an invisible video in it. The panel layout
   * never showed this because it sets width and height to 100% outright.
   */
  width: 100%;
}
.dnv-frame.is-floating .dn-stage-frame {
  /*
   * 130 x 195. Taller than it is wide, which is half again the square it used
   * to be — a 130px square of a 9:16 portrait is mostly face and cuts
   * everything the shot was framed around.
   *
   * 2/3 is also much closer to the source's own 9/16, so the crop this frame
   * has to make is far gentler than the square's was.
   */
  aspect-ratio: 2 / 3;
  height: auto;
  max-height: none;
  border-radius: 0;
}
/*
 * Biased upward — a dead-centre crop of a standing portrait cuts the head off
 * at the brow. Less pronounced now the frame is 2:3 rather than square, since
 * there is much less to crop away.
 */
.dnv-frame.is-floating .dn-stage-video,
.dnv-frame.is-floating .dn-stage-still { object-position: center 42%; }

/* ── The stage itself ───────────────────────────────────────────────────── */
.dn-stage { display: flex; justify-content: center; min-height: 0; }

.dn-stage-frame {
  position: relative;
  aspect-ratio: 9 / 16;
  height: 100%;
  max-height: 100%;
  border-radius: 14px;
  overflow: hidden;
  background: #000;
}

/*
 * Two stacked <video> elements and the poster beneath them.
 *
 * The poster is the floor: it is always there, so the first clip dissolves up
 * out of a still of her rather than out of black, and a clip that fails leaves
 * something to look at instead of a hole.
 */
.dn-stage-still {
  position: absolute;
  inset: 0;
  /* The floor. Explicit, because all three layers sit in the same place and
     paint order would otherwise be decided by DOM order alone. */
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.dn-stage-video {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /*
   * No transition. The clips are bookended on one frame — every one starts and
   * ends on the picture in poster.jpeg — so the outgoing last frame and the
   * incoming first frame are identical and a straight cut is invisible.
   *
   * A fade here made it WORSE, not softer: with the two ends already matching,
   * the only thing a dissolve blends is the incoming clip's first moments of
   * movement against a frozen frame, which reads as a ghost.
   */
  opacity: 0;
}
.dn-stage-video.is-front { opacity: 1; }

.dn-stage-badge {
  position: absolute;
  z-index: 2;
  left: 8px;
  bottom: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 10px;
  /* The scrim must never eat a tap meant for the frame behind it. */
  pointer-events: none;
}

.dn-stage-replay {
  position: absolute;
  z-index: 2;
  inset: auto 0 12px;
  margin: 0 auto;
  width: max-content;
  padding: 6px 14px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  cursor: pointer;
}

/*
 * The buttons are exercises, and saying so is the whole point: a static move
 * cannot be guaranteed to answer arbitrary AI wording, so it must not present
 * itself as the reply.
 */
/*
 * Coaching hints.
 *
 * A reference card, and it must not read as a suggested reply. No send
 * affordance and no chat-bubble shape — the visual language of something to
 * read, not something to click through into the transcript.
 */
.dn-hints { display: flex; flex-direction: column; gap: 12px; }
.dn-hints-note {
  margin: 0;
  color: var(--neutral-500);
  font-size: 11px;
  line-height: 1.5;
  font-style: italic;
}
.dn-hint-card {
  border-left: 3px solid #a855f7;
  border-radius: 0 8px 8px 0;
  background: rgb(135 80 232 / 10%);
  padding: 12px 14px;
}
.dn-hint-label {
  margin: 0 0 4px;
  color: #cc95ff;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.dn-hint-text { margin: 0; color: #fff; font-size: 13px; line-height: 1.55; }

.dn-hint-actions { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.dn-hint-next {
  padding: 7px 14px;
  border: 1px solid var(--neutral-700);
  border-radius: 999px;
  background: none;
  color: var(--neutral-300, #ccc);
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.dn-hint-next:hover { border-color: rgb(156 80 232 / 70%); color: #fff; }
.dn-hint-next:focus-visible { outline: 2px solid #cc95ff; outline-offset: 2px; }
.dn-hint-count { color: var(--neutral-500); font-size: 11px; }

/* All of them, always reachable — browsable in any order is the contract. */
.dn-hint-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.dn-hint-pill {
  padding: 5px 11px;
  border: 1px solid var(--neutral-700);
  border-radius: 999px;
  background: none;
  color: var(--neutral-400);
  font-family: 'Heebo', sans-serif;
  font-size: 11px;
  cursor: pointer;
}
.dn-hint-pill:hover { color: #fff; border-color: var(--neutral-600); }
.dn-hint-pill:focus-visible { outline: 2px solid #cc95ff; outline-offset: 2px; }
.dn-hint-pill.is-active { border-color: #a855f7; color: #fff; background: rgb(135 80 232 / 16%); }
/* Read, not completed — hints finish nothing. */
.dn-hint-pill.is-read { color: var(--neutral-300, #ccc); }

/* ── Mode ───────────────────────────────────────────────────────────────── */
.dn-modes { display: flex; gap: 8px; margin-bottom: 14px; }

.dn-mode {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border: 1px solid var(--neutral-700);
  border-radius: 12px;
  background: var(--neutral-800);
  color: #e9e9e9;
  font-family: 'Heebo', sans-serif;
  text-align: left;
  cursor: pointer;
}
.dn-mode.is-selected {
  border-color: rgb(156 80 232 / 70%);
  background: rgb(135 80 232 / 18%);
}
.dn-mode-label { font-size: 13px; font-weight: 600; color: #fff; }
.dn-mode-sub { font-size: 11px; color: var(--neutral-400); line-height: 1.3; }

/* In the status strip, so the user always knows which mode they are in. */
.dn-mode-badge {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(204, 149, 255, 0.12);
  color: #cc95ff;
  font-weight: 600;
  white-space: nowrap;
}

/*
 * Private mode, stated for the whole date.
 *
 * The warm colour the paid control uses, not the coaching violet, so the strip
 * reads as a different mode rather than a variation on the practice one. It is
 * the only standing evidence that 30 credits bought something: the button that
 * did it removes itself, and a reload leaves nothing else behind.
 */
.dn-mode-private {
  background: rgba(224, 139, 176, .16);
  color: #e08bb0;
}

/* ── Freestyle ──────────────────────────────────────────────────────────── */
.dn-freestyle { display: flex; flex-direction: column; gap: 8px; }
.dn-freestyle-lead { margin: 0; color: var(--neutral-400); font-size: 12px; }

/* Focus is a ring, not a colour shift — colour alone is not an indicator. */
.dn-item:focus-visible,
.dn-venue:focus-visible,
.dn-prompt:focus-visible {
  outline: 2px solid #cc95ff;
  outline-offset: 2px;
}

.dn-linkbtn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--neutral-500);
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}
.dn-linkbtn:hover { color: #fff; }

.dn-treat-btn {
  margin-left: auto;
  padding: 7px 14px;
  border: 1px solid rgb(156 80 232 / 70%);
  border-radius: 999px;
  background: rgb(135 80 232 / 22%);
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

/* ── Purchase card ──────────────────────────────────────────────────────── */
.dn-purchase {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid rgb(156 80 232 / 45%);
  border-radius: 10px;
  background: rgb(135 80 232 / 14%);
  color: #fff;
  font-size: 12px;
}
.dn-purchase-icon { display: inline-flex; color: #cc95ff; flex-shrink: 0; }
.dn-purchase-text { flex: 1 1 auto; }
.dn-purchase-cost { color: var(--neutral-400); }
.dn-purchase-x {
  padding: 2px;
  border: 0;
  background: none;
  color: var(--neutral-500);
  cursor: pointer;
}
.dn-purchase-x:hover { color: #fff; }

/* ── Endings and debrief ────────────────────────────────────────────────── */
/* Centred: it is one line and one button, and left-aligning them against a
   full-width deck left the button stranded in the corner. */
.dn-ending {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-bottom: 5px;
}
.dn-ending-lead { margin: 0; color: #fff; font-size: 14px; text-align: center; }
/* Not an error — the gateway asked for time and this is the time. */
.dn-ending-wait {
  margin: 0;
  color: var(--neutral-400, #9a9a9a);
  font-size: 12px;
  text-align: center;
}
.dn-ending-actions { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; }

.dn-btn-primary {
  padding: 10px 18px;
  border: 0;
  border-radius: 10px;
  background: #7c3aed;
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.dn-btn-primary:hover:not(:disabled) { background: #6d28d9; }
.dn-btn-primary:disabled { opacity: .55; cursor: default; }

.dn-btn-ghost {
  padding: 10px 18px;
  border: 1px solid var(--neutral-700);
  border-radius: 10px;
  background: none;
  color: var(--neutral-300, #ccc);
  font-family: 'Heebo', sans-serif;
  font-size: 13px;
  cursor: pointer;
}
.dn-btn-ghost:hover:not(:disabled) { color: #fff; border-color: var(--neutral-600); }
.dn-btn-ghost:disabled { opacity: .55; cursor: default; }
/* The ended panel's way out is a real link, not a button — same box. */
a.dn-btn-ghost { display: inline-block; text-decoration: none; line-height: 1.2; }

.dn-debrief { display: flex; flex-direction: column; gap: 12px; }
.dn-debrief-title { margin: 0; color: #fff; font-size: 18px; font-weight: 700; }

/*
 * No bars, no numbers, no flag.
 *
 * The gateway computes no assessment — `scores`, `overall`, `mood` and
 * `kiss_outcome` all come back null — so there is nothing to draw. A bar at
 * zero would be a fabricated judgement wearing the old trustworthy styling.
 */
.dn-debrief-summary { margin: 0; color: #e9e9e9; font-size: 13px; line-height: 1.55; }

.dn-debrief-prompts { display: flex; flex-wrap: wrap; gap: 6px; }
.dn-debrief-prompts-lead {
  width: 100%;
  margin: 0 0 2px;
  color: var(--neutral-500);
  font-size: 11px;
}
.dn-prompt {
  padding: 7px 12px;
  border: 1px solid var(--neutral-700);
  border-radius: 999px;
  background: var(--neutral-800);
  color: #e9e9e9;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  cursor: pointer;
}
.dn-prompt:hover:not(:disabled) { border-color: rgb(156 80 232 / 70%); }
.dn-debrief-restart { align-self: flex-start; }

/*
 * The private continuation.
 *
 * Set apart from the coaching above it because it is a different kind of
 * thing: everything else in this drawer is feedback, and this is a door. It
 * gets the warm accent rather than the coaching violet, and a rule above it so
 * it cannot be tapped while skimming the scorecard.
 */
.dn-private {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
  padding-top: 16px;
  border-top: 1px solid var(--neutral-700);
}
.dn-private-lead {
  margin: 0;
  color: var(--neutral-300, #ccc);
  font-size: 13px;
  line-height: 1.5;
}
.dn-private-btn {
  align-self: flex-start;
  background: #b0316f;
}
.dn-private-btn:hover:not(:disabled) { background: #93275c; }

/* ── Modals and the bottom sheet ────────────────────────────────────────── */
.dn-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.72);
}
/* On a phone the paid menu is a sheet, so it sits at the bottom. */
.dn-overlay-sheet { align-items: flex-end; }
@media (min-width: 769px) {
  .dn-overlay-sheet { align-items: center; }
}

.dn-modal,
.dn-sheet {
  width: 100%;
  max-width: 460px;
  max-height: 86vh;
  overflow-y: auto;
  padding: 18px;
  border: 1px solid var(--neutral-700);
  border-radius: 16px;
  background: var(--neutral-900, #121212);
  box-sizing: border-box;
}
/*
 * The dialog takes focus on open so the keyboard and screen reader land inside
 * it, and the browser drew its default ring around the whole card for that —
 * a white rectangle framing a dialog nobody navigated to by tabbing. The ring
 * belongs on the controls within, which keep theirs.
 */
.dn-modal:focus,
.dn-modal:focus-visible,
.dn-sheet:focus,
.dn-sheet:focus-visible { outline: none; }

/*
 * The venue picker is the one dialog taller than a laptop viewport: two mode
 * cards plus nine venues plus the footer. Scrolling the whole card hid the
 * mode titles above the fold and the Begin button below it, so the choice and
 * the action are pinned and only the list moves.
 */
.dn-modal.is-picker {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}
.dn-modal-top { flex-shrink: 0; padding: 18px 18px 0; }
.dn-venues-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 18px;
}
.dn-modal-bottom {
  flex-shrink: 0;
  padding: 12px 18px 18px;
  border-top: 1px solid var(--neutral-800);
}
.dn-modal.is-picker .dn-venues { margin-bottom: 0; }
.dn-modal.is-picker .dn-modal-foot { margin: 0 0 10px; }
.dn-modal.is-picker .dn-toggle-row { margin: 10px 0; }

/* Names the thing being chosen. Without them the two mode cards were just a
   pair of unlabelled paragraphs once their titles scrolled away. */
.dn-field-label {
  margin: 0 0 6px;
  color: var(--neutral-500);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
}

.dn-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.dn-modal-title { margin: 0; color: #fff; font-size: 16px; font-weight: 700; }
.dn-modal-lead { margin: 0 0 10px; color: var(--neutral-400); font-size: 12px; line-height: 1.5; }

.dn-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  padding: 0;
  border: 1px solid var(--neutral-700);
  border-radius: 50%;
  background: none;
  color: var(--neutral-400);
  cursor: pointer;
}
.dn-close:hover:not(:disabled) { color: #fff; border-color: var(--neutral-600); }
.dn-close:disabled { opacity: .4; cursor: default; }

.dn-disclosure {
  margin: 0 0 10px;
  color: var(--neutral-500);
  font-size: 11px;
}

.dn-notice {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--neutral-800);
  color: var(--neutral-300, #ccc);
  font-size: 12px;
  line-height: 1.45;
}
.dn-notice-warn {
  background: rgba(224, 164, 164, 0.1);
  color: #e6c9c9;
}

.dn-venues {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
@media (max-width: 520px) {
  .dn-venues { grid-template-columns: repeat(2, 1fr); }
}

/*
 * A venue card is mostly its poster.
 *
 * It was a 44px thumbnail beside two lines of text, which is the shape of a
 * list row rather than a choice — at that size the coffee shop, the park and
 * the restaurant are three brown rectangles. The poster IS the information
 * here, so it gets the card and the text sits under it.
 */
.dn-venue {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--neutral-700);
  border-radius: 14px;
  background: var(--neutral-800);
  color: #e9e9e9;
  font-family: 'Heebo', sans-serif;
  text-align: left;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}
.dn-venue:hover { border-color: rgb(156 80 232 / 45%); }
.dn-venue.is-selected {
  border-color: rgb(156 80 232 / 85%);
  background: rgb(135 80 232 / 18%);
}

.dn-venue-art {
  width: 100%;
  aspect-ratio: 3 / 4;
  display: block;
  object-fit: cover;
  /*
   * Full opacity, always.
   *
   * These were held back to .82 until chosen, which was fine when the art was
   * a 44px thumbnail and invisible once it filled the card: eight venues still
   * render the placeholder poster, and a blurred image dimmed to 82% beside
   * one sharp full-opacity card reads as disabled. It made venues look locked
   * that were free to walk into.
   *
   * Selection is carried by the border and the tint on `.dn-venue.is-selected`,
   * which say the same thing without implying anything about availability.
   */
  background: var(--neutral-900);
}

.dn-venue-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: 8px 10px 10px;
}
.dn-venue-title { font-size: 14px; font-weight: 600; color: #fff; }
.dn-venue-mood { font-size: 11px; color: var(--neutral-400); line-height: 1.35; }
.dn-venue-meta { font-size: 10px; color: var(--neutral-500); }

.dn-toggle-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 12px;
  color: #e9e9e9;
  font-size: 12px;
  cursor: pointer;
}
.dn-toggle-row input { margin-top: 2px; flex-shrink: 0; }
.dn-toggle-sub { display: block; color: var(--neutral-500); font-size: 11px; }

.dn-modal-foot { margin: 0 0 12px; color: var(--neutral-500); font-size: 11px; }

.dn-begin {
  width: 100%;
  padding: 12px;
  border: 0;
  border-radius: 12px;
  background: #7c3aed;
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.dn-begin:disabled { opacity: .45; cursor: default; }

.dn-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.dn-items-empty { color: var(--neutral-500); font-size: 12px; }

.dn-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--neutral-700);
  border-radius: 10px;
  background: var(--neutral-800);
  color: #e9e9e9;
  font-family: 'Heebo', sans-serif;
  font-size: 13px;
  cursor: pointer;
}
.dn-item:hover:not(:disabled) { border-color: rgb(156 80 232 / 70%); }
.dn-item:disabled { opacity: .45; cursor: default; }
.dn-item-cost { flex-shrink: 0; color: #cc95ff; font-weight: 600; font-size: 12px; }

.dn-confirm { display: flex; flex-direction: column; gap: 8px; }
.dn-confirm-title { margin: 0; color: #fff; font-size: 15px; font-weight: 600; }
.dn-confirm-body { margin: 0; color: var(--neutral-400); font-size: 13px; }
.dn-confirm-actions { display: flex; gap: 8px; margin-top: 6px; }

/* ── Phone ──────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .dn-deck { padding: 8px 12px 0; max-height: 52vh; }
  /* No room for it beside the venue and the mood; the modal and the venue
     picker both still carry the disclosure. */
  .dn-ai-label { display: none; }
  .dn-sheet {
    max-width: none;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    /* Clear of the home indicator on a notched phone. */
    padding-bottom: max(18px, env(safe-area-inset-bottom));
  }
}

/*
 * Reduced motion: the stage already swaps to the poster in JS, and these stop
 * the surrounding chrome from animating too.
 */
@media (prefers-reduced-motion: reduce) {
  .dn-mode,
  .dn-venue { transition: none; }
}

/* ── The walkthrough ────────────────────────────────────────────────────── */
/*
 * Prefixed `dnt-` rather than reusing the chat page's `.tour-*`, which are
 * declared `is:global` there and would then be two features sharing one set of
 * rules across two pages. Changing one would quietly change the other.
 */
.dnt-root {
  position: fixed;
  inset: 0;
  z-index: 9998;
  font-family: 'Heebo', sans-serif;
}

/* Catches the click that closes the tour. The ring above it is the only thing
   that must not be clickable, so the scrim is a plain full-screen layer. */
.dnt-scrim { position: absolute; inset: 0; }

/*
 * The cut-out.
 *
 * One element with an enormous spread shadow: the shadow paints everything
 * outside the box, so the box itself stays clear with no four-rectangle
 * arithmetic and nothing to drift out of alignment when it moves.
 */
.dnt-ring {
  position: fixed;
  border-radius: 10px;
  box-shadow: 0 0 0 4000px rgba(0, 0, 0, .74);
  outline: 2px solid #a855f7;
  pointer-events: none;
  transition: top .22s ease, left .22s ease, width .22s ease, height .22s ease;
}

.dnt-card {
  position: fixed;
  z-index: 1;
  width: min(300px, calc(100vw - 24px));
  padding: 14px 16px;
  border: 1px solid #7c3aed;
  border-radius: 12px;
  background: #1a1a2e;
  color: #fff;
  box-shadow: 0 8px 32px rgba(0, 0, 0, .5);
  box-sizing: border-box;
}

.dnt-title { margin: 0 0 6px; font-size: 14px; font-weight: 700; color: #c4b5fd; }
.dnt-text { margin: 0 0 14px; font-size: 12.5px; line-height: 1.5; color: #ccc; }

.dnt-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.dnt-dots { display: flex; gap: 5px; }
.dnt-dot { width: 7px; height: 7px; border-radius: 50%; background: #444; transition: background .2s; }
.dnt-dot.is-on { background: #7c3aed; }

.dnt-btns { display: flex; gap: 8px; }
.dnt-skip,
.dnt-next {
  border: 0;
  border-radius: 8px;
  padding: 6px 14px;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.dnt-skip { background: transparent; color: #888; }
.dnt-skip:hover { color: #fff; }
.dnt-next { background: #7c3aed; color: #fff; }
.dnt-next:hover { background: #6d28d9; }

@media (prefers-reduced-motion: reduce) {
  .dnt-ring { transition: none; }
}

/* ── A wider profile panel ──────────────────────────────────────────────── */
/*
 * There is no chat list on this page, so the 221px the chat page gives the
 * profile is money left on the table — and her video lives in that panel.
 * Scoped to this page's body class so ordinary chat is untouched.
 *
 * clamp() rather than a fixed number: the centre column is where the
 * conversation happens and must never be squeezed to make room for a portrait.
 */
body.date-night-page .chat-profile-panel,
body.date-night-page .chat-layout:has(.chat-list-panel.collapsed) .chat-profile-panel {
  width: clamp(280px, 30vw, 460px);
  min-width: clamp(280px, 30vw, 460px);
}

/* ── Actions under the composer ─────────────────────────────────────────── */
.dn-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  /* Nearly flush. The composer above it already carries the row's gutter, and
     16px here pushed the first pill out of line with it. */
  padding: 8px 2px 0;
}
/* Two sides, like the chat page's own row: give on the left, say on the
   right — where the sticker and gift buttons used to be. */
.dn-actions-side { display: flex; align-items: center; gap: 8px; min-width: 0; }
.dn-actions:empty { display: none; }

/*
 * These borrow `.quick-prompts-link` from bn_chat.css so they sit in the row
 * as the same kind of control. That class was written for an <a>, so this only
 * undoes the defaults a <button> brings with it.
 */
.dn-link {
  /* Transparent rather than none: the ghost and primary variants carry a 1px
     border, and a borderless sibling is 2px shorter and sits a pixel low. */
  border: 1px solid transparent;
  font-family: 'Heebo', sans-serif;
  cursor: pointer;
}
.dn-link:disabled { opacity: .45; cursor: default; }
.dn-link:disabled:hover { background: var(--neutral-800); color: var(--neutral-400); }

/* The quieter of the two, matching how Video Prompts sits beside Quick
   Prompts — dashed and unfilled, because it ends the session rather than
   doing something in it. */
.dn-link-ghost {
  border: 1px dashed var(--neutral-700, #333);
  background: transparent;
  color: var(--neutral-500);
}
.dn-link-ghost:hover { color: #fff; border-color: var(--neutral-600); }

/* ── Slide-out panels ───────────────────────────────────────────────────── */
/*
 * The pattern that keeps the transcript: nothing that is read once lives above
 * the composer. Closed, a panel costs a handle; open, it covers the chat
 * rather than shortening it.
 */
.dn-slide {
  position: fixed;
  z-index: 990;
  display: flex;
  pointer-events: none;
}
.dn-slide > * { pointer-events: auto; }

/* The handle. Stays put whether open or shut, so the control that opened the
   panel is the one that closes it. */
.dn-slide-handle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border: 1px solid rgb(156 80 232 / 60%);
  border-radius: 999px;
  background: rgb(30 22 44 / 96%);
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
  white-space: nowrap;
}
.dn-slide-handle:hover { border-color: #cc95ff; }
.dn-slide-handle-arrow { display: inline-flex; color: #cc95ff; transition: transform .25s ease; }
.dn-slide.is-open .dn-slide-handle-arrow { transform: rotate(180deg); }

/*
 * A slow breath while the panel is shut — enough to be noticed on the second
 * glance, not enough to compete with the conversation. It stops the moment the
 * panel opens, because then there is nothing left to point at.
 */
@keyframes dn-breathe {
  0%, 100% { transform: scale(1); opacity: .75; }
  50%      { transform: scale(1.35); opacity: 1; }
}
.dn-slide-handle.is-pulsing .dn-slide-handle-arrow {
  animation: dn-breathe 2.4s ease-in-out infinite;
}

.dn-slide-body {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--neutral-700);
  background: var(--neutral-900, #121212);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .55);
  overflow: hidden;
  /* Collapsed rather than removed, so opening it animates. visibility keeps a
     shut panel out of the tab order. */
  transition: transform .28s cubic-bezier(.22,.61,.36,1), opacity .2s ease, visibility .28s;
  visibility: hidden;
  opacity: 0;
}
.dn-slide.is-open .dn-slide-body { visibility: visible; opacity: 1; transform: none; }

.dn-slide-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-shrink: 0;
  padding: 12px 14px;
  border-bottom: 1px solid var(--neutral-800);
}
.dn-slide-title { color: #fff; font-size: 13px; font-weight: 700; }
.dn-slide-close {
  display: inline-flex;
  padding: 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--neutral-500);
  cursor: pointer;
}
.dn-slide-close:hover { color: #fff; }
.dn-slide-content { padding: 14px; overflow-y: auto; }

/* ── Corner variant: the coaching cards ─────────────────────────────────── */
.dn-slide-corner {
  right: 16px;
  bottom: 108px;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}
/* Handle below the card, next to the thumb and the composer it belongs to. */
.dn-slide-corner { flex-direction: column-reverse; }
.dn-slide-corner .dn-slide-body {
  width: min(400px, calc(100vw - 32px));
  /* Taller than a reference panel needs to be, because it now holds the three
     moves as well — those are full sentences and the reason to open it. */
  max-height: 68vh;
  border-radius: 14px;
  transform: translateY(12px) scale(.98);
  transform-origin: bottom right;
}

/* ── Side variant: the debrief ──────────────────────────────────────────── */
.dn-slide-side {
  top: 0;
  right: 0;
  height: 100%;
  align-items: center;
  flex-direction: row-reverse;
}
.dn-slide-side .dn-slide-body {
  width: min(380px, 92vw);
  height: 100%;
  border-radius: 0;
  border-right: 0;
  transform: translateX(100%);
}
/*
 * A slim tab on the edge, reading bottom-to-top.
 *
 * `vertical-rl` alone runs the text downwards; the 180 turns it the right way
 * up for a right-hand edge and, as a side effect, points the chevron INTO the
 * screen — which is the direction the panel comes from.
 *
 * The arrow deliberately has no rotation of its own here. It used to carry
 * -90deg, which composed with the tab's 180 and left it pointing at the floor.
 * Letting the tab's own frame orient it is one transform instead of two
 * fighting.
 */
.dn-slide-side .dn-slide-handle {
  /* A quiet tab, not a lozenge. 999px on a 36px-wide element made a
     half-stadium blob stuck to the edge of the video. */
  border-radius: 8px 0 0 8px;
  /* The purple ring belongs on a button you press once. This one sits against
     the video for the whole date, so it gets an edge you can see and not a
     colour that competes with her. Purple comes back on hover. */
  border: 1px solid rgb(255 255 255 / 10%);
  border-right: 0;
  padding: 16px 7px;
  gap: 8px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  box-shadow: -4px 0 14px rgba(0, 0, 0, .4);
}
.dn-slide-side .dn-slide-handle:hover { border-color: rgb(204 149 255 / 55%); }
.dn-slide-side.is-open .dn-slide-handle.is-pulsing .dn-slide-handle-arrow,
.dn-slide-side .dn-slide-handle.is-pulsing .dn-slide-handle-arrow {
  /* The vertical tab already carries a rotation; breathing it too would fight
     that transform. The tab itself pulses instead. */
  animation: none;
}
.dn-slide-side .dn-slide-handle.is-pulsing { animation: dn-breathe-soft 2.4s ease-in-out infinite; }
@keyframes dn-breathe-soft {
  0%, 100% { box-shadow: 0 6px 20px rgba(0, 0, 0, .45); }
  50%      { box-shadow: 0 6px 26px rgba(204, 149, 255, .45); }
}

/* ── Phone: both become a full-width sheet ──────────────────────────────── */
/*
 * Plain block layout, not the desktop flex arrangement. Reversing a flex
 * column to get the handle below the panel works when the wrapper is sized by
 * its content; anchored to the bottom of the viewport it put the sheet below
 * the fold instead. A bottom-anchored block with an absolutely placed handle
 * has one answer and no interaction between the two.
 */
@media (max-width: 768px) {
  .dn-slide-corner,
  .dn-slide-side {
    display: block;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    height: auto;
    padding: 0;
  }

  .dn-slide-corner .dn-slide-handle,
  .dn-slide-side .dn-slide-handle {
    position: absolute;
    right: 12px;
    bottom: 12px;
    writing-mode: horizontal-tb;
    transform: none;
    /* Back to the pill: the vertical tab's edge-hugging border is undone here,
       or the phone version loses its right-hand side. */
    border: 1px solid rgb(156 80 232 / 60%);
    border-radius: 999px;
    padding: 8px 12px;
  }
  /* Both are bottom sheets on a phone, so both point up: the panel is below
     and comes up. (Open, the handle is hidden and the header's × takes over.) */
  .dn-slide-side .dn-slide-handle-arrow,
  .dn-slide-corner .dn-slide-handle-arrow { transform: rotate(-90deg); }

  /*
   * An open sheet already has a close button in its header, and the handle was
   * floating on top of the sheet's own content. One control at a time.
   */
  .dn-slide.is-open .dn-slide-handle { display: none; }

  .dn-slide-corner .dn-slide-body,
  .dn-slide-side .dn-slide-body {
    width: 100%;
    height: auto;
    max-height: 68vh;
    border-radius: 16px 16px 0 0;
    border-right: 1px solid var(--neutral-700);
    transform: translateY(100%);
    /* Clear of the home indicator. */
    padding-bottom: env(safe-area-inset-bottom);
  }
  /* Written against the same two selectors it has to beat, so the open state
     cannot lose to whichever rule happens to come last. */
  .dn-slide-corner.is-open .dn-slide-body,
  .dn-slide-side.is-open .dn-slide-body { transform: none; }

  .dn-actions { padding: 6px 2px 0; }
}

@media (prefers-reduced-motion: reduce) {
  .dn-slide-handle.is-pulsing,
  .dn-slide-handle.is-pulsing .dn-slide-handle-arrow { animation: none; }
  .dn-slide-body { transition: none; }
}

/* ── The scorecard inside a 380px drawer ────────────────────────────────── */
/*
 * It was laid out for the full width of the deck. In the drawer the bar took
 * its old share and pushed "7/10" off the right edge — the one number on the
 * row that anybody reads.
 */
.dn-slide-content { min-width: 0; }
/* "Recommended" beside a mode name in the picker. */
.dn-mode-pick {
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(204, 149, 255, 0.16);
  color: #cc95ff;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  vertical-align: middle;
}

/* ── Phone: the actions row becomes icons ───────────────────────────────── */
/*
 * Four labelled pills do not fit on one phone row. They wrapped onto two
 * lines, which then collided with the floating panel handle — so the words go
 * and the icons carry it, exactly as Video Prompts does beside Quick Prompts.
 * `title` and `aria-label` keep the name for anyone who needs it.
 */
/* `display: block` matters: the gift icon is an <img> and the rest are inline
   SVGs, and an inline image sits on the text baseline — which put that one
   button 2px lower than its neighbours. */
.dn-link-icon { flex-shrink: 0; display: block; }
.dn-link-text { display: inline-flex; align-items: center; gap: 4px; }

@media (max-width: 640px) {
  .dn-link-text { display: none; }

  /*
   * Bare icons, like the sticker and gift buttons whose place this row took —
   * `.chat-extra-icon` is background: none, border: none, and lets the glyph
   * be the control. A pill around a lone icon reads as a button that lost its
   * label, and the filled primary variant was a purple ring around a pencil.
   */
  .dn-link {
    padding: 0;
    background: none;
    border: 1px solid transparent;
    /* 44px: this is the one place these are thumb targets rather than links. */
    min-width: 44px;
    min-height: 44px;
    justify-content: center;
    color: var(--neutral-400);
  }
  .dn-link:hover:not(:disabled) { background: none; color: #fff; }
  .dn-link-ghost { border-color: transparent; }

  /*
   * Big enough to read at a glance, matching the sticker and gift icons.
   *
   * Two classes deep on purpose: bn_chat.css has `.quick-prompts-link img
   * { width: 12px }`, which outranks a lone `.dn-link-icon` and was keeping
   * the gift glyph small while the inline SVGs grew.
   */
  .dn-link .dn-link-icon { width: 20px; height: 20px; }

  .dn-actions { gap: 2px; }
  .dn-actions-side { gap: 2px; }
}

/* ── Debug panel ────────────────────────────────────────────────────────── */
/*
 * Development only — rendered solely when `?dnDebug=1`, the localStorage flag
 * or `window.__DN_DEBUG` is set, so none of this reaches an ordinary session.
 *
 * Fixed and out of flow on purpose: it must not move the layout it is there to
 * observe. Top-right under the header, narrow enough to leave her video
 * visible beside it, and scrollable rather than tall — a panel that grows with
 * the log would cover the thing being debugged.
 */
.dn-debug {
  position: fixed;
  top: 76px;
  right: 12px;
  z-index: 9000;
  width: min(380px, calc(100vw - 24px));
  border: 1px solid #3d2b57;
  border-radius: 10px;
  background: rgba(10, 8, 14, .96);
  color: #e9e9e9;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  line-height: 1.45;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .6);
  overflow: hidden;
}

.dn-debug-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 8px;
  border-bottom: 1px solid #2a1f3d;
  background: #17102400;
}
.dn-debug-toggle,
.dn-debug-btn {
  border: 0;
  background: none;
  color: #cc95ff;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  padding: 2px 4px;
}
.dn-debug-btn { color: var(--neutral-400, #9a9a9a); font-weight: 400; }
.dn-debug-btn:hover { color: #fff; }
.dn-debug-head-actions { display: flex; gap: 4px; }

.dn-debug-body { max-height: min(62vh, 560px); overflow-y: auto; }

/* Current state: what is true right now. */
.dn-debug-state { margin: 0; padding: 8px; border-bottom: 1px solid #2a1f3d; }
.dn-debug-row { display: flex; gap: 8px; }
.dn-debug-row dt { flex: 0 0 116px; color: var(--neutral-500, #7a7a7a); }
.dn-debug-row dd { margin: 0; min-width: 0; overflow-wrap: anywhere; color: #fff; }
.dn-debug-row dd.is-off { color: #5c5c5c; }

/* The log: what happened, and what to expect from it. */
.dn-debug-log { list-style: none; margin: 0; padding: 4px 8px 8px; }
.dn-debug-empty { color: var(--neutral-500, #7a7a7a); padding: 6px 0; }
.dn-debug-entry { padding: 6px 0; border-bottom: 1px solid #1d1629; }
.dn-debug-entry:last-child { border-bottom: 0; }
.dn-debug-entry-head { display: flex; gap: 8px; margin: 0; }
.dn-debug-time { color: #5c5c5c; }
.dn-debug-event { color: #cc95ff; font-weight: 700; }
/* The half that makes the log readable by someone not holding the flow in
   their head, so it gets the emphasis rather than the raw fields. */
.dn-debug-expect { margin: 2px 0 0; color: #9fd8c4; }
.dn-debug-fields { margin: 2px 0 0; color: var(--neutral-400, #9a9a9a); }
.dn-debug-fields span { margin-right: 10px; white-space: nowrap; }
.dn-debug-fields b { color: #6f6f6f; font-weight: 400; margin-right: 3px; }

@media (max-width: 768px) {
  .dn-debug { top: auto; bottom: 8px; right: 8px; left: 8px; width: auto; }
  .dn-debug-body { max-height: 40vh; }
}

/*
 * Ask Emma for a hint.
 *
 * Set apart from the tip cards above it, because it is a different kind of
 * thing: those are read, this one SENDS. Given the emphasis the cards do not
 * get — it is the useful answer for somebody actually stuck — and a plain line
 * of copy saying what it costs, because a button that quietly spends a message
 * is the kind of surprise people remember.
 */
.dn-hint-ask {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--neutral-700);
}
.dn-hint-ask-btn {
  align-self: flex-start;
  padding: 9px 16px;
  border: 1px solid rgb(156 80 232 / 70%);
  border-radius: 10px;
  background: rgb(135 80 232 / 18%);
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.dn-hint-ask-btn:hover:not(:disabled) { background: rgb(135 80 232 / 30%); }
.dn-hint-ask-btn:focus-visible { outline: 2px solid #cc95ff; outline-offset: 2px; }
.dn-hint-ask-btn:disabled { opacity: .55; cursor: default; }
.dn-hint-ask-note {
  margin: 0;
  color: var(--neutral-500);
  font-size: 11px;
  line-height: 1.5;
}

/*
 * An unresolved treat, and the way out of it.
 *
 * Inside the warning notice rather than beside it: the two buttons only make
 * sense next to the sentence explaining why they exist, and separating them
 * would leave the notice looking like a dead end again — which is exactly the
 * bug this replaced.
 */
.dn-unresolved { display: flex; flex-direction: column; gap: 10px; }
.dn-unresolved-lead { margin: 0; }
.dn-unresolved-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.dn-unresolved-btn { font-size: 12px; padding: 8px 14px; }
.dn-unresolved-note {
  margin: 0;
  color: var(--neutral-500);
  font-size: 11px;
  line-height: 1.45;
}

/* Treats are locked and something needs resolving behind this button. */
.dn-link-attention { color: #e5b45e; }
.dn-link-attention:hover { color: #f5cd85; }

/*
 * The paid private scene.
 *
 * Warm rather than violet, because it is not part of the coaching row's
 * family: everything else there is free and reversible, and this one spends
 * 30 credits. Distinct enough that nobody presses it looking for a hint.
 */
.dn-link-private { color: #e08bb0; }
.dn-link-private:hover:not(:disabled) { color: #f3aecb; }

/* Between the purchase and the gateway's answer. Not a spinner on a button —
   the purchase may outlive this render, and a reload has to find the same
   state rather than a fresh offer. */
.dn-private-pending {
  align-self: center;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(224, 139, 176, .12);
  color: #e08bb0;
  font-size: 11px;
  white-space: nowrap;
}


/* ── Scene controls ────────────────────────────────────────────────────────
 *
 * Only visible inside a paid private scene, and only ever Prev and Next. The
 * arc is three scenes long; a scrubber would be more interface than content.
 */
.dn-scene-nav {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
}
.dnv-frame.is-panel { position: relative; }
.dnv-frame.is-panel .dn-scene-nav {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 2;
}

.dn-scene-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 8px 12px;
  border: 1px solid rgb(255 255 255 / 22%);
  border-radius: 999px;
  /* Over video, so it carries its own contrast rather than borrowing the
     frame's — a light scene would otherwise swallow it. */
  background: rgb(18 12 26 / 72%);
  backdrop-filter: blur(6px);
  color: #fff;
  font-family: 'Heebo', sans-serif;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.dn-scene-btn:hover {
  background: rgb(124 58 237 / 82%);
  border-color: rgb(196 160 245 / 60%);
}
.dn-scene-btn span[aria-hidden] { font-size: 15px; line-height: 1; }

/*
 * Phone: the controls come out of the frame entirely.
 *
 * They used to hang under the floating thumbnail as bare chevrons, which put
 * a 27px control over the transcript — awkward to hit on purpose and easy to
 * hit by accident. Under the composer they sit in the row a thumb already
 * uses, with their labels back.
 */
.dn-scene-nav-bar {
  display: flex;
  justify-content: stretch;
  padding: 6px 0 0;
}
.dn-scene-nav-bar .dn-scene-nav {
  flex: 1;
  gap: 8px;
  justify-content: stretch;
}
.dn-scene-nav-bar .dn-scene-btn {
  flex: 1;
  justify-content: center;
  padding: 10px 12px;
  /* Opaque here: this one is on the input bar, not over video. */
  background: rgb(38 30 50 / 100%);
}
.dn-scene-nav-bar .dn-scene-btn:active { background: rgb(124 58 237 / 90%); }


/* ── Permanent venue entry ─────────────────────────────────────────────────
 *
 * A venue is bought once and kept. The badge sits on the poster because that
 * is where the eye already is while choosing, and because a price found only
 * after selecting is a price found too late.
 */
.dn-venue-artwrap { position: relative; display: block; }

/*
 * The selected card's moving preview.
 *
 * Laid over the poster in the same box instead of replacing it, so selecting
 * a card never shifts the layout. The poster stays as the first frame, the
 * fallback, and what reduced-motion users see.
 */
.dn-venue-preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  z-index: 1;
  pointer-events: none;
}

/* The badge sits above the preview. A price or "Unlocked" disappearing the
   moment the card is chosen is the worst possible moment to lose it. */
.dn-venue-lock { z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  .dn-venue-preview { display: none; }
}

.dn-venue-lock {
  position: absolute;
  right: 6px;
  bottom: 6px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px;
  border-radius: 999px;
  /* Its own contrast, not the poster's — these sit over photographs that are
     light in some venues and dark in others. */
  background: rgb(18 12 26 / 78%);
  border: 1px solid rgb(255 255 255 / 18%);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .01em;
}
.dn-venue-lock.is-owned {
  background: rgb(52 168 116 / 88%);
  border-color: rgb(120 226 180 / 45%);
}
.dn-venue-lock.is-pending {
  background: rgb(196 138 32 / 88%);
  border-color: rgb(240 200 120 / 45%);
}
/* Visible but not open yet. Neutral, not red: nothing is wrong, it's just
   not ready. */
.dn-venue-lock.is-soon {
  background: rgb(28 24 36 / 90%);
  border-color: rgb(196 160 245 / 35%);
  color: #e6dcf5;
}
.dn-venue-lock.is-off {
  background: rgb(60 60 66 / 88%);
  color: var(--neutral-300);
}

.dn-venue-confirm {
  padding: 10px 12px;
  border: 1px solid rgb(156 80 232 / 45%);
  border-radius: 12px;
  background: rgb(135 80 232 / 12%);
}
.dn-venue-confirm-lead {
  margin: 0 0 10px;
  color: #e9e9e9;
  font-size: 12px;
  line-height: 1.45;
}
.dn-venue-confirm-lead strong { color: #fff; }
.dn-venue-confirm-row { display: flex; gap: 8px; }
.dn-venue-confirm-row .dn-btn-ghost { flex: 0 0 auto; }
.dn-venue-confirm-row .dn-begin { flex: 1 1 auto; margin: 0; }
