/* ---------- top bar ---------- */
.topbar {
  display: flex; align-items: center; gap: 20px;
  padding: 14px 26px 22px;
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
  background: var(--topbar-bg);
  backdrop-filter: blur(10px);
}

/* Understated app-wide loading indicator: a thin accent bar riding the bottom
   edge of the topbar. A small sliver slides across (indeterminate — we don't
   know the load duration). Hidden by default; JS adds .is-loading only for
   loads slow enough to notice, and never more than one at a time. */
.loading-bar {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  overflow: hidden; pointer-events: none;
  opacity: 0; transition: opacity 120ms ease;
}
.loading-bar.is-loading { opacity: 1; }
.loading-bar::before {
  content: ""; position: absolute; top: 0; height: 100%;
  width: 34%; left: -34%;
  background: var(--coral, #ff5d8f);
  border-radius: 2px;
}
.loading-bar.is-loading::before { animation: loading-slide 1.05s ease-in-out infinite; }
@keyframes loading-slide {
  0%   { left: -34%; }
  60%  { left: 100%; }
  100% { left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  /* No traveling sliver — a static, gently pulsing full-width bar instead. */
  .loading-bar::before { width: 100%; left: 0; animation: none !important; }
  .loading-bar.is-loading::before { animation: loading-pulse 1.2s ease-in-out infinite !important; }
  @keyframes loading-pulse { 0%,100% { opacity: 0.35; } 50% { opacity: 0.9; } }
}

/* While a view is loading, hold the page tall so the not-yet-populated sections
   don't collapse and float the footer up under the topbar. The min-height is
   released the instant the load settles (see _hide/_showLoadingBar). Views sit
   directly in <body> with the footer as the last sibling, so reserving height
   on the body keeps the footer parked at the bottom of the viewport. */
body.app-loading { min-height: 100vh; }
/* Don't show the footer over empty content — it reappears once sections paint. */
body.app-loading .site-footer { visibility: hidden; }
/* Browse (accent) leads the right cluster: it takes the auto margin so it +
   the user nav sit together on the right, Browse next to the greeting. */
.nav-right-lead { margin-left: auto; }
/* logged-in user + logout, sitting just after Browse on the right */
/* order:1 keeps #userNav visually after #topBrowseBtn (order:0 default) on
   desktop, even though DOM-wise #userNav now sits inside .nav-more-shell
   (a `display:contents` box) *before* #topBrowseBtn -- required so phone
   width can lay the two out as a single flex column inside the shell
   (see .nav-more-shell in the nav-more-overflow section). */
.user-nav { display: inline-flex; align-items: center; gap: 8px; order: 1; }
.user-greeting { font-size: 14px; color: var(--muted); white-space: nowrap; }
/* public_preview (opt-in): the anonymous visitor's way back to the sign-in
   form, styled like the rest of the ghost nav buttons (order matches #userNav
   so it sits in the same nav slot when a preview visitor sees it instead). */
.preview-login-link { order: 1; }
/* LOCAL DEV ONLY: simulated-role switcher (rendered only under the
   RINGSIDE_NO_AUTH bypass). Intentionally understated/muted so it reads as a
   dev affordance, not a normal control. */
.dev-role-switcher {
  display: inline-flex; align-items: center;
  font-size: 12px; color: var(--muted); white-space: nowrap;
  opacity: 0.72;
}
.dev-role-switcher:hover { opacity: 1; }
/* Compact dashed control -- the dashed border moves onto the select itself now
   that the "dev role" label is gone, so the whole thing is just the dropdown.
   width:auto + the short option labels keep the collapsed width tight (native
   selects size to the selected option's text). */
.dev-role-switcher select {
  font-size: 12px; color: inherit; background: transparent;
  border: 1px dashed var(--border, #ccc); border-radius: 8px;
  cursor: pointer; padding: 3px 6px; width: auto; max-width: 128px;
}
.brand {
  display: flex; align-items: center; gap: 11px;
  background: none; border: none; cursor: pointer; padding: 0; flex: 0 0 auto;
}
.brand-star { width: 64px; height: 64px; color: var(--coral); transition: transform 0.25s cubic-bezier(.5,1.6,.5,1); }
.brand:hover .brand-star { transform: rotate(-12deg) scale(1.08); }

/* Shoko critter logo: a pixel-art creature that replaces the star mascot and
   swaps to a growl frame on hover. Only rendered under [data-theme="shoko"];
   .brand-critter is display:none for every other theme (the .brand-star above
   is the logo there). The two <img> frames are stacked absolutely so they
   crossfade in place, and both carry the same zoom+tilt the star uses so the
   hover motion is identical across themes. Sized to match .brand-star's box. */
.brand-critter { display: none; }
[data-theme="shoko"] .brand-star { display: none; }
[data-theme="shoko"] .brand-critter {
  display: block; position: relative; width: 64px; height: 64px; flex: 0 0 auto;
}
.brand-critter img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* crisp pixel edges when the 72x76 art is scaled up to 64px */
  image-rendering: pixelated;
  transition: opacity 0.12s ease, transform 0.25s cubic-bezier(.5,1.6,.5,1);
}
.brand-critter .critter-growl { opacity: 0; }
.brand:hover .brand-critter .critter-idle { opacity: 0; }
.brand:hover .brand-critter .critter-growl { opacity: 1; }
.brand:hover .brand-critter img { transform: rotate(-12deg) scale(1.08); }
.brand-text { display: flex; flex-direction: column; line-height: 1; align-items: flex-start; }
.brand-mark { font-family: var(--title-font, 'Fredoka'), "Noto Sans JP", sans-serif; font-weight: 600; font-size: 38px; color: var(--text); letter-spacing: 0.3px; }
.brand-sub { font-size: 10px; color: var(--muted); letter-spacing: 1.4px; text-transform: uppercase; margin-top: 3px; }

.search-wrap { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; }
.search-row { position: relative; display: flex; gap: 8px; align-items: stretch; }
#search {
  flex: 1; min-width: 0; padding: 11px 18px;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font-size: 14px; outline: none; font-family: inherit;
}
#search:focus { border-color: var(--coral); box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }

/* Filters ▾ button + menu */
.filters-wrap { position: relative; flex: 0 0 auto; }
.filters-btn { height: 100%; padding: 0 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--muted); font-family: inherit; font-size: 12.5px;
  font-weight: 600; cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px; }
.filters-btn:hover { border-color: var(--lavender); color: var(--lavender); }
/* icon-only on phone (see trailing @media block); text label carries it
   above that, so the glyph stays out of the way until space is tight */
.filters-icon { display: none; }
.filters-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; min-width: 220px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; }
.filter-opt { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: none; border: none; color: var(--text); font-family: inherit; font-size: 13.5px;
  padding: 9px 15px; cursor: pointer; text-align: left; }
.filter-opt:hover { background: var(--surface); color: var(--mint); }
.filter-opt code { color: var(--lavender); font-size: 11.5px; background: var(--bg);
  padding: 1px 6px; border-radius: 6px; }

/* nav autocomplete dropdown */
.nav-suggest { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 25;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); }
.nav-suggest-opt { display: flex; align-items: center; gap: 8px; padding: 9px 16px; font-size: 14px;
  cursor: pointer; }
.nav-suggest-opt:hover, .nav-suggest-opt.active { background: var(--surface); color: var(--coral); }
.nav-suggest-opt .ns-kind { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--lavender); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; }
.nav-suggest-opt .ns-sub { margin-left: auto; color: var(--muted); font-size: 12px; }

/* refreshable suggestion chips (replaces the old hint text)
   Absolutely positioned so they hang *below* the search row without adding
   height to .search-wrap — otherwise the taller two-row column throws the
   topbar's vertical centering off and the input sits above the buttons. */
/* one line only: long random chips used to wrap to a 2nd row and overlap the
   content below (the bar is absolutely positioned). nowrap + clip keeps it a
   single tidy row; the shuffle button re-rolls if a name got cut. */
.search-chips { position: absolute; top: 100%; left: 12px; right: 0;
  display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; margin: 6px 0 0;
  overflow: hidden; }
.search-chips .search-chip { flex: 0 0 auto; }
.search-chips .chips-shuffle { flex: 0 0 auto; margin-right: auto; }
.chips-try { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; }
.search-chip { font-family: inherit; font-size: 11.5px; padding: 3px 10px; border-radius: 999px;
  background: var(--surface-2); color: var(--lavender); border: 1px solid var(--border); cursor: pointer; }
.search-chip:hover { border-color: var(--lavender); color: var(--text); }
.chips-shuffle { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 13px;
  padding: 0 4px; }
.chips-shuffle:hover { color: var(--coral); }

/* ---------- buttons ---------- */
.btn-accent, .btn-ghost {
  font-family: inherit; font-size: 13.5px; font-weight: 600;
  padding: 9px 18px; border-radius: 999px; cursor: pointer;
  border: 1px solid transparent; text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.15s ease, background 0.15s;
  white-space: nowrap;
  display: inline-flex; align-items: center; gap: 8px;
}
/* icon-only treatment kicks in on phone (see trailing @media block); above
   that these render as plain text buttons, same as always */
.nav-more-icon { display: none; }
/* color-mix() derives a translucent --coral shadow with no separate rgb-triplet
   variable to keep in sync per theme — was hardcoded to the original pink
   (rgba(255,93,143,...)), so every theme's accent button glowed pink
   regardless of its actual accent color. */
.btn-accent { background: var(--coral); color: var(--ink); box-shadow: 0 4px 0 color-mix(in srgb, var(--coral) 25%, transparent); }
.btn-accent:hover { transform: translateY(-1px); box-shadow: 0 6px 14px color-mix(in srgb, var(--coral) 35%, transparent); }
.btn-accent:active { transform: translateY(1px); box-shadow: 0 2px 0 color-mix(in srgb, var(--coral) 25%, transparent); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn-ghost:hover { border-color: var(--coral); color: var(--coral); }

/* ---------- theme picker ---------- */
.theme-wrap { position: relative; flex: 0 0 auto; }
/* shared circular icon-button look: theme picker + slimmed topbar actions */
.theme-btn, .btn-icon { display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: 0 0 auto; padding: 0;
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
  color: var(--lavender); cursor: pointer; transition: color .15s, border-color .15s, transform .12s; }
.theme-btn:hover, .btn-icon:hover { color: var(--coral); border-color: var(--coral); transform: translateY(-1px); }
.theme-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; min-width: 216px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; padding: 5px; }
.theme-opt { display: flex; align-items: center; gap: 11px; background: none; border: none;
  color: var(--text); font-family: inherit; font-size: 13.5px; font-weight: 500;
  padding: 9px 11px; cursor: pointer; text-align: left; border-radius: 10px; }
.theme-opt:hover { background: var(--surface); }
.theme-opt.active { background: var(--surface); }
.theme-opt.active .theme-opt-name { color: var(--coral); }
.theme-swatch { flex: 0 0 auto; display: flex; border-radius: 999px; overflow: hidden;
  width: 46px; height: 18px; border: 1px solid var(--border); }
.theme-swatch span { flex: 1; }
.theme-opt-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.theme-opt-name { font-weight: 600; }
.theme-opt-sub { display: none; font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.theme-opt .theme-check { margin-left: auto; color: var(--coral); font-size: 13px; opacity: 0; }
.theme-opt.active .theme-check { opacity: 1; }

/* ---------- nav overflow ("more") menu ----------
   Priority+ collapse: brand/search/Browse always stay visible in .topbar;
   below --bp-phone (480px, see convention note near :root) the People/Review/
   Theme/Admin-tools controls move into a single dropdown behind one toggle.
   .nav-more-wrap holds those controls unchanged in the DOM at all widths --
   above the breakpoint it's `display:contents` (invisible as a box; children
   render as if they were direct .topbar children, exactly like before this
   feature existed) so no layout risk above phone width. Below the breakpoint
   it becomes the dropdown panel itself. */
.nav-more-btn { display: none; }
.nav-more-wrap { display: contents; }
.nav-more-shell { display: contents; }
@media (max-width: 480px) {
  .nav-more-btn { display: flex; }
  /* .nav-more-shell (not .nav-more-wrap) is the one true positioned dropdown
     box on phone -- .nav-more-wrap and #userNav become plain flex children
     inside it, so they always stack contiguously with no manual height/gap
     math regardless of how tall the Theme/Admin accordion content gets. */
  .nav-more-shell.open {
    display: flex; flex-direction: column; position: absolute;
    top: calc(100% + 8px); right: 26px; left: 26px; z-index: 30;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: 0 12px 28px rgba(0,0,0,0.45);
    overflow: hidden;
  }
  .nav-more-wrap {
    display: none; flex-direction: column; gap: 6px; padding: 8px;
  }
  .nav-more-shell.open .nav-more-wrap { display: flex; }
  #userNav.hidden { display: none; } /* keep the pre-/api/me hidden state working inside the shell too */
  .nav-more-shell.open #userNav {
    display: flex; justify-content: space-between; align-items: center;
    padding: 10px 11px; border-top: 1px solid var(--border);
  }
  /* Theme/Admin tile layout, accordion behavior, and menu visibility are
     defined together in the trailing "phone-width topbar overrides" section
     at the end of this file (grouped with the row-order/grid rules they
     depend on) rather than here. */
}

/* ---------- admin overflow menu ---------- */
.admin-wrap { position: relative; flex: 0 0 auto; }
.admin-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; min-width: 210px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; padding: 5px; }
.admin-item { display: flex; align-items: center; gap: 10px; background: none; border: none;
  color: var(--text); font-family: inherit; font-size: 13.5px; font-weight: 500;
  padding: 9px 11px; cursor: pointer; text-align: left; border-radius: 10px; width: 100%; }
.admin-item:hover { background: var(--surface); color: var(--coral); }
.admin-sep { height: 1px; background: var(--border); margin: 5px 4px; }
.ai-label { flex: 1; }
.ai-badge { flex: 0 0 auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: var(--coral); color: var(--ink); font-size: 10.5px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; }
.ai-engine { flex: 0 0 auto; font-size: 10.5px; font-weight: 700; letter-spacing: 0.3px;
  padding: 2px 9px; border-radius: 999px; text-transform: lowercase; }
/* provider badge is now a menu row: drop the pill frame, keep the color accent on
   its engine chip only. */
.provider-badge { font-size: inherit; padding: 9px 11px; border-radius: 10px; font-weight: 500;
  border: none; cursor: pointer; letter-spacing: 0; background: none; color: var(--text); }
.provider-badge.local .ai-engine { color: var(--mint); border: 1px solid color-mix(in srgb, var(--mint) 40%, transparent); background: color-mix(in srgb, var(--mint) 10%, transparent); }
.provider-badge.api   .ai-engine { color: var(--butter); border: 1px solid color-mix(in srgb, var(--butter) 40%, transparent); background: color-mix(in srgb, var(--butter) 10%, transparent); }
