/* Find A Brainrot — Bootstrap 5.3 theme (no build step, CSS custom properties only)
   Load order (see layouts): bootstrap.min.css → bootstrap-icons.css → theme.css → rarity.css → application.css
   Palette/type/radius tokens come from FABR/bsaic_css_style_guide (story 3033/3034).
   Rarity tier variables are NOT here: RarityTheme emits them from the rarity_tiers table. */

/* ---- Fredoka (self-hosted variable font, OFL) ---- */
@font-face {
  font-family: "Fredoka";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/fredoka-latin-c7301cae.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Fredoka";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/fredoka-latin-ext-04ba9568.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root,
[data-bs-theme=light] {
  /* ---- palette ---- */
  --fab-void:     #12063A;  /* deepest bg */
  --fab-ink:      #1D0B4E;  /* text on light, surface on dark */
  --fab-purple:   #9B3DFF;  /* primary */
  --fab-pink:     #FF3FBF;  /* secondary */
  --fab-slime:    #5EEB3C;  /* success */
  --fab-lava:     #FF3B3B;  /* danger */
  --fab-gold:     #FFC93C;  /* warning / coins */
  --fab-ice:      #2FE6FF;  /* info */
  --fab-lilac:    #F4EEFF;  /* light bg */

  /* ---- bootstrap theme colors ---- */
  --bs-primary: var(--fab-purple);      --bs-primary-rgb: 155, 61, 255;
  --bs-secondary: var(--fab-pink);      --bs-secondary-rgb: 255, 63, 191;
  --bs-success: var(--fab-slime);       --bs-success-rgb: 94, 235, 60;
  --bs-danger: var(--fab-lava);         --bs-danger-rgb: 255, 59, 59;
  --bs-warning: var(--fab-gold);        --bs-warning-rgb: 255, 201, 60;
  --bs-info: var(--fab-ice);            --bs-info-rgb: 47, 230, 255;
  --bs-dark: var(--fab-void);           --bs-dark-rgb: 18, 6, 58;
  --bs-light: var(--fab-lilac);         --bs-light-rgb: 244, 238, 255;

  /* ---- body ---- */
  --bs-body-bg: var(--fab-lilac);
  --bs-body-bg-rgb: 244, 238, 255;
  --bs-body-color: var(--fab-ink);
  --bs-body-font-family: "Fredoka", "Nunito", system-ui, -apple-system, sans-serif;
  --bs-body-font-weight: 500;
  --bs-body-line-height: 1.4;
  --bs-heading-font-weight: 700;
  --bs-link-color: var(--fab-purple);
  --bs-link-hover-color: #7A1FE0;
  --bs-border-color: rgba(29, 11, 78, .15);
  --bs-border-radius: 1rem;
  --bs-border-radius-sm: .625rem;
  --bs-border-radius-lg: 1.5rem;
  --bs-border-radius-pill: 50rem;

  /* ---- components ---- */
  --bs-btn-padding-y: .8rem;               /* ≥44pt tap target */
  --bs-progress-height: 1.25rem;
  --bs-progress-bg: rgba(29, 11, 78, .12);

  /* ---- fab effects ---- */
  --fab-outline: #1D0B4E;                  /* display text stroke / chunky borders */
  --fab-gradient-primary: linear-gradient(135deg, #B15CFF 0%, #FF3FBF 100%);
  --fab-gradient-ice: linear-gradient(135deg, #2F7BFF 0%, #2FE6FF 100%);
  --fab-gradient-fire: linear-gradient(135deg, #FF5A2D 0%, #FFC93C 100%);
  --fab-gradient-gold: linear-gradient(135deg, #FFAA00 0%, #FFE066 100%); /* the warning/coin colour as a button (2026-09-12) */
  --fab-gradient-slime: linear-gradient(135deg, #22C55E 0%, #C6FF3C 100%);
  --fab-gradient-rainbow: linear-gradient(90deg, #FF3B3B, #FFC93C, #5EEB3C, #2FE6FF, #9B3DFF, #FF3FBF);
  --fab-tap-target: 44px;
}

[data-bs-theme=dark] {
  --bs-body-bg: var(--fab-void);
  --bs-body-color: #F4EEFF;
  --bs-body-bg-rgb: 18, 6, 58;
  --bs-secondary-bg: var(--fab-ink);
  --bs-tertiary-bg: #2A1470;
  --bs-border-color: rgba(244, 238, 255, .14);
  --bs-link-color: #C48CFF;
  --bs-link-hover-color: #DDB4FF;
  --bs-progress-bg: rgba(244, 238, 255, .12);
  --bs-card-bg: #180947; /* a touch lighter than the ink so cards lift off the page (Nick, 2026-09-12) */
  --bs-modal-bg: var(--fab-ink);
  --fab-outline: #0B0324;
}

/* ---- Utilities Bootstrap lacks ---- */
/* Lets a flex/grid child shrink below its content width so text-truncate can work (Tailwind has it, Bootstrap doesn't). */
.min-w-0 { min-width: 0 !important; }

/* ---- Tap targets: every button/input is at least 44pt tall ---- */
.btn, .form-control, .form-select { min-height: var(--fab-tap-target); }
/* No zoom on a tap (story 3091). iOS magnifies the page whenever a focused field's text is under 16px and never
   magnifies back out, so on a touch screen every field is floored at 16px: the small controls keep their compact
   padding and only their text grows. Element selectors elsewhere, so a sized control (.form-control-lg) still wins. */
@media (pointer: coarse) {
  .form-control-sm, .form-select-sm, .input-group-sm > .form-control, .input-group-sm > .form-select { font-size: 16px; }
  input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea { font-size: max(16px, 1em); }
}
/* Double-tap on a control means "press it", never "magnify the page". */
.btn, .form-control, .form-select, .form-check-input, a, button, label, [role="button"] { touch-action: manipulation; }
.btn-sm { --bs-btn-padding-y: .55rem; }
.btn-lg { --bs-btn-padding-y: 1rem; }

/* ---- Display type: the thumbnail treatment ---- */
.fab-display {
  font-weight: 700;
  line-height: 1;
  letter-spacing: .01em;
  color: #fff;
  -webkit-text-stroke: 0.045em var(--fab-outline);
  paint-order: stroke fill;
  text-shadow: 0 .06em 0 var(--fab-outline);
}
.fab-display-gold {
  color: var(--fab-gold);
  text-shadow: 0 .06em 0 var(--fab-outline), 0 -.02em 0 #FFE27A;
}
.fab-display-lg { font-size: clamp(2.25rem, 8vw, 4rem); }
.fab-display-md { font-size: clamp(1.5rem, 5vw, 2.25rem); }

/* ---- Glows: one per theme color, used sparingly ---- */
.fab-glow-primary { box-shadow: 0 0 24px rgba(var(--bs-primary-rgb), .55); }
.fab-glow-secondary { box-shadow: 0 0 24px rgba(var(--bs-secondary-rgb), .55); }
.fab-glow-info { box-shadow: 0 0 24px rgba(var(--bs-info-rgb), .55); }
.fab-glow-warning { box-shadow: 0 0 24px rgba(var(--bs-warning-rgb), .6); }
.fab-glow-danger { box-shadow: 0 0 24px rgba(var(--bs-danger-rgb), .55); }

/* ---- Gradient surfaces ---- */
.fab-bg-primary { background: var(--fab-gradient-primary); color: #fff; }
.fab-bg-ice { background: var(--fab-gradient-ice); color: #fff; }
.fab-bg-fire { background: var(--fab-gradient-fire); color: var(--fab-ink); }
.fab-bg-slime { background: var(--fab-gradient-slime); color: var(--fab-ink); }

/* ---- Buttons: chunky, outlined, no hover tricks ---- */
.btn {
  --bs-btn-font-weight: 700;
  --bs-btn-border-width: 3px;
  --bs-btn-font-size: 1.05rem;
}
.btn-primary { --bs-btn-color: #fff; --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #8A2BF0; --bs-btn-hover-border-color: var(--fab-outline); --bs-btn-active-bg: #7A1FE0; }
.btn-secondary { --bs-btn-color: #fff; --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #F02AAA; --bs-btn-hover-border-color: var(--fab-outline); }
.btn-success { --bs-btn-color: var(--fab-ink); --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #4BD82C; --bs-btn-hover-border-color: var(--fab-outline); }
.btn-danger { --bs-btn-color: #fff; --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #E92E2E; --bs-btn-hover-border-color: var(--fab-outline); }
.btn-warning { --bs-btn-color: var(--fab-ink); --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #F5B900; --bs-btn-hover-border-color: var(--fab-outline); }
.btn-info { --bs-btn-color: var(--fab-ink); --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-bg: #1FD3EC; --bs-btn-hover-border-color: var(--fab-outline); }
.btn:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 2px; }

/* ---- Soft buttons: variant-coloured text on a tint of the same colour, no border. The secondary
   action style — use these wherever Bootstrap would use btn-outline-* (which is banned here). ---- */
.btn-soft-primary, .btn-soft-secondary, .btn-soft-success, .btn-soft-danger, .btn-soft-warning, .btn-soft-info, .btn-soft-light, .btn-soft-dark {
  --bs-btn-color: var(--soft-color); --bs-btn-bg: rgba(var(--soft-rgb), .14); --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--soft-color); --bs-btn-hover-bg: rgba(var(--soft-rgb), .26); --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--soft-color); --bs-btn-active-bg: rgba(var(--soft-rgb), .38); --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-color: var(--soft-color); --bs-btn-disabled-bg: rgba(var(--soft-rgb), .1); --bs-btn-disabled-border-color: transparent;
  --bs-btn-focus-shadow-rgb: var(--soft-rgb); --bs-btn-active-shadow: none;
}
/* Light theme: darker shades of each hue so the text stays legible on the lilac page. */
.btn-soft-primary   { --soft-rgb: var(--bs-primary-rgb);   --soft-color: #7A1FE0; }
.btn-soft-secondary { --soft-rgb: var(--bs-secondary-rgb); --soft-color: #C9128F; }
.btn-soft-success   { --soft-rgb: var(--bs-success-rgb);   --soft-color: #1F8F12; }
.btn-soft-danger    { --soft-rgb: var(--bs-danger-rgb);    --soft-color: #D42020; }
.btn-soft-warning   { --soft-rgb: var(--bs-warning-rgb);   --soft-color: #A66E00; }
.btn-soft-info      { --soft-rgb: var(--bs-info-rgb);      --soft-color: #0B8A9E; }
.btn-soft-light     { --soft-rgb: 29, 11, 78;              --soft-color: var(--fab-ink); }
.btn-soft-dark      { --soft-rgb: var(--bs-dark-rgb);      --soft-color: var(--fab-void); }
/* Dark theme: the raw palette colours already pop on the void background. */
[data-bs-theme=dark] .btn-soft-primary   { --soft-color: var(--bs-primary); }
[data-bs-theme=dark] .btn-soft-secondary { --soft-color: var(--bs-secondary); }
[data-bs-theme=dark] .btn-soft-success   { --soft-color: var(--bs-success); }
[data-bs-theme=dark] .btn-soft-danger    { --soft-color: var(--bs-danger); }
[data-bs-theme=dark] .btn-soft-warning   { --soft-color: var(--bs-warning); }
[data-bs-theme=dark] .btn-soft-info      { --soft-color: var(--bs-info); }
[data-bs-theme=dark] .btn-soft-light     { --soft-rgb: 244, 238, 255; --soft-color: #F4EEFF; }
[data-bs-theme=dark] .btn-soft-dark      { --soft-rgb: 0, 0, 0; --soft-color: #F4EEFF; --bs-btn-bg: rgba(0, 0, 0, .35); --bs-btn-hover-bg: rgba(0, 0, 0, .5); --bs-btn-active-bg: rgba(0, 0, 0, .6); }
/* Toggle groups (avatar picker): a selected soft button gets the active tint plus a ring. */
.btn-check:checked + [class*="btn-soft-"], .btn-check:active + [class*="btn-soft-"] { box-shadow: 0 0 0 3px rgba(var(--soft-rgb), .55); }

/* ---- Gradient buttons: hero actions only (catch, revive, trade) ---- */
.btn-fab {
  --bs-btn-color: #fff; --bs-btn-hover-color: #fff; --bs-btn-active-color: #fff;
  --bs-btn-border-color: var(--fab-outline); --bs-btn-hover-border-color: var(--fab-outline); --bs-btn-active-border-color: var(--fab-outline);
  --bs-btn-bg: transparent; --bs-btn-hover-bg: transparent; --bs-btn-active-bg: transparent;
  background-image: var(--fab-btn-gradient);
  box-shadow: 0 0 22px rgba(var(--fab-btn-glow), .6), inset 0 -4px 0 rgba(0,0,0,.25);
  text-shadow: 0 2px 0 rgba(0,0,0,.35);
}
.btn-fab:hover { filter: brightness(1.08); }
.btn-fab:active { transform: translateY(2px); box-shadow: 0 0 14px rgba(var(--fab-btn-glow), .5), inset 0 -2px 0 rgba(0,0,0,.25); }
.btn-fab-primary { --fab-btn-gradient: var(--fab-gradient-primary); --fab-btn-glow: 255, 63, 191; }
.btn-fab-ice     { --fab-btn-gradient: var(--fab-gradient-ice);     --fab-btn-glow: 47, 230, 255; }
.btn-fab-fire    { --fab-btn-gradient: var(--fab-gradient-fire);    --fab-btn-glow: 255, 201, 60; --bs-btn-color: var(--fab-ink); --bs-btn-hover-color: var(--fab-ink); --bs-btn-active-color: var(--fab-ink); text-shadow: none; }
.btn-fab-slime   { --fab-btn-gradient: var(--fab-gradient-slime);   --fab-btn-glow: 94, 235, 60;  --bs-btn-color: var(--fab-ink); --bs-btn-hover-color: var(--fab-ink); --bs-btn-active-color: var(--fab-ink); text-shadow: none; }
.btn-fab-gold    { --fab-btn-gradient: var(--fab-gradient-gold);    --fab-btn-glow: 255, 201, 60; --bs-btn-color: var(--fab-ink); --bs-btn-hover-color: var(--fab-ink); --bs-btn-active-color: var(--fab-ink); text-shadow: none; }
.btn-fab-rainbow { --fab-btn-gradient: var(--fab-gradient-rainbow); --fab-btn-glow: 155, 61, 255; }

/* ---- Coins ---- */
.fab-coin {
  display: inline-flex; align-items: center; gap: .35em;
  padding: .35em .85em; border-radius: var(--bs-border-radius-pill);
  background: var(--fab-gold); color: var(--fab-ink);
  border: 3px solid var(--fab-outline); font-weight: 700; white-space: nowrap;
}
.fab-coin-lg { font-size: 1.35rem; }
/* the wallet control (2026-09-12): a segmented control — the balance quiet on the left, Collect as a gold fab button.
   Rooms page: collect everything; a brainrot's page: collect that one, ticking with its vitals. Full width once it has a row to itself. */
.fab-wallet-slot { display: flex; align-items: center; }
.fab-wallet { display: inline-flex; align-items: center; gap: .3rem; padding: .3rem; border-radius: 1rem; background: rgba(var(--bs-body-bg-rgb), .75); border: 1px solid var(--bs-border-color); box-shadow: 0 6px 18px rgba(18, 6, 58, .12); }
[data-bs-theme=dark] .fab-wallet { background: rgba(29, 11, 78, .8); border-color: rgba(255, 255, 255, .14); box-shadow: 0 8px 22px rgba(0, 0, 0, .35); }
.fab-wallet__count { display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .85rem .35rem .7rem; color: var(--bs-secondary-color); font-weight: 700; white-space: nowrap; }
.fab-wallet__amount { color: var(--bs-body-color); font-size: 1.15rem; }
.fab-wallet__collect { --bs-btn-padding-y: .45rem; --bs-btn-padding-x: .95rem; --bs-btn-border-radius: .8rem; --bs-btn-font-size: .95rem; --bs-btn-font-weight: 800; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; white-space: nowrap; }
.fab-wallet__collect:disabled { background-image: none; background-color: rgba(var(--bs-secondary-color-rgb, 108, 117, 125), .18); border-color: transparent; color: var(--bs-secondary-color); box-shadow: none; text-shadow: none; opacity: 1; font-weight: 600; }
@media (max-width: 767.98px) {
  .fab-wallet-slot { flex: 1 1 100%; }
  .fab-wallet { width: 100%; }
  .fab-wallet__count { flex: 1 1 auto; }
  .fab-wallet__collect { flex: 1 1 auto; }
}

/* ---- Gradient badges (same look as .btn-fab, but flat: labels, not buttons) ---- */
.badge-fab {
  --fab-badge-gradient: var(--fab-gradient-primary); --fab-badge-glow: 255, 63, 191;
  display: inline-block; padding: .2em .7em; border-radius: var(--bs-border-radius-pill);
  background-image: var(--fab-badge-gradient); color: #fff;
  border: 2px solid var(--fab-outline); font-weight: 600; font-size: .75rem; line-height: 1.3;
  box-shadow: 0 0 8px rgba(var(--fab-badge-glow), .3);
  text-shadow: 0 1px 0 rgba(0,0,0,.35);
  cursor: default; user-select: none;
}
.badge-fab-ice     { --fab-badge-gradient: var(--fab-gradient-ice);     --fab-badge-glow: 47, 230, 255; }
.badge-fab-fire    { --fab-badge-gradient: var(--fab-gradient-fire);    --fab-badge-glow: 255, 201, 60; color: var(--fab-ink); text-shadow: none; }
.badge-fab-slime   { --fab-badge-gradient: var(--fab-gradient-slime);   --fab-badge-glow: 94, 235, 60;  color: var(--fab-ink); text-shadow: none; }
.badge-fab-rainbow { --fab-badge-gradient: var(--fab-gradient-rainbow); --fab-badge-glow: 155, 61, 255; }

/* ---- Need bars (Ui::NeedBar sets need-* + band classes from Game.care thresholds) ---- */
.progress { border: 2px solid var(--fab-outline); }
.progress-bar { font-weight: 700; transition: width .6s linear; }
.need-hunger  { --bs-progress-bar-bg: #FF8A3C;       --fab-bar-gradient: var(--fab-gradient-fire);  --fab-bar-glow: 255, 138, 60; }
.need-thirst  { --bs-progress-bar-bg: var(--fab-ice);   --fab-bar-gradient: var(--fab-gradient-ice);   --fab-bar-glow: 47, 230, 255; }
.need-energy  { --bs-progress-bar-bg: var(--fab-slime); --fab-bar-gradient: var(--fab-gradient-slime); --fab-bar-glow: 94, 235, 60; }
.need-low     { --bs-progress-bar-bg: var(--fab-gold);  --fab-bar-gradient: linear-gradient(90deg, #FFB300, #FFE27A); --fab-bar-glow: 255, 201, 60; }
.need-critical { --bs-progress-bar-bg: var(--fab-lava); --fab-bar-gradient: linear-gradient(90deg, #C41E1E, #FF5A5A); --fab-bar-glow: 255, 59, 59; animation: fab-pulse 1s ease-in-out infinite; }
.need-empty { border-color: var(--fab-lava); }
.progress-sm { --bs-progress-height: .6rem; border-width: 1px; }
.progress-fab { overflow: visible; border-width: 3px; }
.progress-fab .progress-bar {
  background-image: var(--fab-bar-gradient); color: var(--fab-ink);
  border-radius: var(--bs-border-radius-pill);
  box-shadow: 0 0 14px rgba(var(--fab-bar-glow), .6), inset 0 -3px 0 rgba(0,0,0,.2), inset 0 2px 0 rgba(255,255,255,.35);
  text-shadow: none;
}
.progress-fab.need-thirst .progress-bar, .progress-fab.need-critical .progress-bar { color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.35); }
.need-bar__label { display: flex; align-items: center; gap: .4rem; font-weight: 600; font-size: .9rem; margin-bottom: .2rem; }
@keyframes fab-pulse { 50% { opacity: .55; } }

/* ---- Cards ---- */
.card { --bs-card-border-width: 3px; --bs-card-border-color: var(--fab-outline); }
/* Bootstrap re-declares --bs-card-bg on .card itself (as the body bg), so the theme token alone never reached cards:
   set it at card level. #180947 sits a touch above the page's void so cards lift off it (Nick, 2026-09-12). */
[data-bs-theme=dark] .card { --bs-card-bg: #180947; }

/* ---- Brainrot card (Brainrots::Card) ---- */
.brainrot-card { position: relative; text-align: center; }
.brainrot-card__art {
  width: 4.5rem; height: 4.5rem; margin: 0 auto .5rem; border-radius: 50%;
  display: grid; place-items: center; font-size: 2.25rem; overflow: hidden;
  border: 3px solid var(--fab-outline);
}
.brainrot-card__art img { width: 100%; height: 100%; object-fit: cover; }
.brainrot-card--fainted .brainrot-card__art { filter: grayscale(1) opacity(.7); }
.brainrot-card__ghost { position: absolute; top: .5rem; right: .75rem; font-size: 1.25rem; }
.brainrot-card__name { font-weight: 700; margin-bottom: 0; overflow-wrap: anywhere; }
.brainrot-card__species { font-size: .85rem; opacity: .75; }
.brainrot-card__income { font-weight: 700; color: var(--fab-gold); text-shadow: 0 1px 0 var(--fab-outline); }
/* Coins to collect and coins earned so far, on one line that is always there, so a card keeps its height (story 3092) */
.brainrot-card__pending { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .35rem; }
.brainrot-card__earned { font-size: .8rem; opacity: .75; white-space: nowrap; }

/* ---- Floating top bar (web only; native has its own chrome) — Ui::Topbar ---- */
/* Where the chip ends (story 3194): the tools sheet hangs from it, and the hunt's banners and the live notices start
   below its Tools tab (`--fab-below-chip`), so nothing lands under the bar; native.css raises both for the status bar. */
body { --fab-chip-bottom: calc(.75rem + 59px); --fab-below-chip: calc(var(--fab-chip-bottom) + 2.6rem); } /* on <body>: native.css redeclares both there */
.fab-topbar-wrap { position: sticky; top: .75rem; z-index: 1030; padding-top: .75rem; display: flex; align-items: center; gap: 0; } /* the tools sheet hangs under it, absolute (story 3186) */
.fab-topbar-wrap > .fab-topbar { flex: 1 1 auto; min-width: 0; }
/* The chrome's back button (story 3193): a round glass chevron that slides in to the left of the chip when there is a
   page to go back to, the chip narrowing to make room; both ease. Hidden (width 0) otherwise. */
.fab-topbar__back { flex: 0 0 auto; width: 0; height: var(--fab-tap-target); margin-right: 0; padding: 0; overflow: hidden; opacity: 0; transform: translateX(-.5rem) scale(.9);
  display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 50%; color: var(--bs-body-color); font-size: 1.15rem;
  background: linear-gradient(var(--fab-glass-fill), var(--fab-glass-fill)), var(--fab-glass-tint); -webkit-backdrop-filter: blur(24px) saturate(1.8); backdrop-filter: blur(24px) saturate(1.8); box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .35);
  transition: width .28s cubic-bezier(.2, .8, .2, 1), margin-right .28s cubic-bezier(.2, .8, .2, 1), opacity .22s ease, transform .28s cubic-bezier(.2, .8, .2, 1); }
.fab-topbar-wrap.has-back .fab-topbar__back { width: var(--fab-tap-target); margin-right: .5rem; opacity: 1; transform: none; }
.fab-topbar {
  position: relative; z-index: 0; isolation: isolate;
  display: flex; align-items: center; gap: .5rem;
  padding: .4rem .5rem .4rem .5rem;
  border-radius: var(--bs-border-radius-pill);
  border: 1px solid rgba(29, 11, 78, .12);
  box-shadow: 0 12px 32px rgba(18, 6, 58, .16), inset 0 1px 0 rgba(255, 255, 255, .45);
}
/* The frosted glass lives on a pseudo-element: Safari clips the children of anything with backdrop-filter to its
   box, which swallowed the Account menu on iPhones (2026-09-12). The bar itself stays filter-free. */
/* Liquid Glass (Nick, 2026-10-03, stories 3191, 3192): Apple's material is nearly clear in the middle and does its work
   at the rim, where the backdrop is refracted and lit by a specular highlight. Here: a faint, even fill that blurs and
   saturates what is behind (no gradient wash), and a rim drawn as a ring (`--fab-glass-rim`, a mask-composited border)
   that is bright at the top left and dim at the bottom right, plus a hairline of inner light along the top. */
/* The fill is laid as a gradient of one colour over the tint: `background: <color>, <color>` is invalid CSS and was
   dropped by every browser, so until story 3199 the glass was the blur and the highlights alone. */
:root { --fab-glass-fill: rgba(255, 255, 255, .22); --fab-glass-tint: rgba(var(--bs-body-bg-rgb), .3); --fab-glass-rim: linear-gradient(135deg, rgba(255, 255, 255, .95), rgba(255, 255, 255, .25) 35%, rgba(255, 255, 255, .06) 62%, rgba(255, 255, 255, .55)); --fab-glass-shadow: 0 14px 36px rgba(18, 6, 58, .18); }
[data-bs-theme=dark] { --fab-glass-fill: rgba(255, 255, 255, .07); --fab-glass-tint: rgba(29, 11, 78, .34); --fab-glass-rim: linear-gradient(135deg, rgba(255, 255, 255, .7), rgba(255, 255, 255, .18) 35%, rgba(255, 255, 255, .04) 62%, rgba(255, 255, 255, .35)); --fab-glass-shadow: 0 14px 36px rgba(0, 0, 0, .5); }
.fab-topbar::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: linear-gradient(var(--fab-glass-fill), var(--fab-glass-fill)), var(--fab-glass-tint);
  -webkit-backdrop-filter: blur(24px) saturate(1.8);
  backdrop-filter: blur(24px) saturate(1.8);
}
.fab-topbar { border-color: transparent; box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .35); }
.fab-topbar::after { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; background: var(--fab-glass-rim); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
[data-bs-theme=dark] .fab-topbar { box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .14); }
.fab-topbar__brand { display: flex; align-items: center; gap: .55rem; min-height: var(--fab-tap-target); padding-right: .5rem; color: inherit; text-decoration: none; border-radius: var(--bs-border-radius-pill); }
.fab-topbar__brand:hover { color: inherit; }
.fab-topbar__brand:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 2px; }
/* the chip holds two links (2026-09-14): the avatar goes home to the rooms, the name to the profile */
.fab-topbar__home, .fab-topbar__profile { display: inline-flex; align-items: center; gap: .55rem; min-height: var(--fab-tap-target); color: inherit; text-decoration: none; border-radius: var(--bs-border-radius-pill); }
.fab-topbar__home:hover, .fab-topbar__profile:hover { color: inherit; }
.fab-topbar__profile:hover .fab-topbar__name { text-decoration: underline; text-underline-offset: .2em; }
.fab-topbar__home:focus-visible, .fab-topbar__profile:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 2px; }
.fab-topbar__avatar {
  width: 2.5rem; height: 2.5rem; border-radius: 50%; display: grid; place-items: center; font-size: 1.35rem;
  background: var(--fab-gradient-primary);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .5), 0 4px 12px rgba(var(--bs-primary-rgb), .35);
}
.fab-topbar__name { font-weight: 700; }
.fab-list-filter { max-width: 11rem; }
.fab-back { width: var(--fab-tap-target); height: var(--fab-tap-target); padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 1rem; font-size: 1.15rem; }
.fab-topbar__handle { opacity: .6; font-size: .85rem; }
.fab-topbar__actions { margin-left: auto; display: flex; align-items: center; gap: .25rem; }
.fab-topbar__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  min-width: var(--fab-tap-target); height: var(--fab-tap-target); padding: 0 .75rem;
  border-radius: var(--bs-border-radius-pill); color: var(--bs-body-color); text-decoration: none; font-weight: 600;
  transition: background-color .15s ease, color .15s ease, transform .15s ease;
}
.fab-topbar__btn .bi { font-size: 1.2rem; }
/* Every bar button is a <button>, so every one has to shed the browser's own button chrome. This reset used to live on
   the Account menu alone, and the Tools button (story 3094) came out as a grey pill with a white ring in desktop
   Chrome, where the native look is loudest (Nick, 2026-09-17). */
button.fab-topbar__btn { border: 0; background: transparent; font: inherit; font-weight: 600; color: var(--bs-body-color); }
.fab-topbar__btn[aria-expanded="true"] { background: rgba(var(--bs-primary-rgb), .12); }
/* the Account menu (2026-09-12): one button, a soft card of items */
.fab-topbar__btn--menu .fab-topbar__caret { font-size: .7rem; opacity: .6; }
.fab-topbar__btn--menu { position: relative; }
.fab-topbar__badge { top: .1rem; right: .1rem; }
.fab-topbar__menu, .fab-menu { --bs-dropdown-spacer: .5rem; min-width: 14rem; padding: .4rem; border-radius: 1rem; border: 1px solid transparent; position: absolute;
  background: var(--fab-glass-fill), rgba(var(--bs-body-bg-rgb), .66); -webkit-backdrop-filter: blur(24px) saturate(1.8); backdrop-filter: blur(24px) saturate(1.8);
  box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .35); } /* Liquid Glass (stories 3191, 3192) */
.fab-topbar__menu::after, .fab-menu::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none; background: var(--fab-glass-rim); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
/* --bs-card-bg only exists inside .card, so the menu names its own solid colours: the page ground in light, the card colour in dark */
[data-bs-theme=dark] .fab-topbar__menu, [data-bs-theme=dark] .fab-menu { background: var(--fab-glass-fill), rgba(24, 9, 71, .66); box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .14); }
.fab-topbar__item, .fab-menu__item { display: flex; align-items: center; gap: .65rem; padding: .6rem .75rem; border-radius: .7rem; font-weight: 600; color: var(--bs-body-color); }
.fab-topbar__item .bi, .fab-menu__item .bi { font-size: 1.1rem; opacity: .85; }
.fab-topbar__item:hover, .fab-topbar__item:focus, .fab-menu__item:hover, .fab-menu__item:focus { background: rgba(var(--bs-primary-rgb), .12); color: var(--bs-body-color); }
/* A menu row that holds controls rather than being one (the Rooms view, story 3170): no hover wash, the buttons are the targets. */
.fab-menu__item--static:hover, .fab-menu__item--static:focus { background: transparent; }
.fab-menu__segments { display: inline-flex; gap: .25rem; }
.fab-menu__segments .btn { padding: .2rem .6rem; font-size: .8rem; border-radius: var(--bs-border-radius-pill); }
.fab-topbar__item--danger, .fab-topbar__item--danger:hover, .fab-topbar__item--danger:focus,
.fab-menu__item--danger, .fab-menu__item--danger:hover, .fab-menu__item--danger:focus { color: var(--bs-danger); }
.fab-menu__item--danger:hover, .fab-menu__item--danger:focus { background: rgba(var(--bs-danger-rgb), .12); }
.fab-topbar__menu .dropdown-divider, .fab-menu .dropdown-divider { margin: .35rem .25rem; border-color: var(--bs-border-color); opacity: 1; }
/* Menu motion lives with the .dropdown-menu transition below (story 3193). */
/* theme row in the Account menu: Auto / Light / Dark segmented control */
.fab-topbar__theme { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .35rem .75rem .25rem; }
.fab-topbar__theme-label { font-weight: 600; font-size: .85rem; color: var(--bs-secondary-color); }
.fab-topbar__theme-options { display: inline-flex; padding: .2rem; border-radius: .7rem; background: rgba(var(--bs-body-color-rgb), .06); }
.fab-topbar__theme-option { display: inline-flex; align-items: center; gap: .3rem; padding: .3rem .55rem; border: 0; border-radius: .5rem; background: transparent; color: var(--bs-secondary-color); font: inherit; font-size: .8rem; font-weight: 700; transition: background-color .15s ease, color .15s ease; }
.fab-topbar__theme-option:hover { color: var(--bs-body-color); }
.fab-topbar__theme-option[aria-pressed="true"] { background: var(--fab-gradient-primary); color: #fff; box-shadow: 0 4px 12px rgba(255, 63, 191, .35); }
.fab-topbar__theme-option .bi { font-size: .9rem; }
.fab-topbar__btn:hover { background: rgba(var(--bs-primary-rgb), .14); color: var(--bs-primary); }
.fab-topbar__btn:active { transform: scale(.96); }
.fab-topbar__btn:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 1px; }
.fab-topbar__btn--danger:hover { background: rgba(var(--bs-danger-rgb), .14); color: var(--bs-danger); }
.fab-topbar__label { display: none; }
@media (min-width: 576px) { .fab-topbar__label { display: inline; } }
@media (max-width: 575.98px) { .fab-topbar__btn { padding: 0; width: var(--fab-tap-target); } .fab-topbar__handle { display: none; } }
@media (prefers-reduced-transparency: reduce) { .fab-topbar::before { background: var(--bs-body-bg); backdrop-filter: none; -webkit-backdrop-filter: none; } }

/* ---- Bottom tab bar (Ui::TabBarComponent, web only): same floating pill language as the top bar ---- */
body.has-tabbar { padding-bottom: calc(4.75rem + env(safe-area-inset-bottom)); }
.fab-tabbar-wrap { position: fixed; left: 0; right: 0; bottom: max(1.5rem, env(safe-area-inset-bottom)); z-index: 1030; padding: 0 .75rem; pointer-events: none; }
.fab-tabbar {
  pointer-events: auto; margin: 0 auto; max-width: 26rem;
  display: flex; align-items: stretch; gap: .25rem; padding: .3rem;
  border-radius: var(--bs-border-radius-pill);
  /* more opaque than the top bar: it floats over maps and photos, so it must read on any background */
  position: relative;
  background: linear-gradient(var(--fab-glass-fill), var(--fab-glass-fill)), var(--fab-glass-tint);
  -webkit-backdrop-filter: blur(24px) saturate(1.8);
  backdrop-filter: blur(24px) saturate(1.8);
  border: 1px solid transparent;
  box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .35);
}
.fab-tabbar::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none; background: var(--fab-glass-rim);
  /* the ring, less the arc where the dome takes over (story 3202): the first layer cuts a circle around the hero */
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent calc(var(--fab-nook-r) - 1px), #000 var(--fab-nook-r)), linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: source-in, xor;
  mask: radial-gradient(circle at 50% 50%, transparent calc(var(--fab-nook-r) - 1px), #000 var(--fab-nook-r)), linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: intersect, exclude; }
/* The bar rounds out around the Hunt button, above and below (stories 3202, 3203, 3204; Nick: "the tabbar
   encapsulates the Hunt button ... one element"): two bands, `.fab-tabbar__nook` above the bar and
   `.fab-tabbar__nook--below` under it, each a plain box clipped by overflow with a full circle of the same glass
   inside it, centred on the hero, so the pill and the bead read as one shape. Nothing blurred carries a mask: WebKit
   drops a backdrop filter on a masked element (story 3204, the bulges came out clear on the phone), so the circle is
   clipped by its band's box and only the rim ring (no blur) is masked. Each band reaches 2px into the bar to cover the
   bar's inner top highlight, and the bar's rim ring is cut by the same circle, so no line crosses the join. The bar
   has a fixed height for the geometry; the hero is centred in it and keeps its size and rings. */
.fab-tabbar-wrap { --fab-bar-h: 3.8rem; --fab-nook-r: 3.125rem; --fab-nook-band: calc(var(--fab-nook-r) - var(--fab-bar-h) / 2); }
.fab-tabbar { height: var(--fab-bar-h); box-sizing: border-box; }
/* The bead's three spans are the bar's siblings inside the wrap (story 3205): a blurred parent walls its children's
   backdrop in, so inside the bar their blur could only sample the bar, and the page behind the bulges stayed crisp. */
.fab-tabbar__nook, .fab-tabbar__nook--below { position: absolute; z-index: 0; pointer-events: none; left: 50%; translate: -50%; width: calc(var(--fab-nook-r) * 2); height: calc(var(--fab-nook-band) + 2px); overflow: hidden; }
.fab-tabbar__nook { top: calc(-1 * var(--fab-nook-band)); }
.fab-tabbar__nook--below { bottom: calc(-1 * var(--fab-nook-band)); }
.fab-tabbar__nook::before, .fab-tabbar__nook::after, .fab-tabbar__nook--below::before, .fab-tabbar__nook--below::after { content: ""; position: absolute; left: 0; width: calc(var(--fab-nook-r) * 2); height: calc(var(--fab-nook-r) * 2); border-radius: 50%; }
.fab-tabbar__nook::before, .fab-tabbar__nook::after { top: 0; }
.fab-tabbar__nook--below::before, .fab-tabbar__nook--below::after { bottom: 0; }
/* The blur is one full circle (`.fab-tabbar__blur`, no fill), never clipped: iOS blurs only what lies inside a layer's
   own bounds, so a blur clipped to a 20px band had too little to work with and the text behind it stayed readable
   (story 3205). Where the circle overlaps the bar it blurs the bar's already blurred fill, which changes nothing. The
   bands keep the fill, the saturation (pointwise, so clipping is harmless) and the rim. */
.fab-tabbar__blur { position: absolute; z-index: 0; pointer-events: none; left: 50%; top: 50%; translate: -50% -50%; width: calc(var(--fab-nook-r) * 2); height: calc(var(--fab-nook-r) * 2); border-radius: 50%; -webkit-backdrop-filter: blur(24px); backdrop-filter: blur(24px); }
.fab-tabbar__nook::before, .fab-tabbar__nook--below::before { background: linear-gradient(var(--fab-glass-fill), var(--fab-glass-fill)), var(--fab-glass-tint); -webkit-backdrop-filter: saturate(1.8); backdrop-filter: saturate(1.8); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35); }
[data-bs-theme=dark] .fab-tabbar__nook::before, [data-bs-theme=dark] .fab-tabbar__nook--below::before { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14); }
.fab-tabbar__nook::after, .fab-tabbar__nook--below::after { padding: 1px; background: var(--fab-glass-rim); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
.fab-tabbar__item--hero { position: relative; z-index: 1; } /* above the bead */
[data-bs-theme=dark] .fab-tabbar { box-shadow: var(--fab-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .14); }
button.fab-tabbar__item { background: none; border: 0; font-family: inherit; } /* the Base tab is a button (story 3186), dressed as its siblings; `font: inherit` undid their weight and size (story 3196) */
.fab-tabbar__item {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .1rem;
  min-height: var(--fab-tap-target); padding: .35rem .5rem; border-radius: var(--bs-border-radius-pill);
  color: var(--bs-body-color); text-decoration: none; font-weight: 800; font-size: .78rem; line-height: 1.1;
}
.fab-tabbar__item .bi { font-size: 1.4rem; line-height: 1; }
.fab-tabbar__item:hover { color: var(--bs-primary); }
/* active = a frosted glass pill around the glyph and label, the glyph in the Hunt button's pink (Liquid Glass, story 3191); the hero keeps its own look */
.fab-tabbar__item { transition: background .2s ease, color .2s ease, box-shadow .2s ease; }
.fab-tabbar__item.active:not(.fab-tabbar__item--hero) { color: var(--fab-fire, var(--bs-primary)); background: rgba(255, 255, 255, .42); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85), inset 0 0 0 1px rgba(255, 255, 255, .25), 0 4px 12px rgba(18, 6, 58, .1); }
[data-bs-theme=dark] .fab-tabbar__item.active:not(.fab-tabbar__item--hero) { background: rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), inset 0 0 0 1px rgba(255, 255, 255, .1), 0 4px 12px rgba(0, 0, 0, .25); }
.fab-tabbar__item:active:not(.fab-tabbar__item--hero) { transform: scale(.96); }
/* In the app's overlay the active pill is one element that slides from tab to tab (story 3196, Nick's ask): the
   controller sets its place and width; the tab itself keeps only its colour. Hidden while the hero is the page. */
.fab-tabbar__pill { position: absolute; left: 0; top: 0; width: 0; height: 0; border-radius: var(--bs-border-radius-pill); pointer-events: none; opacity: 0; scale: .7;
  background: rgba(255, 255, 255, .42); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85), inset 0 0 0 1px rgba(255, 255, 255, .25), 0 4px 12px rgba(18, 6, 58, .1); }
[data-bs-theme=dark] .fab-tabbar__pill { background: rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), inset 0 0 0 1px rgba(255, 255, 255, .1), 0 4px 12px rgba(0, 0, 0, .25); }
.fab-tabbar__pill.is-set { transition: translate .42s cubic-bezier(.3, 1.3, .5, 1), width .42s cubic-bezier(.3, 1.3, .5, 1), height .42s cubic-bezier(.3, 1.3, .5, 1), opacity .2s ease, scale .3s cubic-bezier(.3, 1.3, .5, 1); }
.fab-tabbar__pill.is-on { opacity: 1; scale: 1; }
.fab-tabbar:has(.fab-tabbar__pill) .fab-tabbar__item.active:not(.fab-tabbar__item--hero) { background: none; box-shadow: none; }
.fab-tabbar:has(.fab-tabbar__pill) .fab-tabbar__item { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .fab-tabbar__pill.is-set { transition: opacity .2s ease; } }
.fab-tabbar__icon { position: relative; display: inline-flex; }
.fab-tabbar__badge { position: absolute; top: -.35rem; right: -.7rem; min-width: 1.15rem; height: 1.15rem; padding: 0 .3rem; border-radius: 999px; background: var(--fab-fire, #ff4d6d); color: #fff; font-size: .68rem; font-weight: 800; line-height: 1.15rem; text-align: center; box-shadow: 0 0 0 2px var(--bs-body-bg); }
.fab-tabbar__item.active:hover { color: var(--fab-fire, var(--bs-primary)); }
/* Hunt: the hero in the middle — a big round button that rises above the bar */
.fab-tabbar__item--hero {
  flex: 0 0 auto; align-self: center; width: 4.4rem; height: 4.4rem; margin: -1rem .15rem; padding: 0; gap: 0; /* symmetric margins: centred in the bar (story 3203) */
  border-radius: 50%; color: #fff;
  background: linear-gradient(140deg, var(--bs-primary) 0%, var(--fab-fire, #ff4d6d) 100%);
  /* solid rings, no glow (story 3193): a translucent halo over the app's map read as a see-through button */
  box-shadow: 0 0 0 4px var(--bs-body-bg), 0 0 0 7px color-mix(in srgb, var(--bs-primary) 45%, var(--bs-body-bg));
  transition: transform .15s ease, box-shadow .15s ease;
}
.fab-tabbar__item--hero .bi { font-size: 1.75rem; }
.fab-tabbar__item--hero .fab-tabbar__label { font-size: .66rem; }
.fab-tabbar__item--hero:hover, .fab-tabbar__item--hero.active, .fab-tabbar__item--hero.active:hover { color: #fff; } /* the pink active rule must not recolour the hero */
.fab-tabbar__item--hero:hover { transform: translateY(-2px); }
.fab-tabbar__item--hero.active { background: linear-gradient(140deg, var(--bs-primary) 0%, var(--fab-fire, #ff4d6d) 100%); box-shadow: 0 0 0 4px var(--bs-body-bg), 0 0 0 7px var(--bs-primary); }
[data-bs-theme=dark] .fab-tabbar__item--hero { box-shadow: 0 0 0 4px #1d0b4e, 0 0 0 7px color-mix(in srgb, var(--bs-primary) 45%, #1d0b4e); }
[data-bs-theme=dark] .fab-tabbar__item--hero.active { box-shadow: 0 0 0 4px #1d0b4e, 0 0 0 7px var(--bs-primary); }
.fab-tabbar-wrap { transition: transform .25s ease, opacity .25s ease; }
body.sheet-open .fab-tabbar-wrap { transform: translateY(140%); opacity: 0; } /* a bottom sheet is up (Ui::SheetComponent) */
@media (prefers-reduced-transparency: reduce) { .fab-tabbar { background: var(--bs-body-bg); backdrop-filter: none; -webkit-backdrop-filter: none; } }

.fab-footer { font-size: .85rem; opacity: .55; }

/* ---- Toasts (Ui::Toast in Ui::Toasts) — solid variant colour, chunky outline, countdown bar ---- */
.fab-toasts { position: fixed; z-index: 1090; top: 5.25rem; right: 1rem; width: min(24rem, calc(100vw - 2rem)); display: grid; pointer-events: none; }
/* One slot (story 3223, Nick: the handover was jumpy): every toast sits in the same grid cell, so a toast on its way out
   takes no room and the one coming in never shifts; on a desktop they share the top edge, on a phone the bottom one. */
.fab-toasts > * { pointer-events: auto; grid-area: 1 / 1; align-self: start; }
@media (max-width: 575.98px) { .fab-toasts { top: auto; bottom: calc(1rem + env(safe-area-inset-bottom)); left: 1rem; right: 1rem; width: auto; } .fab-toasts > * { align-self: end; } }
/* The handover (stories 3197, 3223): a toast rises in from below and settles with a little spring, on a desktop too (it
   used to drop in from above there, which crossed the one leaving); the one it replaces lifts up and away above it at the same time, so both are on screen for a moment and only one is ever the current
   toast. Bootstrap keeps `.showing` on the way in and the way out, and the controller adds `fab-toast--leaving` the
   moment a hide begins, so the hidden rule below is the way in and the leaving rule the way out, whichever way it
   leaves (replaced, timed out, or closed). Bootstrap reads the first transition duration to time its callbacks, so the
   longest one comes first in each rule. */
.fab-toast.showing { opacity: 0; translate: 0 1.25rem; scale: .94; transition: translate .2s ease-in, scale .2s ease-in, opacity .16s ease-in; }
.fab-toast.show:not(.showing) { opacity: 1; translate: 0 0; scale: 1; transition: translate .42s cubic-bezier(.3, 1.35, .5, 1), scale .42s cubic-bezier(.3, 1.35, .5, 1), opacity .2s ease-out; }
.fab-toast.fab-toast--leaving, .fab-toast.fab-toast--leaving.showing { opacity: 0; translate: 0 -1.6rem; scale: .96; transition: translate .3s cubic-bezier(.4, 0, .8, .4), scale .3s ease-in, opacity .24s ease-in; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .fab-toast.showing, .fab-toast.show:not(.showing), .fab-toast.fab-toast--leaving { translate: 0 0; scale: 1; transition: opacity .18s ease; } }
.fab-toast {
  --toast-bg: var(--bs-primary); --toast-bg-rgb: var(--bs-primary-rgb); --toast-fg: #fff; --toast-fg-rgb: 255, 255, 255;
  --toast-close-filter: invert(1) grayscale(100%) brightness(200%);
  --bs-toast-max-width: 100%; --bs-toast-padding-x: 0; --bs-toast-padding-y: 0; --bs-toast-font-size: 1rem;
  --bs-toast-bg: var(--toast-bg); --bs-toast-color: var(--toast-fg);
  --bs-toast-border-width: 3px; --bs-toast-border-color: var(--fab-outline); --bs-toast-border-radius: var(--bs-border-radius-lg);
  --bs-toast-box-shadow: 0 14px 34px rgba(var(--toast-bg-rgb), .35), 0 4px 12px rgba(5, 1, 15, .25), inset 0 -4px 0 rgba(0, 0, 0, .18);
  position: relative; overflow: hidden;
}
.fab-toast--success { --toast-bg: var(--bs-success); --toast-bg-rgb: var(--bs-success-rgb); --toast-fg: var(--fab-ink); --toast-fg-rgb: 29, 11, 78; --toast-close-filter: none; }
.fab-toast--warning { --toast-bg: var(--bs-warning); --toast-bg-rgb: var(--bs-warning-rgb); --toast-fg: var(--fab-ink); --toast-fg-rgb: 29, 11, 78; --toast-close-filter: none; }
.fab-toast--info    { --toast-bg: var(--bs-info);    --toast-bg-rgb: var(--bs-info-rgb);    --toast-fg: var(--fab-ink); --toast-fg-rgb: 29, 11, 78; --toast-close-filter: none; }
.fab-toast--danger  { --toast-bg: var(--bs-danger);  --toast-bg-rgb: var(--bs-danger-rgb);  --toast-fg: #fff; --toast-fg-rgb: 255, 255, 255; }
.fab-toast--primary { --toast-bg: var(--bs-primary); --toast-bg-rgb: var(--bs-primary-rgb); --toast-fg: #fff; --toast-fg-rgb: 255, 255, 255; }
.fab-toast__body { display: flex; align-items: flex-start; gap: .75rem; padding: .85rem 1rem 1rem; }
.fab-toast__icon { flex: none; width: 2.4rem; height: 2.4rem; border-radius: 50%; display: grid; place-items: center; font-size: 1.2rem; color: var(--toast-fg); background: rgba(var(--toast-fg-rgb), .18); box-shadow: inset 0 0 0 2px rgba(var(--toast-fg-rgb), .25); }
.fab-toast__text { flex: 1 1 auto; align-self: center; color: var(--toast-fg); }
.fab-toast__title { font-weight: 700; }
.fab-toast__message { font-weight: 600; }
.fab-toast .btn-close { flex: none; margin: -.25rem -.25rem -.25rem 0; padding: .75rem; filter: var(--toast-close-filter); opacity: .75; }
.fab-toast .btn-close:hover { opacity: 1; }
.fab-toast__progress { position: absolute; left: 0; bottom: 0; height: 5px; width: 100%; background: rgba(var(--toast-fg-rgb), .55); animation: fab-toast-progress var(--toast-delay, 5000ms) linear forwards; }
@keyframes fab-toast-progress { to { width: 0; } }

/* ---- Modals (story 3197): the .modal box is the veil, dimmed and blurred, fading with the dialog; the dialog itself
   grows in with the menus' spring and shrinks out quickly. Bootstrap appends no backdrop (application.js). ---- */
.modal { background: rgba(5, 1, 15, .72); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.modal.fade { transition: opacity .22s ease; }
.modal.fade .modal-dialog { transform: none; scale: .86; opacity: 0; transition: scale .16s ease-in, opacity .14s ease-in; }
.modal.show .modal-dialog { scale: 1; opacity: 1; transition: scale .38s cubic-bezier(.3, 1.35, .5, 1), opacity .18s ease-out; }
@media (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog, .modal.show .modal-dialog { scale: 1; transition: opacity .18s ease; } }
/* ---- Confirm / alert dialog (Ui::ConfirmModal): raised surface, accent border ---- */
.fab-confirm { --bs-modal-bg: #fff; --bs-modal-color: var(--fab-ink); }
[data-bs-theme=dark] .fab-confirm { --bs-modal-bg: #2A1470; --bs-modal-color: #F4EEFF; }
.fab-confirm .modal-dialog { max-width: 26rem; margin-left: auto; margin-right: auto; } /* Bootstrap only centres the dialog from sm up; below 576px it sat left when the screen was wider than 26rem */
/* The confirm dialog grows in like the rest (story 3197); its buttons sit still well before a finger lands. */
.fab-confirm__content {
  --confirm-accent: var(--bs-primary); --confirm-accent-rgb: var(--bs-primary-rgb);
  border: 3px solid rgba(var(--confirm-accent-rgb), .9); border-radius: var(--bs-border-radius-lg); overflow: hidden;
  box-shadow: 0 0 0 6px rgba(var(--confirm-accent-rgb), .16), 0 0 48px rgba(var(--confirm-accent-rgb), .28), 0 32px 80px rgba(5, 1, 15, .6);
}
.fab-confirm__content::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 10rem; pointer-events: none; background: radial-gradient(ellipse at 50% 0%, rgba(var(--confirm-accent-rgb), .3), transparent 70%); }
.fab-confirm--info        { --confirm-accent: var(--bs-info);    --confirm-accent-rgb: var(--bs-info-rgb); }
.fab-confirm--success     { --confirm-accent: var(--bs-success); --confirm-accent-rgb: var(--bs-success-rgb); }
.fab-confirm--warning     { --confirm-accent: var(--bs-warning); --confirm-accent-rgb: var(--bs-warning-rgb); }
.fab-confirm--danger,
.fab-confirm--destructive { --confirm-accent: var(--bs-danger);  --confirm-accent-rgb: var(--bs-danger-rgb); }
.fab-confirm__icon { position: relative; width: 4.5rem; height: 4.5rem; margin: 0 auto 1rem; border-radius: 50%; display: grid; place-items: center; font-size: 2.25rem; color: var(--confirm-accent); background: rgba(var(--confirm-accent-rgb), .18); box-shadow: 0 0 0 8px rgba(var(--confirm-accent-rgb), .1); }
.fab-confirm__title { position: relative; font-weight: 700; }
.fab-confirm__message { position: relative; color: var(--bs-secondary-color); }
.fab-confirm__actions .btn { min-width: 8rem; }

/* ---- Settings page (adults): cards on a grid, avatar chips, status pills ---- */
/* A link to one card (settings/account#account-app) stops below the floating top bar, not under it. */
.fab-settings__card { scroll-margin-top: calc(6.5rem + env(safe-area-inset-top, 0px)); }
.fab-settings__card .card-header,
.fab-settings__card .card-footer { background: transparent; padding: 1rem 1.25rem; }
.fab-settings__card .card-header { border-bottom: 1px solid var(--bs-border-color); display: flex; align-items: center; gap: .6rem; }
.fab-settings__card .card-footer { border-top: 1px solid var(--bs-border-color); }
.fab-settings__title { font-weight: 700; font-size: 1.1rem; margin: 0; display: flex; align-items: center; gap: .5rem; }
.fab-settings__title .bi { color: var(--bs-primary); }
.fab-settings__list { margin: 0; padding: 0; list-style: none; }
.fab-settings__row { display: flex; align-items: center; gap: .85rem; padding: .85rem 0; }
.fab-settings__row:first-child { padding-top: 0; }
.fab-settings__row:last-child { padding-bottom: 0; }
.fab-settings__row + .fab-settings__row { border-top: 1px dashed var(--bs-border-color); }
.fab-settings__row form { margin-left: auto; }
/* messages (story 3071) */
/* The thread fills the screen between the top bar and the tab bar (thread_controller sets the height); only the wall scrolls. */
.fab-thread-main { padding-bottom: 0 !important; }
html:has(.fab-thread--fitted), html:has(.fab-thread--fitted) body { overflow: hidden; }
.fab-thread { display: flex; flex-direction: column; min-height: 16rem; }
.fab-thread > header { flex: none; }
.fab-thread__card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.fab-thread__card > .card-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.fab-thread__scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 1rem; -webkit-overflow-scrolling: touch; }
.fab-thread__messages { display: flex; flex-direction: column; gap: .75rem; min-height: 100%; justify-content: flex-end; }
.fab-thread__composer { flex: none; background: var(--bs-card-bg); }
.fab-message { display: flex; align-items: flex-end; gap: .5rem; max-width: 85%; }
.fab-message--mine { align-self: flex-end; flex-direction: row-reverse; }
.fab-message__avatar { width: 2rem; height: 2rem; font-size: 1.1rem; }
.fab-message__bubble { padding: .5rem .8rem; border-radius: 1rem; background: rgba(var(--bs-body-color-rgb), .06); }
.fab-message--mine .fab-message__bubble { background: rgba(var(--bs-primary-rgb), .16); }
.fab-message__text { font-weight: 600; overflow-wrap: anywhere; }
.fab-message__sticker { width: 6rem; height: 6rem; object-fit: cover; border-radius: .75rem; display: block; }
.fab-thread__phrases { display: grid; grid-auto-flow: column; grid-template-rows: repeat(2, auto); gap: .4rem; overflow-x: auto; padding-bottom: .25rem; scrollbar-width: none; justify-content: start; }
.fab-thread__phrases::-webkit-scrollbar { display: none; }
.fab-thread__phrases form { display: contents; }
.fab-thread__phrases .btn { white-space: nowrap; }
.fab-message__report { font-size: .75rem; color: var(--bs-secondary-color); text-decoration: none; }
.fab-message__report:hover { color: var(--bs-danger); text-decoration: underline; }
.fab-thread__stickers { display: flex; flex-wrap: nowrap; gap: .4rem; overflow-x: auto; padding-bottom: .25rem; scrollbar-width: none; }
.fab-thread__stickers::-webkit-scrollbar { display: none; }
.fab-thread__sticker { flex: none; width: 3rem; height: 3rem; padding: 0; border: 2px solid var(--bs-border-color); border-radius: .75rem; overflow: hidden; background: var(--bs-tertiary-bg); }
.fab-thread__sticker img { width: 100%; height: 100%; object-fit: cover; }
.fab-thread__sticker:hover { border-color: rgb(var(--bs-primary-rgb)); }

/* Page changes (2026-09-13). Bootstrap's reboot smooth-scrolls the root, which made every Turbo visit visibly scroll back
   to the top; pages jump instantly instead and the change itself is a short fade + lift (View Transitions, Turbo 8 via the
   view-transition meta). The chrome is NOT given its own view-transition-name: a named element becomes a backdrop root,
   which starves the top bar's and tab bar's backdrop-filter of anything to blur (they went see-through, 2026-09-13). The
   bars cross-fade with the page instead — identical pixels on both sides, so nothing visibly moves. */
:root { scroll-behavior: auto; }
/* The root paints the body's colour too (story 3133): below a short body when a sheet locks scrolling, and in the app's
   web view, whose own background follows the phone's appearance rather than the account's theme, the page would show
   the other theme's colour there. */
html { background-color: var(--bs-body-bg); }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: fab-page-out 140ms ease-in both; }
  ::view-transition-new(root) { animation: fab-page-in 260ms cubic-bezier(.2, .7, .2, 1) both; }
  @keyframes fab-page-out { to { opacity: 0; } }
  @keyframes fab-page-in { from { opacity: 0; transform: translateY(12px); } }
}
@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; } }

/* The fuse confirm: what can come out, as rarity-coloured cards (brainrots/_fuse_outcomes) */
.fab-confirm__html { text-align: left; }
.fab-fuse-outcomes__cost { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: .25rem; margin-bottom: .75rem; }
.fab-fuse-outcomes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.fab-fuse-outcome { display: grid; justify-items: center; gap: .3rem; padding: .7rem .5rem; border-radius: .9rem; text-align: center; border: 2px solid var(--rarity, var(--bs-border-color)); background: linear-gradient(165deg, rgba(var(--rarity-rgb, 177, 92, 255), .34), rgba(var(--rarity-rgb, 177, 92, 255), .05) 70%); box-shadow: 0 0 14px rgba(var(--rarity-rgb, 177, 92, 255), .18); }
.fab-fuse-outcome__title { font-weight: 700; font-size: .9rem; line-height: 1.2; color: var(--bs-body-color); }
.fab-fuse-outcome__sub { font-size: .75rem; color: var(--bs-secondary-color); line-height: 1.2; }
.fuse__how-toggle .bi { transition: transform .2s; }
.fuse__how-toggle[aria-expanded="true"] .bi { transform: rotate(180deg); }

/* The fuse show (2026-09-13): a dimmed screen with the machine rolling in; every moving part is positioned over the
   artwork in percentages of its square (see brainrots/_fuse_cinema). */
.fab-fuse-cinema { position: fixed; inset: 0; z-index: 1080; display: grid; place-items: center; padding: 1.25rem; background: rgba(8, 3, 32, .84); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); opacity: 0; transition: opacity .35s ease; color: #fff; }
.fab-fuse-cinema.is-open { opacity: 1; }
html.fab-fuse-cinema-open, html.fab-fuse-cinema-open body { overflow: hidden; }
.fab-fuse-cinema__stage { width: min(88vw, 26rem); text-align: center; }
.fab-fuse-machine { position: relative; aspect-ratio: 1; margin: 0 auto 1rem; transform: translateY(28px) scale(.82); opacity: 0; transition: transform .7s cubic-bezier(.2, .8, .2, 1), opacity .45s ease; }
.fab-fuse-cinema.is-open .fab-fuse-machine { transform: none; opacity: 1; }
.fab-fuse-machine__img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: contain; animation: fab-fuse-breathe 2.4s ease-in-out infinite; }
.fab-fuse-machine__glow { position: absolute; inset: -18%; border-radius: 50%; background: radial-gradient(circle at 50% 48%, rgba(47, 230, 255, .35), rgba(177, 92, 255, .25) 35%, transparent 65%); animation: fab-fuse-flicker 1.3s steps(3, end) infinite; pointer-events: none; }
.fab-fuse-machine > .fab-fuse-chamber, .fab-fuse-machine > .fab-fuse-tube, .fab-fuse-machine > .fab-fuse-monitor, .fab-fuse-machine > .fab-fuse-light, .fab-fuse-machine > .fab-fuse-smoke { position: absolute; z-index: 2; pointer-events: none; }
.fab-fuse-chamber { left: var(--l); top: var(--t); width: var(--w); height: var(--h); border-radius: 8%; background: rgba(47, 230, 255, .28); mix-blend-mode: screen; animation: fab-fuse-chamber 1.4s ease-in-out infinite alternate; }
.fab-fuse-tube { left: var(--l); top: var(--t); width: var(--w); height: var(--h); overflow: hidden; border-radius: 14%; }
.fab-fuse-tube i { position: absolute; bottom: 6%; width: 17%; aspect-ratio: 1; border-radius: 50%; background: rgba(255, 255, 255, .78); box-shadow: 0 0 6px rgba(255, 255, 255, .9); animation: fab-fuse-bubble 2.4s ease-in-out infinite alternate; }
.fab-fuse-tube i:nth-child(1) { left: 14%; animation-delay: 0s; }
.fab-fuse-tube i:nth-child(2) { left: 40%; width: 12%; animation-delay: -.7s; animation-duration: 2s; }
.fab-fuse-tube i:nth-child(3) { left: 62%; width: 20%; animation-delay: -1.4s; animation-duration: 2.9s; }
.fab-fuse-tube i:nth-child(4) { left: 30%; width: 9%; animation-delay: -1.9s; animation-duration: 1.7s; }
.fab-fuse-light { left: var(--x); top: var(--y); width: 3.4%; aspect-ratio: 1; border-radius: 50%; transform: translate(-50%, -50%); }
.fab-fuse-light--red { background: #ff4d6d; box-shadow: 0 0 10px 3px rgba(255, 77, 109, .9); animation: fab-fuse-blink 1s steps(2, end) infinite; }
.fab-fuse-light--blue { background: #2fe6ff; box-shadow: 0 0 10px 3px rgba(47, 230, 255, .9); animation: fab-fuse-blink 1s steps(2, end) infinite; animation-delay: .5s; }
.fab-fuse-monitor { left: var(--l); top: var(--t); width: var(--w); height: var(--h); border-radius: 6%; overflow: hidden; }
.fab-fuse-monitor__trace { fill: none; stroke: #2fe6ff; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; filter: drop-shadow(0 0 2px #2fe6ff); animation: fab-fuse-trace 2.2s linear infinite; }
.fab-fuse-smoke { left: var(--x); top: var(--y); width: 12%; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle, rgba(255, 255, 255, .55), rgba(255, 255, 255, 0) 70%); filter: blur(3px); opacity: 0; animation: fab-fuse-smoke 2.8s ease-out infinite; animation-delay: var(--d); }
.fab-fuse-cinema__title { margin: .25rem 0 .75rem; }
.fab-fuse-cinema__bar { height: .8rem; border-radius: 999px; background: rgba(255, 255, 255, .16); overflow: hidden; }
.fab-fuse-cinema__fill { height: 100%; width: 0; border-radius: inherit; background: var(--fab-gradient-primary); box-shadow: 0 0 14px rgba(255, 63, 191, .75); transition: width .25s ease-out; background-size: 200% 100%; animation: fab-fuse-shimmer 1.2s linear infinite; }
.fab-fuse-cinema__status { margin: .6rem 0 0; font-size: .95rem; opacity: .85; }
.fab-confetti { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1090; }
@keyframes fab-fuse-breathe { 0%, 100% { filter: drop-shadow(0 0 18px rgba(177, 92, 255, .45)); } 50% { filter: drop-shadow(0 0 34px rgba(255, 63, 191, .8)); } }
@keyframes fab-fuse-flicker { 0% { opacity: .55; } 50% { opacity: 1; } 100% { opacity: .7; } }
@keyframes fab-fuse-chamber { from { opacity: .25; } to { opacity: .8; } }
@keyframes fab-fuse-bubble { from { transform: translateY(0); } to { transform: translateY(-380%); } }
@keyframes fab-fuse-blink { 0% { opacity: 1; } 50% { opacity: .15; } 100% { opacity: 1; } }
@keyframes fab-fuse-trace { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes fab-fuse-smoke { 0% { transform: translate(-50%, -30%) scale(.6); opacity: 0; } 20% { opacity: .7; } 100% { transform: translate(-50%, -190%) scale(1.9); opacity: 0; } }
@keyframes fab-fuse-shimmer { from { background-position: 0 0; } to { background-position: 200% 0; } }
@media (prefers-reduced-motion: reduce) { .fab-fuse-machine, .fab-fuse-machine *, .fab-fuse-cinema__fill { animation: none !important; transition: none !important; } }

/* the brainrot page's two columns (2026-09-13): a real two-column grid from lg up; below lg the column wrappers dissolve
   (display: contents) so every block is a child of one stack and `order` decides the phone order */
.fab-care-layout { display: grid; gap: 1.5rem; }
.fab-care-layout__col { display: contents; }
.fab-care-layout__item { min-width: 0; }
@media (max-width: 991.98px) {
  .fab-care-layout__item--care { order: 1; } .fab-care-layout__item--nav { order: 2; } .fab-care-layout__item--tools { order: 3; }
  .fab-care-layout__item--fuse { order: 4; } .fab-care-layout__item--nickname { order: 5; } .fab-care-layout__item--release { order: 6; }
  .fab-care-layout__item--viewer { order: 7; }
}
@media (min-width: 992px) {
  .fab-care-layout { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: start; }
  .fab-care-layout__col { display: grid; gap: 1.5rem; align-content: start; min-width: 0; }
}

/* the brainrot page's header row: back arrow, wallet, camera. Below lg the camera button is there and the wallet fills the
   room between them. From lg up the camera is gone and the row mirrors the layout's two columns, so the wallet, at its
   natural width, sits flush with the care card's right edge (Nick, 2026-09-14). */
@media (max-width: 991.98px) {
  .fab-care-head .fab-wallet { width: 100%; }
  .fab-care-head .fab-wallet__count, .fab-care-head .fab-wallet__collect { flex: 1 1 auto; }
}
@media (min-width: 992px) {
  .fab-care-head { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 1.5rem; }
  .fab-care-head__row { grid-column: 1; }
  .fab-care-head .fab-wallet-slot { justify-content: flex-end; }
}

/* previous / next brainrot under the care card (2026-09-13) */
.fab-room-nav { display: flex; flex-wrap: wrap; align-items: center; column-gap: .5rem; row-gap: .4rem; min-width: 0; max-width: 100%; padding: .6rem; border: 1px solid var(--bs-border-color); border-radius: 1.1rem; }
/* the buttons size to their names and share a row while both fit; a long name pushes the second onto its own row, and a name
   longer than the whole row is the one case that gets an ellipsis */
.fab-room-nav__btn { flex: 1 1 auto; min-width: 0; max-width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: .45rem; overflow: hidden; }
.fab-room-nav__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fab-room-nav__count { flex: 0 0 100%; text-align: center; font-size: .75rem; line-height: 1.1; margin-top: -.1rem; }
/* a soft-light pill: the same tint and text tokens as .btn-soft-light, so it matches the soft buttons in both themes */
.badge-soft-light { --soft-rgb: 29, 11, 78; --soft-color: var(--fab-ink); display: inline-block; padding: .2rem .65rem; border-radius: 999px; background: rgba(var(--soft-rgb), .14); color: var(--soft-color); font-weight: 700; }
[data-bs-theme=dark] .badge-soft-light { --soft-rgb: 244, 238, 255; --soft-color: #F4EEFF; }

/* the entry layout (sign in, sign up, forgot password, a kid's join and lock screens) */
.fab-entry__brand { display: flex; align-items: center; justify-content: center; gap: .75rem; padding: 1.25rem 0 .25rem; text-decoration: none; color: inherit; }
.fab-entry__brand img { width: 56px; height: 56px; border-radius: 14px; box-shadow: 0 10px 28px rgba(177, 92, 255, .35); }
.fab-entry__brand-name { font-family: Fredoka, var(--bs-font-sans-serif); font-weight: 700; font-size: 1.55rem; letter-spacing: .01em; }
.fab-entry__card { max-width: 36rem; margin: 0 auto; }
.fab-entry__card > .card-body { padding: 1.5rem; }
@media (min-width: 576px) { .fab-entry__card > .card-body { padding: 2rem; } }

/* Menus move the way iOS menus do (2026-09-14, retuned for story 3193): Bootstrap only toggles .show, so the menu stays
   display:block and animates on its own. A menu grows out of the corner it hangs from with a touch of spring and a quick
   fade, and shrinks back into it faster on the way out; the on-brand select keeps a gentler slide. `translate`/`scale`
   never fight Popper's transform. A closed menu keeps the exact place it opens at (Bootstrap sets top/right only while
   open, [data-bs-popper]); without these an invisible 14rem menu hung off the right edge of the top bar. */
.dropdown-menu, .fab-select__menu { display: block; visibility: hidden; opacity: 0; pointer-events: none;
  transition: opacity .14s ease-in, translate .14s ease-in, scale .16s ease-in, visibility 0s linear .16s; }
.dropdown-menu { scale: .55; translate: 0 -.25rem; transform-origin: top left; }
.fab-select__menu { scale: .96; translate: 0 -.75rem; transform-origin: top center; }
.dropdown-menu.show, .fab-select__menu.show { visibility: visible; opacity: 1; translate: 0 0; scale: 1; pointer-events: auto;
  transition: opacity .18s ease-out, translate .36s cubic-bezier(.3, 1.4, .5, 1), scale .36s cubic-bezier(.3, 1.4, .5, 1), visibility 0s; }
.dropdown-menu { top: 100%; left: 0; margin-top: var(--bs-dropdown-spacer); }
.dropdown-menu-end { right: 0; left: auto; transform-origin: top right; }
.dropup .dropdown-menu { top: auto; bottom: 100%; margin-top: 0; margin-bottom: var(--bs-dropdown-spacer); }
.dropup .dropdown-menu { translate: 0 .25rem; transform-origin: bottom left; }
.dropup .dropdown-menu-end { transform-origin: bottom right; }
.fab-select__menu--up { translate: 0 .75rem; transform-origin: bottom center; }
.dropup .dropdown-menu.show, .fab-select__menu--up.show { translate: 0 0; }
@media (prefers-reduced-motion: reduce) { .dropdown-menu, .fab-select__menu { transition: none; } }

/* the on-brand select (select_controller.js): the native select hides behind a .form-select button and a .dropdown-menu */
.fab-select { position: relative; }
.fab-select__native { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0; margin: 0; opacity: 0; pointer-events: none; }
.fab-select__toggle { display: flex; align-items: center; text-align: start; cursor: pointer; }
.fab-select__toggle:disabled { cursor: default; }
.fab-select__toggle[aria-expanded="true"] { border-color: rgba(var(--bs-primary-rgb), .6); box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), .18); }
.fab-select__value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fab-select__value--placeholder { color: var(--bs-secondary-color); }
/* position, size and place come inline from select_controller.js (fixed, in <body> while open) */
.fab-select__menu { position: fixed; z-index: 1080; box-sizing: border-box; max-height: 18rem; overflow-y: auto; overscroll-behavior: contain; margin: 0; padding: .4rem;
  border-radius: 1rem; border: 1px solid var(--bs-border-color); background: var(--bs-body-bg); box-shadow: 0 16px 40px rgba(18, 6, 58, .35); color: var(--bs-body-color); font-size: 1rem; }
[data-bs-theme=dark] .fab-select__menu { background: #180947; } /* --bs-card-bg only exists inside .card */
.fab-select__option { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .55rem .75rem; border: 0; border-radius: .7rem; background: transparent;
  color: var(--bs-body-color); font: inherit; font-weight: 600; text-align: start; white-space: normal; }
.fab-select__option:hover, .fab-select__option:focus, .fab-select__option:focus-visible { background: rgba(var(--bs-primary-rgb), .12); color: var(--bs-body-color); outline: none; }
.fab-select__option[aria-selected="true"] { color: var(--bs-primary); }
.fab-select__option[aria-selected="true"]::after { content: "\f272"; font-family: bootstrap-icons; margin-left: auto; font-size: 1rem; } /* bi-check-lg */
.fab-select__option:disabled { opacity: .45; }
.fab-select__group { padding: .55rem .75rem .2rem; font-size: .72rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--bs-secondary-color); }
.fab-select__menu::-webkit-scrollbar { width: 8px; } .fab-select__menu::-webkit-scrollbar-thumb { background: rgba(var(--bs-body-color-rgb), .18); border-radius: 8px; }

/* legal documents: /terms and /privacy (LegalController, legal/_document) */
.fab-entry__card--wide { max-width: 46rem; }
.fab-legal .lead { font-size: 1.05rem; }
.fab-legal h2 { font-family: Fredoka, var(--bs-font-sans-serif); font-weight: 700; font-size: 1.15rem; margin: 1.75rem 0 .5rem; }
.fab-legal p, .fab-legal li, .fab-legal dd { line-height: 1.55; }
.fab-legal ul { padding-left: 1.2rem; }
.fab-legal li + li { margin-top: .3rem; }
.fab-legal__list dt { font-weight: 700; margin-top: .75rem; }
.fab-legal__list dd { margin-bottom: 0; }
.fab-footer__legal a { color: inherit; }
.fab-legal-modal .modal-content { border-radius: 1.25rem; border: 1px solid var(--bs-border-color); }
.fab-legal-modal .modal-header { padding: 1rem 1.5rem; }
.fab-legal-modal .modal-body { padding: 1.25rem 1.5rem; }
.fab-legal-modal .modal-footer { padding: .75rem 1.5rem; }
@media (max-width: 575.98px) { .fab-legal-modal .modal-content { border-radius: 0; border: 0; } }

/* ---- A lure at work (story 3089): its own banner beside the test one ---- */
.fab-hunt-banner--lure { background: linear-gradient(120deg, rgba(var(--bs-warning-rgb), .95), rgba(var(--bs-primary-rgb), .95)); color: #1b0b3a; font-weight: 800; }

/* ---- The map's tool pocket (story 3094): the four map tiles, in a dropdown off the top bar ---- */
.fab-topbar__btn--tools { gap: .4rem; }
/* It hangs the full length of the bar (Nick, 2026-09-17). The dropdown itself goes static so the menu's containing
   block is the bar rather than the little button, and then left/right pin it to the bar's own edges. */
/* The tools sheet (story 3192): hangs from the chip's bottom edge, hidden above it, and slides down by --top-sheet-offset
   (the panel's height when open). The Tools tab rides on its bottom edge, flat-topped, flush under the chip. The chip's
   bottom is where the sticky bar ends on the web; in the app the overlay's chip sits the same distance under the notch. */
.fab-topsheet { --top-sheet-offset: 0px; position: fixed; left: .75rem; right: .75rem; top: var(--fab-chip-bottom); bottom: 0; z-index: 1029; pointer-events: none; overflow: hidden; }
/* While a side drawer or the Admin Panel is up, the pocket's tab and sheet step out of the way (story 3240, Nick 2026-10-08:
   in the app the tab showed through the tool inventory drawer); `.showing` so it goes as the drawer starts to slide. */
body:has(.offcanvas:not(.offcanvas-bottom).show, .offcanvas:not(.offcanvas-bottom).showing, .modal[data-fab-bars-away].show) .fab-topsheet { visibility: hidden; }
.fab-topsheet__slide { position: relative; transform: translateY(calc(var(--top-sheet-offset) - 100%)); transition: transform .32s cubic-bezier(.2, .8, .2, 1); }
.fab-topsheet.is-dragging .fab-topsheet__slide { transition: none; }
.fab-topsheet__panel, .fab-topsheet__tab { background: var(--fab-glass-fill), rgba(var(--bs-body-bg-rgb), .62); -webkit-backdrop-filter: blur(24px) saturate(1.8); backdrop-filter: blur(24px) saturate(1.8); box-shadow: var(--fab-glass-shadow), inset 0 -1px 0 rgba(255, 255, 255, .25); }
[data-bs-theme=dark] .fab-topsheet__panel, [data-bs-theme=dark] .fab-topsheet__tab { background: var(--fab-glass-fill), rgba(29, 11, 78, .6); }
.fab-topsheet__panel { pointer-events: auto; padding: .9rem; border-radius: 0 0 1.25rem 1.25rem; }
.fab-topsheet__tab { pointer-events: auto; position: absolute; left: 50%; top: 100%; transform: translateX(-50%); display: flex; align-items: center; gap: .35rem; padding: .3rem .9rem .35rem;
  border-radius: 0 0 .9rem .9rem; border-top: 0; color: var(--bs-body-color); font-size: .78rem; font-weight: 800; touch-action: none; cursor: grab; }
.fab-topsheet__tab:active { cursor: grabbing; }
.fab-topsheet__chevron { font-size: .7rem; transition: transform .25s ease; }
.fab-topsheet.is-open .fab-topsheet__chevron { transform: rotate(180deg); }
.fab-topsheet .tools__grid { margin: 0; }
.tools--bare .tools__grid { margin: 0; }
/* Four across at every width: there are four slots and the menu is now as wide as the bar, so a phone need not stack
   them. The tiles lose a little height and the names a little size instead. */
.tools--map .tools__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 575.98px) {
  .fab-topbar__tools .fab-topbar__label { display: none; }
  .tools--map .tools__slot { min-height: 5rem; padding: .7rem .25rem .6rem; }
  .tools--map .tools__name { font-size: .7rem; line-height: 1.15; }
  .tools--map .tools__icon { font-size: 1.35rem; }
}

/* The pencil beside your own name on your profile (Nick, 2026-09-18): the way into Settings, quiet until you look at it. */
.fab-profile__edit { display: inline-grid; place-items: center; width: 2rem; height: 2rem; margin-left: .25rem; border-radius: 50%; vertical-align: middle;
  color: var(--bs-secondary-color); font-size: .9rem; text-decoration: none; transition: background-color .15s ease, color .15s ease; }
.fab-profile__edit:hover, .fab-profile__edit:focus-visible { background: rgba(var(--bs-primary-rgb), .16); color: var(--bs-primary); }

/* ---- The rooms page header and its menu (Nick, 2026-09-17) ----
   On a phone the menu sits beside the greeting, directly above the wallet's Collect and right-aligned, so it costs no
   extra row. From md up the wallet and the menu share one flex row after the greeting, a little apart, and the menu
   stretches to the wallet's height so the two read as a pair. `display: contents` lets the same markup do both. */
.fab-rooms-head { display: grid; gap: .75rem; }
.fab-rooms-head__wallet { min-width: 0; }
.fab-rooms-head__wallet .fab-wallet { display: flex; width: 100%; justify-content: space-between; } /* one full band on every screen (story 3242) */
/* The rooms count under the balance (story 3242): the two numbers a player checks, together. */
.fab-wallet__count:has(.fab-wallet__caption) { flex-wrap: wrap; row-gap: 0; }
.fab-wallet__caption { flex-basis: 100%; font-size: .78rem; font-weight: 600; opacity: .72; line-height: 1.1; white-space: nowrap; }
/* The grid's heading with its tools (story 3242): quests, codes and the rooms menu, where the cards they act on begin. */
.fab-rooms-grid__head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin: 0 0 .75rem; }
.fab-rooms-head__tools { display: flex; align-items: center; gap: .5rem; }
.fab-rooms-head__tools .fab-rooms-menu { display: flex; }
.fab-rooms-head__tools .fab-rooms-menu__toggle { width: 2.75rem; height: 2.75rem; }
.fab-rooms-menu__toggle { width: 3.5rem; height: auto; }
}
.fab-rooms-menu__toggle {
  position: relative; display: grid; place-items: center; width: var(--fab-tap-target); height: var(--fab-tap-target); padding: 0;
  border-radius: 1rem; border: 1px solid var(--bs-border-color); background: rgba(var(--bs-body-bg-rgb), .75); color: var(--bs-body-color);
  box-shadow: 0 6px 18px rgba(18, 6, 58, .12); font-size: 1.25rem;
  transition: background-color .15s ease, color .15s ease, transform .15s ease;
}
[data-bs-theme=dark] .fab-rooms-menu__toggle { background: rgba(29, 11, 78, .8); border-color: rgba(255, 255, 255, .14); box-shadow: 0 8px 22px rgba(0, 0, 0, .35); }
.fab-rooms-menu__toggle:hover, .fab-rooms-menu__toggle[aria-expanded="true"] { background: rgba(var(--bs-primary-rgb), .2); color: var(--bs-primary); }
.fab-rooms-menu__toggle:active { transform: scale(.96); }
.fab-rooms-menu__toggle:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 2px; }
/* the grid is not showing its plain view */
.fab-rooms-menu__dot { position: absolute; top: .4rem; right: .4rem; width: .6rem; height: .6rem; border-radius: 50%; background: var(--fab-gold); box-shadow: 0 0 0 2px var(--bs-body-bg); }
.fab-rooms-menu .fab-menu { min-width: 16rem; }
.fab-menu__item { width: 100%; border: 0; text-align: start; }

/* The search, filter and sort drawer: rarity chips coloured by their tier, sorts as a pick list with a tick. */
.fab-arrange__chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.fab-arrange__chip {
  display: inline-flex; align-items: center; gap: .45rem; min-height: 2.5rem; padding: .3rem .5rem .3rem .9rem; border-radius: var(--bs-border-radius-pill);
  border: 2px solid transparent; background: rgba(var(--rarity-rgb), .16); color: var(--rarity); font-weight: 700; cursor: pointer; user-select: none;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.fab-arrange__count { min-width: 1.6rem; padding: .05rem .45rem; border-radius: var(--bs-border-radius-pill); background: rgba(var(--rarity-rgb), .2); font-size: .75rem; text-align: center; }
.btn-check:checked + .fab-arrange__chip { background: var(--rarity-gradient, var(--rarity)); color: var(--rarity-contrast, var(--fab-ink)); border-color: var(--fab-outline); box-shadow: 0 0 14px rgba(var(--rarity-rgb), .45); }
.btn-check:checked + .fab-arrange__chip .fab-arrange__count { background: rgba(0, 0, 0, .18); }
.btn-check:focus-visible + .fab-arrange__chip, .btn-check:focus-visible + .fab-arrange__sort { outline: 3px solid var(--fab-ice); outline-offset: 2px; }
.fab-arrange__sorts { display: grid; gap: .35rem; }
.fab-arrange__sort { display: flex; align-items: center; justify-content: space-between; min-height: var(--fab-tap-target); padding: .5rem .95rem; border-radius: .85rem; background: rgba(var(--bs-body-color-rgb), .05); font-weight: 600; cursor: pointer; transition: background .15s ease; }
.fab-arrange__sort:hover { background: rgba(var(--bs-primary-rgb), .1); }
.fab-arrange__tick { opacity: 0; color: var(--bs-primary); font-size: 1.1rem; }
.btn-check:checked + .fab-arrange__sort { background: rgba(var(--bs-primary-rgb), .18); }
.btn-check:checked + .fab-arrange__sort .fab-arrange__tick { opacity: 1; }

/* The line above the grid while it is searched, filtered or sorted, with the way back out. */
.fab-arranged { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .6rem; margin: -.5rem 0 1rem; font-size: .9rem; color: var(--bs-secondary-color); }
.fab-arranged > span:not(:first-child)::before { content: "·"; margin-right: .6rem; opacity: .6; }
.fab-arranged .fw-bold { color: var(--bs-body-color); }
.fab-arranged__clear { margin-left: auto; display: inline-flex; align-items: center; gap: .3rem; padding: .3rem .8rem; border-radius: var(--bs-border-radius-pill); background: rgba(var(--bs-primary-rgb), .14); font-weight: 700; text-decoration: none; }

/* ---- Lucky Blocks (story 3086): the sealed room, its countdown and the opening show ---- */
.fab-lucky__card { text-align: center; }
.fab-lucky__art { width: min(14rem, 60vw); aspect-ratio: 1; display: grid; place-items: center; border-radius: 1.5rem; background: radial-gradient(circle at 50% 40%, rgba(var(--bs-warning-rgb), .22), transparent 68%); }
.fab-lucky__block { width: 100%; height: 100%; object-fit: contain; display: grid; place-items: center; font-size: 5rem; line-height: 1; animation: fab-lucky-hover 3.2s ease-in-out infinite; }
.fab-lucky__clock { font-size: clamp(2.75rem, 12vw, 4.5rem); font-weight: 800; line-height: 1; letter-spacing: .02em; color: var(--bs-primary); font-variant-numeric: tabular-nums; }
.fab-lucky__clock--sm { font-size: 1.5rem; }
.fab-lucky__open { font-size: 1.15rem; }
@keyframes fab-lucky-hover { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-8px) rotate(2deg); } }
@media (prefers-reduced-motion: reduce) { .fab-lucky__block { animation: none; } }
.brainrot-card--sealed .brainrot-card__countdown { text-align: center; }

/* The opening (story 3086, made more of it for Nick 2026-09-17). Four phases, set as classes on the overlay:
   is-open (the screen dims), is-charging (the block rattles harder and harder while rays turn and sparks climb),
   is-bursting (a white flash, two rings and the block gone), is-revealing (the brainrot inside, portrait first and its
   3D model over the top once that has loaded). The overlay takes the outcome's rarity class, so every glow in here is
   the tier's own colour. */
.fab-lucky-cinema {
  position: fixed; inset: 0; z-index: 1085; display: grid; place-items: center; align-content: center; gap: 1.1rem; padding: 1.25rem;
  background: radial-gradient(circle at 50% 42%, rgba(43, 14, 96, .96), rgba(8, 3, 32, .97) 62%);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); opacity: 0; transition: opacity .3s ease; color: #fff; overflow: hidden;
}
.fab-lucky-cinema.is-open { opacity: 1; }
html.fab-lucky-cinema-open, html.fab-lucky-cinema-open body { overflow: hidden; }

/* light turning behind everything: faint while the block rattles, full and tier-coloured once it is open */
.fab-lucky-cinema__rays {
  position: absolute; top: 50%; left: 50%; width: 170vmax; height: 170vmax; translate: -50% -50%; pointer-events: none; opacity: 0; z-index: 0;
  background: repeating-conic-gradient(from 0deg, transparent 0 4deg, rgba(var(--rarity-rgb, 255, 209, 102), .13) 4deg 7deg, transparent 7deg 13deg);
  mask-image: radial-gradient(circle, transparent 0 7%, #000 16%, transparent 54%); -webkit-mask-image: radial-gradient(circle, transparent 0 7%, #000 16%, transparent 54%);
  transition: opacity .6s ease; animation: fab-lucky-turn 26s linear infinite;
}
.fab-lucky-cinema.is-charging .fab-lucky-cinema__rays { opacity: .55; animation-duration: 7s; }
.fab-lucky-cinema.is-revealing .fab-lucky-cinema__rays { opacity: .4; animation-duration: 34s; }
/* the light turns behind the brainrot, never over it */
.fab-lucky-cinema__stage, .fab-lucky-cinema__status, .fab-lucky-cinema__card { position: relative; z-index: 1; }

.fab-lucky-cinema__flash { position: absolute; inset: 0; background: #FFFDF5; opacity: 0; pointer-events: none; }
.fab-lucky-cinema.is-bursting .fab-lucky-cinema__flash { animation: fab-lucky-flash .5s ease-out forwards; }

.fab-lucky-cinema__stage { position: relative; display: grid; place-items: center; width: min(20rem, 76vw); aspect-ratio: 1; }
.fab-lucky-cinema__block {
  grid-area: 1 / 1; width: 100%; height: 100%; object-fit: contain; display: grid; place-items: center; font-size: 6rem; line-height: 1;
  filter: drop-shadow(0 0 22px rgba(var(--bs-warning-rgb), .5)); animation: fab-lucky-shake .38s ease-in-out infinite;
}
.fab-lucky-cinema.is-charging .fab-lucky-cinema__block { animation: fab-lucky-rattle 1.8s ease-in forwards; }
.fab-lucky-cinema.is-bursting .fab-lucky-cinema__block { animation: fab-lucky-pop .5s ease-in forwards; }

.fab-lucky-cinema__burst { position: absolute; inset: -12%; border-radius: 50%; opacity: 0; background: radial-gradient(circle, #FFF6D5 0%, rgba(var(--bs-warning-rgb), .85) 34%, transparent 70%); }
.fab-lucky-cinema.is-bursting .fab-lucky-cinema__burst { animation: fab-lucky-burst .8s ease-out forwards; }
.fab-lucky-cinema__ring { position: absolute; inset: 0; border-radius: 50%; border: 4px solid rgba(var(--rarity-rgb, 255, 209, 102), .9); opacity: 0; }
.fab-lucky-cinema.is-bursting .fab-lucky-cinema__ring { animation: fab-lucky-ring .85s cubic-bezier(.16, .84, .44, 1) forwards; }
.fab-lucky-cinema.is-bursting .fab-lucky-cinema__ring--late { animation-delay: .16s; border-width: 2px; }

/* eight sparks climbing out of the block while it rattles, each on its own line out from the middle */
.fab-lucky-cinema__spark {
  position: absolute; top: 50%; left: 50%; width: .5rem; height: .5rem; margin: -.25rem 0 0 -.25rem; border-radius: 50%; opacity: 0;
  background: var(--fab-gold, #FFD166); box-shadow: 0 0 10px rgba(var(--bs-warning-rgb), .9);
  transform: rotate(calc(var(--spark) * 45deg)) translateY(0);
}
.fab-lucky-cinema.is-charging .fab-lucky-cinema__spark { animation: fab-lucky-spark 1.1s ease-out infinite; animation-delay: calc(var(--spark) * .13s); }

/* what was inside */
.fab-lucky-cinema__reveal { position: absolute; inset: 0; display: grid; place-items: center; }
.fab-lucky-cinema.is-revealing .fab-lucky-cinema__reveal { animation: fab-lucky-arrive .7s cubic-bezier(.2, 1.3, .4, 1) forwards; }
.fab-lucky-cinema__portrait, .fab-lucky-cinema__model { grid-area: 1 / 1; width: min(20rem, 76vw); height: min(20rem, 76vw); }
.fab-lucky-cinema__portrait:not([src]) { display: none; }
.fab-lucky-cinema__portrait { object-fit: contain; filter: drop-shadow(0 0 30px rgba(var(--rarity-rgb, 255, 209, 102), .75)); }
.fab-lucky-cinema__model { opacity: 0; transition: opacity .45s ease; }
.fab-lucky-cinema__model.is-ready { opacity: 1; }
.fab-lucky-cinema__model model-viewer { width: 100%; height: 100%; --poster-color: transparent; background: transparent; }

.fab-lucky-cinema__status { font-weight: 800; font-size: 1.35rem; margin: 0; text-align: center; }
.fab-lucky-cinema__card { display: grid; justify-items: center; gap: .35rem; text-align: center; max-width: 22rem; }
.fab-lucky-cinema.is-revealing .fab-lucky-cinema__card { animation: fab-lucky-rise .5s .15s ease-out both; }
.fab-lucky-cinema__rarity { margin: 0; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; font-size: .8rem; color: var(--rarity, var(--fab-gold)); }
.fab-lucky-cinema__name { font-size: clamp(1.8rem, 8vw, 2.6rem); font-weight: 800; margin: 0; line-height: 1.1; text-wrap: balance; }
.fab-lucky-cinema__first { margin: 0; font-weight: 700; color: var(--fab-gold, #FFD166); }
.fab-lucky-cinema__done { margin-top: .6rem; }

@keyframes fab-lucky-shake { 0%, 100% { transform: translate(0, 0) rotate(-4deg); } 25% { transform: translate(-5px, 2px) rotate(4deg); } 50% { transform: translate(4px, -3px) rotate(-5deg); } 75% { transform: translate(-3px, 3px) rotate(3deg); } }
/* one run, winding up: small and slow at the start, hard enough to blur at the end */
@keyframes fab-lucky-rattle {
  0% { transform: translate(0, 0) rotate(-2deg) scale(1); }
  20% { transform: translate(-4px, 2px) rotate(3deg) scale(1.02); }
  40% { transform: translate(5px, -3px) rotate(-4deg) scale(1.04); }
  60% { transform: translate(-7px, 4px) rotate(6deg) scale(1.07); }
  75% { transform: translate(8px, -5px) rotate(-7deg) scale(1.1); }
  88% { transform: translate(-9px, 3px) rotate(8deg) scale(1.13); }
  100% { transform: translate(0, 0) rotate(0deg) scale(1.16); }
}
@keyframes fab-lucky-pop { 0% { transform: scale(1.16); opacity: 1; } 45% { transform: scale(1.5) rotate(10deg); opacity: 1; } 100% { transform: scale(.15); opacity: 0; } }
@keyframes fab-lucky-burst { 0% { opacity: 0; transform: scale(.2); } 40% { opacity: 1; transform: scale(1.2); } 100% { opacity: 0; transform: scale(2); } }
@keyframes fab-lucky-ring { 0% { opacity: .9; transform: scale(.3); } 100% { opacity: 0; transform: scale(2.6); } }
@keyframes fab-lucky-flash { 0% { opacity: 0; } 18% { opacity: .92; } 100% { opacity: 0; } }
@keyframes fab-lucky-turn { to { rotate: 360deg; } }
@keyframes fab-lucky-spark {
  0% { opacity: 0; transform: rotate(calc(var(--spark) * 45deg)) translateY(-1rem) scale(.6); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: rotate(calc(var(--spark) * 45deg)) translateY(-8rem) scale(1); }
}
@keyframes fab-lucky-arrive { 0% { opacity: 0; transform: scale(.35) rotate(-8deg); } 60% { opacity: 1; } 100% { opacity: 1; transform: scale(1) rotate(0); } }
@keyframes fab-lucky-rise { 0% { opacity: 0; transform: translateY(1.25rem); } 100% { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .fab-lucky-cinema__block, .fab-lucky-cinema__spark, .fab-lucky-cinema__rays { animation: none; }
  .fab-lucky-cinema.is-bursting .fab-lucky-cinema__block, .fab-lucky-cinema.is-bursting .fab-lucky-cinema__burst,
  .fab-lucky-cinema.is-bursting .fab-lucky-cinema__ring, .fab-lucky-cinema.is-bursting .fab-lucky-cinema__flash,
  .fab-lucky-cinema.is-revealing .fab-lucky-cinema__reveal, .fab-lucky-cinema.is-revealing .fab-lucky-cinema__card { animation-duration: .01ms; }
}

/* ---- Admin Panel (story 3082): the modal every admin role opens from the Account menu ---- */
.fab-admin-panel .modal-content { border-radius: 1.25rem; border: 1px solid var(--bs-border-color); }
@media (max-width: 767.98px) { .fab-admin-panel .modal-content { border-radius: 0; border: 0; } }
.fab-admin-panel__tabs { flex: 0 0 auto; overflow-x: auto; scrollbar-width: none; padding-bottom: .75rem; } /* a flex child of .modal-content: never shrunk into a clipped strip */
.fab-admin-panel__tabs::-webkit-scrollbar { display: none; }
.fab-admin-panel__tabs .btn { white-space: nowrap; }
.fab-admin-panel__body .fab-zone-map { height: 300px; min-height: 240px; }
.fab-admin-panel__body .fab-admin-tool { max-width: 100%; }
.fab-role-form { min-width: 15rem; }

/* ---- Pick list (story 3083): a searchable checkbox list for the brainrots of a drop or an event ---- */
.fab-picklist__items { max-height: 13rem; overflow-y: auto; overscroll-behavior: contain; margin-top: .4rem; padding: .3rem; border: 1px solid var(--bs-border-color); border-radius: .85rem; background: var(--bs-body-bg); }
.fab-picklist__item[hidden] { display: none; }
.fab-picklist__option { display: flex; align-items: center; gap: .55rem; margin: 0; padding: .4rem .55rem; border-radius: .6rem; cursor: pointer; font-weight: 600; }
.fab-picklist__option:hover { background: rgba(var(--bs-primary-rgb), .1); }
.fab-picklist__option:has(:checked) { background: rgba(var(--bs-primary-rgb), .16); }
.fab-picklist__option:has(:disabled) { opacity: .5; cursor: not-allowed; }
.fab-picklist__name { flex: 1 1 auto; min-width: 0; }

/* bottom sheets (Ui::SheetComponent: hunt, friend nearby, dex peek) — styled here so every page that renders one has them, not only the hunt */
.fab-sheet.offcanvas.offcanvas-bottom { height: auto; max-height: 88vh; border: 0; border-radius: 1.5rem 1.5rem 0 0; background: rgba(var(--bs-body-bg-rgb), .86); -webkit-backdrop-filter: blur(18px) saturate(1.6); backdrop-filter: blur(18px) saturate(1.6); box-shadow: 0 -12px 32px rgba(18, 6, 58, .18); }
.fab-sheet__handle { width: 2.5rem; height: .3rem; border-radius: 999px; background: rgba(var(--bs-body-color-rgb), .25); margin: .6rem auto 0; }
.fab-sheet__header { padding: .5rem 1.25rem 0; }
.fab-sheet__body { padding: .75rem 1.25rem calc(1rem + env(safe-area-inset-bottom)); overscroll-behavior-y: contain; }
/* drag to dismiss (sheet_drag_controller.js, 2026-09-14): the grip never scrolls the page, a drag follows the finger with no
   easing, the release eases back or out */
.fab-sheet__handle, .fab-sheet__header { touch-action: none; cursor: grab; }
.fab-sheet.is-dragging, .fab-sheet.is-dragging .fab-sheet__handle, .fab-sheet.is-dragging .fab-sheet__header { cursor: grabbing; }
.fab-sheet.is-dragging { transition: none !important; }
.fab-sheet.is-settling { transition: transform .24s cubic-bezier(.2, .8, .2, 1) !important; }

/* kid phones (story 3074) */
.fab-join__code, .fab-lock__pin { font-size: 1.8rem; letter-spacing: .25em; font-weight: 800; font-family: var(--bs-font-monospace); }
.fab-device-code { padding: 1rem; border-radius: 1rem; background: rgba(var(--bs-primary-rgb), .08); border: 1px dashed rgba(var(--bs-primary-rgb), .4); }
.fab-device-code__value { font-family: var(--bs-font-monospace); font-size: 2.2rem; font-weight: 800; letter-spacing: .2em; color: var(--bs-primary); }
.fab-lock .fab-avatar--lg { display: flex; }

/* admin reports (story 3073) */
.fab-report__quote { margin: 0; padding: .5rem .75rem; border-left: 3px solid var(--bs-warning); background: rgba(var(--bs-body-color-rgb), .04); border-radius: .5rem; font-weight: 600; overflow-wrap: anywhere; }
.fab-report__flagged .fab-message__bubble { outline: 2px solid var(--bs-warning); }
.fab-admin-tabs__badge { font-size: .65rem; vertical-align: text-top; }

/* Settings (2026-09-13): sidebar of sections on the left, the section on the right; a sideways strip on phones */
.fab-settings-layout { display: grid; gap: 1.5rem; }
.fab-settings-nav { display: flex; gap: .35rem; min-width: 0; overflow-x: auto; scrollbar-width: none; padding: .15rem; margin: -.15rem; }
.fab-settings-nav::-webkit-scrollbar { display: none; }
.fab-settings-nav__link { display: inline-flex; align-items: center; gap: .55rem; padding: .55rem .85rem; border-radius: .9rem; color: var(--bs-body-color); text-decoration: none; font-weight: 600; white-space: nowrap; transition: background-color .15s ease; }
.fab-settings-nav__link .bi { color: var(--bs-secondary-color); }
.fab-settings-nav__link:hover { background: rgba(var(--bs-primary-rgb), .08); color: var(--bs-body-color); }
.fab-settings-nav__link--active { background: rgba(var(--bs-primary-rgb), .14); color: var(--bs-body-color); }
.fab-settings-nav__link--active .bi { color: rgb(var(--bs-primary-rgb)); }
@media (min-width: 992px) {
  .fab-settings-layout { grid-template-columns: 15rem minmax(0, 1fr); align-items: start; }
  .fab-settings-nav { flex-direction: column; overflow: visible; position: sticky; top: 5.5rem; }
  .fab-settings-nav__link { white-space: normal; }
}

/* profile (story 3068) */
.fab-profile { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.fab-profile__avatar { width: 4.5rem; height: 4.5rem; font-size: 2.4rem; }
.fab-profile__who { flex: 1 1 10rem; min-width: 0; } /* grows from a small basis so the name never drops under the avatar */
@media (max-width: 767.98px) { .fab-profile__actions { flex: 1 1 100%; } }
/* "Friends" in the action row is a status, not a button: quiet, the height of its neighbours */
.fab-profile__status { display: inline-flex; align-items: center; gap: .4rem; padding: .55rem .95rem; border-radius: var(--bs-border-radius-pill); background: rgba(var(--bs-success-rgb), .14); color: var(--bs-success); font-weight: 700; line-height: 1.5; }
.fab-avatar-picker { display: flex; flex-wrap: wrap; gap: .5rem; }
.fab-avatar-picker__option { cursor: pointer; margin: 0; border-radius: 50%; padding: .15rem; border: 3px solid transparent; transition: border-color .15s ease, transform .15s ease; }
.fab-avatar-picker__option:hover { transform: translateY(-2px); }
.fab-avatar-picker__option:has(input:checked) { border-color: var(--fab-ice); }
.fab-avatar-picker__option:has(input:focus-visible) { outline: 3px solid var(--fab-ice); outline-offset: 2px; }
.fab-avatar { flex: none; width: 3rem; height: 3rem; border-radius: 50%; display: grid; place-items: center; font-size: 1.6rem; background: var(--fab-gradient-primary); box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .5), 0 4px 12px rgba(var(--bs-primary-rgb), .35); }
.fab-status { display: inline-flex; align-items: center; gap: .4rem; padding: .3rem .8rem; border-radius: var(--bs-border-radius-pill); border: 2px solid var(--fab-outline); font-weight: 700; font-size: .85rem; white-space: nowrap; }
.fab-status--on { background: var(--bs-success); color: var(--fab-ink); }
.fab-status--off { background: var(--bs-warning); color: var(--fab-ink); }
.fab-status--none { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }

/* ---- 3D model viewer (Species#show; later the Dex and Catch screens) ---- */
/* model-viewer's own :host style fixes height at 150px — height:auto hands it back to aspect-ratio. */
.fab-model-viewer {
  display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; max-height: 78vh;
  border-radius: var(--bs-border-radius-lg); border: 3px solid var(--fab-outline);
  background: radial-gradient(ellipse at 50% 40%, rgba(var(--rarity-rgb, var(--bs-primary-rgb)), .35), transparent 65%), var(--bs-tertiary-bg);
  --poster-color: transparent; --progress-bar-color: var(--fab-ice); --progress-bar-height: 4px;
}
.fab-model-viewer [slot="ar-button"] { position: absolute; bottom: 1rem; right: 1rem; }
/* The Rooms stage (story 3164, the lobby look of 2026-09-30): no box, the brainrot stands over the page on a glowing
   pad in its tier's colour with a column of light behind it, the way a game lobby shows a character. The scene is
   portrait (4:5) so a full body fills it; the portrait stands in until the model loads, then fades under it. The pad
   is a model-viewer hotspot (a point in the model's space), so it stays under the feet as the camera turns; the
   viewer's own hotspot dimming is off, since the feet are always "behind" the model from a low camera. */
.fab-stage { display: grid; grid-template-columns: minmax(0, 1fr); gap: .5rem 1rem; align-items: center; --stage-rgb: var(--rarity-rgb, var(--bs-primary-rgb)); }
@media (min-width: 768px) { .fab-stage { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem; } }
.fab-stage__scene { position: relative; aspect-ratio: 4 / 5; overflow: visible; }
.fab-stage__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 2rem; transition: opacity .4s ease; }
.fab-stage__poster[hidden] { display: block; opacity: 0; pointer-events: none; }
.fab-stage__viewer { position: absolute; inset: 0; width: 100%; height: 100%; --poster-color: transparent; --min-hotspot-opacity: 1; --max-hotspot-opacity: 1; background: transparent; opacity: 0; transition: opacity .4s ease; }
.fab-stage__viewer.is-loaded { opacity: 1; }
.fab-stage__probe { pointer-events: none; width: 0; height: 0; visibility: hidden; }
/* The floor (story 3169): everything under and behind the brainrot, placed at the probe's point by the controller. */
.fab-stage__floor { position: absolute; left: var(--pad-x, 50%); top: var(--pad-y, 84%); width: 0; height: 0; z-index: 0; pointer-events: none; }
.fab-stage__poster, .fab-stage__viewer { z-index: 1; }
.fab-stage__floor > * { position: absolute; left: 50%; top: 50%; }
.fab-stage__disc { width: 15rem; height: 3.6rem; transform: translate(-50%, -50%); border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(var(--stage-rgb), .6) 0%, rgba(var(--stage-rgb), .26) 45%, rgba(var(--stage-rgb), 0) 72%);
  box-shadow: 0 0 2.5rem rgba(var(--stage-rgb), .45); }
.fab-stage__ring { width: 12rem; height: 2.9rem; transform: translate(-50%, -50%); border-radius: 50%;
  border: 2px solid rgba(var(--stage-rgb), .8); box-shadow: 0 0 1rem rgba(var(--stage-rgb), .6), inset 0 0 1rem rgba(var(--stage-rgb), .35); animation: fab-stage-pulse 3.2s ease-in-out infinite; }
.fab-stage__beam { top: auto; bottom: 50%; width: 9rem; height: 22rem; transform: translateX(-50%); border-radius: 50% 50% 0 0 / 8% 8% 0 0;
  background: linear-gradient(to top, rgba(var(--stage-rgb), .3), rgba(var(--stage-rgb), .08) 55%, rgba(var(--stage-rgb), 0)); filter: blur(6px); }
/* The energy: a breathing glow behind the body, slow light rays, sparks drifting up from the pad. Transforms and opacity only. */
.fab-stage__aura { width: 18rem; height: 18rem; transform: translate(-50%, -78%); border-radius: 50%;
  background: radial-gradient(circle at center, rgba(var(--stage-rgb), .34) 0%, rgba(var(--stage-rgb), .12) 40%, rgba(var(--stage-rgb), 0) 70%); animation: fab-stage-breathe 4.2s ease-in-out infinite; }
.fab-stage__rays { width: 26rem; height: 26rem; transform: translate(-50%, -72%); border-radius: 50%; opacity: .55;
  background: repeating-conic-gradient(from 0deg, rgba(var(--stage-rgb), 0) 0 9deg, rgba(var(--stage-rgb), .16) 9deg 13deg, rgba(var(--stage-rgb), 0) 13deg 24deg);
  -webkit-mask: radial-gradient(circle at center, #000 0%, rgba(0, 0, 0, .6) 35%, transparent 68%); mask: radial-gradient(circle at center, #000 0%, rgba(0, 0, 0, .6) 35%, transparent 68%);
  animation: fab-stage-turn 48s linear infinite; }
.fab-stage__sparks { width: 0; height: 0; }
.fab-stage__sparks i { position: absolute; left: var(--x); bottom: 0; width: .3rem; height: .3rem; border-radius: 50%; background: rgba(var(--stage-rgb), .95);
  box-shadow: 0 0 .5rem rgba(var(--stage-rgb), .9); opacity: 0; animation: fab-stage-spark var(--t) ease-out var(--d) infinite; }
.fab-stage__sparks i:nth-child(1) { --x: -4.5rem; --t: 3.6s; --d: 0s; } .fab-stage__sparks i:nth-child(2) { --x: 3rem; --t: 4.4s; --d: .7s; }
.fab-stage__sparks i:nth-child(3) { --x: -1.5rem; --t: 3.1s; --d: 1.4s; } .fab-stage__sparks i:nth-child(4) { --x: 5rem; --t: 4.9s; --d: 2.1s; }
.fab-stage__sparks i:nth-child(5) { --x: -5.5rem; --t: 4s; --d: 2.8s; } .fab-stage__sparks i:nth-child(6) { --x: .5rem; --t: 3.4s; --d: 3.5s; }
.fab-stage__sparks i:nth-child(7) { --x: 4rem; --t: 4.6s; --d: 4.2s; } .fab-stage__sparks i:nth-child(8) { --x: -3rem; --t: 3.8s; --d: .35s; }
.fab-stage__sparks i:nth-child(9) { --x: 1.8rem; --t: 5.2s; --d: 1.05s; } .fab-stage__sparks i:nth-child(10) { --x: -.5rem; --t: 3.3s; --d: 2.45s; }
@keyframes fab-stage-pulse { 0%, 100% { opacity: .8; transform: translate(-50%, -50%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.04); } }
@keyframes fab-stage-breathe { 0%, 100% { opacity: .75; transform: translate(-50%, -78%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -78%) scale(1.08); } }
@keyframes fab-stage-turn { to { transform: translate(-50%, -72%) rotate(360deg); } }
@keyframes fab-stage-spark { 0% { opacity: 0; transform: translateY(0) scale(.6); } 12% { opacity: 1; } 100% { opacity: 0; transform: translateY(-11rem) scale(1.1); } }
@media (prefers-reduced-motion: reduce) { .fab-stage__ring, .fab-stage__aura, .fab-stage__rays, .fab-stage__sparks i { animation: none; } .fab-stage__sparks { display: none; } }
@media (min-width: 768px) { .fab-stage__disc { width: 19rem; height: 4.6rem; } .fab-stage__ring { width: 15rem; height: 3.6rem; } .fab-stage__beam { width: 11rem; height: 28rem; }
  .fab-stage__aura { width: 22rem; height: 22rem; } .fab-stage__rays { width: 32rem; height: 32rem; } }
/* The stage's arrows (story 3241): one at each edge at the brainrot's mid height, stepping through the grid's cards in
   their order and wrapping. Faint until pressed, 44 pt, above the canvas so a tap never starts an orbit, and no backdrop
   blur over the WebGL canvas (the GPU diet). Hidden with one brainrot. */
.fab-stage__arrow { position: absolute; top: 50%; translate: 0 -50%; z-index: 2; width: 2.75rem; height: 2.75rem; padding: 0; border-radius: 50%; border: 1px solid rgba(var(--bs-body-color-rgb), .18); background: rgba(var(--bs-body-color-rgb), .08); color: inherit; display: grid; place-items: center; font-size: 1.15rem; opacity: .6; cursor: pointer; transition: opacity .15s ease, scale .15s ease; }
.fab-stage__arrow--prev { left: 0; }
.fab-stage__arrow--next { right: 0; }
.fab-stage__arrow:hover, .fab-stage__arrow:focus-visible { opacity: .95; }
.fab-stage__arrow:active { opacity: 1; scale: .94; }
.fab-stage__arrow[hidden] { display: none; }
.fab-stage__details { text-align: center; }
@media (min-width: 768px) { .fab-stage__details { text-align: left; } }
/* The card's On stage button sits top left: the top right corner belongs to the ghost and the sleeping face. */
.fab-stage-pick { position: absolute; top: .5rem; left: 1.25rem; z-index: 2; width: 2.25rem; height: 2.25rem; border-radius: 50%; border: 1px solid rgba(var(--bs-primary-rgb), .35); background: rgba(var(--bs-body-bg-rgb), .85); color: var(--bs-primary); display: grid; place-items: center; font-size: 1rem; }
.fab-stage-pick:hover, .fab-stage-pick:focus-visible { background: var(--bs-primary); color: #fff; }
.btn-soft-primary.active { --bs-btn-active-bg: rgba(var(--soft-rgb), .38); }

/* ---- Admin roster table ---- */
/* admin section header (2026-09-12): the + for the section's new form sits right after the title */
.fab-admin-plus { display: inline-grid; place-items: center; font-size: 1.05rem; flex-shrink: 0; }
.fab-admin-head { margin-top: 1rem;}
.fab-admin-head__mark { font-size: .55em; opacity: .55; }
/* a detail page's small links under its title (Edit, Brainrot page), 2026-09-14 */
.fab-admin-head__link { display: inline-flex; align-items: center; gap: .35rem; font-weight: 700; color: var(--bs-secondary-color); text-decoration: none; }
.fab-admin-head__link:hover, .fab-admin-head__link:focus-visible { color: var(--bs-primary); text-decoration: underline; }
.fab-admin-tabs { min-width: 0; flex: 1 1 100%; max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; padding: .15rem; margin: -.15rem; }
@media (min-width: 992px) { .fab-admin-tabs { flex: 0 0 auto; } }
.fab-admin-tabs::-webkit-scrollbar { display: none; }
.fab-admin-tabs .btn { white-space: nowrap; }
/* Eleven tabs and the title share one row on a laptop. "Brainrots" is a longer word than "Species" was (story 3157),
   so below 1400px the tabs give up a little padding and the title keeps its + button beside it. */
@media (min-width: 1200px) and (max-width: 1399.98px) { .fab-admin-tabs .btn { padding-inline: .375rem; } }
.fab-admin-table { --bs-table-bg: transparent; --bs-table-color: inherit; --bs-table-hover-bg: rgba(var(--bs-primary-rgb), .07); --bs-table-hover-color: inherit; --bs-table-border-color: var(--bs-border-color); }
.fab-admin-table th { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--bs-secondary-color); font-weight: 700; }
.fab-admin-table td, .fab-admin-table th { padding: .7rem .9rem; }
.fab-thumb { display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: .9rem; object-fit: cover; border: 2px solid var(--fab-outline); background: var(--bs-tertiary-bg); font-weight: 700; }
.fab-admin-files .bi { font-size: 1.1rem; margin-right: .35rem; }
/* Admin tables stack on phones (story 3081): below md the header row hides and every <tr> becomes a grid card, cells placed by
   their fab-cell-* class, small labels drawn from data-label. Same markup as the wide table, so a column change happens once. */
@media (max-width: 767.98px) {
  .fab-admin-table--stack, .fab-admin-table--stack tbody { display: block; }
  .fab-admin-table--stack thead { display: none; }
  .fab-admin-table--stack tr { display: grid; column-gap: .75rem; row-gap: .4rem; align-items: center; padding: .85rem .35rem; border-bottom: 1px solid var(--bs-table-border-color); }
  .fab-admin-table--stack tbody tr:last-child { border-bottom: 0; }
  .fab-admin-table--stack.table-hover tbody tr:hover { background: var(--bs-table-hover-bg); }
  .fab-admin-table--stack td { display: block; padding: 0; border: 0; box-shadow: none; min-width: 0; white-space: normal !important; text-align: left !important; }
  .fab-admin-table--stack td[data-label]::before { content: attr(data-label); display: inline-block; margin-right: .4rem; font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; font-weight: 700; color: var(--bs-secondary-color); }
  .fab-admin-table--stack .fab-cell-actions { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: flex-end; }
  /* the species roster: thumb down the left, then name · rarity + status · rig · clips · files + buttons */
  .fab-admin-table--species tr { grid-template-columns: auto minmax(0, 1fr) auto; }
  .fab-admin-table--species .fab-cell-thumb { grid-column: 1; grid-row: 1 / span 5; align-self: start; width: auto !important; }
  .fab-admin-table--species .fab-cell-name { grid-column: 2 / -1; grid-row: 1; }
  .fab-admin-table--species .fab-cell-rarity { grid-column: 2; grid-row: 2; }
  .fab-admin-table--species .fab-cell-status { grid-column: 3; grid-row: 2; justify-self: end; }
  .fab-admin-table--species .fab-cell-rig { grid-column: 2 / -1; grid-row: 3; }
  .fab-admin-table--species .fab-cell-clips { grid-column: 2 / -1; grid-row: 4; }
  .fab-admin-table--species .fab-cell-files { grid-column: 2; grid-row: 5; }
  .fab-admin-table--species .fab-cell-actions { grid-column: 3; grid-row: 5; }
  /* the accounts list: email + badge with Open beside it, the players, then last sign-in and joined on one line */
  .fab-admin-table--accounts tr { grid-template-columns: minmax(0, 1fr) auto; }
  .fab-admin-table--accounts .fab-cell-account { grid-column: 1; grid-row: 1; }
  .fab-admin-table--accounts .fab-cell-actions { grid-column: 2; grid-row: 1; align-self: start; }
  .fab-admin-table--accounts .fab-cell-players { grid-column: 1 / -1; grid-row: 2; }
  .fab-admin-table--accounts .fab-cell-seen { grid-column: 1; grid-row: 3; }
  .fab-admin-table--accounts .fab-cell-joined { grid-column: 2; grid-row: 3; justify-self: end; }
}
/* Admin economics editor (story 3044): the tier colour preview and the history list */
.fab-swatch { display: inline-flex; padding: .5rem .75rem; border-radius: .75rem; background: var(--bs-body-bg); border: 1px solid var(--bs-border-color); }
.fab-swatch--dark { background: #1a1033; border-color: rgba(255, 255, 255, .12); }
.fab-history li:first-child { border-top: 0 !important; }

.fab-thumb--lg { width: 4.5rem; height: 4.5rem; border-radius: 1.1rem; font-size: 1.6rem; }
.fab-thumb--portrait { object-fit: contain; background: rgba(var(--bs-primary-rgb), .18); } /* the cut-out on the game's ground */
.fab-help { color: var(--bs-secondary-color); cursor: help; font-size: .85em; vertical-align: middle; border-radius: .3rem; }
.fab-help:hover, .fab-help:focus-visible { color: var(--bs-primary); outline: none; }
.fab-help:focus-visible { box-shadow: 0 0 0 3px rgba(var(--bs-info-rgb), .5); }
.fab-tooltip-wide { --bs-tooltip-max-width: 19rem; text-align: left; font-weight: 500; }
.fab-run { margin: 0; }

/* ---- Multiview card + review banner ---- */
.fab-multiview__view { display: flex; flex-direction: column; }
.fab-multiview__img { width: 100%; aspect-ratio: 1 / 1; object-fit: contain; border-radius: .9rem; border: 2px solid var(--fab-outline); background: #fff; }
.fab-review-banner { padding: .85rem 1rem; border-radius: var(--bs-border-radius-lg); border: 3px solid rgba(var(--bs-warning-rgb), .8); background: rgba(var(--bs-warning-rgb), .12); }
.fab-review-banner .bi { color: var(--bs-warning); font-size: 1.3rem; }

/* ---- Drop zone (reference image upload) ---- */
.fab-dropzone-form { margin: 0; }
.fab-dropzone {
  display: flex; flex-direction: column; align-items: center; gap: .25rem; text-align: center;
  padding: 1.75rem 1rem; cursor: pointer; border-radius: var(--bs-border-radius-lg);
  border: 3px dashed rgba(var(--bs-primary-rgb), .55); background: rgba(var(--bs-primary-rgb), .06);
  transition: background-color .15s ease, border-color .15s ease, transform .15s ease;
}
.fab-dropzone:hover, .fab-dropzone:focus-visible { background: rgba(var(--bs-primary-rgb), .12); border-color: var(--bs-primary); outline: none; }
.fab-dropzone:focus-visible { box-shadow: 0 0 0 3px rgba(var(--bs-info-rgb), .5); }
.fab-dropzone.is-dragover { background: rgba(var(--bs-primary-rgb), .22); border-color: var(--bs-primary); border-style: solid; transform: scale(1.01); }
.fab-dropzone.is-uploading, .fab-dropzone.is-disabled { opacity: .55; pointer-events: none; }
.fab-dropzone__icon { font-size: 2.4rem; line-height: 1; color: var(--bs-primary); }
.fab-dropzone__title { font-weight: 700; }
.fab-dropzone--compact { flex-direction: row; text-align: left; gap: .8rem; padding: .8rem 1rem; }
.fab-dropzone--compact .fab-dropzone__icon { font-size: 1.6rem; }
.fab-dropzone--compact .fab-dropzone__title { font-size: .95rem; }
.fab-file { display: inline-block; cursor: help; border-radius: .4rem; }
.fab-file:focus-visible { outline: 3px solid var(--fab-ice); outline-offset: 2px; }

/* ---- Pagination (Ui::PagerComponent, story 3080): separate rounded pills, soft primary, solid when active; they wrap on a narrow card ---- */
.fab-pager .pagination {
  gap: .4rem; flex-wrap: wrap; justify-content: flex-end;
  --bs-pagination-padding-x: .95rem; --bs-pagination-padding-y: .55rem; --bs-pagination-font-size: .95rem;
  --bs-pagination-border-width: 3px; --bs-pagination-border-color: transparent; --bs-pagination-border-radius: .85rem;
  --bs-pagination-bg: rgba(var(--bs-primary-rgb), .14); --bs-pagination-color: #7A1FE0;
  --bs-pagination-hover-bg: rgba(var(--bs-primary-rgb), .26); --bs-pagination-hover-color: #7A1FE0; --bs-pagination-hover-border-color: transparent;
  --bs-pagination-focus-bg: rgba(var(--bs-primary-rgb), .3); --bs-pagination-focus-color: #7A1FE0; --bs-pagination-focus-box-shadow: 0 0 0 3px rgba(var(--bs-info-rgb), .55);
  --bs-pagination-active-bg: var(--bs-primary); --bs-pagination-active-color: #fff; --bs-pagination-active-border-color: var(--fab-outline);
  --bs-pagination-disabled-bg: rgba(var(--bs-primary-rgb), .07); --bs-pagination-disabled-color: rgba(122, 31, 224, .45); --bs-pagination-disabled-border-color: transparent;
}
[data-bs-theme=dark] .fab-pager .pagination { --bs-pagination-color: var(--bs-primary); --bs-pagination-hover-color: var(--bs-primary); --bs-pagination-focus-color: var(--bs-primary); --bs-pagination-disabled-color: rgba(var(--bs-primary-rgb), .45); }
.fab-pager .pagination .page-item .page-link { border-radius: var(--bs-pagination-border-radius); min-width: 2.75rem; text-align: center; font-weight: 700; margin-left: 0; line-height: 1.2; }
.fab-pager .pagination .page-item.active .page-link { box-shadow: 0 4px 12px rgba(var(--bs-primary-rgb), .35); }
.fab-pager__of .page-link { min-width: auto; padding-inline: .8rem; white-space: nowrap; }

.tooltip { --bs-tooltip-bg: var(--fab-ink); --bs-tooltip-color: #F4EEFF; --bs-tooltip-border-radius: .6rem; --bs-tooltip-font-size: .8rem; --bs-tooltip-padding-x: .7rem; --bs-tooltip-padding-y: .45rem; --bs-tooltip-opacity: 1; font-weight: 600; }
[data-bs-theme=dark] .tooltip { --bs-tooltip-bg: #F4EEFF; --bs-tooltip-color: var(--fab-ink); }

/* ---- Copy buttons + command list (Ui::CopyButton, species Pipeline card) ---- */
.fab-copy { flex: none; --bs-btn-padding-x: .6rem; }
.fab-copy.is-copied { --soft-rgb: var(--bs-success-rgb); --soft-color: #1F8F12; }
[data-bs-theme=dark] .fab-copy.is-copied { --soft-color: var(--bs-success); }
.fab-copy--inline { min-height: 0; --bs-btn-padding-y: .1rem; --bs-btn-padding-x: .4rem; --bs-btn-font-size: .8rem; }
.fab-commands { --bs-list-group-bg: transparent; --bs-list-group-color: inherit; --bs-list-group-border-color: var(--bs-border-color); --bs-list-group-item-padding-x: 0; --bs-list-group-item-padding-y: .6rem; }
.fab-commands code { font-size: .78rem; color: var(--bs-body-color); opacity: .8; }

/* ---- Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .btn-fab:active { transform: none; }
  .fab-topbar__btn:active { transform: none; }
  .fab-toast__progress { animation: none; display: none; }
  .modal-backdrop { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .need-critical { animation: none; }
  .progress-bar { transition: none; }
}

/* ---- Badges (Nick, 2026-09-18) ----
   One medal, every family the same shape: a disc in the family's two colours with a ring, a shine across the top and a
   level pip once a family has more than one. That is what makes "the coin ones" read as a set and a legendary read as
   its tier. A locked medal is the same medal with the colour drained, a padlock and the rule where the date goes. Pure
   CSS and a Bootstrap glyph, so a new badge needs no artwork. */
.fab-medals { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr)); gap: 1rem .75rem; }
.fab-medal { display: flex; flex-direction: column; align-items: center; text-align: center; gap: .35rem; }
.fab-medal__disc {
  position: relative; display: grid; place-items: center; width: 4rem; height: 4rem; border-radius: 50%;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, .55), rgba(255, 255, 255, 0) 45%),
              linear-gradient(150deg, var(--medal), var(--medal-2));
  border: 3px solid var(--fab-outline); color: var(--fab-ink); font-size: 1.5rem;
  box-shadow: 0 6px 14px rgba(18, 6, 58, .35), inset 0 -3px 6px rgba(0, 0, 0, .18);
}
/* the notched rim: a ring of light around the disc, which reads as metal rather than a flat circle */
.fab-medal__disc::before {
  content: ""; position: absolute; inset: -7px; border-radius: 50%; z-index: -1;
  background: repeating-conic-gradient(from 0deg, rgba(var(--bs-body-color-rgb), .35) 0 6deg, transparent 6deg 12deg);
}
.fab-medal__level {
  position: absolute; right: -.15rem; bottom: -.15rem; min-width: 1.35rem; height: 1.35rem; padding: 0 .25rem;
  display: grid; place-items: center; border-radius: 999px; border: 2px solid var(--fab-outline);
  background: var(--bs-body-bg); color: var(--bs-body-color); font-size: .7rem; font-weight: 800;
}
.fab-medal__name { font-size: .8rem; font-weight: 800; line-height: 1.2; }
.fab-medal__caption { font-size: .7rem; line-height: 1.25; color: var(--bs-secondary-color); }
.fab-medal.is-locked .fab-medal__disc { background: rgba(var(--bs-body-color-rgb), .12); border-style: dashed; border-color: rgba(var(--bs-body-color-rgb), .3); color: var(--bs-secondary-color); box-shadow: none; }
.fab-medal.is-locked .fab-medal__disc::before { opacity: .25; }
.fab-medal.is-locked .fab-medal__name { color: var(--bs-secondary-color); }
/* An earned medal is never quite still (Nick, 2026-09-18): its colours drift slowly across the disc, as if the light in
   the room were moving, and every so often a shine crosses it. Both stop dead for anyone who asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
  .fab-medal.is-earned .fab-medal__disc { background-size: 220% 220%, 220% 220%; animation: fab-medal-drift 9s ease-in-out infinite alternate; }
  .fab-medal.is-earned .fab-medal__disc::after {
    content: ""; position: absolute; inset: 0; border-radius: 50%;
    background: linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, .55) 46%, transparent 54%);
    animation: fab-medal-shine 6s ease-in-out infinite;
  }
  /* the families take turns, so a shelf of them does not blink in unison */
  .fab-medal:nth-child(3n) .fab-medal__disc, .fab-medal:nth-child(3n) .fab-medal__disc::after { animation-delay: -2.4s; }
  .fab-medal:nth-child(3n + 1) .fab-medal__disc, .fab-medal:nth-child(3n + 1) .fab-medal__disc::after { animation-delay: -4.8s; }
}
@keyframes fab-medal-shine { 0%, 70%, 100% { opacity: 0; } 80% { opacity: 1; } }
@keyframes fab-medal-drift { from { background-position: 0% 30%, 20% 0%; } to { background-position: 100% 70%, 80% 100%; } }

/* the badges card in the Index's narrow column: what you have, small, wrapping to fit whatever width it is given */
.fab-medal--sm .fab-medal__disc { width: 3rem; height: 3rem; font-size: 1.1rem; border-width: 2px; }
.fab-medal--sm .fab-medal__level { min-width: 1.1rem; height: 1.1rem; font-size: .6rem; }
.fab-medal--sm .fab-medal__name { font-size: .7rem; }
.fab-medal--sm .fab-medal__caption { font-size: .65rem; }
.fab-medals--column { grid-template-columns: repeat(auto-fill, minmax(4.75rem, 1fr)); gap: .9rem .5rem; }


/* The first-run safety cards (story 3133): a sheet read to the end. No close button, a static backdrop, the drag locked. */
.fab-safety .btn-close { display: none; }
.fab-safety.offcanvas.offcanvas-bottom { max-height: 92vh; }
.fab-safety__list { padding-left: 1.2rem; margin: 0; }
.fab-safety__list li { margin-bottom: .55rem; }
.fab-safety__list--kid { list-style: none; padding-left: 0; font-size: 1.05rem; font-weight: 600; }
.fab-safety__list--kid li { display: flex; gap: .6rem; align-items: flex-start; margin-bottom: .8rem; }
.fab-safety__emoji { font-size: 1.5rem; line-height: 1.2; flex: 0 0 auto; }
.fab-safety__dots { display: flex; gap: .4rem; }
.fab-safety__dot { width: .5rem; height: .5rem; border-radius: 999px; background: rgba(var(--bs-body-color-rgb), .25); }
.fab-safety__dot.is-on { background: var(--bs-primary); }

/* The QR on the Devices card (story 3076) */
.fab-device-code__scan { width: 9rem; height: 9rem; padding: .4rem; border-radius: .75rem; background: #fff; }
.fab-device-code__scan svg { display: block; width: 100%; height: 100%; }

/* Base skin swatches in the Shop (story 3189): the skin's palette as a little building front. */
.fab-skin-swatch { background: linear-gradient(to bottom, var(--skin-wall) 0 62%, var(--skin-carpet) 62% 78%, var(--skin-wall) 78%); box-shadow: inset 0 0 0 3px var(--skin-neon), inset 12px 0 0 var(--skin-pillar), inset -12px 0 0 var(--skin-pillar); }

/* Quests (story 3231): the trophy in the top bar, the rows on the page, the strip above the rooms. */
.fab-topbar__btn--trophy { position: relative; color: var(--fab-gold); }
.fab-topbar__dot { position: absolute; top: .35rem; right: .35rem; width: .55rem; height: .55rem; border-radius: 50%; background: var(--fab-gold); box-shadow: 0 0 0 2px var(--bs-body-bg); }
.fab-quest-list { display: grid; gap: .75rem; }
.fab-quest { display: flex; gap: .85rem; align-items: flex-start; padding: .85rem 1rem; border-radius: 1rem; background: var(--fab-glass-fill, rgba(var(--bs-body-color-rgb), .04)); border: 1px solid var(--fab-outline, rgba(var(--bs-body-color-rgb), .08)); }
.fab-quest__icon { flex: 0 0 2.75rem; width: 2.75rem; height: 2.75rem; display: grid; place-items: center; border-radius: .9rem; font-size: 1.35rem; color: #fff; background: linear-gradient(145deg, var(--fab-quest-from), var(--fab-quest-to)); }
.fab-quest__icon .fab-medal { width: 2.75rem; height: 2.75rem; }
.fab-quest__body { flex: 1 1 auto; min-width: 0; }
.fab-quest__name { font-weight: 700; }
.fab-quest__bar { height: .5rem; margin: .4rem 0; border-radius: 1rem; overflow: hidden; background: rgba(var(--bs-body-color-rgb), .1); }
.fab-quest__bar span { display: block; height: 100%; border-radius: 1rem; background: linear-gradient(90deg, var(--fab-quest-from), var(--fab-quest-to)); transition: width .4s ease; }
.fab-quest--done { border-color: color-mix(in srgb, var(--fab-gold) 55%, transparent); }
.fab-quest--done .fab-quest__prize { color: var(--fab-gold); }
.fab-quest__week { font-weight: 700; font-size: 1.1rem; }
.fab-quest__icon--medal { background: none; width: auto; height: auto; flex-basis: 3.4rem; overflow: visible; padding-top: .1rem; }
.fab-quest__icon--medal .fab-medal__name, .fab-quest__icon--medal .fab-medal__caption { display: none; } /* the row names and dates it */
.fab-quest-sheet .fab-quest { padding: .7rem .8rem; }

/* The trophy page's tabs and the global boards (story 3235). */
.fab-trophy-tabs { display: inline-flex; gap: .25rem; padding: .25rem; border-radius: 2rem; background: rgba(var(--bs-body-color-rgb), .07); }
.fab-trophy-tabs__tab { display: inline-flex; align-items: center; gap: .4rem; padding: .45rem 1rem; border-radius: 2rem; min-height: 2.5rem; font-weight: 700; color: inherit; text-decoration: none; }
.fab-trophy-tabs__tab.is-active { background: var(--fab-btn-gradient, linear-gradient(135deg, var(--fab-purple), var(--fab-pink))); color: #fff; }
.fab-lb__boards { display: flex; gap: .4rem; overflow-x: auto; padding-bottom: .25rem; scrollbar-width: none; }
.fab-lb__boards::-webkit-scrollbar { display: none; }
.fab-lb__board { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .35rem; padding: .35rem .8rem; border-radius: 1rem; min-height: 2.25rem; font-size: .9rem; color: inherit; text-decoration: none; background: rgba(var(--bs-body-color-rgb), .07); white-space: nowrap; }
.fab-lb__board.is-active { color: #fff; background: linear-gradient(135deg, var(--fab-gold), #FF7A1A); }
.fab-lb { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.fab-lb__row { display: grid; grid-template-columns: 3rem auto minmax(0, 1fr) auto; align-items: center; gap: .6rem; padding: .5rem .75rem; border-radius: .9rem; background: rgba(var(--bs-body-color-rgb), .05); min-height: 2.75rem; }
.fab-lb__row.is-top { background: color-mix(in srgb, var(--fab-gold) 14%, transparent); }
.fab-lb__row.is-you { outline: 2px solid var(--fab-ice); outline-offset: -2px; }
.fab-lb__row.is-circle .fab-lb__name { color: var(--fab-ice); }
.fab-lb__row .fab-avatar { width: 2.25rem; height: 2.25rem; font-size: 1.1rem; flex: 0 0 auto; }
.fab-lb__rank { font-weight: 800; opacity: .8; }
.fab-lb__name { font-weight: 600; display: flex; align-items: baseline; gap: .4rem; min-width: 0; }
.fab-lb__link { color: inherit; text-decoration: none; }
.fab-lb__link:hover { text-decoration: underline; }
.fab-lb__tag { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .7; }
.fab-lb__value { font-weight: 700; white-space: nowrap; }
.fab-lb__unit { font-weight: 400; font-size: .8rem; opacity: .7; }
.fab-lb__everyone { color: inherit; text-decoration: none; font-weight: 600; }
.fab-lb__crown { font-size: .95rem; }
.fab-lb__podium { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem 1rem; font-weight: 600; }
.fab-lb__podium-rank { opacity: .7; font-weight: 800; }
.fab-profile__crown { color: var(--fab-gold); font-weight: 700; }

