/* ==========================================================================
   Festivus Hunger Games — Phase 6
   Dialogs, toasts, badges, notifications, and the visual system that ties the
   earlier phases together.

   Loaded last, so it can harmonise what came before without rewriting it.

   DIRECTION: playful and competitive, with Christmas warmth — never childish,
   and never casino-like. No slot imagery, no coins, no jackpot language, no
   flashing. Readability on a phone wins every argument.

   MOTION: every transition and animation in this file is disabled wholesale by
   the prefers-reduced-motion block at the bottom. Nothing animates forever,
   nothing flashes, and nothing blocks input.
   ========================================================================== */

:root {
  /* ---- a real spacing scale, so padding stops being invented per component */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* ---- a type scale with a clear hierarchy ------------------------------- */
  --fs-eyebrow: 0.68rem;   /* uppercase labels */
  --fs-small: 0.78rem;
  --fs-body: 0.9rem;
  --fs-lead: 1rem;
  --fs-title: 1.15rem;
  --fs-display: 1.6rem;

  --radius-sm: 10px;
  --radius-lg: 18px;

  /* ---- the seasonal accents, used sparingly ------------------------------ */
  --holly: #e23b3b;
  --pine: #2f9e6b;
  --snow: #eef5f3;

  --motion-fast: 140ms;
  --motion-base: 240ms;
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* ==========================================================================
   Dialogs
   ========================================================================== */

/* The sheet is reused from Phase 4; Phase 6 gives it a scroll body so a long
   form on a short phone is reachable without the actions sliding off. */
.sheet__panel {
  max-height: min(88vh, 720px);
  display: flex;
  flex-direction: column;
}

.sheet__body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1 1 auto;
  min-height: 0;
}

.sheet__actions {
  flex: 0 0 auto;
  padding-top: var(--sp-3);
}

.sheet__actions .btn {
  flex: 1 1 0;
  min-width: 0;
}

.dialog__text {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-lead);
  line-height: 1.45;
}

.dialog__detail {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-small);
  color: var(--text-dim);
  line-height: 1.45;
}

.dialog__form {
  gap: var(--sp-3);
}

.dialog__form .field__hint {
  white-space: pre-line;
}

/* ==========================================================================
   Toasts
   ========================================================================== */

.toasts {
  position: fixed;
  /* Above the tab bar, never covering it: a celebration must not stand between
     somebody and the next thing they want to do. */
  left: 50%;
  bottom: calc(72px + env(safe-area-inset-bottom, 0px) + var(--sp-3));
  transform: translateX(-50%);
  width: min(calc(100vw - 32px), 420px);
  display: grid;
  gap: var(--sp-2);
  z-index: 60;
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-lg);
  font-size: var(--fs-body);
  line-height: 1.35;
  color: var(--text);
  background: var(--bg-elev-2);
  border: 1px solid var(--line);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  transition: opacity var(--motion-base) var(--ease), transform var(--motion-base) var(--ease);
  overflow-wrap: anywhere;
}

.toast--ok { border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.toast--warn { border-color: color-mix(in srgb, var(--gold) 55%, var(--line)); }
.toast--bad { border-color: color-mix(in srgb, var(--holly) 55%, var(--line)); }

.toast--leaving {
  opacity: 0;
  transform: translateY(8px);
}

/* ---- the achievement celebration ---------------------------------------- */

.toast--badge {
  background: linear-gradient(135deg, color-mix(in srgb, var(--gold) 20%, var(--bg-elev-2)) 0%, var(--bg-elev-2) 70%);
  border-color: color-mix(in srgb, var(--gold) 55%, var(--line));
}

.toast__badge-icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  font-size: 1.5rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--gold) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
}

.toast__eyebrow {
  margin: 0;
  font-size: var(--fs-eyebrow);
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gold);
}

.toast__title {
  margin: 2px 0 0;
  font-size: var(--fs-lead);
  font-weight: 750;
}

/* A single modest entrance. It plays once and stops. */
.toast--pop {
  animation: toast-pop var(--motion-base) var(--ease) both;
}

@keyframes toast-pop {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to { opacity: 1; transform: none; }
}

/* ==========================================================================
   Notifications
   ========================================================================== */

.bell {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--text);
  cursor: pointer;
}

.bell__icon {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

.bell__count {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  font-size: 0.65rem;
  font-weight: 800;
  color: #fff;
  background: var(--holly);
  border: 2px solid var(--bg);
}

.notifications {
  display: grid;
  gap: var(--sp-2);
}

.notifications__row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
  /* A row is a tap target. */
  min-height: 44px;
}

/* "New" is marked by a border AND a dot, never by colour alone. */
.notifications__row--new {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}

.notifications__row--new .notifications__text::after {
  content: " •";
  color: var(--accent);
  font-weight: 900;
}

.notifications__row--emphasis {
  background: color-mix(in srgb, var(--gold) 10%, var(--bg-elev));
}

.notifications__icon { flex: 0 0 auto; font-size: 1.1rem; line-height: 1.3; }
.notifications__text { margin: 0; font-size: var(--fs-body); line-height: 1.35; overflow-wrap: anywhere; }
.notifications__sub { margin: 2px 0 0; font-size: var(--fs-small); color: var(--text-dim); }
.notifications__when { flex: 0 0 auto; margin-left: auto; font-size: var(--fs-small); color: var(--text-dim); }

/* ==========================================================================
   Badges

   NAMED .achievement, NOT .badge.

   `.badge` has meant the small "Development" chip since Phase 1, and that rule
   sets `white-space: nowrap`. Reusing the name made every achievement card
   un-wrappable, so its min-content was the full unwrapped sentence and the
   page was 444px wide on a 390px phone. The phone smoke found it; the lesson
   is that a new component needs a new name, not a plausible one.
   ========================================================================== */

/*
 * minmax(0, 1fr), not the default.
 *
 * A grid column sized `auto` (or `1fr`) has a MIN of min-content, so a card
 * whose contents will not wrap any smaller pushes the column -- and the page --
 * wider than the phone. The badges screen was 444px wide on a 390px viewport
 * because of exactly this, and the overflow was invisible to a check that
 * compared content against the layout viewport, because Chrome had widened the
 * layout viewport to match. minmax(0, ...) lets the column shrink.
 */
.achievement-grid,
.challenge-list,
.review,
.entries,
.notifications {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-3);
}

/* And the flex children inside them must be allowed to shrink too. */
.achievement,
.notifications__row,
.entries__row,
.review__card {
  min-width: 0;
}

.achievement {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  padding: var(--sp-4);
  border-radius: var(--radius-lg);
  background: var(--bg-elev);
  border: 1px solid var(--line);
}

.achievement--earned {
  background: linear-gradient(135deg, color-mix(in srgb, var(--gold) 12%, var(--bg-elev)) 0%, var(--bg-elev) 65%);
  border-color: color-mix(in srgb, var(--gold) 42%, var(--line));
}

.achievement__icon {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  font-size: 1.7rem;
  border-radius: 50%;
  background: var(--bg-elev-2);
  border: 1px solid var(--line);
}

/* A locked badge is dimmed AND says "not yet earned": never colour alone. */
.achievement--locked .achievement__icon {
  filter: grayscale(1);
  opacity: 0.5;
}

.achievement__body { flex: 1 1 auto; min-width: 0; }

.achievement__name {
  margin: 0;
  font-size: var(--fs-lead);
  font-weight: 750;
  overflow-wrap: anywhere;
}

.achievement__description {
  margin: 2px 0 var(--sp-2);
  font-size: var(--fs-small);
  color: var(--text-dim);
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.achievement__state {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-small);
  font-weight: 650;
  color: var(--text-dim);
}

.achievement--earned .achievement__state { color: var(--gold); }

.achievement__progress,
.meter {
  height: 8px;
  border-radius: 999px;
  background: var(--bg-elev-2);
  border: 1px solid var(--line);
  overflow: hidden;
  position: relative;
}

.achievement__progress::after,
.meter::after {
  content: "";
  position: absolute;
  inset: 0;
  width: var(--progress, 0%);
  background: linear-gradient(90deg, var(--accent-dim), var(--accent));
  transition: width var(--motion-base) var(--ease);
}

/*
 * PROGRESS WIDTHS COME FROM THE STYLESHEET, NOT FROM AN INLINE STYLE.
 *
 * These used to be set with `node.style.setProperty("--progress", ...)`, which
 * the Content Security Policy (`style-src 'self'`) blocks outright -- so every
 * badge meter in the app silently rendered at 0%, the `var(--progress, 0%)`
 * fallback above. A Phase 7 browser smoke reported the violation.
 *
 * The bar is quantised to 5% so a fixed set of classes can express it. The
 * `aria-valuenow` beside it carries the EXACT figure, so nothing a screen
 * reader announces is rounded -- only the pixels are.
 */
.achievement__progress.is-p0::after,
.meter.is-p0::after { width: 0%; }
.achievement__progress.is-p5::after,
.meter.is-p5::after { width: 5%; }
.achievement__progress.is-p10::after,
.meter.is-p10::after { width: 10%; }
.achievement__progress.is-p15::after,
.meter.is-p15::after { width: 15%; }
.achievement__progress.is-p20::after,
.meter.is-p20::after { width: 20%; }
.achievement__progress.is-p25::after,
.meter.is-p25::after { width: 25%; }
.achievement__progress.is-p30::after,
.meter.is-p30::after { width: 30%; }
.achievement__progress.is-p35::after,
.meter.is-p35::after { width: 35%; }
.achievement__progress.is-p40::after,
.meter.is-p40::after { width: 40%; }
.achievement__progress.is-p45::after,
.meter.is-p45::after { width: 45%; }
.achievement__progress.is-p50::after,
.meter.is-p50::after { width: 50%; }
.achievement__progress.is-p55::after,
.meter.is-p55::after { width: 55%; }
.achievement__progress.is-p60::after,
.meter.is-p60::after { width: 60%; }
.achievement__progress.is-p65::after,
.meter.is-p65::after { width: 65%; }
.achievement__progress.is-p70::after,
.meter.is-p70::after { width: 70%; }
.achievement__progress.is-p75::after,
.meter.is-p75::after { width: 75%; }
.achievement__progress.is-p80::after,
.meter.is-p80::after { width: 80%; }
.achievement__progress.is-p85::after,
.meter.is-p85::after { width: 85%; }
.achievement__progress.is-p90::after,
.meter.is-p90::after { width: 90%; }
.achievement__progress.is-p95::after,
.meter.is-p95::after { width: 95%; }
.achievement__progress.is-p100::after,
.meter.is-p100::after { width: 100%; }

.meter { height: 10px; margin: var(--sp-2) 0; }

/* ---- the latest badge on Home ------------------------------------------- */

.recent-badge {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  min-height: 64px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--gold) 14%, var(--bg-elev)) 0%, var(--bg-elev) 70%);
  border: 1px solid color-mix(in srgb, var(--gold) 38%, var(--line));
}

.recent-achievement__icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  font-size: 1.4rem;
  border-radius: 50%;
  background: var(--bg-elev-2);
  border: 1px solid var(--line);
}

.recent-badge__eyebrow {
  margin: 0;
  font-size: var(--fs-eyebrow);
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.recent-achievement__name {
  margin: 2px 0 0;
  font-size: var(--fs-lead);
  font-weight: 750;
}

/* ==========================================================================
   Admin: preview and entries
   ========================================================================== */

.preview {
  padding: var(--sp-4);
  border-radius: var(--radius-lg);
  background: var(--bg-elev);
  border: 1px dashed var(--line);
}

.preview__kind {
  margin: 0;
  font-size: var(--fs-eyebrow);
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.preview__title {
  margin: var(--sp-1) 0 var(--sp-2);
  font-size: var(--fs-title);
  font-weight: 800;
  overflow-wrap: anywhere;
}

.preview__text {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-body);
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.preview__options {
  margin: 0 0 var(--sp-2);
  padding-left: 1.1rem;
  font-size: var(--fs-body);
}

.preview__options li { margin-bottom: var(--sp-1); overflow-wrap: anywhere; }

.preview__facts {
  margin: 0;
  font-size: var(--fs-small);
  font-weight: 650;
  color: var(--accent);
}

.entries { display: grid; gap: var(--sp-2); }

.entries__row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  font-size: var(--fs-body);
}

.entries__who { font-weight: 700; }
.entries__answer { color: var(--text-dim); overflow-wrap: anywhere; }
.entries__row .pill { margin-left: auto; }

.review__photo--full {
  max-height: 60vh;
  width: 100%;
  object-fit: contain;
}

/* ==========================================================================
   Images
   ========================================================================== */

/*
 * Every image the application renders carries width/height, so the browser can
 * reserve the box from the aspect ratio before any bytes arrive. These rules
 * make that reservation survive the responsive sizing.
 */
.prize__img,
.review__photo,
.answer__preview {
  max-width: 100%;
  height: auto;
  /* A placeholder tint while the bytes are in flight, instead of a white flash. */
  background: var(--bg-elev-2);
}

/* ==========================================================================
   States: empty, loading, error
   ========================================================================== */

.footnote {
  margin: var(--sp-5) 0 0;
  font-size: var(--fs-small);
  color: var(--text-dim);
  text-align: center;
  line-height: 1.45;
}

.list__empty {
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  color: var(--text-dim);
  font-size: var(--fs-body);
  line-height: 1.5;
}

/* ==========================================================================
   Touch targets
   ========================================================================== */

/*
 * The floor for anything tappable. Phase 5 raised the base button to 44px;
 * this catches everything else that is interactive, so a new control cannot
 * quietly ship below the threshold.
 */
.bell,
.notifications__row,
.gamesbar__link,
.sheet__actions .btn,
.optionrow__correct {
  min-height: 44px;
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

/*
 * One block, at the end, covering everything this phase added AND the earlier
 * phases' animations. If somebody has asked their device for less movement,
 * they get none of it — the information is identical either way.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  .toast--pop,
  .pick__done--new,
  .flash-accepted {
    animation: none !important;
  }

  .pick__box--chosen {
    transform: none !important;
  }
}
