/* ============================================================================
   Rip Sim — the Pro pull simulator. Its own file because the module is its own
   component now: own template (_pack_sim.html), own JS (sim.js), own tests, and
   as of this change it renders on ALL THREE pack skins rather than v3 only.

   Scoped to `.pack-detail`, which every skin carries — v2 adds .pack-detail-v2
   on top, v3 adds both. Living in pack_v3.css under a .pack-detail-v3 scope was
   what tied it to one skin: that file is only linked from pack_v3.html, so on
   v1 and v2 none of these rules existed at all.

   Symmetry is the design rule here, not a default: a $4 common and a grail get
   the same card, the same motion, the same weight. The ONLY colour asymmetry in
   the module is the session net and the per-rip P/L, and it runs the honest
   direction — down reads as down. No celebration state, no confetti keyframes.
   ============================================================================ */

.pack-detail .rr {
  margin: 18px 0; padding: 16px 18px;
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-warm); box-shadow: var(--shadow-card-sm);
}
.pack-detail .rr-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.pack-detail .rr-title { font: 800 18px var(--display); color: var(--ink); letter-spacing: -0.01em; margin: 0; }
.pack-detail .rr-sub { font: 400 12.5px var(--sans); color: var(--muted); line-height: 1.5; margin: 4px 0 0; max-width: var(--measure-prose); }
.pack-detail .rr-badge {
  flex: none; font: 700 9px var(--mono); text-transform: uppercase; letter-spacing: .1em;
  background: var(--sky-soft); color: var(--sky-deep); border: 1px solid var(--sky-deep);
  border-radius: 999px; padding: 2px 8px;
}

/* Ledger — the part that must never be out of sight while ripping. */
.pack-detail .rr-ledger {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); border-radius: var(--radius-small);
  overflow: hidden; margin-bottom: 12px;
}
.pack-detail .rr-cell { background: var(--paper); padding: 9px 12px; display: flex; flex-direction: column; gap: 3px; }
.pack-detail .rr-cell-lbl { font: 700 9.5px var(--mono); text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
.pack-detail .rr-cell-val { font: 800 19px var(--display); color: var(--ink); font-variant-numeric: tabular-nums; }
.pack-detail .rr-cell-net.is-down .rr-cell-val { color: var(--coral-deep); }
.pack-detail .rr-cell-net.is-up   .rr-cell-val { color: var(--lime-deep); }

.pack-detail .rr-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.pack-detail .btn.rr-btn { font: 700 13px var(--mono); padding: 7px 16px; }
.pack-detail .rr-reset {
  background: none; border: none; cursor: pointer; padding: 4px 2px;
  font: 600 12px var(--mono); color: var(--muted); text-decoration: underline;
}
.pack-detail .rr-reset:hover { color: var(--ink); }

/* `hidden` must beat any display we set. The UA stylesheet's `[hidden] {
   display: none }` loses to a class selector, so `.rr-reveal { display: flex }`
   silently defeated `el.reveal.hidden = true` — Reset cleared the ledger and
   left the last card sitting on screen next to the "Rip one" prompt. Any state
   in the stage that sets display needs this; .rr-beat already had its own. */
.pack-detail .rr-stage > [hidden] { display: none; }

/* Reveal. One row, identical for every outcome. */
.pack-detail .rr-reveal {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border: 1px solid var(--rule); border-radius: var(--radius-small);
  background: var(--paper);
}
.pack-detail .rr-reveal-slot {
  flex: none; width: 46px; height: 46px; display: grid; place-items: center;
  border: 1.5px dashed var(--rule); border-radius: var(--radius-small);
  font: 700 12px var(--mono); color: var(--muted); font-variant-numeric: tabular-nums;
}
.pack-detail .rr-reveal-body { flex: 1 1 auto; min-width: 0; }
.pack-detail .rr-reveal-name { font: 700 15px var(--sans); color: var(--ink); }
.pack-detail .rr-reveal-sub { font: 400 11.5px var(--mono); color: var(--muted); margin-top: 2px; }
.pack-detail .rr-reveal-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.pack-detail .rr-reveal-money { flex: none; text-align: right; }
.pack-detail .rr-reveal-val { font: 800 20px var(--display); color: var(--ink); font-variant-numeric: tabular-nums; }
.pack-detail .rr-reveal-pl { font: 600 11.5px var(--mono); color: var(--muted); margin-top: 2px; font-variant-numeric: tabular-nums; }
.pack-detail .rr-reveal-pl.is-down { color: var(--coral-deep); }
.pack-detail .rr-reveal-pl.is-up   { color: var(--lime-deep); }
.pack-detail .rr-empty { font: 400 12.5px var(--sans); color: var(--muted); margin: 0 2px; }

/* The only motion in the module: one neutral background beat, the same for a
   common and a grail. Mirrors the EV numeral's ev-flash (style.css) rather than
   introducing a second animation vocabulary. */
@keyframes rr-flash { from { background: var(--paper-dim); } to { background: var(--paper); } }
.pack-detail .rr-reveal.rr-flash { animation: rr-flash .5s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .pack-detail .rr-reveal.rr-flash { animation: none; }
}

/* Session markers over the pool's published shape. */
.pack-detail .rr-dist { margin-top: 14px; }
.pack-detail .rr-dist-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.pack-detail .rr-dist-lbl { font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
.pack-detail .rr-dist-key { font: 500 10px var(--mono); color: var(--muted); display: inline-flex; align-items: center; gap: 4px; }
.pack-detail .rr-dist-key-bar { width: 9px; height: 9px; border-radius: 2px; background: var(--rule); display: inline-block; }
.pack-detail .rr-dist-key-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-soft); display: inline-block; }
/* The chart is now the same SVG the pack page's pool composition uses, so the
   old flex-bar rules (.rr-dist-bars/.rr-bin/.rr-dot) are gone with it — the
   geometry, the hatch and the reference lines all live in the markup. */
.pack-detail .rr-dist-svg { display: block; width: 100%; height: auto; max-height: 190px; margin-top: 6px; }

.pack-detail .rr-fine { font: 400 11px var(--sans); color: var(--muted); line-height: 1.55; margin: 12px 0 0; max-width: var(--measure-prose); }

/* Free-tier lock: a true fact plus what Pro adds. No blurred or invented rows. */
.pack-detail .rr-locked { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.pack-detail .rr-locked-text { flex: 1 1 280px; font: 400 13px var(--sans); color: var(--ink-soft); line-height: 1.5; }

@media (max-width: 760px) {
  .pack-detail .rr-ledger { grid-template-columns: repeat(2, 1fr); }
  .pack-detail .rr-reveal { flex-wrap: wrap; }
  .pack-detail .rr-reveal-money { text-align: left; }
  .pack-detail .rr-dist-key { display: none; }
}

/* Collect Safe comparison row — same simulated rips, insured. Styled a step
   quieter than the uninsured ledger (it's the counterfactual, not the session),
   but with the identical net colouring so "insurance cost me money" reads as
   loudly as "insurance saved me". */
.pack-detail .rr-ledger-ss { margin-top: -1px; }
.pack-detail .rr-ledger-ss .rr-cell { background: var(--paper-warm); }
.pack-detail .rr-ledger-ss .rr-cell-val { font-size: 16px; }
.pack-detail .rr-cell-ss-lbl .rr-cell-lbl { color: var(--ink-soft); }
.pack-detail .rr-cell-note { font: 500 10px var(--mono); color: var(--muted); }

/* ── Rip Sim modal ─────────────────────────────────────────────────────────
   Geometry mirrors _pack_activity_modal.html deliberately: same backdrop wash,
   same card chrome, same head/scroll split, same 520px breakpoint. The sim is
   a second dialog on this page and the two should feel like one component, not
   two people's modals. Wider than the activity modal (720 vs 620) because the
   ledger is a four-column grid that gets cramped below ~680. */
/* height, not just `inset: 0`, and dvh before anything else is computed.
   On iOS Safari `vh` resolves against the LARGE viewport — the height the page
   would have if the URL bar and toolbar were retracted — so with the chrome
   actually showing, a fixed `inset: 0` box is ~190px taller than the visible
   area on a 16 Pro. Centering a card in that box pushes its ends behind the
   chrome at both ends, and the header is OUTSIDE .rr-modal-scroll by design, so
   the close button becomes unreachable rather than merely clipped.

   `dvh` tracks the currently visible viewport instead. The `vh` line first is
   the fallback: a browser that can't parse `dvh` drops that declaration and
   keeps this one, so the order is load-bearing, not stylistic. */
.pack-detail .rr-modal {
  position: fixed; inset: 0; z-index: 200;
  height: 100vh;
  height: 100dvh;
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.pack-detail .rr-modal[hidden] { display: none; }
.pack-detail .rr-modal-backdrop { position: absolute; inset: 0; background: rgba(26, 22, 18, .45); }
/* The modal card is already a 720px measure, so the prose max-widths that made
   sense for a full-page section just wrap the text early and leave a ragged
   gutter down the right of the dialog. */
.pack-detail .rr-modal .rr-sub,
.pack-detail .rr-modal .rr-fine { max-width: none; }
.pack-detail .rr-modal-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 720px;
  /* Percentage of the flex container's CONTENT box, so the modal's own padding
     is already subtracted — the card can never exceed what's on screen. The
     880px cap keeps it from becoming an absurd column on a tall desktop.

     This leans on the universal `box-sizing: border-box` reset in style.css,
     which every skin loads before this file. Under content-box .rr-modal's
     BORDER box would be 100dvh + 40px, so the card would still hang ~20px off
     the bottom and this fix would half-regress while looking untouched here.
     A test pins the reset for exactly that reason. */
  max-height: min(100%, 880px);
  display: flex; flex-direction: column;
  background: var(--paper-warm);
  border: var(--card-border); border-radius: var(--radius-card);
  box-shadow: 0 18px 44px rgba(26, 22, 18, .3);
}
.pack-detail .rr-modal .rr-head {
  padding: 16px 18px 12px; border-bottom: 1px solid var(--rule);
  flex: 0 0 auto; margin-bottom: 0;
}
/* overscroll-behavior stops iOS chaining the scroll to the page underneath. */
.pack-detail .rr-status { font: 600 11px var(--mono); color: var(--muted); margin: 6px 0 0; letter-spacing: .02em; }
.pack-detail .rr-status-ev.is-down { color: var(--coral-deep); }
.pack-detail .rr-status-ev.is-up { color: var(--lime-deep); }
.pack-detail .rr-refresh {
  background: none; border: none; padding: 0; cursor: pointer;
  font: 600 11px var(--mono); color: var(--sky-deep); text-decoration: underline;
  letter-spacing: .02em;
}
.pack-detail .rr-refresh:hover { color: var(--ink); }
.pack-detail .rr-refresh:disabled { color: var(--muted); cursor: default; text-decoration: none; }
.pack-detail .rr-modal-scroll { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 14px 18px 18px; }
.pack-detail .rr-x { background: none; border: none; font-size: 21px; line-height: 1; color: var(--muted); cursor: pointer; padding: 0 2px; }
.pack-detail .rr-x:hover { color: var(--ink); }

/* Rollout badge on the launcher. Sky, matching the button it sits on and the
   modal's PRO badge, so it reads as part of the control rather than an alert.
   Deliberately quiet: this announces a feature, it does not demand a click, and
   the row already has Rip Mode as its primary action. */
.pack-detail .rr-new-badge {
  margin-left: 6px; padding: 1px 5px; border-radius: 4px;
  background: var(--sky-deep); color: var(--paper);
  font: 700 9px var(--mono); letter-spacing: .06em; vertical-align: 1px;
}

/* The chase result. Muted and set in the mono face like the fine print, not
   like a prize announcement — it is an accounting line, and the number that
   matters in it is the money. */
.pack-detail .rr-chase-out {
  margin: -4px 0 12px; max-width: none;
  font: 500 12px/1.5 var(--mono); color: var(--muted);
}

/* Launcher, sitting beside Rip Mode. Inherits .boost-btn's geometry so the two
   read as one control group, then steps DOWN a level: Rip Mode is the primary
   action on this page and was being matched stroke for stroke, which left the
   row with two equal-weight greens and no signal about which one matters.

   Two changes carry the hierarchy, and it needs both. Weight: no tint, so a
   filled button sits next to an outlined one. Hue: sky rather than lime, which
   is also the accent the modal already uses for its PRO badge — so the button
   is coloured like the thing it opens. Colour alone would not have demoted it;
   a second saturated fill reads as a second primary. */
.pack-detail .rr-open-btn {
  cursor: pointer;
  background: var(--paper);
  border-color: var(--sky-deep);
  color: var(--sky-deep);
}
.pack-detail .rr-open-btn svg { fill: var(--sky-deep); }
/* .boost-btn:hover forces lime + --ink, so both need overriding here. */
.pack-detail .rr-open-btn:hover {
  background: var(--sky-soft);
  border-color: var(--sky-deep);
  color: var(--sky-deep);
}

/* The free tier's description panel. */
.pack-detail .rr-locked-lede { font: 400 13px var(--sans); color: var(--ink); line-height: 1.6; margin: 0 0 10px; }
.pack-detail .rr-locked-stat { font: 400 12.5px var(--sans); color: var(--muted); line-height: 1.6; margin: 0; }

/* ── The beat ──────────────────────────────────────────────────────────────
   Three dots settling, and nothing else. It shows no card identity ON PURPOSE:
   a reel of real card names would flash grails ~1-in-61 against their ~0.04%
   pull rate, manufacturing near-misses the odds never earned. Same duration and
   same motion whatever was drawn, because the draw already happened and this
   animation cannot know what it is holding.

   Reuses the dot-pulse idiom already on this page rather than introducing a
   third animation vocabulary. */
/* One stage, three states. The beat, the reveal and the empty prompt share a
   single grid cell and the stage holds a fixed min-height, so swapping between
   them cannot move anything below it. They were siblings of different heights
   before, which made every rip shove the chart and fine print down and yank
   them back — the whole panel jerked on each click. */
.pack-detail .rr-stage { display: grid; min-height: 72px; margin-top: 12px; }
.pack-detail .rr-stage > * { grid-area: 1 / 1; align-self: center; }
.pack-detail .rr-beat { display: flex; align-items: center; justify-content: center; gap: 7px; min-height: 72px; }
.pack-detail .rr-beat[hidden] { display: none; }
.pack-detail .rr-beat-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .35; animation: rr-beat 1.05s ease-in-out infinite; }
.pack-detail .rr-beat-dot:nth-child(2) { animation-delay: .14s; }
.pack-detail .rr-beat-dot:nth-child(3) { animation-delay: .28s; }
@keyframes rr-beat {
  0%, 100% { opacity: .25; transform: translateY(0); }
  50%      { opacity: .85; transform: translateY(-3px); }
}
/* Collapses to a still row; sim.js also resolves the beat immediately under the
   same query, so the visual and the timing can never disagree. */
@media (prefers-reduced-motion: reduce) {
  .pack-detail .rr-beat-dot { animation: none; opacity: .5; }
}

@media (max-width: 520px) {
  .pack-detail .rr-modal { padding: 12px; }
  /* No max-height override here any more. The old `90vh` was the bug on iOS:
     it re-introduced the large-viewport unit on exactly the devices where the
     browser chrome makes it wrong. The base `min(100%, 880px)` is already
     correct at every width. */
  .pack-detail .rr-modal-scroll { padding: 12px 14px 14px; }
}
