/* ══ overlays ════════════════════════════════════════════════════════════
 * Everything that sits over the page: the modal, the two ⋯ menus, and the
 * toast stack. All three become something else on a phone - see the foot of
 * the file.
 */

/* ── modal ──────────────────────────────────────────────────────────── */
.overlay { position: fixed; inset: 0; background: rgba(8, 10, 20, .74);
  backdrop-filter: blur(4px); display: grid; place-items: center; z-index: 60;
  padding: 20px; animation: fade-in .15s ease; overscroll-behavior: contain; }
.modal { width: min(900px, 96vw); max-height: 86vh; max-height: 86dvh;
  display: flex; flex-direction: column; background: var(--panel);
  border-color: var(--border-hi); box-shadow: 0 24px 80px rgba(0, 0, 0, .65);
  animation: pop .18s ease; }
/* The head is a *lid*, not a first row. It was the same color as everything
   under it with a hairline beneath, so a modal read as one undifferentiated
   column of stuff and the title had to compete with the content for the eye. A
   step up in surface and a hard rule under it does the separating, which lets
   the title go back to being read first because it is at the top rather than
   because it is the loudest thing there. */
.modal-head { display: flex; justify-content: space-between; align-items: center;
  padding: 14px 18px; border-bottom: 2px solid var(--border-hi);
  background: var(--panel-2); flex: none; }
.modal-head > b { font-family: var(--font-display); font-size: 17px; font-weight: 600;
  color: var(--text-hi); }
/* The body sits on the *sheet* color, a step below the card it is inside. Every
   card, field and shelf row in it is a slab on that sheet - the same figure and
   ground the page itself uses, so a modal is a small page rather than a
   different kind of surface with its own rules. */
.modal-body { padding: 16px 18px; overflow-y: auto; display: grid; gap: 12px;
  align-content: start; background: var(--bg);
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }

/* ── rooms inside a modal ─────────────────────────────────────────────
   A sheet that has grown past one scroll splits into rooms rather than
   getting longer - see `kit::input::rooms`. The strip stays put while the
   room under it scrolls, because a tab you have to scroll back up to reach
   is a tab people stop using; and it sits on the head's surface, so head and
   strip read as one lid with two lines in it rather than as two bars. */
.modal-body > .subtabs { position: sticky; top: -16px; z-index: 3;
  margin: -16px -18px 0; padding: 12px 18px;
  background: var(--panel-2); border-bottom: 2px solid var(--border-soft); }
/* `.room` itself is styled in controls.css beside the strip that switches it. */
/* Every modal shows what you can spend, so nothing has to be closed to check. */
.chip.purse { margin-left: auto; margin-right: 8px; background: rgba(255, 196, 66, .1);
  border-color: rgba(255, 196, 66, .35); color: var(--gold);
  font-family: var(--font-num); font-weight: 600; }
/* Every dialog ends the same way: do the thing, or don't - and it ends there
   whatever the body is doing. Pinned below the scroll rather than trailing the
   content, because on a sheet-height modal the primary action was reliably
   under the fold of the form it belonged to. Mirrors the head: a step up in
   surface, a hard rule, so the sheet reads as lid / contents / base. */
.modal-foot { flex: none; margin: 0; padding: 12px 18px calc(12px + env(safe-area-inset-bottom));
  background: var(--panel-2); border-top: 2px solid var(--border-hi); }
.modal-foot .primary { flex: 1; }
@keyframes fade-in { from { opacity: 0; } }
@keyframes pop { from { transform: scale(.97) translateY(8px); opacity: 0; } }
@keyframes rise { from { transform: translateY(100%); } }

/* ── the menu behind the ⋯ ──────────────────────────────────────────── */
/* Leaving is rare and final-feeling; it does not deserve a permanent button
   under the nav, competing with the seven things a player actually does. */
.menu-scrim { position: fixed; inset: 0; z-index: 40; }
.menu-pop { position: absolute; top: 52px; right: 12px; z-index: 41; overflow: hidden;
  min-width: 150px; background: var(--panel); border: 2px solid var(--border-hi);
  border-radius: var(--r-md); box-shadow: 0 12px 30px rgba(0, 0, 0, .6); }
.menu-pop button { display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 10px 13px; background: transparent; border: none; border-radius: 0;
  box-shadow: none; font-size: 13px; font-weight: 700; text-align: left; color: var(--err); }
.menu-pop button:active:not(:disabled) { transform: none; }
@media (hover: hover) {
  .menu-pop button:hover { background: rgba(255, 93, 115, .12); color: var(--err); }
}

/* ── the ⋯ on a card in a grid ───────────────────────────────────────
   A person's row only ever has one action it exists *for* - message, accept,
   add - so everything else (give orbs, give an item, introduce, unfriend)
   moves behind this instead of stretching the card into a button wall. Same
   shape as `.char-menu`/`.menu-pop`, anchored to the card itself rather than
   the header, since a grid can hold dozens of these at once. */
.card-menu { position: absolute; top: 10px; right: 10px; z-index: 2;
  background: var(--panel); border: 1.5px solid var(--border); color: var(--muted);
  box-shadow: none;
  width: 27px; height: 27px; min-height: 0; padding: 0; border-radius: var(--r-sm);
  font-size: 13px; line-height: 1; display: grid; place-items: center; }
.card-menu-pop { top: 42px; right: 10px; }
.card-menu-pop button { color: var(--text); }
@media (hover: hover) {
  .card-menu-pop button:hover { background: var(--panel-3); color: var(--text); }
}

/* ── toasts ───────────────────────────────────────────────────────────
 * For most of Orbis this is not a receipt, it is *the interface*. A shift's
 * whole haul, a craft's quality, a refusal and its reason all arrive here and
 * nowhere else - so it is read far more often than any card, and it used to be
 * the plainest thing on the screen.
 *
 * Three things carry it, in the order the eye takes them:
 *
 *   1. a **mark in a colored disc** - the only part legible from the corner of
 *      an eye that is still looking at the button it pressed;
 *   2. the **sentence**, at a size meant to be read rather than squinted at;
 *   3. the **rows**, if it brought a list, each one an item with its own icon.
 *
 * Plus a drain along the bottom, so it leaves for a visible reason.
 */
.toasts { position: fixed; top: 16px; right: 16px; z-index: 100; display: grid; gap: 9px;
  width: min(370px, calc(100vw - 32px)); }
.toast { position: relative; overflow: hidden;
  padding: 12px 14px; border-radius: var(--r-lg); border: 2px solid var(--border-hi);
  cursor: pointer; background: var(--panel-2); box-shadow: 0 14px 34px rgba(0, 0, 0, .6);
  animation: slide-in .25s cubic-bezier(.2, .9, .3, 1.2); }
/* The tint is the level, said twice: a bar down the side for the glance, and a
   wash behind the whole slab so a red toast is red before a word is read. Each
   family's own `--tint-*` pair, so this borrows the sheet's vocabulary rather
   than inventing a second set of status colors. */
.toast.success { border-left: 5px solid var(--ok); background: var(--tint-ok); }
.toast.info { border-left: 5px solid var(--accent); background: var(--tint-cool); }
.toast.warn { border-left: 5px solid var(--warn); background: var(--tint-gold); }
.toast.error { border-left: 5px solid var(--err); background: var(--tint-err); }

.toast-head { display: flex; align-items: center; gap: 10px; }
/* The disc. Sized for a fingertip's worth of attention rather than for the text
   beside it - this is the part that has to work at arm's length. */
.toast-badge { flex: none; width: 34px; height: 34px; border-radius: var(--r-pill);
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .07); border: 1.5px solid var(--border-hi); }
.toast-icon { font-size: 18px; line-height: 1; }
img.toast-icon { width: 20px; height: 20px; object-fit: contain; }
.toast.success .toast-badge { border-color: var(--ok); color: var(--ok); }
.toast.info .toast-badge { border-color: var(--accent); color: var(--accent-2); }
.toast.warn .toast-badge { border-color: var(--warn); color: var(--warn); }
.toast.error .toast-badge { border-color: var(--err); color: var(--err); }
/* 14px and a real line height: this is a sentence somebody has to read, often
   the only place a number is ever said. It was 13px and cramped. */
.toast-title { font-weight: 800; font-size: 14px; line-height: 1.35; color: var(--text-hi);
  min-width: 0; overflow-wrap: break-word; }

/* What you got, one row each. Indented under the sentence rather than starting
   at the edge, so the list reads as belonging to the line above it. */
.toast-lines { margin: 8px 0 2px 44px; display: grid; gap: 4px; }
.toast-line { display: flex; align-items: center; gap: 7px;
  padding: 4px 8px; border-radius: var(--r-sm);
  background: rgba(0, 0, 0, .22);
  font-size: 12.5px; font-weight: 600; color: var(--text); }
.toast-line-icon { font-size: 15px; line-height: 1; flex: none; }
img.toast-line-icon { width: 16px; height: 16px; object-fit: contain; }

/* The clock. Sits on the bottom edge and drains left; `animation-duration` is
   written on the element by whoever knows how long this toast lives, so the bar
   and the removal can never disagree. */
.toast-life { position: absolute; left: 0; bottom: 0; height: 3px; width: 100%;
  transform-origin: left center; background: currentColor; opacity: .5;
  animation-name: toast-drain; animation-timing-function: linear;
  animation-fill-mode: forwards; }
.toast.success .toast-life { color: var(--ok); }
.toast.info .toast-life { color: var(--accent); }
.toast.warn .toast-life { color: var(--warn); }
.toast.error .toast-life { color: var(--err); }
@keyframes toast-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }

@keyframes slide-in { from { transform: translateX(30px); opacity: 0; } }

/* ── popups become sheets ─────────────────────────────────────────────
   A centred box on a phone puts its buttons where the hand isn't. */
@media (max-width: 920px) {
  .overlay { padding: 0; place-items: end stretch; }
  .modal { width: 100%; max-width: none; max-height: 92dvh;
    border-radius: 22px 22px 0 0; border-bottom: none;
    animation: rise .24s cubic-bezier(.2, .8, .2, 1); }
  .modal::before { content: ""; flex: none; width: 38px; height: 4px; margin: 9px auto 0;
    border-radius: var(--r-pill); background: var(--border-hi); }
  .modal-head { padding: 12px 16px; gap: 8px; }
  .modal-head b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .modal-head .small { min-width: 40px; min-height: 40px; }
  .modal-body { padding: 14px 16px calc(16px + env(safe-area-inset-bottom)); }
  /* Follows the body's own padding, which is tighter here than on a desktop -
     the strip has to cover exactly the gap it is pulled up into or a sliver of
     scrolling content shows through above it. */
  .modal-body > .subtabs { top: -14px; margin: -14px -16px 0; padding: 10px 16px; }
  .chip.purse { margin-right: 0; }

  /* ── toasts move off the header and above the thumb ─────────────── */
  .toasts { top: auto; left: 12px; right: 12px; width: auto; max-width: none;
    bottom: calc(var(--nav-h) + env(safe-area-inset-bottom) + 12px); }
  .toast { animation: toast-up .25s ease; }
  @keyframes toast-up { from { transform: translateY(14px); opacity: 0; } }

  /* A modal here is a sheet docked to the same bottom edge the toast stack
     lives on - a toast landing mid-modal would sit right on top of its confirm
     button. Move the stack above the topbar instead for as long as one is open. */
  .toasts.modal-open { top: calc(var(--top-h) + env(safe-area-inset-top) + 10px);
    bottom: auto; }
  .toasts.modal-open .toast { animation: slide-in .25s ease; }
}
/* A phone held sideways has no vertical room to give away. */
@media (max-width: 920px) and (max-height: 480px) {
  .modal { max-height: 96dvh; }
}

/* ── the face, full size ──────────────────────────────────────────────────
 * Not a `.modal`: a modal is a place where you spend, so it carries a title
 * bar, your purse and a footer. This is a picture, and everything around a
 * picture is in the way of it. All it borrows is `.overlay` and the rule that
 * pressing away closes it.
 *
 * A rare hat is worth exactly what somebody will pay to be seen in it, and at
 * 38px in a sidebar nobody can see it. This is where they can.
 */
.face-viewer-frame { position: relative; display: grid; justify-items: center; gap: 12px; }
/* Written as `.face-viewer .avatar.face-huge` rather than `.avatar.face-huge`,
   and the extra class is not decoration. The viewer is drawn *inside* whatever
   avatar was pressed, so any rule elsewhere that sizes an avatar by its
   surroundings - `.profile-head .avatar`, and whatever the next one is - also
   matches the enlarged copy. Three classes here outrank two of those, wherever
   in the cascade they sit. */
.face-viewer .avatar.face-huge {
  width: min(78vw, 52vh, 460px); height: min(78vw, 52vh, 460px);
  font-size: min(30vw, 20vh, 170px);
  border-width: 3px; box-shadow: 0 0 0 3px var(--accent), 0 30px 70px -20px rgba(0,0,0,.8); }
.face-viewer-name { font-family: var(--font-display); font-size: 19px; font-weight: 600;
  color: var(--text-hi); }
/* Off the picture entirely. A round frame has no corner to tuck a button into
   without it landing on somebody's head. */
.face-viewer > .ghost { position: fixed; top: 14px; right: 14px; }
/* An avatar that opens is an avatar that has to look pressable. */
.zoomable { cursor: zoom-in; }
@media (hover: hover) {
  .zoomable:hover { box-shadow: 0 0 0 3px var(--accent); }
}
