/* Shared listing-card / directory UI components — used by every page that
   renders bot/channel/group cards (home, detail, category, guides) or the
   breadcrumb+footer chrome below the homepage. One file, cached once by the
   browser (see topbar.css/theme.css for the same reasoning), instead of the
   near-identical <style> blocks that used to be copy-pasted into every page
   template. That copy-pasting had already drifted in two places fixed here:
     - vote buttons had hover/active color feedback on the homepage only;
       detail/category/guide pages had the same buttons with no feedback.
     - the 18+ badge used a dark-mode-aware color on the homepage but a
       hardcoded light-mode-only color on the detail page.
   Page-specific layout (hero, sidebar, forms, article typography) stays
   inline in each page's own <style> block — this file is only the pieces
   that are reused, or that benefit from being cached across page views
   (a visitor browsing several listing pages in a row now downloads this
   once instead of on every single navigation).

   Borderless by design: every surface below gets its definition from
   --shadow-sm/-md (cards/popovers) or a tinted --badge-bg fill one step
   away from --surface (pills/chips/buttons) — never a 1px stroke. Where a
   fill alone wouldn't read as distinct from its container (a chip sitting
   on a card that's already --surface-colored), the chip uses --badge-bg
   specifically so it stays visible without a border. */

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .9rem; }

.listing { border-radius: 1.1rem; padding: 1.1rem; display: flex; gap: .8rem; background: var(--surface); align-items: stretch; box-shadow: var(--shadow-sm); transition: transform .15s ease, box-shadow .15s ease; }
.listing:hover { transform: translateY(-4px); box-shadow: var(--shadow-md), 0 0 0 1px color-mix(in srgb, var(--tg) 25%, transparent); }
a.listing { color: inherit; text-decoration: none; }
.listing:active { background: var(--badge-bg); }

.l-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.l-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.l-name { font-weight: 800; font-size: 1.05rem; color: var(--text); text-decoration: none; }
.l-name:hover { text-decoration: underline; }
.l-user { color: var(--tg); font-weight: 600; text-decoration: none; font-size: .82rem; display: block; margin-top: .05rem; }
.l-user:hover { text-decoration: underline; color: var(--tg-dark); }
.l-desc { color: var(--text-secondary); font-size: .84rem; margin: .4rem 0 .5rem; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Base size (46px) matches cardHtml()'s output as used by the category and
   guide pages. cardHtml() is one shared function also used for the
   homepage's server-rendered cards, so per-page size differences (home's
   cards read at 48px, the detail page's own header avatar at 64px, its
   "similar listings" avatars at 48px) stay as small size-only overrides in
   each page's own <style> block, scoped by ancestor — never a bare `.avatar`
   redeclaration that silently drops the shared radius/font rules below.
   The inset shadow is a subtle rim so an image avatar never blends into a
   similarly-toned background — not a border, so it stays even here.

   2026-09-21: square (.85rem radius) -> circular. Every Telegram profile
   photo — user, bot, channel, group — is a circle; that's the one visual
   cue this whole product is built around, so a directory *of* those things
   should use the same shape, not a generic rounded-app-icon square. */
.avatar { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; color: #fff; object-fit: cover; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); width: 46px; height: 46px; border-radius: 50%; font-size: 1rem; }

.l-type { font-size: .62rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; padding: .2em .6em; border-radius: 1em; vertical-align: middle; }
.l-type.bot { background: color-mix(in srgb, var(--tg) 18%, var(--surface)); color: var(--tg-dark); }
.l-type.channel { background: var(--badge-bg); color: var(--text-secondary); }
.l-type.group { background: color-mix(in srgb, var(--tg-dark) 20%, var(--surface)); color: var(--tg-dark); }
.badge.tag { background: var(--badge-bg); color: var(--text-secondary); border-radius: 1em; font-weight: 600; margin: 0 .25rem .25rem 0; font-size: .72rem; }
/* Monthly-user-count badge — inline SVG (matches the vote/share icon
   vocabulary already used elsewhere on a card) instead of a 👥 emoji glyph
   repeated on every single card in the grid. */
.badge.tag.stat-badge { display: inline-flex; align-items: center; gap: .25em; }
.badge.tag.stat-badge svg { width: 11px; height: 11px; flex: 0 0 auto; }
/* Flattened back to the plain neutral badge fill per "only use 2 colors" —
   was tinted per-category via --cat-hue (see hueOf() in server.js and its
   client-side mirror in index.html); that plumbing still sets --cat-hue
   inline on every card/badge, it's just unconsumed here now. */
.category-badge { background: var(--badge-bg); color: var(--text-secondary); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; padding: .2em .6em; border-radius: 1em; text-decoration: none; }
.category-badge:hover { text-decoration: underline; }
.rating-badge { color: var(--text-secondary); }

.l-actions { display: flex; align-items: center; gap: .35rem; margin-top: auto; padding-top: .6rem; flex-wrap: wrap; }
/* Same hover/active feedback everywhere a vote button appears — previously
   only the homepage's inline <style> had these states. Fill (--badge-bg),
   not a stroke, is what separates the button from the --surface card behind
   it; hover/active swap the fill/text color instead of adding a border. */
.vote-btn { display: inline-flex; align-items: center; gap: .25rem; min-height: 34px; border: 0; background: var(--badge-bg); border-radius: 2rem; padding: .2rem .55rem; font-weight: 700; color: var(--text-secondary); font-size: .72rem; cursor: pointer; transition: background .15s ease, color .15s ease; }
.vote-btn svg { width: 13px; height: 13px; flex: 0 0 13px; }
.vote-btn:disabled { opacity: .5; cursor: default; }
/* Like/dislike used to differ by hue (pink vs. blue-gray) — both neutral
   now, so the two states read apart by icon direction and fill intensity
   (active = deeper tint) instead of color. */
.vote-btn.like-btn:hover { background: color-mix(in srgb, var(--text) 12%, var(--badge-bg)); color: var(--text); }
.vote-btn.like-btn.active { background: color-mix(in srgb, var(--text) 20%, var(--badge-bg)); color: var(--text); }
.vote-btn.dislike-btn:hover { background: color-mix(in srgb, var(--text) 12%, var(--badge-bg)); color: var(--text); }
.vote-btn.dislike-btn.active { background: color-mix(in srgb, var(--text) 20%, var(--badge-bg)); color: var(--text); }

/* CTA shape: squared-off rounded-rect instead of the oval pill used by
   vote-btn/badges everywhere else, so the primary action reads as visually
   distinct rather than just a recolored pill — that distinction is carried
   by fill (solid --tg vs. neutral --badge-bg) too, so shape doesn't have to
   do all the work alone; radius is a touch softer than the sidebar controls
   for a slightly more "premium app" feel without becoming a pill. Arrow
   glyph slides right on hover for a clear tactile cue. .open-btn is the
   small card-grid version, .open-btn-lg the bigger standalone CTA on the
   detail page.
   2026-09-21: dropped the gradient fill and the infinite ctaPulse glow loop
   on .open-btn-lg/.btn-tg — flat solid --tg instead, motion now only ever
   responds to a hover/click, never runs ambient/looping (see brief: "avoid
   excessive gradients", plus looping animation is the single most common
   tell of an unreviewed AI-generated page — one attention-getting trick,
   not three running at once). */
.open-btn, .open-btn-lg, .btn-tg { border-radius: .7rem; }
.open-btn span, .open-btn-lg span { display: inline-block; transition: transform .15s ease; margin-left: .25em; }
.open-btn:hover span, .open-btn-lg:hover span { transform: translateX(3px); }

.open-btn { display: inline-flex; align-items: center; min-height: 34px; box-sizing: border-box; font-size: .72rem; padding: .2rem .55rem; font-weight: 700; text-decoration: none; white-space: nowrap; background: var(--tg); color: #fff; border: none; transition: background .15s ease, box-shadow .15s ease; }
.open-btn:hover { background: var(--tg-dark); box-shadow: var(--shadow-sm); }
.open-btn-lg { height: 34px; box-sizing: border-box; background: var(--tg); color: #fff; font-weight: 700; padding: 0 .55rem; text-decoration: none; font-size: .68rem; line-height: 32px; white-space: nowrap; box-shadow: var(--shadow-sm); transition: transform .15s ease, box-shadow .15s ease, background .15s ease; }
.open-btn-lg:hover { color: #fff; background: var(--tg-dark); box-shadow: var(--shadow-md); transform: translateY(-1px); }

/* Submit/confirm CTA -- same flat fill + lift on hover, same rounded-rect
   shape as the Open buttons above, kept in the site's own accent color. */
.btn-tg { font-weight: 700; background: var(--tg) !important; box-shadow: var(--shadow-sm); transition: transform .15s ease, box-shadow .15s ease, background .15s ease; }
.btn-tg:hover { background: var(--tg-dark) !important; box-shadow: var(--shadow-md); transform: translateY(-2px); }

/* No persistent top navbar — removed twice by explicit request. Brand mark
   (the "T" square) is a shared primitive still used by the footer's brand
   column below, kept minimal on its own. */
.brand-mark { width: 2rem; height: 2rem; border-radius: .6rem; background: var(--tg); color: #fff; font-weight: 800; font-size: 1.05rem; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }

/* Breadcrumb + footer chrome shared by every page below the homepage. Uses
   the theme tokens (var(--muted)/var(--text-secondary)) instead of the
   hardcoded grays (#9aabb9, #7b8ea0, #8aa0b3...) those blocks used to carry,
   which read slightly wrong against the dark-mode surface. */
.breadcrumb-nav { font-size: .82rem; color: var(--muted); margin: 1.2rem 0 0; display: flex; gap: .4rem; flex-wrap: wrap; }
.breadcrumb-nav a { color: var(--text-secondary); text-decoration: none; font-weight: 600; }
.breadcrumb-nav a:hover { color: var(--tg-dark); text-decoration: underline; }

/* Structured, multi-column footer — replaces the earlier single centered
   line of middot-separated links, which read as a placeholder rather than a
   finished site. Built once as siteFooterHtml() in server.js (index.html
   carries its own hand-synced copy, same convention as the header) and used
   on every page. */
footer.site-footer { border-top: 1px solid var(--border); margin-top: 2.5rem; padding-top: 2.2rem; color: var(--muted); font-size: .85rem; }
.site-footer-inner { max-width: 1180px; margin: 0 auto; padding: 0 1rem; display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: 1.6rem 1rem; }
@media (max-width: 640px) { .site-footer-inner { grid-template-columns: 1fr 1fr; } }
.footer-brand-row { display: flex; align-items: center; gap: .5rem; margin-bottom: .6rem; }
.footer-brand-row .brand-mark { width: 1.7rem; height: 1.7rem; font-size: .88rem; border-radius: .5rem; }
.footer-brand-word { font-weight: 800; color: var(--text); font-size: 1rem; }
.footer-brand p { margin: 0; line-height: 1.55; max-width: 24rem; }
.footer-col h3 { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; color: var(--text-secondary); margin: 0 0 .8rem; }
.footer-col a { display: block; color: var(--muted); text-decoration: none; margin-bottom: .55rem; font-weight: 600; }
.footer-col a:hover { color: var(--tg-dark); text-decoration: underline; }
.site-footer-bottom { max-width: 1180px; margin: 2rem auto 0; padding: 1.1rem 1rem; border-top: 1px solid var(--border); text-align: center; font-size: .78rem; }

/* ---- Detail page: main content card + related-listings list ----
   .detail-content/.detail-card had a border and no shadow at all before —
   removing the border without adding a shadow would leave them with zero
   definition against the page background, so both pick up --shadow-sm here. */
.detail-content { background: var(--surface); border-radius: 1rem; padding: 1.6rem; margin: 0 0 2rem; color: var(--text-secondary); box-shadow: var(--shadow-sm); }
.detail-content h2 { font-size: 1.05rem; font-weight: 800; color: var(--text); border-left: 4px solid var(--tg); padding: .1rem 0 .1rem .7rem; margin: 1.4rem 0 .6rem; }
.detail-content h2:first-child { margin-top: 0; }
.detail-content p, .detail-content li { font-size: .92rem; line-height: 1.6; }
.detail-content ul, .detail-content ol { padding-left: 1.3rem; margin: 0; }
.detail-content li { margin-bottom: .3rem; }
/* FAQ accordion items: a fill (not a border) marks the tappable region, and
   deepens further on hover/open instead of swapping a border color. */
.detail-content details, .faq-list details { background: var(--badge-bg); border-radius: .7rem; padding: 0; margin-bottom: .6rem; transition: background .15s; overflow: hidden; }
.detail-content details:hover, .faq-list details:hover { background: color-mix(in srgb, var(--badge-bg) 70%, var(--border-strong)); }
.detail-content details[open], .faq-list details[open] { background: color-mix(in srgb, var(--tg) 10%, var(--badge-bg)); }
.detail-content summary, .faq-list summary { font-weight: 700; color: var(--text); cursor: pointer; list-style: none; padding: .75rem .9rem; display: flex; justify-content: space-between; align-items: center; gap: .75rem; }
.detail-content summary::-webkit-details-marker, .faq-list summary::-webkit-details-marker { display: none; }
.detail-content summary::after, .faq-list summary::after { content: '+'; flex: 0 0 auto; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--surface); color: var(--tg-dark); font-size: 1rem; font-weight: 800; line-height: 1.4rem; text-align: center; transition: transform .15s, background .15s; }
.detail-content details[open] summary::after, .faq-list details[open] summary::after { content: '−'; background: var(--tg); color: #fff; }
.detail-content details p, .faq-list details p { margin: 0; padding: 0 .9rem .8rem; color: var(--text-secondary); }

.detail-card { background: var(--surface); border-radius: 1rem; padding: 1.6rem; margin: 1.2rem 0 1rem; box-shadow: var(--shadow-sm); }
.detail-title { font-size: 1.35rem; font-weight: 800; color: var(--text); margin-bottom: .3rem; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.detail-stats { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .2rem; }
.detail-user { color: var(--tg); font-weight: 600; text-decoration: none; font-size: .9rem; }
.detail-user:hover { text-decoration: underline; }
.detail-desc { margin: .8rem 0; color: var(--text-secondary); font-size: .92rem; line-height: 1.55; }
.share-btn { display: inline-flex; align-items: center; gap: .3rem; height: 34px; box-sizing: border-box; border: 0; background: var(--badge-bg); border-radius: .7rem; padding: 0 .6rem; font-weight: 700; color: var(--text-secondary); font-size: .72rem; cursor: pointer; white-space: nowrap; transition: background .15s ease, color .15s ease; }
.share-btn svg { width: 13px; height: 13px; }
.share-btn:hover { background: color-mix(in srgb, var(--tg) 16%, var(--badge-bg)); color: var(--tg-dark); }

h2.section-h { font-size: 1.05rem; font-weight: 800; color: var(--text); margin: 0 0 .9rem; }
/* Secondary/supplementary content (related listings) reads as a plain
   divided list instead of stacking more shadowed cards under the main
   content card -- repeating that treatment for every related item is what
   made the page feel like "boxes everywhere". Rows are separated by a hover
   fill and a little extra padding instead of a divider line. */
.similar-list { background: var(--surface); border-radius: 1rem; box-shadow: var(--shadow-sm); overflow: hidden; }
.similar-item { display: flex; gap: .75rem; align-items: flex-start; padding: 1rem; text-decoration: none; color: inherit; transition: background .15s ease; }
.similar-item:hover { background: var(--badge-bg); }
.similar-item:hover .l-name { text-decoration: underline; }

/* ---- Category page: related-categories chip list ---- */
.other-cats { margin: 2.5rem 0 2rem; }
.other-cats h2 { font-size: 1.05rem; font-weight: 800; color: var(--text); margin-bottom: .8rem; }
.other-cats-list { display: flex; flex-wrap: wrap; gap: .5rem; }
.other-cats-list a { background: var(--badge-bg); color: var(--text-secondary); border-radius: 2rem; padding: .35rem .9rem; font-size: .84rem; font-weight: 600; text-decoration: none; transition: background .15s ease; }
.other-cats-list a:hover { background: color-mix(in srgb, var(--tg) 12%, var(--badge-bg)); }
.other-cats-list a span { opacity: .7; }

/* ---- Guides: cross-guide nav chips + guides-index list cards ---- */
.guide-nav { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1.5rem 0 .5rem; }
.guide-nav a { background: var(--badge-bg); color: var(--text-secondary); border-radius: 2rem; padding: .35rem .9rem; font-size: .82rem; font-weight: 600; text-decoration: none; transition: background .15s ease, color .15s ease; }
.guide-nav a.active { background: var(--tg); color: #fff; }
.guide-list { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1rem; }
.guide-card { display: flex; align-items: flex-start; gap: 1rem; background: var(--surface); border-radius: 1rem; padding: 1.2rem 1.4rem; text-decoration: none; transition: box-shadow .15s ease, transform .15s ease; box-shadow: var(--shadow-sm); }
.guide-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.guide-card-icon { flex: none; display: flex; align-items: center; justify-content: center; width: 2.75rem; height: 2.75rem; border-radius: var(--radius-md); background: var(--badge-bg); font-size: 1.2rem; font-weight: 800; color: var(--tg-dark); }
.guide-card-body { min-width: 0; }
.guide-card h2 { font-size: 1.1rem; font-weight: 800; color: var(--text); margin: 0 0 .2rem; }
.guide-card-date { color: var(--muted); font-size: .75rem; font-weight: 600; margin: 0 0 .4rem; }
.guide-card p:last-child { color: var(--text-secondary); font-size: .88rem; margin: 0; }

/* ---- Home: category-silo preview grid (the "Popular categories" section) ---
   Left accent bar + emoji chip used the per-category --cat-hue rainbow
   before; flattened to the single --tg accent + neutral fill per "only use
   2 colors" — see components.css's .category-badge comment above. */
.cat-silo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .8rem; margin: .2rem 0 1.6rem; }
.cat-silo-card { display: flex; gap: .7rem; align-items: flex-start; background: var(--surface); border-radius: .9rem; padding: .85rem .9rem; text-decoration: none; color: inherit; box-shadow: var(--shadow-sm); border-left: 4px solid var(--tg); transition: box-shadow .15s ease, transform .15s ease; }
.cat-silo-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
/* Letter monogram, not emoji — same treatment as the avatar-without-image
   fallback (tinted box + bold initial), reused here instead of a second icon
   system. Raw emoji render inconsistently across platforms/fonts and read as
   visual clutter repeated across a whole grid of cards. */
.cat-silo-emoji { font-weight: 800; color: var(--tg-dark); font-size: 1rem; line-height: 1; flex: 0 0 auto; width: 2.1rem; height: 2.1rem; display: inline-flex; align-items: center; justify-content: center; border-radius: .7rem; background: var(--badge-bg); }
.cat-silo-body { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.cat-silo-name { font-weight: 700; color: var(--text); font-size: .92rem; }
.cat-silo-count { color: var(--muted); font-weight: 500; }
.cat-silo-blurb { color: var(--text-secondary); font-size: .8rem; line-height: 1.35; }

/* ---- Shared top utility bar (logo + search + language select) ----
   Was inline-only in index.html; moved here so detail/category/guide/static
   pages (server.js templates) can render the same header via siteHeaderHtml()
   instead of just a bare breadcrumb. Still not a navbar (no nav links, no
   menu) per explicit request -- search is a utility control, same category
   as the language select next to it, not navigation.

   Mobile-first: on a narrow viewport the search box wraps to its own full-
   width row below brand+language (order: 3, flex-basis 100%) rather than
   being squeezed into one crowded row -- this is the one row most likely to
   get tapped on a phone, so it gets the width. From 640px up there's room
   for all three side by side. */
.top-utility-bar { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem .6rem; max-width: 1180px; margin: 0 auto; padding: .6rem .8rem; border-bottom: 1px solid var(--border); }
.utility-brand { display: inline-flex; align-items: center; gap: .4rem; font-weight: 800; font-size: 1.2rem; color: var(--text); letter-spacing: -.01em; text-decoration: none; white-space: nowrap; min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; order: 1; }
.utility-brand .brand-mark { flex: 0 0 auto; }
.utility-search { position: relative; display: flex; align-items: center; order: 3; flex: 1 1 100%; }
.utility-search svg { position: absolute; left: .7rem; width: 14px; height: 14px; color: var(--muted); pointer-events: none; }
.utility-search input { width: 100%; box-sizing: border-box; border: 0; border-radius: var(--radius-pill); background: color-mix(in srgb, var(--text) 5%, var(--bg)); padding: .45rem .8rem .45rem 2.1rem; font-size: .85rem; font-family: inherit; color: var(--text); transition: background .15s ease, box-shadow .15s ease; }
.utility-search input:hover { background: color-mix(in srgb, var(--text) 8%, var(--bg)); }
.utility-search input:focus { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--tg) 22%, transparent); background: var(--surface); }
.utility-controls { display: flex; align-items: center; gap: .4rem; flex-wrap: nowrap; flex: 0 0 auto; order: 2; margin-left: auto; }
.utility-controls #langSelect { width: auto; max-width: 96px; flex: 0 0 auto; }
[dir="rtl"] .utility-search svg { left: auto; right: .7rem; }
[dir="rtl"] .utility-search input { padding: .45rem 2.1rem .45rem .8rem; }
@media (min-width: 640px) {
  .utility-search { order: 2; flex: 1 1 auto; max-width: 340px; }
  .utility-controls { order: 3; margin-left: 0; }
}
@media (max-width: 480px) {
  .top-utility-bar { padding: .5rem .6rem; }
  .utility-brand { font-size: .98rem; }
  .utility-brand .brand-mark { width: 1.5rem; height: 1.5rem; font-size: .8rem; border-radius: .45rem; }
  .utility-controls #langSelect { max-width: 74px; font-size: .72rem; padding-left: .4rem; padding-right: 1.2rem; }
}

/* ---- Social share links (X/Facebook/WhatsApp/Telegram) on detail pages ----
   Complements the native Web Share API button (.share-btn) -- that one only
   works via a user gesture and isn't visible to static-HTML SEO scanners, so
   these give both real desktop users and crawlers actual share affordances. */
.social-share-row { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .6rem; }
.social-share-row a { background: var(--badge-bg); color: var(--text-secondary); font-size: .78rem; font-weight: 600; padding: .3rem .7rem; border-radius: 2rem; text-decoration: none; transition: background .15s ease, color .15s ease; }
.social-share-row a:hover { background: color-mix(in srgb, var(--tg) 14%, var(--badge-bg)); color: var(--tg-dark); }
