:root {
  --cream: #f3eee4;
  --ink: #14141a;
  --blue: #0053db;
  --blue-deep: #003ea8;
  --yellow: #fed01b;
  --orange: #ff7a3d;
  --green: #22c55e;
  --purple: #a855f7;
  /* Extra accent colors for tag icons/pills -- deeper and more saturated
     than the pastel tile backgrounds above, so a small icon stays legible
     on a light chip background instead of washing out. */
  --gold: #d69e00;
  --teal: #0d9488;
  --pink: #db2777;
  --brown: #b45309;
  --rose: #e11d48;
  --bueno-brown: #3d2314;
  --bueno-cream: #f5e6c8;
  --bueno-hazelnut: #e8d5b7;
  --bueno-gold: #c9952a;
  --border-w: 2px;
  --shadow-off: 4px;
  /* Broken-site report text/icon: previously 4 slightly different reds
     scattered across these rules, one of them (var(--red, ...)) referencing
     a variable that was never actually defined. Consolidated to one pair. */
  --red-text: #c0392b;
  --red-text-hover: #a82f2f;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  font-family: "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  color: var(--ink);
  background-color: var(--blue);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.12) 1px, transparent 1px);
  background-size: 24px 24px;
  min-height: 100vh;
  padding: clamp(12px, 3vw, 32px);
}

button {
  font-family: inherit;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration: none;
}

.frame {
  max-width: 1640px;
  margin: 0 auto;
  background: var(--cream);
  border: 3px solid var(--ink);
  border-radius: 40px;
  overflow: hidden;
  box-shadow: 0 0 0 8px rgba(0, 83, 219, 0.25);
  display: flex;
  flex-direction: column;
}

/* Ticker */
.ticker {
  background: var(--ink);
  overflow: hidden;
  white-space: nowrap;
  border-bottom: 3px solid var(--ink);
  touch-action: pan-x;
  cursor: grab;
}

.ticker.ticker--dragging {
  cursor: grabbing;
  user-select: none;
}

.ticker__track {
  display: inline-flex;
  /* No gap here: it would add extra space between the two halves that isn't
     accounted for by the 50% loop-point math below, causing a visible skip
     once per loop. Each half already spaces its own items via its own gap. */
  padding: 0.6rem 0;
  color: var(--cream);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  will-change: transform;
}

.ticker__half {
  display: inline-flex;
  align-items: center;
  gap: 2.5rem;
  margin-right: 2.5rem;
  white-space: nowrap;
}

.ticker__item {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

.ticker__item:hover,
.ticker__item:focus-visible {
  color: var(--yellow);
  text-decoration: underline;
}

.ticker__track .sep {
  color: var(--yellow);
}

/* Header */
.header {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  padding: 1.25rem clamp(1.25rem, 3vw, 2.5rem) 0;
  gap: 0.75rem 1rem;
}

.header::after {
  content: "";
  grid-column: 1 / -1;
  width: 100%;
  height: 8px;
  margin-top: 1.25rem;
  pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='8' viewBox='0 0 48 8'%3E%3Cpath d='M0 4 Q12 1 24 4 T48 4' fill='none' stroke='%2314141a' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") repeat-x center;
  background-size: 48px 8px;
}

.header:has(.search) {
  grid-template-columns: auto 1fr auto;
  grid-template-areas: "brand search actions";
}

.header:has(.search) .brand {
  grid-area: brand;
}

.header:has(.search) .search {
  grid-area: search;
  justify-self: end;
  width: 100%;
  max-width: 360px;
  min-width: 0;
}

.header:has(.search) .header__actions {
  grid-area: actions;
}

.header__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Search field (shared look for header + browse overlay) */
.search-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: #fff;
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.4rem 1rem;
  width: 100%;
}

.search-field__icon {
  display: inline-flex;
  opacity: 0.55;
  flex-shrink: 0;
}

.search-field__icon svg {
  width: 1.05rem;
  height: 1.05rem;
  display: block;
}

.search-field input {
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-weight: 600;
  color: var(--ink);
  width: 100%;
}

.search-field input::placeholder {
  color: rgba(20, 20, 26, 0.45);
  font-weight: 500;
}

/* Header search + live results dropdown */
.search {
  position: relative;
  min-width: 0;
}

.search__results {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 0;
  right: 0;
  background: var(--cream);
  border: 2px solid var(--ink);
  border-radius: 18px;
  box-shadow: 4px 4px 0 0 var(--ink);
  max-height: 360px;
  overflow-y: auto;
  z-index: 60;
  padding: 0.4rem;
}

.search__results[hidden] {
  display: none;
}

.search__result {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  border: none;
  background: none;
  padding: 0.55rem 0.6rem;
  border-radius: 10px;
  font-family: inherit;
}

.search__result:hover,
.search__result:focus-visible {
  background: rgba(20, 20, 26, 0.07);
}

.search__result-icon {
  display: inline-flex;
  flex-shrink: 0;
  margin-top: 0.1rem;
}

.search__result-icon svg {
  width: 1.2rem;
  height: 1.2rem;
  display: block;
  color: var(--tag-c, var(--ink));
}

.search__result-text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.search__result-name {
  font-weight: 700;
  font-size: 0.9rem;
}

.search__result-desc {
  font-size: 0.78rem;
  color: rgba(20, 20, 26, 0.6);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.search__empty {
  margin: 0;
  padding: 0.6rem;
  font-size: 0.85rem;
  color: rgba(20, 20, 26, 0.6);
  text-align: center;
}

.brand {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}

.brand__name {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.brand__tag {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--blue);
}

.btn {
  border: 2px solid var(--ink);
  border-radius: 999px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.btn--pill {
  background: var(--yellow);
  color: var(--ink);
  padding: 0.6rem 1.25rem;
  font-size: 0.9rem;
  box-shadow: var(--shadow-off) var(--shadow-off) 0 0 var(--ink);
}

.btn--ghost {
  background: #fff;
}

.btn--bueno {
  background: linear-gradient(135deg, var(--bueno-cream), var(--bueno-hazelnut));
  color: var(--bueno-brown);
  border-color: var(--bueno-brown);
  box-shadow: var(--shadow-off) var(--shadow-off) 0 0 var(--bueno-brown);
}

.btn--bueno:hover {
  background: linear-gradient(135deg, var(--bueno-hazelnut), var(--bueno-gold));
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 0 var(--bueno-brown);
}

.btn--pill:hover,
.btn--hero:hover,
.tile:hover {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 0 var(--ink);
}

/* Main layout */
.main {
  display: flex;
  gap: clamp(1rem, 3vw, 2rem);
  padding: clamp(1.25rem, 3vw, 2.5rem);
  flex: 1;
}

.hero {
  flex: 1 1 320px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.25rem;
}

.chip {
  display: inline-flex;
  align-self: flex-start;
  background: var(--cream);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.hero__title {
  font-size: clamp(2.1rem, 4.4vw, 3.6rem);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}

.hero__title .hl {
  color: var(--blue);
}

.hero__sub {
  font-size: 1.05rem;
  line-height: 1.55;
  max-width: 34rem;
  color: rgba(20, 20, 26, 0.75);
  margin: 0;
}

.btn--hero {
  background: var(--ink);
  color: var(--cream);
  padding: 1.1rem 1.75rem;
  border-radius: 999px;
  font-size: 1.15rem;
  width: fit-content;
  box-shadow: var(--shadow-off) var(--shadow-off) 0 0 rgba(0,0,0,0.6);
  margin-top: 0.25rem;
}

.btn--hero:hover {
  background: var(--blue);
}

.arrow {
  display: inline-block;
  transition: transform 0.15s ease;
}

.btn--hero:hover .arrow,
.btn--pill:hover .arrow {
  transform: translate(2px, -2px);
}

.dice {
  display: inline-flex;
  transition: transform 0.2s ease;
}

.dice svg {
  width: 1.15em;
  height: 1.15em;
  display: block;
}

.btn--hero:hover .dice,
.btn--pill:hover .dice {
  transform: rotate(-14deg);
}

.bueno {
  display: inline-flex;
  transition: transform 0.2s ease;
}

.bueno svg {
  width: 1.15em;
  height: 1.15em;
  display: block;
}

.btn--bueno:hover .bueno {
  transform: rotate(-8deg) scale(1.05);
}

/* Category tiles */
.tiles {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0.75rem;
  margin-top: 1rem;
}

.tile {
  aspect-ratio: 1 / 1;
  border: 2px solid var(--ink);
  border-radius: 16px;
  background-color: #fff;
  background-image:
    linear-gradient(rgba(20, 20, 26, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(20, 20, 26, 0.06) 1px, transparent 1px);
  background-size: 14px 14px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.35rem;
  padding: 0.7rem;
  box-shadow: 2px 2px 0 0 var(--ink);
  font-weight: 700;
}

.tile__icon {
  font-size: 1.9rem;
  line-height: 1;
}

.tile__icon svg {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.15em;
}

.tile__label {
  font-size: 0.85rem;
}

.tile--games { background-color: #b4c5ff; }
.tile--weird { background-color: var(--yellow); }
.tile--creative { background-color: var(--orange); }
.tile--explore { background-color: var(--green); }
.tile--useful { background-color: #d4b5ff; }
.tile--music { background-color: #ffc8e8; }

/* Recently visited -- compact header dropdown */
.history-drop {
  position: relative;
}

.history-drop__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  font-size: 0.68rem;
  background: var(--ink);
  color: var(--cream);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  margin-left: 0.2rem;
}

.history-drop__count[hidden] {
  display: none;
}

.history-drop__panel {
  position: fixed;
  top: 0;
  left: 0;
  width: min(300px, calc(100vw - 1.5rem));
  background: var(--ink);
  color: var(--cream);
  border: 2px solid var(--ink);
  border-radius: 18px;
  box-shadow: 4px 4px 0 0 var(--ink);
  padding: 0.7rem;
  z-index: 70;
  max-height: min(340px, 52vh);
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  overflow: hidden;
}

.history-drop__panel[hidden] {
  display: none;
}

.history__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-shrink: 0;
}

.history__title {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.history__header-actions,
.history__header-confirm {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-shrink: 0;
}

.history__header-confirm[hidden],
.history__header-actions[hidden] {
  display: none;
}

.history__confirm-text {
  font-size: 0.72rem;
  font-weight: 700;
  opacity: 0.85;
  white-space: nowrap;
}

.history__clear {
  background: none;
  border: none;
  color: var(--cream);
  opacity: 0.6;
  font-size: 0.72rem;
  font-weight: 700;
  text-decoration: underline;
  padding: 0.15rem;
  font-family: inherit;
  cursor: pointer;
}

.history__clear:hover {
  opacity: 1;
}

.history__clear--danger {
  opacity: 0.95;
  color: #ffb4b4;
}

.history__list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  overflow-x: hidden;
  overflow-y: auto;
  min-height: 0;
}

.history__list:empty {
  display: none;
}

.history__row {
  display: flex;
  align-items: stretch;
  gap: 0.2rem;
  min-width: 0;
}

.history__item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex: 1;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  text-align: left;
  border: none;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
  padding: 0.45rem 0.6rem;
  font-family: inherit;
  color: var(--cream);
  transition: background 0.15s ease;
  cursor: pointer;
}

.history__remove {
  flex-shrink: 0;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.65rem;
  height: 1.65rem;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--cream);
  opacity: 0.45;
  padding: 0;
  cursor: pointer;
  transition: opacity 0.15s ease, background 0.15s ease;
}

.history__remove svg {
  width: 0.85rem;
  height: 0.85rem;
  display: block;
}

.history__remove:hover,
.history__remove:focus-visible {
  opacity: 1;
  background: rgba(255, 255, 255, 0.12);
}

.history__item:hover,
.history__item:focus-visible {
  background: rgba(255, 255, 255, 0.14);
}

.history__item-icon {
  display: inline-flex;
  flex-shrink: 0;
}

.history__item-icon svg {
  width: 1.05rem;
  height: 1.05rem;
  display: block;
  color: var(--tag-c, var(--cream));
}

.history__item-text {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
}

.history__item-name {
  font-weight: 700;
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.history__item-category {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.history__empty {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  opacity: 0.7;
}

.history__empty[hidden] {
  display: none;
}

/* Footer */
.footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1.25rem clamp(1.25rem, 3vw, 2.5rem);
  border-top: 3px solid var(--ink);
}

.footer__count {
  font-size: 0.85rem;
  font-weight: 700;
}

.footer__copy {
  font-size: 0.8rem;
  color: rgba(20, 20, 26, 0.6);
  margin: 0;
}

.footer__about {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-size: inherit;
  font-weight: 700;
  color: var(--blue);
  text-decoration: underline;
  text-decoration-color: rgba(0, 83, 219, 0.35);
  text-underline-offset: 0.2em;
  cursor: pointer;
  transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.footer__about:hover {
  color: var(--blue-deep);
  text-decoration-color: rgba(0, 62, 168, 0.55);
}

.footer__sep {
  margin: 0 0.35rem;
  color: rgba(20, 20, 26, 0.35);
}

body.no-scroll {
  overflow: hidden;
}

/* Overlays (Browse + Preview) */
.overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 26, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vw, 32px);
  z-index: 1000;
}

#previewOverlay {
  z-index: 1010;
}

.overlay[hidden] {
  display: none;
}

/* Backdrop fade for every overlay (cheap, no JS needed). Closing fade-out
   is JS-driven (.is-closing) only for the preview overlay, which delays
   `hidden` until the panel's shared-element animation finishes -- see
   closePreview() in app.js. Same specificity as the fade-in rule below, so
   source order (this one last) decides which wins while a close is playing. */
@keyframes overlay-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes overlay-fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.overlay:not([hidden]) {
  animation: overlay-fade-in 0.18s ease;
}

.overlay.is-closing {
  /* The backdrop fades out immediately but `hidden` isn't set until the JS
     close timer finishes (it waits for the panel's own close animation).
     Without this, the invisible-but-still-present backdrop swallows the
     very next click/tap -- most noticeable right after a swipe-dismiss,
     where the next tap often lands within that window. */
  pointer-events: none;
  animation: overlay-fade-out 0.22s ease forwards;
}

.overlay__panel {
  position: relative;
  background: var(--cream);
  border: 3px solid var(--ink);
  border-radius: 32px;
  box-shadow: 8px 8px 0 0 var(--ink);
  width: 100%;
  max-width: 720px;
  max-height: 85vh;
  overflow-y: auto;
  padding: clamp(1.25rem, 3vw, 2rem);
}

#browseOverlay {
  align-items: flex-start;
}

.overlay__panel--browse {
  max-width: 960px;
  display: flex;
  flex-direction: column;
  min-height: min(85vh, 720px);
  max-height: 85vh;
  margin-top: clamp(12px, 3vw, 32px);
  overflow: hidden;
}

.overlay__panel--browse .overlay__header,
.overlay__panel--browse .browse__search,
.overlay__panel--browse .browse__filters {
  flex-shrink: 0;
  /* Swipe-to-dismiss (app.js) needs to own vertical touch movement here from
     the first pixel, same reasoning as .overlay__panel--preview below --
     deliberately NOT applied to the whole browse panel, since its site list
     scrolls internally and this would fight that. */
  touch-action: none;
}

.browse-body-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* Fades hint that the list continues past the visible edge -- hidden via
   the is-at-top/is-at-bottom classes (toggled in app.js) once you've
   actually scrolled all the way, so they never point at content that
   isn't there. */
.browse-body-wrap::before,
.browse-body-wrap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 18px;
  pointer-events: none;
  z-index: 4;
  transition: opacity 0.15s ease;
}

.browse-body-wrap::before {
  top: 0;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--cream) 85%, transparent), transparent);
}

.browse-body-wrap::after {
  bottom: 0;
  background: linear-gradient(to top, color-mix(in srgb, var(--cream) 85%, transparent), transparent);
}

.browse-body-wrap.is-at-top::before,
.browse-body-wrap.is-at-bottom::after {
  opacity: 0;
}

.overlay__panel--browse .overlay__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 0.85rem;
  padding-bottom: 1.25rem;
  scrollbar-width: thin;
  scrollbar-color: var(--ink) rgba(20, 20, 26, 0.08);
}

/* Custom draggable scrollbar thumb, touch devices only -- desktop already
   has a real draggable scrollbar via ::-webkit-scrollbar above, but mobile
   browsers ignore that entirely and show a non-interactive indicator
   instead, so touch users have no way to drag-scroll without this. Native
   indicator is hidden in the same media block below so the two don't
   render on top of each other. */
.browse-scrollbar {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 16px;
  z-index: 5;
}

@media (pointer: coarse) {
  .browse-scrollbar {
    display: block;
  }

  .overlay__panel--browse .overlay__body::-webkit-scrollbar,
  .overlay__panel--browse .overlay__body {
    scrollbar-width: none;
  }

  .overlay__panel--browse .overlay__body::-webkit-scrollbar {
    display: none;
    width: 0;
  }
}

/* The thumb's actual hit box spans the full track width (16px, a real
   touch target) -- ::after draws a slimmer 8px bar centered inside it so
   it doesn't look chunky, without shrinking what's actually grabbable. */
.browse-scrollbar__thumb {
  position: absolute;
  right: 0;
  width: 16px;
  touch-action: none;
  will-change: transform;
}

/* Eases back to scaleY(1) after a fast swipe -- removed while actively
   scrolling so the stretch itself always applies instantly, not eased. */
.browse-scrollbar__thumb.is-settling {
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.browse-scrollbar__thumb::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 1px;
  width: 8px;
  border-radius: 999px;
  background: var(--ink);
  opacity: 0.55;
  transition: opacity 0.15s ease;
}

.browse-scrollbar__thumb.is-dragging::after {
  opacity: 0.8;
}

.overlay__panel--browse .overlay__body::-webkit-scrollbar {
  width: 11px;
}

.overlay__panel--browse .overlay__body::-webkit-scrollbar-track {
  background: rgba(20, 20, 26, 0.06);
  border-radius: 999px;
  margin: 2px 0 0;
}

.overlay__panel--browse .overlay__body::-webkit-scrollbar-thumb {
  background: var(--ink);
  border: 2px solid var(--cream);
  border-radius: 999px;
  box-shadow: 1px 1px 0 rgba(20, 20, 26, 0.35);
}

.overlay__panel--browse .overlay__body::-webkit-scrollbar-thumb:hover {
  background: var(--blue-deep);
}

.overlay__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1.25rem;
  gap: 1rem;
}

.overlay__title {
  margin: 0;
  font-size: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.overlay__close {
  flex-shrink: 0;
  border: 2px solid var(--ink);
  background: #fff;
  border-radius: 999px;
  width: 2.25rem;
  height: 2.25rem;
  font-size: 1.25rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 2px 2px 0 0 var(--ink);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.overlay__close:hover {
  transform: translate(2px, 2px);
  box-shadow: none;
}

/* Preview overlay */
.overlay__panel--preview {
  max-width: 480px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  will-change: transform, opacity;
  /* Swipe-to-dismiss (app.js) needs to own vertical touch movement on this
     panel from the first pixel, otherwise the browser's own scroll/refresh
     gesture can kick in before JS decides it's a drag. Preview content is
     short enough that this panel doesn't need its own internal scrolling. */
  touch-action: none;
}

.overlay__panel--submit,
.overlay__panel--about {
  max-width: 520px;
  /* Same reasoning as .overlay__panel--preview above -- their content is
     short and fixed, not data-driven, so unlike browse there's no internal
     scroll to protect. */
  touch-action: none;
}

.overlay__panel--submit .overlay__header,
.overlay__panel--about .overlay__header {
  margin-bottom: 0.75rem;
}

.submit-overlay__sub {
  font-size: 0.98rem;
  line-height: 1.5;
  color: rgba(20, 20, 26, 0.75);
  margin: 0 0 1rem;
}

.about__body {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.about__body p {
  margin: 0;
  font-size: 0.98rem;
  line-height: 1.55;
  color: rgba(20, 20, 26, 0.82);
}

.about__link {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-size: inherit;
  font-weight: 700;
  color: var(--blue);
  text-decoration: underline;
  text-decoration-color: rgba(0, 83, 219, 0.35);
  text-underline-offset: 0.2em;
  cursor: pointer;
  transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.about__link:hover {
  color: var(--blue-deep);
  text-decoration-color: rgba(0, 62, 168, 0.55);
}

.about__donate {
  align-self: flex-start;
}

#aboutOverlay,
#submitOverlay {
  z-index: 1005;
}

.overlay__panel--preview .overlay__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
}

.overlay__panel--preview .overlay__share {
  position: absolute;
  top: 1rem;
  left: 1rem;
  z-index: 2;
  border: none;
  background: transparent;
  padding: 0.35rem;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink);
  opacity: 0.55;
  transition: opacity 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.overlay__panel--preview .overlay__share svg {
  width: 1.2rem;
  height: 1.2rem;
  display: block;
  pointer-events: none;
}

.overlay__panel--preview .overlay__share:hover {
  opacity: 1;
  color: var(--blue);
}

.overlay__panel--preview .overlay__share.is-copied {
  opacity: 1;
  color: var(--green);
}

.overlay__share-toast {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 0;
  z-index: 3;
  background: var(--green);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
  padding: 0.3rem 0.6rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  box-shadow: 2px 2px 0 0 var(--ink);
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.overlay__share.is-copied .overlay__share-toast {
  opacity: 1;
  transform: translateY(0);
}

.preview__tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  gap: 0.4rem;
  margin-top: 0.5rem;
  /* Reserve room for the absolutely-positioned share/close buttons
     (each ~2.25rem wide, inset 1rem) so tag chips wrap before sliding
     underneath them instead of clipping on narrow screens. */
  padding: 0 3.5rem;
  /* Most sites have 1-2 tags (one row); a few have 3-4 (two rows). Reserving
     two rows' worth of height means the buttons below always land in the
     same place regardless of which a given site has, instead of the whole
     card resizing site to site. A single row just centers within it. */
  min-height: 3.25rem;
}

/* Shared tag-chip component: used in the preview overlay, browse cards,
   and reused (via the same class names) in the admin panel. Each tag sets
   its own --tag-c custom property inline (see TAGS in app.js/admin.js) so
   this one rule set colors every tag differently without needing a CSS
   rule per tag -- the icon picks up the color directly, and the chip's own
   background becomes a light tint of the same hue. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: color-mix(in srgb, var(--tag-c, var(--ink)) 14%, var(--cream));
  white-space: nowrap;
}

.tag-chip svg {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.15em;
  color: var(--tag-c, var(--ink));
}

/* "New" badge: computed from a site's addedAt, not a real tag -- see
   isNewSite() in js/app.js. Shows up anywhere a site's name is rendered. */
.badge-new {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: var(--yellow);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.08rem 0.45rem;
  margin-left: 0.4rem;
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  vertical-align: middle;
}

.badge-new svg {
  width: 0.85em;
  height: 0.85em;
  display: inline-block;
}

.preview__name {
  font-size: 1.75rem;
  margin: 0;
  line-height: 1.15;
  /* Same reasoning as .preview__tags/.preview__desc above: the "New" badge
     now sits on its own line under the title (see openPreview in app.js),
     which would otherwise make "New" sites' cards taller than others and
     shift the buttons below. Reserving room for that second line always,
     and centering within it, keeps every card the same height regardless. */
  min-height: 4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* Sits on its own line under the title here (see openPreview in app.js),
   unlike the browse-card version which stays inline next to the name --
   the margin-left there doesn't apply to this stacked layout. */
.preview__name .badge-new {
  margin-left: 0;
  margin-top: 0.4rem;
}

/* A min-height guess can't actually guarantee a constant size -- the same
   description wraps to more lines on a narrow phone than on a wide desktop
   panel, so "reserve space for N lines" needs a different N per width.
   Fixing this wrapper's height and clamping the paragraph inside to match
   (same clamp technique as .search__result-desc above) sidesteps that
   entirely: the box is always exactly this tall regardless of viewport or
   text length. The wrapper centers short descriptions so they don't look
   like they're floating above dead space; long ones fill it and truncate
   with an ellipsis instead of reflowing the whole card. */
.preview__desc-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 4.5rem;
}

.preview__desc {
  font-size: 1rem;
  line-height: 1.5;
  color: rgba(20, 20, 26, 0.75);
  margin: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

@media (max-width: 560px) {
  .preview__desc-wrap {
    /* Narrower panel -> same text wraps to more lines -> reserve more. */
    height: 6rem;
  }

  .preview__desc {
    -webkit-line-clamp: 4;
  }
}

.preview__actions {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
  margin-top: 0.5rem;
}

.preview__actions .btn--hero,
.preview__actions .btn--pill {
  width: 100%;
  justify-content: center;
}

.preview__report-wrap {
  display: flex;
  justify-content: center;
  margin-top: 0.5rem;
}

.preview__report {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  color: rgba(20, 20, 26, 0.5);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.preview__report[hidden],
.preview__report-confirm[hidden] {
  display: none;
}

.preview__report-confirm {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.45rem 0.55rem;
}

.preview__report-confirm-text {
  font-size: 0.88rem;
  font-weight: 600;
  color: rgba(20, 20, 26, 0.65);
}

.preview__report-action {
  background: none;
  border: none;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  color: rgba(20, 20, 26, 0.55);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  padding: 0;
}

.preview__report-action--confirm {
  color: var(--red-text);
}

.preview__report-action:hover {
  color: rgba(20, 20, 26, 0.85);
}

.preview__report-action--confirm:hover {
  color: var(--red-text-hover);
}

.preview__report-icon {
  display: inline-flex;
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  color: var(--red-text);
}

.preview__report-icon svg {
  width: 100%;
  height: 100%;
}

.preview__report:hover:not(:disabled) .preview__report-icon {
  color: var(--red-text-hover);
}

.preview__report:hover:not(:disabled) {
  color: rgba(20, 20, 26, 0.7);
}

.preview__report:disabled {
  cursor: default;
  text-decoration: none;
  color: rgba(20, 20, 26, 0.35);
}

.preview__report:disabled .preview__report-icon {
  color: rgba(20, 20, 26, 0.3);
}

.preview__report-status {
  margin: 0.35rem 0 0;
  font-size: 0.82rem;
  color: rgba(20, 20, 26, 0.65);
  text-align: center;
}

.preview__report-status[hidden] {
  display: none;
}

.preview__report-status.submit-status--error {
  color: var(--red-text);
}

/* Browse overlay */
.browse__search {
  margin-bottom: 0.9rem;
}

.browse__count {
  font-size: 0.7rem;
  background: var(--cream);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  font-weight: 700;
  white-space: nowrap;
}

/* Tag filter pills -- replace the old fold/unfold-by-category accordion.
   Multiple can be active at once (AND: a site must match every active pill). */
.browse__filters {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.browse__clear-tags {
  border: none;
  background: transparent;
  color: var(--red-text);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 800;
  text-decoration: underline;
  text-underline-offset: 0.22em;
  padding: 0.1rem;
  cursor: pointer;
  box-shadow: none;
  transition: color 0.15s ease;
}

.browse__clear-tags:hover {
  color: var(--red-text-hover);
}

.browse__clear-tags[hidden] {
  display: none;
}

.browse__filter-group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.browse__filter-heading {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.6rem;
}

.browse__filter-label {
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: rgba(20, 20, 26, 0.55);
}

.browse__filter-toggle {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
}

.browse__filter-chevron {
  width: 0.8rem;
  height: 0.8rem;
  color: rgba(20, 20, 26, 0.55);
  transition: transform 0.2s ease;
}

.browse__filter-toggle[aria-expanded="false"] .browse__filter-chevron {
  transform: rotate(-90deg);
}

/* grid-template-rows 1fr/0fr trick: animates open/closed without JS having
   to measure content height, and handles the pill row wrapping to more
   lines just as well as a single line. */
.browse__filter-collapse {
  display: grid;
  grid-template-rows: 1fr;
  overflow: hidden;
  transition: grid-template-rows 0.2s ease;
}

.browse__filter-collapse.is-collapsed {
  grid-template-rows: 0fr;
}

.browse__filter-collapse > .browse__filter-pills {
  min-height: 0;
  /* Real padding (not canceled by a negative margin) so the pills'
     box-shadow and hover press (translate(1px, 1px) below) have actual
     room to render before the grid-rows collapse trick's overflow: hidden
     clips them. */
  padding: 3px;
}

.browse__filter-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tag-filter {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.28rem 0.85rem;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.82rem;
  background: #fff;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.tag-filter svg {
  width: 1.05em;
  height: 1.05em;
  display: inline-block;
  vertical-align: -0.15em;
  color: var(--tag-c, var(--ink));
}

.tag-filter:hover {
  transform: translate(1px, 1px);
}

.tag-filter.is-active {
  background: color-mix(in srgb, var(--tag-c, var(--yellow)) 35%, #fff);
  box-shadow: 2px 2px 0 0 var(--ink);
}

.tag-filter.is-excluded {
  background: #ffe8e8;
  color: #b91c1c;
  box-shadow: 2px 2px 0 0 var(--ink);
}

.tag-filter.is-excluded svg {
  color: #b91c1c;
}

.tag-filter.is-excluded::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    to top right,
    transparent calc(50% - 1.25px),
    #dc2626 calc(50% - 1.25px),
    #dc2626 calc(50% + 1.25px),
    transparent calc(50% + 1.25px)
  );
  pointer-events: none;
}

.browse__empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem 1rem;
  color: rgba(20, 20, 26, 0.6);
}

.browse__empty[hidden] {
  display: none;
}

.browse__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem;
  padding-bottom: 0.25rem;
}

.browse__card {
  text-align: left;
  border: 2px solid var(--ink);
  border-radius: 14px;
  background: #fff;
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  box-shadow: 2px 2px 0 0 var(--ink);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  font-family: inherit;
}

.browse__card:hover {
  transform: translate(2px, 2px);
  box-shadow: none;
}

.browse__card-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.browse__card-top .badge-new {
  margin-left: 0;
  height: 1.5rem;
  padding: 0 0.5rem;
  border-radius: 7px;
}

.browse__card-tags {
  display: flex;
  flex-shrink: 0;
  gap: 0.25rem;
}

.tag-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  flex-shrink: 0;
  border-radius: 7px;
  border: 2px solid var(--ink);
  background: color-mix(in srgb, var(--tag-c, var(--ink)) 35%, #fff);
  color: var(--tag-c, var(--ink));
}

.tag-dot svg {
  width: 0.85rem;
  height: 0.85rem;
  display: block;
}

.browse__card-name {
  font-weight: 700;
  font-size: 0.95rem;
}

.browse__card-desc {
  font-size: 0.8rem;
  line-height: 1.4;
  color: rgba(20, 20, 26, 0.65);
}

/* Submit overlay (form styles shared with admin) */
.submit-form {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.submit-form__field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-weight: 700;
  font-size: 0.9rem;
}

.submit-form__field-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.submit-form__char-count {
  font-weight: 600;
  font-size: 0.75rem;
  color: rgba(20, 20, 26, 0.5);
}

.submit-form__char-count.is-near-limit {
  color: var(--red-text);
}

.submit-form__field input,
.submit-form__field select,
.submit-form__field textarea {
  border: 2px solid var(--ink);
  border-radius: 14px;
  padding: 0.75rem 1rem;
  font: inherit;
  font-weight: 500;
  background: #fff;
  color: var(--ink);
  resize: vertical;
}

.submit-form__field input:focus,
.submit-form__field select:focus,
.submit-form__field textarea:focus {
  outline: 3px solid var(--yellow);
  outline-offset: 1px;
}

.tag-fieldset {
  border: none;
  margin: 0;
  padding: 0;
}

.tag-fieldset legend {
  padding: 0;
  margin-bottom: 0.5rem;
  font-weight: 700;
  font-size: 0.9rem;
}

.tag-fieldset__hint {
  font-weight: 500;
  font-size: 0.78rem;
  color: rgba(20, 20, 26, 0.55);
}

.tag-fieldset__hint--secondary {
  margin: 0.5rem 0 0.15rem;
}

.tag-checkboxes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tag-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.5rem 0.9rem;
  font-weight: 600;
  font-size: 0.85rem;
  background: #fff;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

.tag-checkbox:has(input:checked) {
  background: color-mix(in srgb, var(--tag-c, var(--yellow)) 35%, #fff);
  box-shadow: 2px 2px 0 0 var(--ink);
}

.tag-checkbox input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--ink);
}

.tag-checkbox__icon {
  display: inline-flex;
  color: var(--tag-c, var(--ink));
}

.tag-checkbox__icon svg {
  width: 1.1em;
  height: 1.1em;
}

.submit-form__note {
  font-size: 0.85rem;
  color: rgba(20, 20, 26, 0.6);
  line-height: 1.5;
  margin: 0.25rem 0 0;
}

.submit-form__note a {
  text-decoration: underline;
}

.submit-status {
  border: 2px solid var(--ink);
  border-radius: 16px;
  background: var(--green);
  color: #fff;
  padding: 1rem 1.25rem;
  font-weight: 700;
  text-align: center;
  margin: 0;
}

.submit-status[hidden] {
  display: none;
}

.submit-status--error {
  background: #e04b4b;
}

/* Responsive */
@media (max-width: 820px) {
  .header:has(.search) {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "brand actions"
      "search search";
  }

  .header:has(.search) .search {
    justify-self: stretch;
    max-width: none;
  }
}

@media (max-width: 900px) {
  .main {
    flex-direction: column;
  }

  .tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .frame {
    border-radius: 28px;
  }

  .header {
    padding: 1rem 1.25rem 0;
  }

  .header::after {
    margin-top: 1rem;
  }

  .main {
    padding-top: 1rem;
  }

  .header:has(.search) {
    grid-template-columns: 1fr;
    grid-template-areas:
      "brand"
      "search"
      "actions";
  }

  .header__actions {
    width: 100%;
  }

  .header__actions .btn {
    flex: 1 1 auto;
    justify-content: center;
  }

  .tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .btn--hero {
    width: 100%;
    justify-content: space-between;
  }

  .footer {
    justify-content: center;
    text-align: center;
  }

  .browse__grid {
    grid-template-columns: 1fr;
  }

  .overlay__panel {
    border-radius: 24px;
  }

  .overlay__panel--browse {
    min-height: min(94vh, 720px);
    max-height: 94vh;
    margin-top: 0;
  }
}
