/* BUTTON HOVER IS NOT DECLARED HERE. Every button on the site shares one
   hover — a small highlight and a 1px lift — and it lives in
   base/button-hover.css, which is linked on every page. The per-button
   `:hover` rules this file used to carry were removed when that file was
   written; a new one here would silently win (page sheets load after it) and
   put this page's buttons back out of step with the rest of the site. */

/* Tokens (base/root.css) are linked once site-wide — see fragments/header.html. */
@import url(animations.css);
@import url(buttons.css);
/* Canonical text-field look + character counter. A popup's fields are the same
   fields as a page's; the local copy of these rules that used to live at the
   bottom of this file has been removed in favour of the shared one. */
@import url(field.css);

/* ============================================================================
   POPUP / MODAL SYSTEM — shared skeleton for every popup.
   ----------------------------------------------------------------------------
   ONE popup look, site-wide. The reference implementation is the join-group
   popup (fragments/popup/popup-join-group-general.html): centered header
   (optional icon → title → subtitle), body, footer with equal-width buttons,
   round × in the top-right corner.

   Standard markup:

     <div class="modal-overlay">
       <div class="modal">                    ← add a size modifier if needed
         <button class="modal-close">×</button>
         <div class="modal-header">
           <div class="modal-icon"></div>   ← or modal-icon--danger, --pulse, --float
           <h2 class="modal-title">…</h2>
           <p class="modal-subtitle">…</p>
         </div>
         <div class="modal-body">…</div>
         <div class="modal-footer">
           <button class="btn btn-cancel">Cancel</button>
           <button class="btn btn-primary">OK</button>
         </div>
       </div>
     </div>

   Colours/scrim/blur come from the --popup-* tokens in base/root.css. Never
   hardcode them here or in a component file.

   Component popup CSS files should @import this file and ONLY add what is
   unique to them. Never redefine overlay/container/header/footer/buttons —
   the whole point of the unification is that those live here alone.
   ========================================================================== */

/* ── Popup motion — ONE open/close animation for every popup ──────────
   The rule: a popup FADES IN while MOVING UP, and FADES OUT while MOVING
   DOWN. Nothing else in a popup animates. The only exception is the header
   emoji's pulse (.modal-icon--danger / --pulse), which is a state signal
   ("read this"), not decoration.

   Popups used to each bring their own entrance — scale-in for the search
   pickers, a bobbing icon for some, a plain fade for others — which is why
   two popups that are the same component read as two different widgets.
   Do not add `animation:` to anything inside a popup. If a new popup needs
   an entrance, it already has one: this one.

   Closing is JS-driven: closePopup() (fragments/header.html) adds
   .modal-overlay--closing, waits --popup-anim-out, then empties the
   container. Keep the timeout there in step with the duration below. */

:root {
    --popup-anim-in: 0.28s;
    --popup-anim-out: 0.2s;
    --popup-anim-shift: 24px;
}

@keyframes popupOverlayIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes popupOverlayOut {
    from { opacity: 1; }
    to   { opacity: 0; }
}

/* Up + fade in. */
@keyframes popupIn {
    from { opacity: 0; transform: translateY(var(--popup-anim-shift)); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Down + fade out. */
@keyframes popupOut {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(var(--popup-anim-shift)); }
}

/* Closing state, set by closePopup() just before the container is emptied.
   Written as a COMPOUND selector (.modal-overlay.modal-overlay--closing), not
   the modifier alone: the modifier on its own ties with `.modal-overlay` on
   specificity, and `.modal-overlay` is declared further down this file, so the
   entrance animation would win and the popup would flash back IN as it left. */
.modal-overlay.modal-overlay--closing {
    animation: popupOverlayOut var(--popup-anim-out) ease-in forwards;
    pointer-events: none;
}

.modal-overlay.modal-overlay--closing .modal {
    animation: popupOut var(--popup-anim-out) ease-in forwards;
}

/* Motion is decoration here — the popup still opens and closes without it. */
@media (prefers-reduced-motion: reduce) {
    .modal-overlay,
    .modal,
    .modal-overlay.modal-overlay--closing,
    .modal-overlay.modal-overlay--closing .modal,
    .modal-icon,
    .modal-icon--danger,
    .modal-icon--pulse { animation: none !important; }
}

option { color: black; }

a {
    text-decoration: inherit;
    color: inherit;
    cursor: pointer;
    display: block;
}

/* ── Overlay ─────────────────────────────────────────────────────── */

.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--popup-scrim);
    backdrop-filter: var(--popup-scrim-blur);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100;
    animation: popupOverlayIn var(--popup-anim-in) ease-out;
    overflow-y: auto;
    padding: 2rem;
}

.modal-overlay.active { display: flex; }

/* Sits above other popups (e.g. a search picker opened from another modal).
   Must be HIGHER than .modal-overlay (100), not lower — otherwise the nested
   popup renders behind the modal that opened it.

   LAYERING ONLY. It used to also darken harder (--overlay-dark-lg), which is
   why "the popup that opens another popup" looked like a different component:
   the scrim jumped a shade. Every overlay now shares --popup-scrim. */
.modal-overlay--front { z-index: 200; }

/* Popups are swapped into two sibling containers declared in fragments/header.html:
   #popup-container      → base popups (quote, character create, event suggest, ...)
   #nested-popup-container → popups opened from *inside* a base popup (character search)

   Layering by container is authoritative, because --front is applied inconsistently:
   several base popups carry it, so class alone can't tell "base" from "nested" and a
   nested picker ends up behind the modal that opened it. The id+class selector below
   outranks .modal-overlay--front (0,1,0) on specificity, so it always wins. */
#nested-popup-container .modal-overlay { z-index: 300; }

/* Show/hide helpers toggled by JS. */
.open   { visibility: visible; opacity: 1; transition: opacity 0.3s ease, visibility 0.3s ease; }
.closed { visibility: hidden;  opacity: 0; transition: opacity 0.3s ease, visibility 0.3s ease; }

/* ── Container ───────────────────────────────────────────────────── */

.modal {
    background: var(--popup-bg);
    backdrop-filter: var(--popup-blur);
    border-radius: var(--popup-radius);
    border: var(--popup-border);
    box-shadow: var(--popup-shadow);
    max-width: 550px;
    width: 100%;
    animation: popupIn var(--popup-anim-in) ease-out;
    margin: auto;
    position: relative;
}

/* Size / behavior modifiers — combine with .modal.
   These change WIDTH and SCROLLING only. They must never change the fill,
   border, radius or shadow: that is what makes two popups look like two
   different components. */

.modal--wide {                       /* long forms, e.g. report popup */
    max-width: 650px;
    max-height: 90vh;
    overflow-y: auto;
}

/* It used to open with its own scale-in (transform: scale(.9) + @keyframes
   scaleIn), which is why the search pickers popped while every other popup
   slid. Width and scrolling only now — the entrance is .modal's popupIn. */

/* COLUMN LAYOUT, NOT JUST A max-height. `max-height: 90vh` on its own only
   promises to CLIP: the header (~230px) plus the footer (~130px) already eat
   more than the 300px the old `.character-search-body` allowance assumed, so a
   full result list pushed the box past 90vh — it grew up over the navbar and
   `overflow: hidden` sliced the footer off, which is why the Done button sat
   flush on the popup's bottom edge with no padding under it.

   As a flex column the header, pager and footer keep their natural height and
   the body is the only part that gives, so the popup is never taller than the
   cap and the footer is always fully inside it. */
.modal--search {                     /* search popups with internal scroll */
    max-width: 600px;
    max-height: 90vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* Header / pager / footer: natural height, never squeezed. */
.modal--search > * { flex: 0 0 auto; }

/* The scrolling middle. `min-height: 0` is what actually lets it shrink — a
   flex item's default min-height is its content, which would push the box back
   past the cap. The own max-height is dropped here: the cap now comes from the
   modal, so the body no longer has to guess the header and footer's height. */
.modal--search > .character-search-body,
.modal--search > .modal-body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
}

.modal--form {                       /* big create/edit form popups */
    width: 90%;
    max-width: 800px;
    max-height: 90vh;
    overflow-y: auto;
}

/* DEPRECATED — kept so old markup doesn't break. It used to paint its own
   solid purple panel; that colour is now the global --popup-bg, so this
   modifier has nothing left to do. Drop the class when you touch the markup. */
.modal--solid { /* no-op */ }

/* Overlay that starts hidden and is shown by JS adding .active. */
.modal-overlay--hidden { display: none; }

/* Compact: never taller than the viewport. Combine with .modal (550px) or
   with .modal--form / .modal--wide to keep their width.
   Used by: add/edit tier, character slot.

   It used to also shrink the padding, labels, fields, help text and × button.
   That is what made "the tier popup" and "the join popup" read as two
   different widgets. Sizing is global now — this modifier is scroll only. */
.modal--compact {
    max-height: 88vh;
    overflow-y: auto;
}

/* ── Structure ───────────────────────────────────────────────────── */

/* ONE header. Centered, 2rem padding, optional icon → title → subtitle.
   The 3.75rem side gutter keeps long titles clear of the absolute × button. */
.modal-header {
    padding: 2rem 3.75rem 2rem;
    text-align: center;
    border-bottom: var(--popup-divider);
}

/* DEPRECATED header modifiers.
   --compact shrank the title to 1.5rem and the padding to 1.5rem; --row moved
   the title left and put the × inline beside it. Between them, four popups
   that are the same thing looked like three different components. Both are
   now no-ops that fall through to the canonical .modal-header above; they stay
   only so existing markup keeps rendering. Remove the class when you edit a
   fragment — do not add them to anything new. */
.modal-header--compact,
.modal-header--row { /* no-op — see .modal-header */ }

.modal-body { padding: 2rem; }

.modal-footer {
    padding: 2rem;
    display: flex;
    gap: 1rem;
    border-top: var(--popup-divider);
    justify-content: center;
}

/* DEPRECATED — footers are uniform now: equal-width buttons filling the row
   (see the `.modal-footer .btn` rule below). Right-aligning one popup's
   buttons is exactly the kind of drift this file exists to prevent. */
.modal-footer--end { /* no-op */ }

/* ── Header content ──────────────────────────────────────────────── */

.modal-icon {
    width: 70px;
    height: 70px;
    background: var(--gradient-primary-subtle);
    border: 3px solid var(--overlay-xl);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.5rem;
    margin: 0 auto 1rem;
}

/* Danger version: red pulse. Reserved for anything that carries a warning —
   delete confirmations, reports, disputes — so the red pulse keeps meaning
   "read this before you continue" instead of just "here is an emoji". */
.modal-icon--danger {
    width: 80px;
    height: 80px;
    font-size: 3rem;
    background: var(--danger-bg-hover);
    border-color: var(--danger-border-strong);
    animation: pulse 2s infinite;
}

/* Same pulse, brand purple. For popups that want the attention-grabbing
   heartbeat without implying a warning (e.g. invites). */
.modal-icon--pulse {
    width: 80px;
    height: 80px;
    font-size: 3rem;
    background: var(--primary-bg-hover);
    border-color: var(--primary-border);
    animation: pulseBrand 2s infinite;
}

@keyframes pulseBrand {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand-2) 60%, transparent);
    }
    50% {
        transform: scale(1.05);
        box-shadow: 0 0 0 18px transparent;
    }
}

/* DEPRECATED — no-op. It used to be a bare 4rem emoji with no circle, bobbing
   up and down forever. The circle came back first; the bob is gone too now
   that the only motion allowed inside a popup is the open/close slide and the
   pulse on --danger / --pulse. Falls through to the standard .modal-icon.
   Don't add it to new markup. */
.modal-icon--float { /* no-op — see .modal-icon */ }

.modal-title {
    font-size: 1.8rem;
    font-weight: 700;
    margin-bottom: 0.5rem;
}

.modal-subtitle {
    color: var(--color-text-muted);
    font-size: 0.95rem;
}

/* ── Close button ─────────────────────────────────────────────────────
   ONE × for every popup: 40px circle, top-right, spins on hover and turns
   red. `.modal-close` is the canonical name; `.close-button` (and its
   `--inline` variant) are aliases kept alive because a lot of markup uses
   them. They render identically — there used to be three different ×s.
   Prefer `.modal-close` in new markup.                                   */

.modal-close,
.close-button,
.close-button--inline {
    position: absolute;
    top: 1.5rem;
    right: 1.5rem;
    width: 40px;
    height: 40px;
    border: var(--glass-border);
    background: var(--overlay-lg);
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    line-height: 1;
    color: white;
    transition: var(--transition);
    z-index: 10;
    padding: 0;
}

.modal-close:hover,
.close-button:hover,
.close-button--inline:hover {
    background: color-mix(in srgb, var(--hue-danger) 40%, transparent);
    border-color: var(--color-danger-text);
    color: white;
    transform: rotate(90deg);
}

/* ── Scrollbar ───────────────────────────────────────────────────── */

.modal::-webkit-scrollbar { width: 8px; }

.modal::-webkit-scrollbar-track {
    background: var(--overlay-xs);
    border-radius: var(--radius-md);
}

.modal::-webkit-scrollbar-thumb {
    background: var(--overlay-xl);
    border-radius: var(--radius-md);
}

.modal::-webkit-scrollbar-thumb:hover { background: var(--overlay-xxl); }

/* ── Popup buttons ───────────────────────────────────────────────────
   .btn-cancel / .btn-create pair with .btn (base in buttons.css).
   .btn-confirm-danger is the "disabled until user confirms" pattern:
   starts non-clickable; JS adds .enabled when the checkbox is ticked.   */

/* Footer buttons share the row equally — the join-group look, applied
   everywhere. Two buttons = two halves.

   The cap is what stops a lone button from becoming a banner. `flex: 1` on its
   own means "one button = full width", which is fine at 550px and absurd on a
   one-button footer in a 900px .modal--form: the Manage Group Invites popup was
   a single "Cancel" stretched across the whole bottom edge. With the cap the
   pair still splits the row evenly (they land well under 280px in every modal
   size), and a solitary button sits at a button's width, centered by the
   footer's own `justify-content: center`. */
.modal-footer .btn,
.modal-footer .modal-btn {
    flex: 1 1 0;
    min-width: 0;
    max-width: 280px;
}

/* `.modal-btn*` is the join-group popup's private button naming. It is now
   just an alias of the shared `.btn*` variants so the two spellings can't
   drift apart; `.modal-btn-primary` maps to the success (green) fill it has
   always had. New markup should use `.btn .btn-success` / `.btn .btn-cancel`. */
.modal-btn {
    padding: 1rem 2rem;
    border: none;
    border-radius: var(--radius-md);
    font-weight: 600;
    cursor: pointer;
    font-size: 1rem;
    transition: var(--btn-transition);
    white-space: nowrap;
    word-break: auto-phrase;
}

.modal-btn-primary {
    background: var(--gradient-success);
    color: white;
    box-shadow: var(--shadow-success);
}

.modal-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

.modal-btn-secondary {
    background: var(--overlay-lg);
    color: white;
    border: var(--glass-border);
}

.btn-create {
    background: var(--gradient-primary);
    color: white;
    box-shadow: var(--shadow-primary);
}

.btn-confirm-danger {
    background: var(--gradient-danger);
    color: white;
    box-shadow: var(--shadow-danger);
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-confirm-danger.enabled {
    opacity: 1;
    cursor: pointer;
}

/* ── Shared form elements (popup context) ────────────────────────── */

.form-group { margin-bottom: 1.5rem; }

label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    font-size: 0.95rem;
}

/* .required MOVED to base/field.css. The popups used to paint the star red
   while every page painted it gold; it is now gold everywhere. */

/* The field + focus-ring rules that used to sit here are now base/field.css,
   imported at the top of this file. They were an exact copy of it. */

.help-text {
    font-size: 0.85rem;
    color: var(--color-text-soft);
    margin-top: 0.5rem;
}

/* "I understand…" checkbox row used by delete/report confirmations. */
.confirmation-checkbox {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    padding: 1rem;
    background: var(--overlay-sm);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: var(--transition);
}

.confirmation-checkbox:hover { background: var(--overlay-md); }

.confirmation-checkbox input[type="checkbox"] {
    width: 20px;
    height: 20px;
    cursor: pointer;
    accent-color: var(--color-danger);
}

.confirmation-checkbox label {
    cursor: pointer;
    font-size: 0.95rem;
    user-select: none;
}

/* ── Search block (shared by character/event/profile search popups) ── */

.character-search-body {
    padding: 2rem;
    max-height: calc(90vh - 300px);
    overflow-y: auto;
}

.character-search-body::-webkit-scrollbar { width: 8px; }

.character-search-body::-webkit-scrollbar-track {
    background: var(--overlay-sm);
    border-radius: var(--radius-md);
}

.character-search-body::-webkit-scrollbar-thumb {
    background: var(--overlay-xl);
    border-radius: var(--radius-md);
}

.character-search-body::-webkit-scrollbar-thumb:hover { background: var(--overlay-xxl); }

.search-box {
    position: relative;
    margin-bottom: 2rem;
}

.search-input {
    width: 100%;
    padding: 1.2rem 3.5rem 1.2rem 1.5rem;
    background: var(--overlay-sm);
    border: var(--glass-border);
    border-radius: var(--radius-lg);
    color: white;
    font-size: 1.1rem;
    font-family: inherit;
    transition: var(--transition);
}

.search-input::placeholder { color: var(--color-text-subtle); }

.search-input:focus {
    outline: none;
    border-color: var(--color-gold);
    box-shadow: var(--shadow-gold);
    background: var(--overlay-md);
}

.search-icon {
    position: absolute;
    right: 1.2rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 1.5rem;
    color: var(--color-text-soft);
    pointer-events: none;
}

.results-count {
    color: var(--color-text-muted);
    font-size: 0.9rem;
    margin-bottom: 1rem;
}

/* ── Room for the results ─────────────────────────────────────────────
   A search popup is its result list; everything else is chrome. Capped at 90vh
   the canonical chrome — a 70px icon, 2rem of header padding top and bottom,
   2rem under the search box — left barely two rows showing on a laptop, so a
   list of six was mostly scrollbar.

   This is a rule for the WHOLE .modal--search family, not one popup's private
   restyle: that distinction is what the deprecation notes on
   .modal-header--compact are about. Every search popup gets the same trimmed
   header, so they still read as one component. Fill, border, radius and shadow
   are untouched. */
.modal--search .modal-header { padding: 1.5rem 3.75rem 1.25rem; }

.modal--search .modal-icon {
    width: 56px;
    height: 56px;
    font-size: 2rem;
    margin-bottom: 0.75rem;
}

.modal--search .modal-title { font-size: 1.5rem; margin-bottom: 0.25rem; }

.modal--search > .character-search-body { padding: 1.25rem 2rem; }

.modal--search .search-box { margin-bottom: 1rem; }

.modal--search .modal-footer { padding: 1.25rem 2rem; }

/* The rows sit closer together than they do on a page, for the same reason. */
.modal--search .character-list { gap: 0.5rem; }

/* Pager between the result list and the footer (multi-select pickers). It sits
   OUTSIDE the scrolling body on purpose — a pager you have to scroll to reach
   is a pager nobody finds. `[hidden]` has to be spelled out because
   `.results-page` sets `display: flex`, which would otherwise show it. */
.picker-pager {
    padding: 0 2rem 1rem;
    justify-content: center;
}

.picker-pager[hidden] { display: none; }

.picker-pager .previous-btn:disabled,
.picker-pager .next-btn:disabled {
    opacity: 0.4;
    cursor: default;
    box-shadow: none;
    transform: none;
}

/* ── Result list (character/event/profile items) ─────────────────── */

.character-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.character-item {
    background: var(--overlay-sm);
    padding: 1.2rem;
    border-radius: var(--radius-lg);
    border: 2px solid var(--overlay-lg);
    cursor: pointer;
    transition: var(--transition);
    display: flex;
    align-items: center;
    gap: 1rem;
}

.character-item:hover {
    background: var(--overlay-md);
    border-color: var(--color-gold);
    transform: translateX(5px);
}

.character-avatar {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: var(--gradient-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    flex-shrink: 0;
    /* Ring: base/avatar.css (site-wide). Do not re-declare it here. */
}

/* When the avatar is a real thumbnail rather than an emoji/initial, crop it to
   the circle instead of letting it stretch. */
img.character-avatar {
    object-fit: cover;
    display: block;
}

.character-info { flex: 1; }

.character-name {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 0.3rem;
}

.character-details {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

/* ── Responsive ──────────────────────────────────────────────────── */

@media (max-width: 600px) {
    .modal-footer { flex-direction: column-reverse; }
    /* Stacked, they SHOULD fill the row — a phone-width modal is narrower than
       the desktop cap anyway, so lift it rather than leave them off-centre. */
    .modal-footer .btn,
    .modal-footer .modal-btn { width: 100%; max-width: none; }
}

/* RESPONSIVE FIX: modals fill small screens comfortably instead of being
   squeezed by the 2rem overlay padding; internal padding scales down. */
@media (max-width: 768px) {
    .modal-overlay { padding: 0.75rem; }

    .modal--form { width: 100%; max-height: 94vh; }
    .modal--wide, .modal--search { max-height: 94vh; }

    /* Keep the side gutter so the absolute close button never overlaps text. */
    .modal-header { padding: 1.25rem 3.25rem 1.25rem; }
    .modal-body { padding: 1.25rem; }
    .modal-footer { padding: 1.25rem; }

    .modal--compact { max-height: 94vh; }

    .modal-title { font-size: 1.4rem; }

    .modal-close, .close-button, .close-button--inline { top: 1rem; right: 1rem; }

    .modal-icon { width: 55px; height: 55px; font-size: 1.9rem; }
    .modal-icon--danger,
    .modal-icon--pulse { width: 62px; height: 62px; font-size: 2.2rem; }
    .modal-icon--float { font-size: 2.8rem; }

    .character-search-body { padding: 1.25rem; max-height: calc(94vh - 220px); }
    /* The column layout above owns the height inside .modal--search. */
    .modal--search > .character-search-body { max-height: none; }
    .picker-pager { padding: 0 1.25rem 0.75rem; }
    .search-box { margin-bottom: 1.25rem; }
    .character-item { padding: 0.9rem; }
    .character-avatar { width: 48px; height: 48px; font-size: 1.5rem; }

    /* Long names/details inside result rows truncate cleanly. */
    .character-info { min-width: 0; }
    .character-name {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}
