/*
 * The whole design, hand-written.
 *
 * This replaces Tailwind: the utility classes the React components carried have
 * been collapsed into one rule per element. The design tokens below are copied
 * verbatim from the original `app/globals.css`, so the palette is identical —
 * what changed is that the rules are written out rather than generated, which is
 * why there is no build step, no PostCSS and no `node_modules` behind this file.
 *
 * Breakpoints match Tailwind's defaults, and are used mobile-first exactly as the
 * `sm:` / `md:` / `lg:` / `xl:` prefixes were:
 *   sm 640px   md 768px   lg 1024px   xl 1280px
 */

/* ------------------------------------------------------------------ tokens */

:root {
  color-scheme: light;

  --background: #ffffff;
  --foreground: #09090b;
  --card: #ffffff;
  --card-foreground: #09090b;
  --primary: #18181b;
  --primary-foreground: #fafafa;
  --secondary: #f4f4f5;
  --secondary-foreground: #18181b;
  --muted: #f4f4f5;
  --muted-foreground: #71717a;
  --accent: #f4f4f5;
  --accent-foreground: #18181b;
  --border: #e4e4e7;
  --ring: #a1a1aa;
  --radius: 0.625rem;

  /* `bg-muted/30` and friends need the channels, not the hex. */
  --muted-rgb: 244 244 245;
  --primary-rgb: 24 24 27;
  --background-rgb: 255 255 255;

  /* The card hover wash: black at 5% in light, white at 10% in dark. */
  --card-hover: rgb(0 0 0 / 0.05);
  /* The icon circle's inner shadow, which reads as darker in light mode. */
  --icon-inner-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
}

.dark {
  color-scheme: dark;

  --background: #09090b;
  --foreground: #fafafa;
  --card: #09090b;
  --card-foreground: #fafafa;
  --primary: #fafafa;
  --primary-foreground: #18181b;
  --secondary: #27272a;
  --secondary-foreground: #fafafa;
  --muted: #27272a;
  --muted-foreground: #a1a1aa;
  --accent: #27272a;
  --accent-foreground: #fafafa;
  --border: #27272a;
  --ring: #d4d4d8;

  --muted-rgb: 39 39 42;
  --primary-rgb: 250 250 250;
  --background-rgb: 9 9 11;

  --card-hover: rgb(255 255 255 / 0.1);
  --icon-inner-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.25);
}

/* ------------------------------------------------------------------- reset */

*,
*::before,
*::after {
  box-sizing: border-box;
  border-color: var(--border);
}

* {
  margin: 0;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--background);
  color: var(--foreground);
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color 200ms ease, color 200ms ease;
}

::selection {
  background: var(--primary);
  color: var(--primary-foreground);
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

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

/*
 * `hidden` has to win over any `display` a class sets, or an element that is
 * hidden in the DOM stays on screen: the UA sheet's `[hidden] { display: none }`
 * loses to any author rule with a `display`. Every element this page hides —
 * cards, sections, chips, the clear button, the dialog's optional rows — sets
 * its own display, so the rule is stated once here rather than per selector.
 */
[hidden] {
  display: none !important;
}

/* The icon sprite itself must never take up space or be read out. */
.sprite {
  display: none;
}

/*
 * Stroke geometry lives here rather than on each `<symbol>`: a symbol does not
 * pass its root attributes down to a `<use>`, so setting it once in CSS is what
 * makes the sprite work at all. It also lets every icon inherit `currentColor`
 * and its size from the class beside it, the way the React icon props did.
 */
.icon {
  display: block;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  width: 1rem;
  height: 1rem;
}

/* Everything that only animates for decoration stops for a visitor who asked. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------------------ layout */

.page {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--background);
  color: var(--foreground);
  transition: background-color 300ms, color 300ms;
}

.main {
  padding: 1.5rem 0.75rem;
}

.panel {
  width: 100%;
  max-width: 1600px;
  margin-inline: auto;
}

@media (min-width: 640px) {
  .main {
    padding-inline: 1.5rem;
  }
}

@media (min-width: 1024px) {
  .main {
    padding-inline: 2rem;
  }
}

/* -------------------------------------------------------------------- hero */

.hero {
  position: relative;
  overflow: hidden;
  border-radius: 1.5rem;
  padding: 1.5rem;
  margin-bottom: 1.5rem;
  background-image: linear-gradient(
    to bottom right,
    rgb(var(--muted-rgb) / 0.3),
    transparent,
    rgb(var(--muted-rgb) / 0.2)
  );
}

@media (min-width: 640px) {
  .hero {
    padding: 2rem;
  }
}

/* Two soft colour washes behind the heading. Purely decorative, so they are
   pointer-transparent and never intercept a click meant for a control. */
.hero__blob {
  position: absolute;
  pointer-events: none;
  border-radius: 9999px;
  filter: blur(64px);
}

.hero__blob--one {
  top: -5rem;
  right: -5rem;
  width: 16rem;
  height: 16rem;
  background: rgb(var(--primary-rgb) / 0.05);
}

.hero__blob--two {
  bottom: -5rem;
  left: -5rem;
  width: 12rem;
  height: 12rem;
  background: rgb(99 102 241 / 0.05);
}

.hero__controls {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

@media (min-width: 640px) {
  .hero__controls {
    top: 1.25rem;
    right: 1.25rem;
  }
}

.hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 1.25rem;
  padding-top: 2rem;
}

@media (min-width: 640px) {
  .hero__content {
    align-items: center;
    text-align: center;
    padding-top: 1rem;
  }
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 9999px;
  background: rgb(var(--background-rgb) / 0.8);
  padding: 0.375rem 0.75rem;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  color: var(--muted-foreground);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.eyebrow__icon {
  width: 0.875rem;
  height: 0.875rem;
  color: #f59e0b;
}

.hero__text {
  max-width: 42rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.hero__title {
  font-size: 1.875rem;
  line-height: 2.25rem;
  font-weight: 800;
  letter-spacing: -0.025em;
}

@media (min-width: 640px) {
  .hero__title {
    font-size: 2.25rem;
    line-height: 2.5rem;
  }
}

@media (min-width: 768px) {
  .hero__title {
    font-size: 3rem;
    line-height: 1;
  }
}

/* Gradient text. The fallback `color` matters: if a browser ignores
   `background-clip: text` the heading stays readable instead of going invisible. */
.hero__title-line {
  background-clip: text;
  -webkit-background-clip: text;
}

.hero__title-line--one {
  color: var(--foreground);
  background-image: linear-gradient(
    to right,
    var(--foreground),
    var(--foreground),
    var(--muted-foreground)
  );
}

.hero__title-line--two {
  color: #6366f1;
  background-image: linear-gradient(to right, #6366f1, #a855f7);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .hero__title-line {
    color: transparent;
  }
}

.hero__summary {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

@media (min-width: 640px) {
  .hero__summary {
    font-size: 1rem;
    line-height: 1.5rem;
  }
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: flex-start;
}

@media (min-width: 640px) {
  .chips {
    justify-content: center;
  }
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  border: 1px solid var(--border);
  border-radius: 9999px;
  padding: 0.25rem 0.625rem;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  background: var(--secondary);
  color: var(--secondary-foreground);
}

.chip__count {
  border-radius: 9999px;
  background: rgb(var(--background-rgb) / 0.8);
  padding-inline: 0.375rem;
  font-size: 0.625rem;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------- icon buttons */

.icon-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9999px;
  background: rgb(var(--background-rgb) / 0.8);
  backdrop-filter: blur(4px);
  border: 1px solid var(--border);
  color: var(--foreground);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition: background-color 200ms ease-out, color 200ms ease-out,
    border-color 200ms ease-out;
}

.icon-button:hover {
  background: var(--accent);
  color: var(--accent-foreground);
}

.icon-button__icon {
  width: 1.25rem;
  height: 1.25rem;
}

/* The search button swaps a magnifier for a close cross, and inverts, while the
   search field is open. Driven off `aria-expanded` so the state the assistive
   technology sees and the state the eye sees cannot drift apart. */
.icon-button__icon--alt,
#search-toggle[aria-expanded="true"] .icon-button__icon:not(.icon-button__icon--alt) {
  display: none;
}

#search-toggle[aria-expanded="true"] .icon-button__icon--alt {
  display: block;
}

#search-toggle[aria-expanded="true"] {
  background: var(--primary);
  color: var(--primary-foreground);
  border-color: var(--primary);
}

/* Only one of the three theme icons shows at a time, chosen by the stored
   preference that `theme.js` stamps onto <html>. */
.theme-icon {
  display: none;
}

html[data-theme="light"] .theme-icon--light,
html[data-theme="dark"] .theme-icon--dark,
html[data-theme="system"] .theme-icon--system,
html:not([data-theme]) .theme-icon--system {
  display: block;
}

/* ------------------------------------------------------------------ search */

/*
 * The expand/collapse is a `grid-template-rows` transition from `0fr` to `1fr`:
 * it animates to the content's natural height without anyone having to measure
 * it or hard-code a max-height that clips on a narrow screen.
 *
 * The element starts with `hidden` in the markup and `app.js` swaps that for
 * `.search--ready` on load. Without JavaScript there is nothing to filter with,
 * so the field stays out of the page entirely rather than sitting there inert.
 */
.search {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  margin-bottom: 0;
  transition: grid-template-rows 300ms ease-out, opacity 300ms ease-out,
    margin-bottom 300ms ease-out;
}

.search--open {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-bottom: 2.5rem;
}

.search__inner {
  overflow: hidden;
}

.search__field {
  position: relative;
  max-width: 42rem;
  margin-inline: auto;
}

.search__input {
  width: 100%;
  padding: 0.875rem 1.25rem;
  border-radius: 1rem;
  font: inherit;
  font-size: 1rem;
  background: rgb(var(--background-rgb) / 0.8);
  border: 1px solid var(--border);
  border-color: rgb(from var(--border) r g b / 0.6);
  color: var(--foreground);
  transition: border-color 200ms, box-shadow 200ms;
}

.search__input::placeholder {
  color: var(--muted-foreground);
}

.search__input:focus {
  outline: none;
  border-color: rgb(var(--primary-rgb) / 0.3);
  box-shadow: 0 0 0 2px rgb(var(--primary-rgb) / 0.3);
}

.search__clear {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  translate: 0 -50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 9999px;
  color: var(--muted-foreground);
  transition: background-color 150ms, color 150ms;
}

.search__clear:hover {
  background: var(--accent);
  color: var(--accent-foreground);
}

.search__clear-icon {
  width: 1rem;
  height: 1rem;
}

/* --------------------------------------------------------------- sections */

.sections {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.category {
  border-radius: 1.5rem;
  padding: 0.75rem;
  background-image: linear-gradient(to right, var(--cat-from), var(--cat-via), var(--cat-to));

  /* Fallback wash for a category with no entry in CATEGORY_CONFIG. */
  --cat-from: rgb(var(--muted-rgb) / 0.5);
  --cat-via: rgb(var(--muted-rgb) / 0.4);
  --cat-to: rgb(var(--muted-rgb) / 0.3);
  --cat-badge-bg: var(--secondary);
  --cat-badge-fg: var(--secondary-foreground);
  --cat-badge-border: var(--border);
}

@media (min-width: 640px) {
  .category {
    padding: 1.25rem;
  }
}

.category__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.category__icon {
  width: 1rem;
  height: 1rem;
  color: var(--primary);
}

.category__title {
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  color: var(--foreground);
}

.category__chevron {
  width: 1rem;
  height: 1rem;
  color: var(--muted-foreground);
}

.category__count {
  margin-left: auto;
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
}

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.25rem;
}

@media (min-width: 640px) {
  .card-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.5rem;
  }
}

@media (min-width: 768px) {
  .card-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .card-grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

@media (min-width: 1280px) {
  .card-grid {
    grid-template-columns: repeat(7, minmax(0, 1fr));
  }
}

/*
 * Per-category colour. The gradient stops and the chip colours for a category
 * are the two things that differ, so they are custom properties set by one rule
 * each — adding a category is this block plus an entry in `build.mjs`.
 *
 * The chip in the hero and the section it belongs to share `data-category`, so a
 * single selector list colours both.
 */
.category[data-category="saas"] {
  --cat-from: rgb(99 102 241 / 0.1);
  --cat-via: rgb(168 85 247 / 0.1);
  --cat-to: rgb(236 72 153 / 0.1);
}
.chip[data-category="saas"] {
  background: rgb(99 102 241 / 0.1);
  color: #4f46e5;
  border-color: rgb(99 102 241 / 0.2);
}
.dark .chip[data-category="saas"] {
  color: #a5b4fc;
}

.category[data-category="browser-extension"] {
  --cat-from: rgb(245 158 11 / 0.1);
  --cat-via: rgb(249 115 22 / 0.1);
  --cat-to: rgb(239 68 68 / 0.1);
}
.chip[data-category="browser-extension"] {
  background: rgb(245 158 11 / 0.1);
  color: #d97706;
  border-color: rgb(245 158 11 / 0.2);
}
.dark .chip[data-category="browser-extension"] {
  color: #fcd34d;
}

.category[data-category="desktop"] {
  --cat-from: rgb(14 165 233 / 0.1);
  --cat-via: rgb(59 130 246 / 0.1);
  --cat-to: rgb(139 92 246 / 0.1);
}
.chip[data-category="desktop"] {
  background: rgb(14 165 233 / 0.1);
  color: #0284c7;
  border-color: rgb(14 165 233 / 0.2);
}
.dark .chip[data-category="desktop"] {
  color: #7dd3fc;
}

.category[data-category="mini-web-app"] {
  --cat-from: rgb(16 185 129 / 0.1);
  --cat-via: rgb(20 184 166 / 0.1);
  --cat-to: rgb(6 182 212 / 0.1);
}
.chip[data-category="mini-web-app"] {
  background: rgb(16 185 129 / 0.1);
  color: #059669;
  border-color: rgb(16 185 129 / 0.2);
}
.dark .chip[data-category="mini-web-app"] {
  color: #6ee7b7;
}

.category[data-category="nda-free"] {
  --cat-from: rgb(100 116 139 / 0.1);
  --cat-via: rgb(113 113 122 / 0.1);
  --cat-to: rgb(115 115 115 / 0.1);
}
.chip[data-category="nda-free"] {
  background: rgb(100 116 139 / 0.1);
  color: #475569;
  border-color: rgb(100 116 139 / 0.2);
}
.dark .chip[data-category="nda-free"] {
  color: #cbd5e1;
}

/* ------------------------------------------------------------------- card */

/*
 * Mobile is one project per row — icon left, text right, chevron trailing.
 * From `sm` up it becomes the centred tile in a grid. Items align to the top on
 * mobile so the icon lines up with the name rather than floating beside the
 * description.
 */
.card {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 0.75rem;
  width: 100%;
  padding: 0.75rem;
  border-radius: 1rem;
  text-align: left;
  color: var(--card-foreground);
  cursor: pointer;
  transition: background-color 300ms ease-out, translate 300ms ease-out;
}

.card:hover {
  background: var(--card-hover);
}

.card:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
  box-shadow: 0 0 0 2px var(--background), 0 0 0 4px rgb(from var(--ring) r g b / 0.5);
}

@media (min-width: 640px) {
  .card {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.625rem;
    padding: 1rem;
    text-align: center;
  }

  .card:hover {
    translate: 0 -0.125rem;
  }
}

.card__icon-wrap {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-top: 0.125rem;
  border-radius: 9999px;
  background: var(--secondary);
  color: var(--secondary-foreground);
  transition: background 300ms ease-out, color 300ms ease-out, box-shadow 300ms ease-out;
}

.card:hover .card__icon-wrap {
  background-image: linear-gradient(
    to bottom right,
    var(--primary),
    rgb(var(--primary-rgb) / 0.8)
  );
  color: var(--primary-foreground);
  box-shadow: var(--icon-inner-shadow);
}

@media (min-width: 640px) {
  .card__icon-wrap {
    width: 3rem;
    height: 3rem;
    margin-top: 0;
  }
}

.card__icon {
  width: 1.25rem;
  height: 1.25rem;
  color: var(--muted-foreground);
  transition: color 300ms ease-out, scale 300ms ease-out;
}

.card:hover .card__icon {
  color: var(--primary-foreground);
  scale: 1.1;
}

@media (min-width: 640px) {
  .card__icon {
    width: 1.5rem;
    height: 1.5rem;
  }
}

/*
 * Name, description and badges. Beside the icon on mobile, below it from `sm`.
 * `min-width: 0` is what lets the text clamp instead of forcing the row wider.
 *
 * The badges sit in this column rather than in a trailing gutter: two of them
 * stacked on the right squeezed the description and made row heights jump
 * between cards.
 */
.card__body {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

@media (min-width: 640px) {
  .card__body {
    gap: 0.625rem;
    align-items: center;
  }
}

.card__name {
  font-size: 0.875rem;
  line-height: 1.375;
  font-weight: 600;
  transition: color 300ms;

  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  overflow: hidden;
}

.card:hover .card__name {
  color: var(--foreground);
}

@media (min-width: 640px) {
  .card__name {
    font-size: 0.75rem;
    text-align: center;
    -webkit-line-clamp: 2;
    line-clamp: 2;
  }
}

.card__description {
  font-size: 0.75rem;
  line-height: 1.375;
  color: var(--muted-foreground);

  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

@media (min-width: 640px) {
  .card__description {
    font-size: 0.625rem;
    line-height: 1.25;
    text-align: center;
  }
}

.card__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.125rem;
}

@media (min-width: 640px) {
  .card__badges {
    margin-top: 0;
    justify-content: center;
  }
}

/* Mobile only: a disclosure chevron, because tapping a row opens a preview
   rather than navigating straight out. */
.card__chevron {
  flex: none;
  align-self: center;
  width: 1rem;
  height: 1rem;
  color: var(--muted-foreground);
  color: rgb(from var(--muted-foreground) r g b / 0.6);
}

@media (min-width: 640px) {
  .card__chevron {
    display: none;
  }
}

/* ----------------------------------------------------------------- badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;
  font-weight: 500;
  white-space: nowrap;
  background: var(--muted);
  color: var(--muted-foreground);
}

.badge--sm {
  font-size: 0.625rem;
}

.badge--md {
  font-size: 0.6875rem;
}

.badge__icon {
  width: 0.625rem;
  height: 0.625rem;
}

.badge--md .badge__icon {
  width: 0.75rem;
  height: 0.75rem;
}

.badge--status-in-progress {
  background: rgb(254 243 199 / 0.8);
  color: #b45309;
}
.dark .badge--status-in-progress {
  background: rgb(69 26 3 / 0.5);
  color: #fcd34d;
}

.badge--status-offline {
  background: rgb(226 232 240 / 0.8);
  color: #475569;
}
.dark .badge--status-offline {
  background: rgb(30 41 59 / 0.6);
  color: #cbd5e1;
}

.badge--pricing-free {
  background: rgb(209 250 229 / 0.8);
  color: #047857;
}
.dark .badge--pricing-free {
  background: rgb(2 44 34 / 0.5);
  color: #6ee7b7;
}

.badge--pricing-free-trial {
  background: rgb(224 242 254 / 0.8);
  color: #0369a1;
}
.dark .badge--pricing-free-trial {
  background: rgb(8 47 73 / 0.5);
  color: #7dd3fc;
}

.badge--pricing-subscription {
  background: rgb(237 233 254 / 0.8);
  color: #6d28d9;
}
.dark .badge--pricing-subscription {
  background: rgb(46 16 101 / 0.5);
  color: #c4b5fd;
}

/* ------------------------------------------------------------ empty state */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding-block: 5rem;
  text-align: center;
  border-radius: 1rem;
  border: 1px dashed var(--border);
  background: rgb(var(--muted-rgb) / 0.3);
}

.empty__icon {
  width: 2.5rem;
  height: 2.5rem;
  color: var(--muted-foreground);
  margin-bottom: 0.75rem;
}

.empty__title {
  color: var(--foreground);
  font-weight: 500;
}

.empty__hint {
  margin-top: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

/* ----------------------------------------------------------------- dialog */

.dialog {
  margin: auto;
  width: calc(100% - 2rem);
  max-width: 28rem;
  padding: 0;
  border-radius: 1rem;
  background: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}

.dialog::backdrop {
  background: rgb(0 0 0 / 0.5);
  backdrop-filter: blur(4px);
}

.dialog__panel {
  padding: 1.25rem;
}

@media (min-width: 640px) {
  .dialog__panel {
    padding: 1.5rem;
  }
}

.dialog__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.dialog__heading {
  min-width: 0;
}

.dialog__title {
  font-size: 1.125rem;
  line-height: 1.375;
  font-weight: 600;
}

.dialog__category {
  margin-top: 0.125rem;
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
}

.dialog__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.375rem;
  border-radius: 0.5rem;
  color: var(--muted-foreground);
  transition: background-color 150ms, color 150ms;
}

.dialog__close:hover {
  background: var(--accent);
  color: var(--accent-foreground);
}

.dialog__close-icon {
  width: 1rem;
  height: 1rem;
}

.dialog__description {
  font-size: 0.875rem;
  line-height: 1.625;
  color: var(--muted-foreground);
  margin-bottom: 1rem;
}

.dialog__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  margin-bottom: 1.25rem;
}

.dialog__badges:empty {
  display: none;
}

.dialog__notice {
  border-radius: 0.75rem;
  border: 1px dashed var(--border);
  background: rgb(var(--muted-rgb) / 0.4);
  padding: 0.75rem 1rem;
  margin-bottom: 1.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

/* Where the Visit button goes, shown before it is pressed, so the extra step is
   informative rather than just an extra click. */
.dialog__host {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
  margin-bottom: 1.25rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dialog__host-name {
  font-weight: 500;
  color: var(--foreground);
}

.dialog__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.dialog__visit {
  display: inline-flex;
  flex: 1 1 0;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.625rem 1rem;
  border-radius: 0.75rem;
  font-size: 0.875rem;
  font-weight: 500;
  background: var(--primary);
  color: var(--primary-foreground);
  transition: opacity 150ms;
}

.dialog__visit:hover {
  opacity: 0.9;
}

.dialog__visit-icon {
  width: 1rem;
  height: 1rem;
}

.dialog__cancel {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.625rem 1rem;
  border-radius: 0.75rem;
  font-size: 0.875rem;
  font-weight: 500;
  background: var(--secondary);
  color: var(--secondary-foreground);
  transition: background-color 150ms, color 150ms;
}

.dialog__cancel:hover {
  background: var(--accent);
  color: var(--accent-foreground);
}

/* With no Visit button the Close button takes the full width, as it did when the
   React component swapped in `flex-1`. */
.dialog__visit[hidden] ~ .dialog__cancel {
  flex: 1 1 0;
}

.icon-button:focus-visible,
.search__clear:focus-visible,
.dialog__close:focus-visible,
.dialog__cancel:focus-visible,
.dialog__visit:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}
