/*
 * Pog Shop Party on the web. Tokens ported from the app:
 * Color.kt (PageBg/Surface/Text/TextMuted/Divider) and PartyScreen.kt
 * (PartyGreen #46632F, PartyAccent #9FCB6A, PartyDeep #1B2614).
 *
 * Phone-width column on purpose. Every layout in the app assumes phone width —
 * five roster slots across, a collapsing header, chat bubbles at 272px — so the
 * port keeps that and centres it rather than inventing a desktop design.
 * Light and dark both, as the app ships them — see the dark token block at the
 * foot of this file.
 *
 * Dialogs (.pp-modal) are appended to <body>, outside the page's own element,
 * so the tokens, resets and component rules are scoped to both. Scoped to the
 * page alone, a dialog's buttons fell back to the theme's bare default buttons.
 */
.pog-party, .pp-modal {
  --green: #46632f;
  --accent: #9fcb6a;
  --deep: #1b2614;
  --page: #fcfcfc;
  --surface: #ffffff;
  --muted-surface: #f6f7fb;
  --text: #1a1a1a;
  --text-muted: #6b7280;
  --divider: #ececef;
  --danger: #c5000f;
  --amber: #b26a00;
  --purple: #6a35a8;
  /* Discord's own blurple: the voice card, Main and the live dot. */
  --discord: #5865f2;
  --discord-deep: #4752c4;
  --discord-ink: #5865f2;
  --shadow: 0 1px 6px rgba(0, 0, 0, .05), 0 2px 4px rgba(0, 0, 0, .07);
  color: var(--text);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  text-align: left;
}
.pog-party {
  max-width: 420px;
  margin: 0 auto;
  background: var(--page);
  border-radius: 18px;
  overflow: hidden;
  position: relative;
  min-height: 560px;
}
:is(.pog-party, .pp-modal) *, :is(.pog-party, .pp-modal) *::before, :is(.pog-party, .pp-modal) *::after { box-sizing: border-box; }
/* A real reset, not :where(): the theme sets a plain `button { color }` that a
   zero-specificity rule loses to, which painted every chip's label the theme's
   colour on our own white pill — invisible. This sits at 0,1,1 so it beats the
   theme, while every component class below is scoped to 0,2,0 so it beats this. */
:is(.pog-party, .pp-modal) p { margin: 0; }
/* The storefront theme (Ecomus) styles every bare button: its padding, a 0.25s
   transition on everything, clipped overflow, and a gradient "shine" drawn in
   ::after that sweeps across on hover. Party draws its own buttons, so that is
   all undone here and each component sets only what it means. */
:is(.pog-party, .pp-modal) button {
  font: inherit; cursor: pointer; border: 0; background: none; color: var(--text);
  padding: 0; appearance: none; transition: none; overflow: visible; text-transform: none; letter-spacing: normal;
}
:is(.pog-party, .pp-modal) button::before, :is(.pog-party, .pp-modal) button::after { content: none; animation: none; }
:is(.pog-party, .pp-modal) a { color: var(--green); }

/* ── header ─────────────────────────────────────────────────────────────── */
.pp-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; min-width: 0;
  padding: 14px 12px 6px;
  background: linear-gradient(180deg, rgba(70, 99, 47, .32), var(--page));
}
/* The app's own rule, same asset (logo_wordmark.png, 616x336): height 36 with
   the width left entirely to the aspect ratio, beside a 24px title.
   `max-width` is deliberately absent — a width cap plus `contain` shrinks a wide
   logo's HEIGHT to fit it, which is exactly the squashed, undersized mark it was
   meant to prevent. The site's own logo can be any shape, so the height is the
   one fixed dimension and the width follows it. */
.pog-party .pp-logo {
  height: 36px !important; width: auto !important;
  object-fit: contain; flex: none;
}
.pp-title { font-size: 24px; font-weight: 700; color: var(--green); padding: 0 8px; white-space: nowrap; }
.pp-title small { font-size: 12px; font-weight: 600; opacity: .6; margin-left: 6px; }
.pp-spacer { flex: 1; }

/* Chips and circles share the app's floating lift and 44px geometry. */
:is(.pog-party, .pp-modal) .pp-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: 50px;
  background: var(--surface); color: var(--text); box-shadow: var(--shadow);
  font-size: 13px; font-weight: 600; white-space: nowrap;
}
.pp-chip[aria-pressed="true"] { background: rgba(70, 99, 47, .16); color: var(--green); }
:is(.pog-party, .pp-modal) .pp-chip[disabled] { opacity: .55; cursor: default; box-shadow: none; }
.pp-chip.solid { background: var(--green); color: #fff; }
.pp-chip.tall { height: 44px; }
.pog-party .pp-icon {
  width: 44px; height: 44px; border-radius: 50%; flex: none; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); box-shadow: var(--shadow); color: var(--green);
}
/* Themes routinely size bare svg/img; force ours or the glyph collapses to a
   speck inside its 44px circle. */
:is(.pog-party, .pp-modal) .pp-icon svg, :is(.pog-party, .pp-modal) .pp-chip svg, :is(.pog-party, .pp-modal) .pp-send svg, :is(.pog-party, .pp-modal) .pp-vote svg {
  width: 20px !important; height: 20px !important; fill: currentColor; display: block;
}
.pp-tabs { display: flex; gap: 8px; padding: 0 12px 2px; align-items: center; }

/* ── generic surfaces ───────────────────────────────────────────────────── */
.pp-card {
  background: var(--surface); border-radius: 18px; padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
}
.pp-list { padding: 2px 16px 24px; display: flex; flex-direction: column; gap: 14px; }
.pp-row { display: flex; align-items: center; gap: 8px; }
.pp-muted { color: var(--text-muted); }
.pp-small { font-size: 12px; }
.pp-label { font-size: 11px; color: var(--text-muted); letter-spacing: .04em; }
.pp-bold { font-weight: 700; }
.pp-center { text-align: center; }
.pp-empty { padding: 48px 16px; text-align: center; color: var(--text-muted); }
/* The room list's own header: title left, "Buat room" top right. */
.pp-list-head { display: flex; align-items: center; gap: 8px; }
:is(.pog-party, .pp-modal) .pp-btn.pp-create { align-self: auto; display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px 8px 11px; }
:is(.pog-party, .pp-modal) .pp-btn.pp-create svg { width: 16px !important; height: 16px !important; }
.pp-empty-rooms { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 36px 16px; }
.pp-empty-rooms .pp-bold { color: var(--text); }
.pp-empty-rooms p:not(.pp-bold) { max-width: 300px; font-size: 14px; }
:is(.pog-party, .pp-modal) .pp-empty-rooms .pp-btn.pp-create { align-self: center; margin-top: 10px; }
.pp-empty-rooms .pp-empty-help { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 14px; font-size: 13px; }
:is(.pog-party, .pp-modal) .pp-empty-rooms .pp-help-link { align-self: center; text-decoration: none; }

/* Pill, always: the fill and the radius are what make it read as an action. */
:is(.pog-party, .pp-modal) .pp-btn {
  padding: 11px 18px; border-radius: 50px; background: var(--green); color: #fff;
  font-weight: 600; font-size: 14px; flex: 1; text-align: center;
  display: inline-block; text-decoration: none; line-height: 1.3;
}
:is(.pog-party, .pp-modal) .pp-btn[disabled] { opacity: .45; cursor: not-allowed; }
:is(.pog-party, .pp-modal) .pp-btn.ghost { background: transparent; color: var(--text); box-shadow: inset 0 0 0 1px var(--divider); }
:is(.pog-party, .pp-modal) .pp-btn.link { background: none; color: var(--text-muted); font-size: 12px; padding: 4px 0; flex: none; text-align: left; }
:is(.pog-party, .pp-modal) .pp-send { background: var(--green); color: #fff; }
:is(.pog-party, .pp-modal) .pp-vote.up { background: rgba(70, 99, 47, .14); color: var(--green); }
:is(.pog-party, .pp-modal) .pp-vote.down { background: rgba(197, 0, 15, .14); color: var(--danger); }
/* Decisions read by colour as well as by word: green to let someone in, red to
   turn them down or remove them. Accept keeps the brand fill; red is a tint,
   so a list of requests is not a wall of alarm. */
:is(.pog-party, .pp-modal) .pp-btn.accept, :is(.pog-party, .pp-modal) .pp-btn.decline {
  flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  padding: 8px 13px 8px 10px; font-size: 13px;
}
:is(.pog-party, .pp-modal) .pp-btn.decline, :is(.pog-party, .pp-modal) .pp-btn.danger {
  background: rgba(197, 0, 15, .12); color: var(--danger);
}
/* A button with an icon sets it beside its label — icon() makes every glyph a
   block, which otherwise stacked Main's play arrow above the word. */
:is(.pog-party, .pp-modal) .pp-btn:has(> svg) { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
/* Small, for a secondary action that should not compete with the main one. */
:is(.pog-party, .pp-modal) .pp-btn.small { flex: none; align-self: flex-start; padding: 7px 14px; font-size: 12px; }

/* Colour and state changes fade instead of snapping, as the app animates them
   (animateColorAsState): pills, buttons, the ready frame, a role in conflict,
   a message settling from sending to sent. */
:is(.pog-party, .pp-modal) .pp-btn, :is(.pog-party, .pp-modal) .pp-chip {
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, opacity .2s ease;
}
.pp-pill, .pp-tone { transition: color .42s ease, background-color .42s ease; }
.pp-avatar { transition: box-shadow .32s ease; }
.pp-role, .pp-bubble { transition: color .3s ease, background-color .3s ease; }

/* Fields, whatever the theme says about inputs. Its input[type=text], select and
   textarea rules outrank a bare class, and re-bordered, re-coloured and
   square-cornered every field, filled it white (grey text on white in dark
   mode), and animated its border over 0.4s on every hover, click and focus —
   the chat box's "blinking" border. Each state is pinned here. */
:is(.pog-party, .pp-modal) .pp-field {
  width: 100%; height: auto; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--divider); background-color: var(--surface); color: var(--text);
  font: inherit; line-height: 1.4; box-shadow: none; transition: none; appearance: none; outline: none;
}
:is(.pog-party, .pp-modal) .pp-field:is(:hover, :active) { border-color: var(--divider); }
:is(.pog-party, .pp-modal) .pp-field:focus { border-color: var(--divider); outline: 2px solid rgba(70, 99, 47, .35); outline-offset: 1px; }
/* The platform's own arrow, not the theme's drawn one. */
:is(.pog-party, .pp-modal) select.pp-field { appearance: auto; background-image: none; }
/* The consent box: the platform's own checkbox in Party green, not the theme's drawn square. */
:is(.pog-party, .pp-modal) .pp-agree input[type="checkbox"] {
  appearance: auto; accent-color: var(--green); width: 16px; height: 16px; margin: 0; position: static; flex: none;
}
:is(.pog-party, .pp-modal) .pp-agree input[type="checkbox"]::before,
:is(.pog-party, .pp-modal) .pp-agree input[type="checkbox"]::after { content: none; display: none; }

/* ── room cards ─────────────────────────────────────────────────────────── */
.pp-room { background: var(--surface); border-radius: 18px; padding: 16px; display: grid; gap: 14px; position: relative; overflow: hidden; }
.pp-room-top { display: flex; align-items: flex-start; gap: 8px; }
.pp-pill { padding: 6px 10px; border-radius: 50px; font-size: 11px; font-weight: 600; white-space: nowrap; display: inline-flex; align-items: center; }
.pp-state-recruiting { color: var(--green); background: rgba(70, 99, 47, .14); }
.pp-state-ready { color: var(--amber); background: rgba(178, 106, 0, .14); }
.pp-state-formed { color: var(--purple); background: rgba(106, 53, 168, .14); }
.pp-state-playing { color: var(--discord-ink); background: rgba(88, 101, 242, .14); padding-left: 6px; }
.pp-state-ended { color: var(--text-muted); background: rgba(107, 114, 128, .14); }

/* The recruiting ripple, radiating from the room name like the app's card. The
   repaint keeps the card node, so the wave runs on uninterrupted. */
.pp-room.recruiting::before {
  content: ""; position: absolute; left: 18%; top: 30%;
  width: 20px; height: 20px; margin: -10px 0 0 -10px; border-radius: 50%;
  background: radial-gradient(circle, transparent 42%, rgba(159, 203, 106, .05) 66%,
              rgba(159, 203, 106, .22) 88%, transparent 100%);
  animation: pp-ripple 2.6s linear infinite; pointer-events: none;
}
@keyframes pp-ripple { from { transform: scale(1); opacity: .9 } to { transform: scale(34); opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .pp-room.recruiting::before { animation: none; display: none } }

/* ── Steam card ─────────────────────────────────────────────────────────── */
/* Ported from PartySteamConnectionCard: Steam's own dark palette, so the panel
   that shows a Steam identity looks like Steam on both surfaces. */
.pp-steam {
  border-radius: 12px; padding: 16px; display: grid; gap: 12px;
  background: linear-gradient(180deg, #2b2d44, #20222e);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .25);
}
.pp-steam-head { display: flex; align-items: center; gap: 6px; }
.pp-steam-head img { width: 16px; height: 16px; }
.pp-steam-head span { color: #66c0f4; font-size: 12px; font-weight: 600; }
.pp-steam-id { display: flex; align-items: center; gap: 12px; }
.pp-steam-face {
  width: 56px; height: 56px; border-radius: 8px; flex: none; object-fit: cover;
  background: rgba(255, 255, 255, .08); display: grid; place-items: center; color: #9aa4b8;
}
.pp-steam b { color: #f1f3f6; display: block; }
.pp-steam .linked { color: #6fe08a; font-size: 12px; }
.pp-steam .unlinked { color: #9aa4b8; font-size: 12px; }
.pp-steam .hint { color: #9aa4b8; font-size: 12px; }
.pp-steam .pp-btn { background: #66c0f4; color: #20222e; display: block; }
.pog-party a.pp-steam-cta { background: #66c0f4; color: #20222e; }
.pp-steam .recheck { color: #66c0f4; font-size: 12px; text-align: center; padding: 2px; }

/* ── roster ─────────────────────────────────────────────────────────────── */
.pp-roster { display: flex; gap: 8px; align-items: flex-start; }
/* Face, nickname, role: each with room of its own, so the role never sits on
   the nickname and the pill never touches the buttons below. */
.pp-slot { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 5px; }
.pp-face { position: relative; width: 100%; aspect-ratio: 1; }
.pp-avatar {
  width: 100%; height: 100%; border-radius: 23%; object-fit: cover; display: block;
  background: rgba(70, 99, 47, .1); box-shadow: inset 0 0 0 1px rgba(70, 99, 47, .25);
}
.pp-avatar.ready { box-shadow: inset 0 0 0 2px var(--green); }
.pp-avatar.empty { display: grid; place-items: center; color: var(--green); font-size: 20px; }
.pp-medal { position: absolute; right: 2px; bottom: 2px; width: 40%; max-width: 26px; }
.pp-crown { position: absolute; right: 3px; top: 3px; width: 34%; max-width: 21px; line-height: 1; }
.pp-ready {
  position: absolute; left: 3px; top: 3px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--green); color: var(--page); display: grid; place-items: center;
}
/* Tidak siap: the same badge, red, with a cross. */
.pp-ready.no { background: var(--danger); }
.pp-nick { font-size: 11px; line-height: 13px; margin-top: 2px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A role may reach 4px into the gap on either side — the whole gap between two
   long roles, never past it — so "Hard Support" reads whole on a phone's seat. */
.pp-role {
  font-size: 11px; line-height: 14px; color: var(--green);
  max-width: calc(100% + 8px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pp-role.conflict { color: var(--amber); }
.pp-role.none { color: var(--text-muted); }
/* Your own role: the pill shows, the native menu laid invisibly over it picks. */
.pp-role.pick {
  position: relative; display: inline-flex; align-items: center; gap: 1px; overflow: visible;
  padding: 3px 4px 3px 8px; border-radius: 50px;
  background: rgba(70, 99, 47, .14); cursor: pointer;
}
.pp-role.pick.conflict { background: rgba(178, 106, 0, .14); }
.pp-role.pick span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pp-role.pick:focus-within { outline: 2px solid rgba(70, 99, 47, .45); outline-offset: 1px; }
/* 16px so iOS does not zoom the page when the menu takes focus. */
.pp-role-menu { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: 0; cursor: pointer; font-size: 16px; }

/* ── room view ──────────────────────────────────────────────────────────── */
/* Sections are spaced by margins, not gap: a margin folds away with its
   section, where a gap would jump. */
.pp-room-view { display: flex; flex-direction: column; height: 78vh; min-height: 520px; }
.pp-room-view > * + * { margin-top: 12px; }
.pp-room-view > .pp-roster { padding: 0 12px; }
.pp-room-head { padding: 6px 16px 0; display: grid; gap: 3px; }
/* Bold like the app's, whatever weight and face the theme gives its headings. */
:is(.pog-party, .pp-modal) :is(h2, h3) { font-family: inherit; font-weight: 700; letter-spacing: normal; text-transform: none; color: var(--text); line-height: 1.3; }
.pp-room-head h2 { margin: 0; font-size: 17px; }
.pp-actions { display: flex; gap: 8px; padding: 0 12px; }
.pp-room-view > .pp-actions { margin-top: 16px; }
.pp-conflicts { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 0 16px; }
.pp-conflicts svg { color: var(--amber); }
/* Why "Minta bergabung" is greyed out, under it, with the fix beside it when there is one. */
.pp-why { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; padding: 0 16px; text-align: center; }

/* The ready check floats over the page: the minute on a big clock, a bar that
   drains with it, every seat's answer, then Siap / Tidak siap. Amber, the
   colour a room wears during its check. */
.pp-ready-shade { position: fixed; inset: 0; z-index: 9990; display: grid; place-items: center; padding: 20px; background: rgba(0, 0, 0, .45); }
.pp-ready-card { width: min(380px, 100%); max-height: 90vh; overflow: auto; background: #f0f6e8; border-radius: 22px; padding: 22px 20px 10px; display: grid; gap: 10px; }
.pp-ready-title { font-size: 20px; font-weight: 800; text-align: center; color: var(--amber); }
.pp-ready-clock { font: 800 46px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; text-align: center; font-variant-numeric: tabular-nums; }
.pp-ready-bar { height: 6px; border-radius: 50px; background: rgba(178, 106, 0, .18); overflow: hidden; }
/* Each repaint moves it one second along; the linear transition makes that a steady drain. */
.pp-ready-bar i { display: block; height: 100%; border-radius: inherit; background: var(--amber); transition: width 1s linear, background-color .3s ease; }
/* The last ten seconds turn red, clock and bar, as in the app. */
.pp-ready-card.urgent .pp-ready-clock { color: var(--danger); }
.pp-ready-card.urgent .pp-ready-bar i { background: var(--danger); }
.pp-ready-actions { display: flex; gap: 8px; margin-top: 4px; }
:is(.pog-party, .pp-modal) .pp-ready-later { justify-self: center; padding: 6px 12px; }

/* Forms: a label per field, a red asterisk on what is required, and the
   refusal shown inside the form rather than behind it. */
.pp-field-group { display: grid; gap: 6px; }
.pp-req { color: var(--danger); font-weight: 700; margin-left: 2px; }
.pp-form-error { color: var(--danger); }
.pp-form-error:empty { display: none; }
/* Reviews still owed, counted on the Ulasan tab. */
.pp-badge {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 50px; display: inline-grid; place-items: center;
  background: var(--danger); color: var(--page); font-size: 11px; font-weight: 700;
}
.pp-chat-col { display: flex; flex-direction: column; flex: 1; min-height: 0; }

/* Join requests: a counted heading, then one row per applicant with its decision. */
.pp-requests { display: grid; gap: 8px; padding: 0 12px; }
.pp-requests-head { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 0 4px; }
.pp-count {
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: 50px; display: inline-grid; place-items: center;
  background: var(--green); color: var(--page); font-size: 11px; font-weight: 700;
}
/* min-width 0: a grid item otherwise grows to its unwrapped text and pushes Tolak off the card. */
.pp-card.pp-request { flex-direction: row; align-items: center; gap: 10px; padding: 10px 12px; min-width: 0; }
.pp-request .pp-face { width: 40px; flex: none; }
.pp-request-who { flex: 1; min-width: 0; }
.pp-request-who > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-request-actions { display: flex; gap: 6px; flex: none; }

/* The folded header: identity and live detail on top, then the whole roster
   across the width at a size that still reads — face and role for every seat. */
.pp-strip { display: grid; gap: 8px; padding: 8px 12px 10px; background: var(--page); cursor: pointer; }
.pp-strip-top { padding: 0 4px; }
.pp-room-view > .pp-strip + .pp-chat-col { margin-top: 0; }
.pp-strip-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.pp-strip-titles { flex: 1; min-width: 0; }
.pp-strip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-strip-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-strip-meta .pp-live { vertical-align: middle; margin-left: 0; }
.pp-strip-ask {
  flex: none; padding: 5px 10px; border-radius: 50px; font-size: 11px; font-weight: 700;
  background: var(--green); color: var(--page);
}
.pp-roster.compact { gap: 6px; }
.pp-roster.compact .pp-role { max-width: calc(100% + 6px); font-size: 10.5px; }
.pp-roster.compact .pp-slot { gap: 4px; }
.pp-roster.compact .pp-face { width: 48px; }
.pp-roster.compact .pp-medal { max-width: 18px; }
.pp-roster.compact .pp-crown { max-width: 14px; font-size: 10px; right: 1px; top: 1px; }
.pp-roster.compact .pp-ready { width: 14px; height: 14px; left: 1px; top: 1px; }
.pp-roster.compact .pp-ready svg { width: 10px !important; height: 10px !important; }
.pp-roster.compact .pp-avatar.empty { font-size: 16px; }

.pp-chat { flex: 1; overflow-y: auto; padding: 10px 14px; display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--divider); }
.pp-day { align-self: center; background: var(--surface); box-shadow: var(--shadow); border-radius: 10px; padding: 5px 12px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
:is(.pog-party, .pp-modal) .pp-older { align-self: center; font-size: 12px; }
/* A message rises in from its own side of the thread. */
.pp-msg { display: flex; align-items: flex-end; gap: 8px; transform-origin: 0 100%; }
.pp-msg.mine { justify-content: flex-end; transform-origin: 100% 100%; }
.pp-msg .pp-avatar { width: 28px; height: 28px; flex: none; }
.pp-bubble { max-width: 272px; padding: 7px 13px; border-radius: 16px 16px 16px 4px; background: var(--surface); box-shadow: var(--shadow); }
.pp-msg.mine .pp-bubble { background: var(--green); color: #fff; border-radius: 16px 16px 4px 16px; }
/* Specific enough to beat the sent bubble's own rule, which is what kept a
   sending message from ever looking any different. */
.pp-msg.mine .pp-bubble.pending { background: rgba(70, 99, 47, .45); color: #fff; }
.pp-msg.mine .pp-bubble.failed { background: rgba(197, 0, 15, .14); color: var(--text); cursor: pointer; }
.pp-sender { font-size: 11px; font-weight: 600; color: var(--green); }
.pp-time { font-size: 10px; opacity: .75; margin-left: 8px; float: right; margin-top: 4px; }
.pp-bubble a { color: inherit; text-decoration: underline; }

.pp-composer { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--page); }
/* The composer is a soft pill that never grows a border: not on hover, not on focus. */
:is(.pog-party, .pp-modal) .pp-composer .pp-field, :is(.pog-party, .pp-modal) .pp-composer .pp-field:is(:hover, :active, :focus) {
  border-radius: 24px; background-color: var(--muted-surface); border-color: transparent; outline: none;
}
:is(.pog-party, .pp-modal) .pp-send { width: 44px; height: 44px; border-radius: 50%; background: var(--green); color: #fff; display: grid; place-items: center; flex: none; }

/* ── dialogs ────────────────────────────────────────────────────────────── */
.pp-modal { position: fixed; inset: 0; background: rgba(0, 0, 0, .45); display: grid; place-items: center; padding: 20px; z-index: 9999; animation: pp-fade-in .18s ease both; }
.pp-modal-card { background: #f0f6e8; color: var(--text); border-radius: 22px; padding: 22px; width: min(400px, 100%); max-height: 86vh; overflow: auto; display: grid; gap: 12px; animation: pp-lift-in .26s cubic-bezier(.2, 0, 0, 1) both; }
.pp-modal.closing { animation: pp-fade-out .16s ease both; }
.pp-modal.closing .pp-modal-card { animation: pp-sink-out .16s ease both; }
@keyframes pp-fade-in { from { opacity: 0 } }
@keyframes pp-fade-out { to { opacity: 0 } }
@keyframes pp-lift-in { from { opacity: 0; transform: translateY(12px) scale(.97) } }
@keyframes pp-sink-out { to { opacity: 0; transform: translateY(8px) scale(.98) } }
@media (prefers-reduced-motion: reduce) { .pp-modal, .pp-modal-card, .pp-modal.closing, .pp-modal.closing .pp-modal-card { animation: none } }
.pp-modal-card h3 { margin: 0; font-size: 18px; }
/* The page's divider all but vanishes on the dialog's tinted card — which left
   the create form's name field with no visible edge at all. */
:is(.pog-party, .pp-modal) .pp-modal-card .pp-btn.ghost { box-shadow: inset 0 0 0 1px rgba(27, 38, 20, .2); }
:is(.pog-party, .pp-modal) .pp-modal-card .pp-field, :is(.pog-party, .pp-modal) .pp-modal-card .pp-field:is(:hover, :active, :focus) { border-color: rgba(27, 38, 20, .26); background-color: var(--surface); }
/* Numbered steps and rules: wrapped lines hang under the text, not the number. */
.pp-modal-card .pp-steps { margin: 0; padding-left: 20px; display: grid; gap: 4px; }
/* Under the sentence it belongs to: indented past the checkbox, no gap between. */
.pog-party .pp-agree .pp-btn.link { display: block; margin: 2px 0 0 21px; padding: 0; color: var(--green); font-weight: 600; }
.pp-modal-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* ── reviews ────────────────────────────────────────────────────────────── */
.pp-review { display: flex; align-items: center; gap: 12px; }
.pp-review .pp-face { width: 46px; flex: none; }
:is(.pog-party, .pp-modal) .pp-vote { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; box-shadow: var(--shadow); flex: none; transition: opacity .25s ease, box-shadow .25s ease, background-color .25s ease, color .25s ease; }
.pp-vote.up { background: rgba(70, 99, 47, .14); color: var(--green); }
.pp-vote.down { background: rgba(197, 0, 15, .14); color: var(--danger); }
/* One look before and after answering, as in the app: the answer fills solid in
   its own colour, the other side dims. */
.pp-vote.dim { opacity: .35; box-shadow: none; }
/* "Belum diulas · 2" / "Sudah diulas · 3": a section title with its count as a pill. */
.pp-group { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px; color: var(--text); padding-top: 4px; }
.pp-group-count { min-width: 22px; padding: 1px 8px; border-radius: 50px; background: rgba(70, 99, 47, .14); color: var(--green); font-size: 12px; text-align: center; transition: background-color .25s ease; }
/* The thumbs-up confirm: the card's own green, tinted, under the chips. */
.pp-confirm { display: flex; flex-direction: column; gap: 10px; padding: 10px 12px; border-radius: 14px; background: rgba(70, 99, 47, .08); }
.pp-confirm .pp-row { justify-content: flex-end; gap: 8px; }
:is(.pog-party, .pp-modal) .pp-confirm .pp-btn.small { align-self: auto; }
:is(.pog-party, .pp-modal) .pp-confirm .pp-btn:has(> svg) svg { width: 16px !important; height: 16px !important; }
:is(.pog-party, .pp-modal) .pp-vote.chosen.up { background: var(--green); color: var(--page); }
:is(.pog-party, .pp-modal) .pp-vote.chosen.down { background: var(--danger); color: var(--page); }
:is(.pog-party, .pp-modal) .pp-vote[disabled] { cursor: default; }

/* ── misc ───────────────────────────────────────────────────────────────── */
.pp-spinner { width: 22px; height: 22px; border: 3px solid rgba(70, 99, 47, .25); border-top-color: var(--green); border-radius: 50%; animation: pp-spin .9s linear infinite; }
@keyframes pp-spin { to { transform: rotate(360deg) } }
/* Centred by margins, not a transform, so its pop-in can scale it in place. */
.pp-busy { position: absolute; top: 74px; left: 0; right: 0; width: 40px; margin: 0 auto; background: var(--surface); box-shadow: var(--shadow); border-radius: 50%; padding: 9px; z-index: 5; }
.pp-loading { display: grid; place-items: center; padding: 72px 16px; }
.pp-notice { margin: 0 16px; padding: 10px 12px; border-radius: 12px; background: rgba(70, 99, 47, .12); font-size: 13px; }
.pp-error { margin: 0 16px; padding: 10px 12px; border-radius: 12px; background: rgba(197, 0, 15, .1); color: var(--danger); font-size: 13px; }
.pp-verified { background: linear-gradient(120deg, #e4f2ff, #cde5fa); border-radius: 18px; padding: 18px; display: grid; gap: 8px; color: #194f78; }
/* Waiting and not-approved: the verified card's shape in amber and red. */
.pp-verified.pending { background: linear-gradient(120deg, #fff4de, #ffe3b0); color: #6b4300; }
.pp-verified.rejected { background: linear-gradient(120deg, #ffedeb, #ffd6d1); color: #7e1a12; }
.pp-verified .pp-row { gap: 8px; }
/* A verification on its way: the page dims under one spinner and takes no taps. */
.pp-dim { position: fixed; inset: 0; z-index: 9998; background: rgba(0, 0, 0, .45); display: grid; place-items: center; animation: pp-dim-in .2s ease both; }
.pp-dim-card { background: var(--surface); border-radius: 18px; padding: 18px; box-shadow: var(--shadow); }
@keyframes pp-dim-in { from { opacity: 0 } to { opacity: 1 } }
.pp-evidence { position: relative; width: 100%; height: 170px; border-radius: 16px; border: 1px solid rgba(70, 99, 47, .35); display: grid; place-items: center; overflow: hidden; background: var(--surface); }
.pp-evidence img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* The saved or just-picked screenshot fills the box, letterboxed rather than
   cropped — a rank badge sits in a corner and must not be cut off. Pinned to the
   box's own edges: a percentage height against a grid area that is free to grow
   resolves to the image's full height, which is how it came out cropped. */
.pp-shot { position: absolute; inset: 0; overflow: hidden; }
/* The image is positioned, not centred by the grid: a percentage height on a
   grid item resolves against an indefinite area and comes back as the image's
   own full height, which is what cropped it. Insets give it a definite box and
   `contain` letterboxes the picture inside that. */
.pp-shot img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: contain; }
.pp-flow { display: flex; flex-wrap: wrap; gap: 6px; }

/* ── verify promo ───────────────────────────────────────────────────────── */
/* Gated, the page is one screen tall: the wash and its lockup have to be what
   the visitor sees, with a card or two of feed above them, however many rooms
   the feed actually holds. */
.pog-party.pp-gated { max-height: min(760px, 92vh); overflow: hidden; }
/* Ported from PartyVerifyPromo. The wash starts under the filter chips, goes
   fully solid about a third of the way down and runs to the bottom, so the
   lockup sits on flat colour instead of a fade and the feed behind stays
   readable at the top. */
.pp-promo {
  position: absolute; left: 0; right: 0; top: 150px; bottom: 0;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 0 26px 22px; text-align: center; z-index: 4;
  background:
    radial-gradient(120% 60% at 50% 64%, rgba(70, 99, 47, .22), transparent 70%),
    linear-gradient(180deg,
      rgba(252, 252, 252, 0) 0%, rgba(252, 252, 252, .45) 10%,
      rgba(252, 252, 252, .90) 22%, var(--page) 32%, var(--promo-wash) 100%);
}
.pp-promo.tight { top: 104px; }
.pp-promo-lock { display: grid; gap: 12px; justify-items: center; height: 72%; align-content: center; }
.pog-party .pp-promo-logo { height: 66px !important; width: auto !important; object-fit: contain; }
.pp-promo-title { font-size: 44px; line-height: 50px; font-weight: 800; color: var(--green); }
.pp-promo-title small { font-size: 18px; font-weight: 600; opacity: .6; margin-left: 8px; }
.pp-promo-lede { font-size: 16px; color: var(--text); }
.pp-promo-line { font-size: 14px; color: var(--text-muted); }
.pog-party .pp-promo-cta { flex: none; width: 100%; padding: 15px 18px; border-radius: 14px; font-weight: 700; }

/* ── coming soon ────────────────────────────────────────────────────────── */
/* Ported from PartyComingSoon: the whole page while Party is not open to this
   visitor. The same green light as the promo, behind a fan of three medals. */
.pp-soon {
  display: grid; gap: 14px; justify-items: center; align-content: center;
  min-height: 560px; padding: 16px 26px 36px; text-align: center;
  background:
    radial-gradient(120% 45% at 50% 20%, rgba(70, 99, 47, .22), transparent 70%),
    linear-gradient(180deg, var(--page) 35%, var(--promo-wash) 100%);
}
.pp-soon-medals { display: flex; align-items: center; justify-content: center; height: 132px; }
.pog-party .pp-soon-medals img {
  width: 84px !important; height: 84px !important; object-fit: contain;
  margin: 14px -12px 0 !important; opacity: .9;
}
.pog-party .pp-soon-medals img:first-child { transform: rotate(-12deg); }
.pog-party .pp-soon-medals img:last-child { transform: rotate(12deg); }
.pog-party .pp-soon-medals img:nth-child(2) {
  width: 124px !important; height: 124px !important; margin-top: 0 !important;
  opacity: 1; position: relative; z-index: 1;
}
.pp-soon-pill {
  background: var(--green); color: #fff; border-radius: 999px;
  padding: 6px 16px; font-size: 13px; font-weight: 700; letter-spacing: 2px;
}
.pog-party .pp-soon-list {
  list-style: none !important; margin: 6px 0 0 !important; padding: 18px !important;
  width: 100%; box-sizing: border-box; display: grid; gap: 14px; text-align: left;
  background: var(--surface); border-radius: 18px; box-shadow: var(--shadow);
}
.pog-party .pp-soon-list li { display: flex; align-items: center; gap: 12px; margin: 0 !important; font-size: 14px; color: var(--text); }
.pp-soon-list svg { color: var(--green); }

/* ── Discord ────────────────────────────────────────────────────────────── */
/* Ported from PartyDiscordCard, PartyLiveDot and PartyDiscordConnectionCard. */
/* The live dot: a core that breathes and a halo that swells behind it, in the
   pill's own colour. */
.pp-live { position: relative; width: 7px; height: 7px; margin: 0 7px 0 3px; flex: none; display: inline-block; color: inherit; }
.pp-live::before, .pp-live::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: currentColor; }
.pp-live::after { animation: pp-breathe 1.3s ease-in-out infinite alternate; }
.pp-live::before { animation: pp-halo 1.3s ease-in-out infinite alternate; }
@keyframes pp-breathe { from { transform: scale(.82); opacity: .72 } to { transform: scale(1); opacity: 1 } }
@keyframes pp-halo { from { transform: scale(1); opacity: .42 } to { transform: scale(2); opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .pp-live::before { display: none } .pp-live::after { animation: none } }
/* A join request waiting on the master: amber, with the breathing dot. */
.pp-waiting { display: inline-flex; align-items: center; color: var(--amber); font-weight: 600; }
.pp-waiting-card {
  display: flex; align-items: flex-start; gap: 6px; margin: 16px 12px 0; padding: 12px 14px;
  border-radius: 14px; background: rgba(178, 106, 0, .10); color: var(--amber);
}
.pp-waiting-card .pp-live { margin-top: 7px; }
.pp-waiting-card .pp-bold { color: var(--text); font-size: 14px; }
.pp-waiting-card .pp-small { color: var(--text-muted); }
.pp-room-view > .pp-waiting-card + .pp-actions { margin-top: 10px; }

.pog-party .pp-btn.discord { background: var(--discord); color: #fff; }
.pog-party .pp-btn.discord, .pog-party .pp-discord .pp-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.pp-discord {
  margin-left: 12px; margin-right: 12px; border-radius: 18px; padding: 16px; display: grid; gap: 10px;
  background: linear-gradient(135deg, var(--discord), var(--discord-deep)); color: #fff;
}
.pp-discord.profile { margin: 0; border-radius: 12px; gap: 12px; box-shadow: 0 1px 4px rgba(0, 0, 0, .25); }
.pp-discord-head { display: flex; align-items: center; gap: 10px; }
.pp-discord-head b { flex: 1; font-size: 15px; }
.pp-discord.profile .pp-discord-head b { font-size: 12px; }
.pp-discord-head .pp-small { color: rgba(255, 255, 255, .75); }
.pp-discord-body { display: grid; gap: 10px; }
.pp-discord p { color: rgba(255, 255, 255, .86); font-size: 13px; }
.pp-discord .pp-row { gap: 8px; }
.pp-discord .pp-row svg { flex: none; }
.pp-tag { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 50px; background: rgba(255, 255, 255, .18); font-size: 11px; font-weight: 600; white-space: nowrap; }
.pp-code { display: flex; align-items: center; gap: 10px; }
.pp-code b { flex: 1; font: 800 30px/1.1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 3px; }
.pog-party .pp-code button { width: 44px; height: 44px; border-radius: 50%; background: rgba(255, 255, 255, .16); color: #fff; display: grid; place-items: center; flex: none; }
.pog-party .pp-discord .pp-btn { background: #fff; color: var(--discord); font-weight: 700; }
.pog-party .pp-discord .pp-btn.ghost { background: transparent; color: #fff; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6); font-weight: 600; }
.pog-party .pp-discord .pp-btn.link { background: none; color: rgba(255, 255, 255, .9); justify-self: center; flex: none; }
.pog-party .pp-discord-link {
  display: inline-flex; align-items: center; gap: 6px; justify-self: start;
  padding: 7px 12px; border-radius: 50px; background: rgba(255, 255, 255, .16); color: #fff; font-size: 12px; font-weight: 600;
}
.pp-discord-id { display: flex; align-items: center; gap: 12px; }
.pp-discord-id b { display: block; }
.pp-discord-face {
  width: 56px; height: 56px; border-radius: 50%; flex: none; object-fit: cover;
  background: rgba(255, 255, 255, .14); display: grid; place-items: center; color: #fff;
}
/* The opening bar eases toward the end from the tap and is completed only by
   the real answer. */
.pp-voice-bar { height: 6px; border-radius: 50px; background: rgba(255, 255, 255, .22); overflow: hidden; }
.pp-voice-bar i { display: block; height: 100%; width: 92%; border-radius: inherit; background: #fff; transform-origin: left;
  animation: pp-voice 4.2s cubic-bezier(0, 0, .2, 1) both; }
.pp-voice-bar.done i { width: 100%; animation: pp-voice-done .32s ease both; }
@keyframes pp-voice { from { transform: scaleX(0) } to { transform: scaleX(1) } }
@keyframes pp-voice-done { from { transform: scaleX(.92) } to { transform: scaleX(1) } }
@media (prefers-reduced-motion: reduce) { .pp-voice-bar i { animation: none } }

/* ── dark ───────────────────────────────────────────────────────────────── */
/* The app ships light and dark side by side (ThemeMode in Theme.kt), so the web
   port follows the visitor's system setting rather than staying light-only.
   Tokens only — every component below reads them. */
.pog-party { --promo-wash: #dbead0; }
@media (prefers-color-scheme: dark) {
  .pog-party, .pp-modal {
    --green: #9fcb6a;
    --page: #121212;
    --surface: #1e1e1e;
    --muted-surface: #262626;
    --text: #f2f2f2;
    --text-muted: #a1a1a6;
    --divider: #2e2e2e;
    --danger: #ff6b6b;
    --amber: #e0a33c;
    --purple: #b98cf0;
    --discord-ink: #949cf7;
    --promo-wash: #1b2614;
    --shadow: 0 1px 6px rgba(0, 0, 0, .45), 0 2px 4px rgba(0, 0, 0, .5);
  }
  .pp-head { background: linear-gradient(180deg, rgba(159, 203, 106, .18), var(--page)); }
  .pp-chip[aria-pressed="true"] { background: rgba(159, 203, 106, .18); color: var(--green); }
  .pp-chip.solid, :is(.pog-party, .pp-modal) .pp-btn, .pp-msg.mine .pp-bubble { background: #46632f; color: #fff; }
  :is(.pog-party, .pp-modal) .pp-btn.decline, :is(.pog-party, .pp-modal) .pp-btn.danger { background: rgba(255, 107, 107, .16); color: var(--danger); }
  .pp-role.pick { background: rgba(159, 203, 106, .16); }
  .pp-role.pick.conflict { background: rgba(224, 163, 60, .16); }
  .pp-verified { background: linear-gradient(120deg, #15364e, #234d6c); color: #96d1ff; }
  .pp-verified.pending { background: linear-gradient(120deg, #3d2a08, #5a3f10); color: #ffe6b8; }
  .pp-verified.rejected { background: linear-gradient(120deg, #4a1512, #6a1f19); color: #ffd9d4; }
  .pp-modal-card, .pp-ready-card { background: #24301e; }
  :is(.pog-party, .pp-modal) .pp-modal-card .pp-btn.ghost { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .2); }
  :is(.pog-party, .pp-modal) .pp-modal-card .pp-field, :is(.pog-party, .pp-modal) .pp-modal-card .pp-field:is(:hover, :active, :focus) { border-color: rgba(255, 255, 255, .22); }
  .pp-ready-bar { background: rgba(224, 163, 60, .2); }
  .pp-waiting-card { background: rgba(224, 163, 60, .14); }
  .pp-confirm { background: rgba(159, 203, 106, .1); }
  .pp-group-count { background: rgba(159, 203, 106, .18); }
  .pp-soon-pill { color: var(--deep); }
  .pp-soon {
    background:
      radial-gradient(120% 45% at 50% 20%, rgba(159, 203, 106, .22), transparent 70%),
      linear-gradient(180deg, var(--page) 35%, var(--promo-wash) 100%);
  }
  .pp-promo {
    background:
      radial-gradient(120% 60% at 50% 64%, rgba(159, 203, 106, .22), transparent 70%),
      linear-gradient(180deg,
        rgba(18, 18, 18, 0) 0%, rgba(18, 18, 18, .45) 10%,
        rgba(18, 18, 18, .90) 22%, var(--page) 32%, var(--promo-wash) 100%);
  }
}

/* ── desktop ────────────────────────────────────────────────────────────── */
/* Everything above is the phone layout, and it stays the layout up to 900px —
   the apps only ever have phone width, and a narrow browser is the same shape.
   Past that the page is not a phone and should not pretend to be one: the
   column opens out, the feed becomes a grid of cards, and the room puts its
   roster and its chat side by side instead of stacking them into a 78vh tube. */
@media (min-width: 900px) {
  .pog-party {
    max-width: 1120px; border-radius: 24px; min-height: 680px;
    font-size: 16px; box-shadow: var(--shadow);
  }

  /* Header: room to breathe, and the wordmark scaled with it. */
  .pp-head { padding: 22px 28px 10px; gap: 12px; }
  .pog-party .pp-logo { height: 46px !important; }
  .pp-title { font-size: 32px; }
  .pp-title small { font-size: 14px; }
  .pp-tabs { padding: 0 28px 6px; }
  :is(.pog-party, .pp-modal) .pp-chip { font-size: 14px; padding: 9px 18px; }

  /* Room feed: cards in a grid, filters spanning the top of it. */
  .pp-list { padding: 6px 28px 32px; }
  .pp-rooms {
    display: grid; gap: 18px;
    grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
    align-content: start;
  }
  .pp-rooms > .pp-flow, .pp-rooms > .pp-card { grid-column: 1 / -1; }
  /* "Buat room" is an action, not a card — it should not stretch a whole row. */
  .pp-rooms > .pp-btn { grid-column: 1 / -1; justify-self: start; flex: none; padding: 13px 28px; }
  .pp-rooms > .pp-empty, .pp-rooms > .pp-list-head { grid-column: 1 / -1; }

  /* Reviews read as a two-column list; the headings span both. */
  .pp-reviews { display: grid; gap: 14px; grid-template-columns: repeat(2, 1fr); align-content: start; }
  .pp-reviews > .pp-flow, .pp-reviews > .pp-bold, .pp-reviews > p, .pp-reviews > .pp-group { grid-column: 1 / -1; }

  /* The profile form is a form: one readable column, not a stretched one. */
  .pp-profile { max-width: 620px; margin: 0 auto; }

  /* Room view: roster, actions and requests on the left, the chat thread as a
     full-height column on the right. The chat is the one part that scrolls. */
  .pp-room-view {
    display: grid; grid-template-columns: minmax(320px, 400px) 1fr;
    column-gap: 24px; padding: 0 28px 24px; min-height: 74vh;
    align-content: start; grid-auto-rows: min-content;
  }
  .pp-room-view > * { grid-column: 1; }
  /* The left column is as tall as its content; the chat sets the room's height
     itself, because a grid row only grows to what is in it and the roster is
     always shorter than a thread. */
  .pp-room-view > .pp-chat-col {
    display: flex; flex-direction: column; align-self: start;
    height: 70vh; min-height: 520px; margin-top: 0;
    grid-column: 2; grid-row: 1 / span 99;
    border-left: 1px solid var(--divider); padding-left: 4px;
  }
  /* `display: contents` holder in between, so this cannot be a child selector. */
  .pp-chat-col .pp-chat { flex: 1; min-height: 0; }
  /* The left column has its own 28px gutter; its sections sit flush in it. */
  .pp-room-head, .pp-actions, .pp-strip, .pp-requests, .pp-conflicts, .pp-room-view > .pp-roster { padding-left: 0; padding-right: 0; }
  .pp-waiting-card { margin-left: 0; margin-right: 0; }
  .pp-room-view > .pp-discord { margin-left: 0; margin-right: 0; }
  .pp-conflicts, .pp-why { justify-content: flex-start; }
  .pp-why { padding: 0; text-align: left; }
  .pp-room-head h2 { font-size: 22px; }
  .pp-chat { border-top: 0; padding: 4px 14px 10px; }
  .pp-bubble { max-width: 70%; }
  .pp-composer { padding: 10px 14px 0; }
  /* The roster fills the column's 400px — narrower, and a seat is too slim for
     its own role picker. Smaller rooms stop there rather than turning two seats
     into two portraits. */
  .pp-roster { max-width: 400px; }
  .pp-roster:not(.compact) .pp-slot { max-width: 74px; }

  /* The promo keeps its lockup phone-width and centred — a 44px title stretched
     across 1120px is a billboard, not a call to action. */
  .pp-promo { top: 190px; padding-bottom: 36px; }
  .pp-promo.tight { top: 136px; }
  .pp-promo-lock { max-width: 460px; margin: 0 auto; }
  .pp-soon { min-height: 620px; }
  .pp-soon > * { max-width: 460px; }
  .pp-modal-card { width: min(520px, 100%); }
}

/* ── page backdrop ──────────────────────────────────────────────────────── */
/* The page that carries the shortcode gets the promo page's dark green
   gradient (pogshop-web-party-landing.html #pp-hero) instead of the theme's
   white, so the card floats on the same ground as the promo. :has() keeps it
   to Party's page only; the theme's content wrappers are cleared so they don't
   paint white over it. Same in both themes — the card carries its own palette. */
body:has(.pog-party) {
  background:
    radial-gradient(ellipse 85% 75% at 50% 18%, rgba(159,203,106,.22), transparent 62%),
    linear-gradient(135deg, #131c0d 0%, #080a09 70%, #050605 100%) fixed !important;
}
body:has(.pog-party) :is(#page, .site, .site-content, .site-main, #content, #primary, main, article, .page-content, .entry-content, .container, .elementor-section, .elementor-widget-wrap):has(.pog-party) {
  background: transparent !important;
}
