/* Code Library — list / detail / form / per-doc card. Theme tokens only. */

.snip-shell {
  max-width: 1100px;
  margin: 12px auto 64px;
  padding: 0 20px;
}
.snip-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 20px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.snip-hero-text { flex: 1; min-width: 0; }
.snip-title {
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 600;
  color: var(--text-0);
  margin: 0 0 6px;
  letter-spacing: -0.01em;
}
.snip-subtitle {
  font-size: 14px;
  color: var(--text-2);
  margin: 0;
  font-family: var(--font-sans);
}

.snip-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 18px;
  background: var(--accent) !important;
  color: #1a0e00 !important;
  border-radius: 8px;
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--accent) !important;
  white-space: nowrap;
  transition: filter 120ms ease, transform 120ms ease;
}
.snip-cta:hover { filter: brightness(1.05); transform: translateY(-1px); }
/* CTA now sits on the left, stacked under the title. */
.snip-hero-text .snip-cta { margin-top: 14px; }

/* ─── Anonymous-visitor signup CTA (Code Library + snippet detail) ───
   A small terminal window that mirrors the real /login/ card: a
   `$ rvtdocs auth` prompt + two OAuth provider buttons, with a darker
   footer strip of one-line perk tickboxes. Orange stays an accent. */
.snip-join {
  position: relative;
  margin: 0 0 28px;
  background: color-mix(in srgb, var(--bg-0) 84%, #000);
  border: 1px solid var(--accent);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.18), 0 0 0 4px var(--accent-glow);
}
/* When woven into the card grid (spanning all columns) it gets its own
   breathing room above + below so it doesn't crowd the snippet rows. */
.snip-join-gridwrap { grid-column: 1 / -1; margin: 16px 0; }
.snip-join-gridwrap .snip-join { margin: 0; }
.snip-join-bar {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 15px;
  background: color-mix(in srgb, var(--bg-0) 68%, #000);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-3);
}
.snip-join-dots { display: inline-flex; gap: 6px; flex-shrink: 0; }
.snip-join-dots > span {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--bg-3);
  animation: snip-join-dot-pulse 3.6s ease-in-out infinite;
}
.snip-join-dots > span:nth-child(2) { animation-delay: 0.5s; }
.snip-join-dots > span:nth-child(3) { animation-delay: 1s; }
@keyframes snip-join-dot-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.snip-join-dots > span:nth-child(1) { background: #ff6b8a; }
.snip-join-dots > span:nth-child(2) { background: var(--accent); }
.snip-join-dots > span:nth-child(3) { background: #7ad9b9; }
.snip-join-path { letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.snip-join-body { padding: 24px 28px 26px; }
.snip-join-cmd {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-2);
  margin: 0 0 14px;
}
/* Blinking block caret after the typed command, like a live terminal. */
.snip-join-cmd::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 14px;
  margin-left: 7px;
  vertical-align: -2px;
  background: var(--accent);
  animation: snip-join-caret 1.1s steps(1) infinite;
}
@keyframes snip-join-caret {
  50% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .snip-join-cmd::after,
  .snip-join-dots > span { animation: none; }
}
.snip-join-prompt { color: var(--accent); font-weight: 700; margin-right: 9px; }
.snip-join-title {
  font-family: var(--font-sans);
  font-size: 23px;
  font-weight: 700;
  color: var(--text-0);
  letter-spacing: -0.012em;
  line-height: 1.15;
  margin: 0 0 16px;
}
/* OAuth provider options, terminal-style like /login/: no card background,
   just a number + brand icon + label that tints on hover. */
.snip-join-providers {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.snip-join-provider {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: 5px;
  color: var(--text-0);
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  transition: background 120ms ease;
}
.snip-join-provider:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.snip-join-provider-num {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
}
.snip-join-provider-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.snip-join-provider-icon svg { width: 18px; height: 18px; display: block; }
.snip-join-provider-label { flex: 1; }

/* Darker footer strip: one-line perk tickboxes, spaced equally across the
   width (wraps to centered on narrow widths). */
.snip-join-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  padding: 12px 22px;
  background: color-mix(in srgb, var(--bg-0) 68%, #000);
  border-top: 1px solid var(--border);
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-2);
}
.snip-join-perk { position: relative; padding-left: 17px; white-space: nowrap; }
.snip-join-perk::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  width: 4px;
  height: 8px;
  border: solid var(--accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ─── Filter bar — 3 rows: search · lang pills · tag chips ──────────── */
.snip-filterbar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 24px;
}
.snip-filterbar-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.snip-search-box {
  position: relative;
  flex: 1;
  min-width: 220px;
}
.snip-search-box svg {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-3);
  pointer-events: none;
}
.snip-search-box input {
  width: 100%;
  padding: 10px 12px 10px 36px;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-0);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.snip-search-box input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.snip-search-box input::placeholder { color: var(--text-3); }

/* Language pills — chip-style toggles. */
.snip-lang-pills {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.snip-lang-pill {
  padding: 5px 12px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Casing preserved as written (pyRevit / C# / Dynamo). */
  color: var(--text-2);
  text-decoration: none;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.snip-lang-pill:hover { border-color: var(--accent); color: var(--accent); }
.snip-lang-pill.is-active {
  background: var(--accent-glow);
  border-color: var(--accent);
  color: var(--accent);
}

/* Active filter chips with × removal — one row of currently-applied filters. */
.snip-active-chips {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-3);
}
.snip-active-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 4px 3px 10px;
  background: var(--accent-glow);
  border: 1px solid var(--accent);
  border-radius: 999px;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  text-decoration: none;
}
.snip-active-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: transparent;
  color: var(--accent);
  font-size: 14px;
  line-height: 1;
}
.snip-active-chip:hover .snip-active-chip-x {
  background: var(--accent);
  color: #1a0e00;
}
.snip-clear-all {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  text-decoration: none;
  margin-left: auto;
}
.snip-clear-all:hover { color: var(--accent); }

.snip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 22px;
}
.snip-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  transition: transform 120ms ease, border-color 120ms ease;
  color: inherit;
  position: relative;
  /* No overflow:hidden — the kebab dropdown needs to pop OUT of the
     card. We rely on individual children (preview, etc.) to clip
     their own overflow as needed. */
}
.snip-card:hover { transform: translateY(-2px); border-color: var(--accent); }

/* Card foot. Author with avatar on left, likes + views on right. */
.snip-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
}
.snip-card-author {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-2);
  text-decoration: none;
}
.snip-card-avatar,
.snip-card-avatar-fallback {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--bg-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  object-fit: cover;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--text-0);
}
.snip-card-stats {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.snip-card-vote {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-2);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
  position: relative;
  z-index: 3;
}
.snip-card-vote:hover { color: var(--accent); border-color: var(--accent); }
.snip-card-vote.is-active {
  background: var(--accent-glow);
  border-color: var(--accent);
  color: var(--accent);
}

/* Stretched-link pattern: the card title is the actual <a>, but its
   ::after extends to cover the entire card so the whole card behaves
   like a single clickable link. Other interactive children (the
   language chip) sit on a higher z-index so their own clicks win. */
.snip-card-stretched {
  color: var(--text-0);
  text-decoration: none;
}
.snip-card-stretched::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
}
.snip-card > * { position: relative; z-index: 1; }
.snip-card-hd, .snip-tags, .snip-related-badges { position: relative; z-index: 2; }
.snip-lang-chip--link {
  text-decoration: none;
  cursor: pointer;
  transition: filter 120ms ease, background 120ms ease;
}
.snip-lang-chip--link:hover { filter: brightness(1.1); background: var(--accent-glow); }

/* Single-line card head: title flex:1 with ellipsis, then a small chip
   group on the right (lock + lang tag + kebab). The whole row sits at
   the same baseline. */
.snip-card-hd {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.snip-card-hd .snip-card-title {
  flex: 1;
  min-width: 0;
  margin: 0;
}
.snip-card-hd .snip-card-title .snip-card-stretched {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.snip-card-hd-right {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
/* Description preview between title and code on list cards. 2-line
   clamp, dark-gray so it reads as supporting text rather than primary
   content. The card's stretched-link still wins clicks (z-index 0). */
.snip-card-desc {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Low-key language tag — plain mono text, no bubble. Replaces the orange
   chip on cards. Hover shows the underline cue so it still reads as a
   filterable link. */
.snip-lang-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  text-decoration: none;
  letter-spacing: 0.02em;
  position: relative;
  z-index: 3;
  transition: color 120ms ease;
}
.snip-lang-tag:hover { color: var(--accent); }
/* Card-mounted kebab. Smaller than the detail-page kebab so it doesn't
   dominate the head row. Sits above the stretched-link via z-index. */
.snip-card-kebab { z-index: 3; }
.snip-card-kebab .snip-kebab-btn {
  width: 22px;
  height: 22px;
  font-size: 14px;
  line-height: 1;
}

/* Footer flipped variant: stats on the left, author on the right. The
   default uses justify-content: space-between which already lays children
   out left-to-right; we just rely on the template emitting them in the
   reversed order. This modifier exists as a hook in case we want to add
   alignment tweaks later (and so the modifier name documents intent). */
.snip-card-foot--flipped {}
.snip-lang-chip {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text-1);
  border: 1px solid var(--border-strong);
  /* Casing preserved as written: pyRevit / C# / Dynamo. No uppercase. */
}
/* Language chips share the warm-carbon accent (orange) so they read
   as part of the theme. The data-lang attribute is preserved as a
   hook for future subtle differentiation (icon, opacity) without
   reverting to off-theme colors. */
.snip-lang-chip[data-lang="pyrevit"],
.snip-lang-chip[data-lang="python"],
.snip-lang-chip[data-lang="csharp"],
.snip-lang-chip[data-lang="dynamo"] {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-glow);
}

.snip-card-title {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  color: var(--text-0);
  margin: 0;
}
.snip-card-meta {
  display: flex;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  align-items: center;
}
.snip-card-preview {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-1);
  background: var(--bg-2);
  border: 1px solid var(--border);
  padding: 8px 10px;
  border-radius: 8px;
  white-space: pre;
  overflow: hidden;
  max-height: 110px;
  position: relative;
}
.snip-card-preview::after {
  content: '';
  position: absolute;
  inset: 60% 0 0 0;
  background: linear-gradient(180deg, transparent, var(--bg-1) 95%);
  pointer-events: none;
}

/* Related-class badges on each snippet card — uses the type-icon
   convention (C in a blue chip = Class). */
.snip-related-badges {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  align-items: center;
}
.snip-related-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px 2px 4px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-1);
}
.snip-related-badge-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--type-class);
  border-radius: 3px;
}
.snip-related-badge-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}
.snip-related-badge--more {
  padding: 2px 8px;
  color: var(--text-3);
  font-weight: 600;
}

.snip-tags {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}
.snip-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  background: var(--bg-2);
  color: var(--text-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}

/* Detail page */
.snip-detail-grid {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 24px;
}
@media (max-width: 900px) { .snip-detail-grid { grid-template-columns: 1fr; } }
.snip-detail-card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 22px;
}
.snip-detail-title {
  font-family: var(--font-sans);
  font-size: 24px;
  font-weight: 600;
  color: var(--text-0);
  margin: 0 0 6px;
}
.snip-detail-author {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-2);
  margin-bottom: 18px;
}

/* Constrain the byline avatar. Without an explicit size the <img>
   renders at the source's native resolution (often ~1k px and
   blowing the layout). 28x28 round, object-fit cover. Same shape
   for the initials fallback span. */
.snip-detail-avatar,
.snip-detail-avatar-fallback {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  object-fit: cover;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-0);
  flex-shrink: 0;
  border: 1px solid var(--border-strong);
}
.snip-detail-author--compact {
  flex-wrap: wrap;
}

/* Single-card layout (no right sidebar). */
.snip-shell--single { max-width: 920px; }
.snip-detail-card { position: relative; }

/* Top-right floating cluster: visibility icon + 3-dot menu. Absolute
   so opening the menu lays over content without resizing the card. */
.snip-detail-corner {
  position: absolute;
  top: 14px;
  right: 14px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  z-index: 10;
}
/* Topbar: stats on the left, visibility + kebab on the right. */
.snip-detail-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.snip-detail-stats {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.snip-vote-btn--inline {
  padding: 5px 12px;
  font-size: 12.5px;
}
.snip-detail-stat {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-2);
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid var(--border);
}
.snip-detail-topbar-right {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Override the absolute-positioned vis-icon when used inline in the topbar. */
.snip-vis-icon--inline {
  position: static;
  top: auto;
  right: auto;
}

/* Kebab menu (3 dots). */
.snip-kebab { position: relative; }
.snip-kebab-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border) !important;
  background: var(--bg-2) !important;
  border-radius: 50%;
  color: var(--text-2);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snip-kebab-btn:hover { color: var(--accent); border-color: var(--accent) !important; }
.snip-kebab-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 140px;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  z-index: 50;
  display: flex;
  flex-direction: column;
}
.snip-kebab-menu[hidden] { display: none; }
.snip-kebab-menu a,
.snip-kebab-menu button {
  display: block;
  text-align: left;
  width: 100%;
  padding: 8px 10px;
  background: transparent !important;
  border: 0 !important;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-1);
  cursor: pointer;
  border-radius: 6px;
  text-decoration: none;
}
.snip-kebab-menu a:hover,
.snip-kebab-menu button:hover { background: var(--bg-2) !important; color: var(--text-0); }
.snip-kebab-menu form { margin: 0; }
.snip-kebab-menu .snip-kebab-danger:hover { color: var(--danger); }

/* ── New: bullet-proof code box ───────────────────────────────────────
   Single scroll container (`.snip-codebox`) wraps a flex row of
   gutter + <pre>. Neither child has its own overflow — vertical and
   horizontal scroll happen ONLY at the wrapper, so the gutter line
   numbers always track the code as the user scrolls. */
.snip-codebox {
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: auto;
  /* `max-height` is set inline by the JS when collapsed (>20 lines). */
}
.snip-codebox-row {
  display: flex;
  align-items: stretch;
  /* Row's natural height = max(gutter, code) = full content height.
     The wrapper's max-height + overflow does the actual scrolling. */
}
.snip-codebox-gutter {
  flex-shrink: 0;
  padding: 12px 10px 12px 14px;
  min-width: 40px;
  background: var(--bg-0);
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.5;
  white-space: pre;
  text-align: left;
  user-select: none;
  border-right: 1px solid var(--border);
}
.snip-codebox-pre {
  flex: 1;
  margin: 0;
  padding: 12px 14px;
  background: transparent !important;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.5;
  white-space: pre;
  /* CRITICAL: do NOT set overflow here — let the wrapper scroll. */
  overflow: visible !important;
}
.snip-codebox-pre code,
.snip-codebox-pre code.hljs {
  background: transparent !important;
  display: block;
  padding: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  color: var(--text-0);
  white-space: pre;
  overflow: visible !important;
}

/* Code block: flex row so gutter sits beside the code, not above.
   Background unified with the card's bg-1 so it reads as part of the
   card surface, not a contrasting embedded box. The gutter is the same
   bg-1 with only a 1px right border for visual separation. */
.snip-detail-code {
  display: flex;
  align-items: stretch;
  margin: 0;
  padding: 0;
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.5;
  /* Single scroll container — both axes scroll on the <pre> so gutter
     and code track together (no double scrollbar). The collapse JS
     sets max-height to clip past 20 lines. */
  overflow: auto;
}
.snip-detail-gutter {
  flex-shrink: 0;
  padding: 12px 10px 12px 14px;
  background: var(--bg-0);
  color: var(--text-3);
  /* Left-aligned so all numbers start flush at the same x position
     (1, 2, ..., 10, 11 read as a clean column instead of right-shifted
     single-digits sitting where the tens column will eventually be). */
  text-align: left;
  user-select: none;
  white-space: pre;
  border-right: 1px solid var(--border);
  min-width: 40px;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
}
.snip-detail-code code,
.snip-detail-code code.hljs {
  flex: 1;
  min-width: 0;
  padding: 12px 14px;
  background: transparent !important;
  display: block;
  white-space: pre;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  color: var(--text-0);
}

/* Hero image. */
.snip-detail-hero {
  display: block;
  max-width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 10px;
  margin: 0 0 16px;
}

/* Description block. Visually wrapped in its own subtle card so it's
   clearly separated from the code section below. */
.snip-detail-description {
  margin-bottom: 18px;
  padding: 16px 18px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.snip-detail-description .cmt-body {
  color: var(--text-1);
}
.snip-detail-description .cmt-body :first-child { margin-top: 0; }
.snip-detail-description .cmt-body :last-child  { margin-bottom: 0; }

/* Code section header: section title on the left, small language
   label on the right. Replaces the old corner overlay tag (which
   was clashing with the Copy button). */
.snip-detail-code-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.snip-detail-code-lang {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-3);
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid var(--border);
}
.snip-detail-section-title {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 8px;
}
.snip-detail-code-section { margin-bottom: 16px; }

/* Show-more for collapsed code. */
.snip-code-expand {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 6px 14px;
  background: var(--bg-2) !important;
  border: 1px dashed var(--border-strong) !important;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-2);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snip-code-expand:hover {
  color: var(--accent);
  border-color: var(--accent) !important;
  background: var(--accent-glow) !important;
}
/* The base rule sets display:inline-flex, which would override the HTML
   `hidden` attribute (a CSS display wins over [hidden]'s UA display:none).
   Restore it so the button only shows once the script unhides it for code
   longer than the collapse threshold. */
.snip-code-expand[hidden] { display: none !important; }

/* YouTube link */
.snip-detail-yt {
  margin: 14px 0 0;
  font-family: var(--font-mono);
  font-size: 12px;
}
.snip-detail-yt a { color: var(--accent); text-decoration: none; }
.snip-detail-yt a:hover { text-decoration: underline; }

/* Inline meta strip: linked pages + tags. Replaces the old right
   sidebar entirely. Sits flush under the author byline; the
   separator line is below this block (between meta and the
   description/hero). */
.snip-detail-meta {
  margin-top: 0;
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.snip-detail-meta-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex-wrap: wrap;
}
.snip-detail-meta-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
  min-width: 70px;
  padding-top: 4px;
  flex-shrink: 0;
}
.snip-detail-meta-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
.snip-detail-page-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 4px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-1);
  text-decoration: none;
  transition: border-color 120ms ease, color 120ms ease;
}
.snip-detail-page-badge:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.snip-detail-page-badge-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--type-class);
  border-radius: 4px;
}

/* Liked-by row */
.snip-detail-upvoters {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}
.snip-detail-upvoters .snip-upvoters-stack {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.snip-detail-comments { margin-top: 24px; }
.snip-detail-author a { color: var(--accent); text-decoration: none; }

.snip-code-wrap { position: relative; }
.snip-code-wrap pre {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  overflow-x: auto;
}
/* Detail-card variant: the inner <pre class="snip-detail-code"> handles
   its own scroll on the parent (single bar across gutter + code). The
   generic `.snip-code-wrap pre` rule above adds an extra overflow-x
   that creates a duplicate horizontal scrollbar — neutralise it here. */
.snip-code-wrap--detail .snip-detail-code {
  background: var(--bg-0);
  padding: 0;
  overflow: auto;
}
.snip-code-wrap--detail pre {
  overflow-x: visible;
}
.snip-copy-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 4px 8px;
  background: var(--bg-3) !important;
  color: var(--text-1);
  border-radius: 6px;
  border: 1px solid var(--border-strong) !important;
  cursor: pointer;
}
.snip-copy-btn:hover { color: var(--accent); }

.snip-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.snip-side-card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
}
.snip-side-card-title {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 8px;
}

.snip-pagepins {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.snip-pagepin {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-1);
  text-decoration: none;
}
.snip-pagepin:hover { color: var(--accent); border-color: var(--accent); }

/* ─── Upvoter avatars (small stack on the snippet detail card) ─────── */
.snip-upvoters-stack {
  display: flex;
  align-items: center;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.snip-upvoter-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  margin-left: -6px;
  background: var(--bg-3);
  border: 2px solid var(--bg-1);
  overflow: hidden;
  text-decoration: none;
  position: relative;
  transition: transform 120ms ease, z-index 0s 120ms;
}
.snip-upvoter-avatar:first-child { margin-left: 0; }
.snip-upvoter-avatar:hover {
  transform: translateY(-2px) scale(1.08);
  z-index: 5;
  transition-delay: 0s;
}
.snip-upvoter-avatar img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.snip-upvoter-fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  color: var(--text-0);
  background: var(--bg-3);
}
.snip-upvoter-more {
  margin-left: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
}
.snip-upvoter-more:hover { text-decoration: underline; }

/* Full-list upvoters page rows. */
.snip-upvoters-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.snip-upvoter-row {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 10px;
  text-decoration: none;
  color: inherit;
}
.snip-upvoter-row:hover { border-color: var(--accent); }
.snip-upvoter-when {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
}

.snip-vote-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  background: var(--bg-2) !important;
  border: 1px solid var(--border-strong) !important;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-1);
  cursor: pointer;
}
.snip-vote-btn:hover { color: var(--accent); border-color: var(--accent) !important; }
.snip-vote-btn.is-active { color: var(--accent); border-color: var(--accent) !important; background: var(--accent-glow) !important; }

/* Form page */
.snip-form-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 18px;
  margin-top: 24px;  /* breathing room below the subtitle */
}
@media (max-width: 900px) { .snip-form-grid { grid-template-columns: 1fr; } }

/* Hero-image widget on the snippet form. Custom replacement for the
   default `<input type="file">` chrome so the user sees the image
   itself, not a Windows-style file path. */
.snip-image-widget {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.snip-image-preview {
  width: 100%;
  max-width: 320px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.snip-image-preview img {
  display: block;
  width: 100%;
  max-height: 200px;
  object-fit: cover;
}
.snip-image-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.snip-image-actions .cmt-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  background: transparent !important;
  border: 1px solid var(--border) !important;
  color: var(--text-3) !important;
  border-radius: 6px;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.snip-image-actions .cmt-btn:hover {
  color: var(--text-1) !important;
  border-color: var(--border-strong) !important;
}
.snip-image-actions .snip-image-remove:hover {
  color: var(--danger) !important;
  border-color: var(--danger) !important;
}

/* ───────────────────────────────────────────────────────────────────
   Form -> preview block. Lives at the bottom of /snippets/new and
   /snippets/<slug>/edit, separated from the form above by a subtle
   accent-tinted divider. Inside, two rows (card + detail-page) stack
   vertically, EACH rendered by the same partial the live library /
   detail page uses (`_snip_grid_card.html` + `_snip_detail_card.html`).
   ─────────────────────────────────────────────────────────────────── */
.snip-preview-divider {
  margin: 36px 0 24px;
  height: 1px;
  border: 0;
  /* Transparent at the edges, faint warm-accent through the middle so
     the eye reads it as a soft glow rather than a hard rule. */
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(255, 180, 84, 0.04) 15%,
    rgba(255, 180, 84, 0.22) 50%,
    rgba(255, 180, 84, 0.04) 85%,
    transparent 100%
  );
}

.snip-preview-section { margin-bottom: 48px; }

.snip-preview-header { margin-bottom: 20px; }
.snip-preview-title {
  margin: 0 0 4px;
  font-family: var(--font-sans);
  font-size: 22px;
  font-weight: 600;
  color: var(--text-0);
}
.snip-preview-sub {
  margin: 0;
  font-size: 13px;
  color: var(--text-2);
}

.snip-preview-stack {
  display: flex;
  flex-direction: column;
  gap: 32px;
}
.snip-preview-row { display: block; }

.snip-preview-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 10px;
}

/* Card preview wrapper: width-matched to a single live library card
   so the preview reads "this is what one card looks like" not "a
   sea of cards". Library uses a CSS-grid of cards; here we constrain
   to one column's worth (max ~360px). */
.snip-preview-card-host {
  max-width: 360px;
}

/* Disable real navigation inside the preview block — links and
   buttons there are decorative; clicking them would either 404 (slug
   is `preview`) or fire form actions (kebab/delete) that don't make
   sense here. The block stays visually identical to the live render,
   it just doesn't accept clicks. */
.snip-preview-section a,
.snip-preview-section button:not([type="submit"]) {
  pointer-events: none;
}

/* Line-numbered code editor for the snippet form.

   Layout: a column container with two children:
     1) `.snip-code-tabs`   — the language tab strip on top
     2) `.snip-code-body`   — a row containing the line-number gutter
                              on the left and the textarea on the right
*/
.rvt-code-input.snip-code-box {
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  overflow: hidden;
  /* Stretch to match the right column's height (the grid row) so
     both columns end on the same line. A long paste does NOT grow
     the box — the inner textarea scrolls (overflow: auto). The flex
     chain has `min-height: 0` all the way down so the textarea
     respects the box height instead of pushing it taller. */
  flex: 1;
  min-height: 360px;
  resize: vertical;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.rvt-code-input.snip-code-box:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* Tab strip (Python / C#). Sits flush against the top of the box,
   subtle background, active tab gets a colored bottom border. */
.snip-code-tabs {
  display: flex;
  flex-shrink: 0;
  background: var(--bg-1);
  border-bottom: 1px solid var(--border);
}
.snip-code-tab {
  padding: 9px 18px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-3);
  cursor: pointer;
  user-select: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snip-code-tab:hover { color: var(--text-1); }
input[name="language"]:checked + .snip-code-tab {
  color: var(--accent);
  border-bottom-color: var(--accent);
  background: var(--bg-2);
}

/* Body: gutter (left) + textarea (right), stretched to fill height.
   Position relative so the syntax-highlight overlay can layer in. */
.snip-code-body {
  display: flex;
  align-items: stretch;
  flex: 1;
  min-height: 0;
  position: relative;
}

/* Overlay where hljs renders the colored code. The textarea above
   has transparent text but a visible caret; the user types and the
   colors appear through here. Same font / line-height / padding as
   the textarea so glyphs align exactly. */
.snip-code-overlay {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 36px;       /* matches gutter min-width */
  right: 0;
  margin: 0;
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
  background: transparent !important;
  color: var(--text-0);
}
.snip-code-overlay code,
.snip-code-overlay code.hljs {
  background: transparent !important;
  padding: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  white-space: pre;
  display: block;
}

/* Textarea: caret visible, glyphs transparent so the overlay's
   colors come through. Internal scroll — content past the box height
   scrolls inside instead of growing the box (which would push the
   form-grid taller than the right column). The user can manually
   resize the box from the corner via `resize: vertical` on .snip-code-box. */
.rvt-code-input.snip-code-box textarea {
  position: relative;
  z-index: 2;
  color: transparent !important;
  caret-color: var(--text-0);
  -webkit-text-fill-color: transparent;
  flex: 1;
  min-height: 0;
  overflow: auto;
  resize: none;
}
/* Selection still readable: keep the highlight alpha so the user
   can see what they've selected even though the text is transparent. */
.rvt-code-input.snip-code-box textarea::selection {
  background: var(--accent-glow);
  color: var(--text-0);
  -webkit-text-fill-color: var(--text-0);
}
.rvt-code-gutter {
  flex-shrink: 0;
  padding: 10px 10px 10px 12px;
  margin: 0;
  background: var(--bg-1);
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
  /* Left-aligned: numbers start flush at the gutter's left edge,
     so multi-digit lines don't visually shove single-digit ones. */
  text-align: left;
  user-select: none;
  border-right: 1px solid var(--border);
  white-space: pre;
  min-width: 36px;
  overflow: hidden;
}
.rvt-code-input textarea {
  flex: 1;
  min-width: 0;
  border: 0 !important;
  background: transparent !important;
  padding: 10px 12px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-0);
  outline: none !important;
  box-shadow: none !important;
  resize: none;
  white-space: pre;
  overflow-x: auto;
  overflow-y: auto;
  tab-size: 4;
  width: 100%;
}
.snip-code-field { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.snip-code-field .rvt-code-input { flex: 1; min-height: 0; }

/* Subtle language tag on the code block (replaces the bright pyRevit
   chip in the byline). Positioned top-right of the code wrap, low
   opacity, mono. Hover reveals it fully. */
.snip-code-wrap { position: relative; }
.snip-code-lang-tag {
  position: absolute;
  top: 6px;
  right: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--text-3);
  background: transparent;
  padding: 1px 6px;
  border-radius: 4px;
  pointer-events: none;
  text-transform: lowercase;
  z-index: 2;
}
.snip-code-wrap:hover .snip-code-lang-tag { color: var(--text-1); }

/* Card preview language tag: same idea, positioned over the .pre
   preview area on the small card. */
.snip-card-preview { position: relative; }
.snip-card-preview .snip-code-lang-tag {
  top: 4px;
  right: 6px;
}

/* Visibility icon (top-right corner of card / detail-card). Just an
   icon, hover for a tooltip via the `title` attr. */
.snip-vis-icon {
  position: absolute;
  top: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-2);
  color: var(--text-2);
  z-index: 3;
}
.snip-vis-icon[data-vis="private"] { color: var(--accent); border-color: var(--accent); }

/* Layout: each column at its natural height. JS measures the right
   column's height and clamps the left column's `max-height` to it so
   the code editor never exceeds the form fields on the right. The
   textarea inside scrolls to absorb overflow. The user can still
   manually grow the box from the corner via `resize: vertical` on
   `.snip-code-box`. */
/* Stretch both columns to the row's natural height (driven by the
   right-column form fields), so the code editor on the left grows to
   match the right column instead of being shorter. The textarea
   inside scrolls to absorb overflow when the user types more code
   than fits. */
.snip-form-grid {
  align-items: stretch;
}
.snip-form-left {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.snip-form-right {
  display: flex;
  flex-direction: column;
}

/* Right-gutter preview takeover. Used by `profile_edit.html` (the
   profile-card live preview hijacks `#rvt-toc` so the user sees their
   changes as a sticky card on the right). The snippet form no longer
   uses this mount — its preview lives at the bottom of the form, see
   `.snip-preview-section` above — but the class is kept for profile-edit
   and any future gutter takeover. */
.rvt-toc--preview {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.rvt-toc--preview .rvt-toc-header {
  flex-shrink: 0;
  margin: 0;
  padding: 14px 14px 10px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-0);
}
.rvt-toc--preview .rvt-toc-list { display: none; }
.rvt-toc--preview > #profile-preview-block {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 14px 16px;
}

/* Subtler hint suffix on field labels — used for "(up to 10)" etc. so the
   primary label reads as the action and the count reads as a footnote. */
.field-label .snip-label-hint {
  margin-left: 6px;
  font-weight: 400;
  font-size: 11.5px;
  color: var(--text-3);
  letter-spacing: 0;
}

/* Description textarea — placeholder is multi-line markdown example, so
   it needs `white-space: pre-wrap` to render the line breaks. */
textarea.rvt-input[name="description"] {
  white-space: pre-wrap;
  min-height: 160px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
}
textarea.rvt-input[name="description"]::placeholder {
  white-space: pre-wrap;
  font-family: var(--font-mono);
  color: var(--text-3);
  opacity: 0.65;
}

/* Language picker. Three chips, click to select. Casing preserved. */
.snip-lang-picker {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.snip-lang-pick {
  padding: 8px 16px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
  cursor: pointer;
  user-select: none;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease, transform 120ms ease;
}
.snip-lang-pick:hover { border-color: var(--accent); color: var(--accent); }
input[name="language"]:checked + .snip-lang-pick {
  background: var(--accent-glow);
  border-color: var(--accent);
  color: var(--accent);
}
/* Selected state for ALL language picks: warm accent (theme-aligned).
   data-lang stays as a hook in case we want subtle per-language
   differentiation later (icon glyph, weight) without leaving theme. */
input[name="language"]:checked + .snip-lang-pick {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-glow);
}
input[name="language"]:focus-visible + .snip-lang-pick {
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* Top header on the snippet form: title left, visibility switch right. */
.snip-form-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}
/* Stacked variant — title + subtitle + Public/Private toggle on the left,
 * each on its own line. Used since the toggle now lives under the title
 * instead of in a right-rail position. */
.snip-form-header--stack {
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
.snip-form-header--stack .snip-visibility-row { align-self: flex-start; }

/* Segmented Public / Private switch. Two halves visually fused into
   one pill so it reads as a single toggle, not two unrelated buttons. */
.snip-visibility-row {
  display: inline-flex;
  flex-wrap: nowrap;       /* keep both halves on one line, never stack */
  align-items: center;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 3px;
  gap: 0;
  flex-shrink: 0;
}
.snip-vis-pick {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  background: transparent;
  border: 0;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-3);
  cursor: pointer;
  user-select: none;
  transition: color 120ms ease, background 120ms ease;
}
.snip-vis-pick:hover { color: var(--text-0); }
input[name="visibility"]:checked + .snip-vis-pick {
  background: var(--accent);
  color: #1a0e00;
}
.snip-vis-pick svg { flex-shrink: 0; }

/* Picked related-pages render as inline chips (flow + wrap), not as
   full-width rows, so a snippet with several refs reads as a compact
   chip group instead of a wasteful list. */
.snip-related-list {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.snip-related-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 4px 4px 10px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-1);
  max-width: 100%;
}
.snip-related-item > span:first-child {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 220px;
}
.snip-related-item button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  background: transparent !important;
  border: 0 !important;
  color: var(--text-3);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  border-radius: 50%;
  transition: color 120ms ease, background 120ms ease;
}
.snip-related-item button:hover {
  color: var(--danger);
  background: var(--bg-3) !important;
}

.snip-related-search-results {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 220px;
  overflow-y: auto;
  margin-top: 4px;
}
.snip-related-search-result {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 12px;
}
.snip-related-search-result:hover { border-color: var(--accent); }
.snip-related-result-title { color: var(--text-0); white-space: nowrap; flex-shrink: 0; }

/* Overload arg signature: grayed and smaller than the name, so identical
   titles are told apart without competing with it. Shared by the search
   results and the picked chips. */
.snip-related-result-args,
.snip-related-item-args {
  color: var(--text-3);
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Picked-chip label: type icon + name + (optional) args on one row, with
   the name+args sharing the ellipsis budget. */
.snip-related-item-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 300px;
}
.snip-related-item-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

textarea.rvt-mono {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
}

/* Per-doc-page snippets card — now uses the shared .card + .card-toolbar
 * shell from modern-docs.css for visual consistency with Parameters /
 * Examples / Exceptions. Only the right-side CTA and empty-state styles
 * remain snippet-specific. */
.snip-doc-card-cta {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--accent);
  text-decoration: none;
}
.snip-doc-card-cta:hover { text-decoration: underline; }
.snip-doc-empty {
  font-size: 13px;
  color: var(--text-2);
  font-style: italic;
}

/* ─── Sidebar Library tab ───────────────────────────────────────────── */

/* Themed search input with leading icon. Replaces the bare browser
   default that rendered as a square box. */
.snip-sidebar-search {
  position: relative;
  margin: 8px 10px 4px;
}
.snip-sidebar-search svg {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-3);
  pointer-events: none;
}
.snip-sidebar-search input {
  width: 100%;
  padding: 8px 10px 8px 32px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-0);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.snip-sidebar-search input::placeholder { color: var(--text-3); }
.snip-sidebar-search input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.snip-sidebar-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px;
}
.snip-sidebar-empty {
  padding: 14px;
  color: var(--text-3);
  font-size: 12px;
  font-style: italic;
}
.snip-sidebar-item {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 9px 11px;
  /* Recede by default: darker than the sidebar surface with gray text, so a
     long list reads calmly. The active/hovered item is what lights up. */
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: 7px;
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-2);
  transition: border-color 130ms ease, background 130ms ease,
              transform 130ms ease, box-shadow 130ms ease;
}
.snip-sidebar-item:hover {
  border-color: var(--accent);
  background: var(--bg-2);
  transform: translateX(2px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
}
/* Currently-open snippet (URL match). Lit accent border + faint
   accent-tinted background so it stands out without shouting. */
/* Selected snippet: even accent-tinted surface + a solid 3px accent rail on
   the left, white bold title. Clean and unambiguous, no streaky gradient. */
.snip-sidebar-item.is-active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-2));
  box-shadow: inset 3px 0 0 var(--accent);
  transform: none;
}
.snip-sidebar-item.is-active:hover { transform: none; }
.snip-sidebar-item.is-active .snip-sidebar-item-title { color: var(--text-0); font-weight: 700; }
.snip-sidebar-item.is-active .snip-sidebar-author-handle,
.snip-sidebar-item.is-active .snip-sidebar-item-desc,
.snip-sidebar-item.is-active .snip-sidebar-item-likes { color: var(--text-1); }
.snip-sidebar-item-titlerow {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.snip-sidebar-item-title {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  color: var(--text-2);   /* gray by default so the hover-to-white reads */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 130ms ease;
}
/* Hover lifts the text to full white so the focused item reads clearly. */
.snip-sidebar-item:hover .snip-sidebar-item-title { color: var(--text-0); }
.snip-sidebar-item:hover .snip-sidebar-item-desc { color: var(--text-2); }
.snip-sidebar-item:hover .snip-sidebar-author-handle { color: var(--text-1); }
/* Footer left group: small like count, then the author chip. */
.snip-sidebar-item-footer-left {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.snip-sidebar-item-footer-left .snip-sidebar-item-author { margin: 0; }
/* First two lines of description as a preview, ellipsised after.
   Greyed out so it reads as supporting text under the title. */
.snip-sidebar-item-desc {
  font-size: 11.5px;
  color: var(--text-3);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color 130ms ease;
}
/* Author row: small avatar + @handle. */
.snip-sidebar-item-author {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 2px;
}
/* Avatar / fallback. !important on the dimensions so any global
   img selector (Tailwind/DaisyUI preflight, etc.) can't override
   and let the image render at native resolution. */
.snip-sidebar-author-avatar,
.snip-sidebar-author-fallback {
  width: 18px !important;
  height: 18px !important;
  max-width: 18px !important;
  max-height: 18px !important;
  min-width: 18px;
  border-radius: 50%;
  background: var(--bg-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  object-fit: cover;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--text-0);
  flex-shrink: 0;
}
/* Defensive belt-and-braces: any <img> inside a sidebar item gets
   capped to a small icon so we never end up with a 1k-px hero photo
   bleeding the layout if the rule above gets overridden. */
.snip-sidebar-item img {
  max-width: 18px;
  max-height: 18px;
}
.snip-sidebar-author-handle {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 130ms ease;
}
.snip-sidebar-item-meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-3);
  margin-top: 2px;
}

/* Compact footer — author (avatar + handle) on the left, lang chip
   on the right. Replaces the older two-row author + meta layout so
   sidebar cards are tighter. */
.snip-sidebar-item-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 4px;
}
.snip-sidebar-item-footer .snip-sidebar-item-author {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  min-width: 0;
}
.snip-sidebar-item-footer .snip-sidebar-item-lang {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--accent);
  letter-spacing: 0.02em;
}
/* Language badge icon (pyRevit / C# / Dynamo) in the footer, in place of
   the text label. */
.snip-sidebar-item-lang-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  object-fit: contain;
  border-radius: 3px;
}

/* Private snippets live in their own "My Snippets > Private" subsection,
   so no border/background treatment is needed. Just dim the title a bit
   and the lock chip in the title row covers the rest. */
.snip-sidebar-item--private .snip-sidebar-item-title {
  color: var(--text-2);
}
.snip-sidebar-private-badge {
  display: inline-block;
  margin-right: 4px;
  font-size: 11px;
}
.snip-sidebar-item-likes {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text-3);
  white-space: nowrap;
  transition: color 130ms ease;
}
.snip-sidebar-item:hover .snip-sidebar-item-likes { color: var(--text-2); }
.snip-sidebar-item-lang {
  color: var(--accent);
  letter-spacing: 0.02em;
}

/* Collapsible groups in the Library tab. */
.snip-sidebar-group {
  margin: 4px 10px;
  border-radius: 8px;
}
.snip-sidebar-group > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  border: 1px solid transparent;
  border-radius: 6px;
  transition: background 120ms ease, border-color 120ms ease;
}
.snip-sidebar-group > summary::-webkit-details-marker { display: none; }
.snip-sidebar-group > summary::before {
  content: '\25B8';
  font-size: 9px;
  color: var(--text-3);
  transition: transform 160ms ease;
  display: inline-block;
}
.snip-sidebar-group[open] > summary::before { transform: rotate(90deg); }
.snip-sidebar-group > summary:hover {
  background: var(--bg-2);
  border-color: var(--border);
}
.snip-sidebar-group-name {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}
.snip-sidebar-group-count {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-3);
}
.snip-sidebar-group .snip-sidebar-list {
  padding: 4px 0 6px;
}

/* Nested subgroups inside "My Snippets" (Shared / Private). Slightly
   indented, smaller / less-loud header so it reads as a child of the
   parent group. */
.snip-sidebar-subgroup {
  margin: 2px 0 2px 8px;
}
.snip-sidebar-subgroup > summary {
  display: flex;
  align-items: center;
  gap: 6px;
  list-style: none;
  cursor: pointer;
  padding: 4px 8px;
  user-select: none;
  border-radius: 6px;
  transition: background 120ms ease;
}
.snip-sidebar-subgroup > summary::-webkit-details-marker { display: none; }
.snip-sidebar-subgroup > summary::before {
  content: '\25B8';
  font-size: 8px;
  color: var(--text-3);
  transition: transform 160ms ease;
  display: inline-block;
}
.snip-sidebar-subgroup[open] > summary::before { transform: rotate(90deg); }
.snip-sidebar-subgroup > summary:hover { background: var(--bg-2); }
.snip-sidebar-subgroup-name {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-2);
}
.snip-sidebar-subgroup .snip-sidebar-list {
  padding: 2px 0 4px;
}

/* Compact toolbar: search input + sort pulldown + filters toggle +
   reset link, all on one horizontal line. Filters panel hangs below
   collapsed by default. */
.snip-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.snip-toolbar .snip-search-box {
  margin-bottom: 0;
  flex: 1;
  min-width: 220px;
}
/* Sort pulldown — compact button + absolutely-positioned menu. */
.snip-sort { position: relative; flex-shrink: 0; }
.snip-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 11px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-1);
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}
.snip-sort-btn:hover { border-color: var(--accent); color: var(--accent); }
.snip-sort-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.snip-sort-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 200px;
  z-index: 60;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}
.snip-sort-menu[hidden] { display: none; }
.snip-sort-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 6px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-1);
  text-decoration: none;
}
.snip-sort-item:hover { background: var(--bg-2); color: var(--text-0); }
.snip-sort-item.is-active { color: var(--accent); }
.snip-sort-item-check {
  width: 14px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--accent);
}
/* Filters toggle — icon-only settings gear, mirrors the sidebar cog. */
.snip-filters-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  color: var(--text-1);
  cursor: pointer;
  flex-shrink: 0;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snip-filters-toggle:hover { border-color: var(--accent); color: var(--accent); }
.snip-filters-toggle.is-open,
.snip-filters-toggle.has-active { border-color: var(--accent); color: var(--accent); }
/* Active-filter count as a small corner badge on the gear. */
.snip-filters-count {
  position: absolute;
  top: -6px;
  right: -6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  background: var(--accent);
  color: var(--bg-0);
  border: 2px solid var(--bg-1);
  border-radius: 10px;
  font-size: 10px;
  font-weight: 700;
}
/* Filter panel — only visible when toggled open. Stacks rows that
   were previously inline in the now-removed always-on panel. */
.snip-filters-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
  padding: 12px 14px 14px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.snip-filters-panel[hidden] { display: none; }
/* Search input row aligns the input flex:1 with the Reset button on the
   right edge. */
.snip-search-row--input {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
}
/* Reset filters — icon-only square, mirrors the sidebar reset button. */
.snip-reset-filters {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-2);
  flex-shrink: 0;
  transition: color 120ms ease, border-color 120ms ease;
}
.snip-reset-filters:hover { color: var(--accent); border-color: var(--accent); }
.snip-search-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.snip-search-row-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  min-width: 100px;
  flex-shrink: 0;
}
.snip-search-row-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: 1;
}
@media (max-width: 640px) {
  .snip-search-row { align-items: flex-start; }
  .snip-search-row-label { min-width: 0; width: 100%; }
}
/* Author + page chips share this style (kept legacy class name). */
.snip-adv-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-1);
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snip-adv-chip:hover { color: var(--accent); border-color: var(--accent); }
.snip-adv-chip.is-active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-glow);
}
.snip-adv-chip-count {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bg-1);
}

/* Tag filter: active-tag chips + the type-to-add input sit on one wrapping
   row. The input is a slim themed text field that grows to fill the row. */
.snip-tag-filter {
  align-items: center;
}
.snip-tag-input {
  flex: 1 1 160px;
  min-width: 140px;
  padding: 5px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-0);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.snip-tag-input::placeholder { color: var(--text-3); }
.snip-tag-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* ─── Filter typeahead (tags / linked pages / authors) ─────────────── */
.snip-ac { position: relative; display: inline-flex; flex: 1 1 180px; min-width: 150px; }
.snip-ac-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-0);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.snip-ac-input::placeholder { color: var(--text-3); }
.snip-ac-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
/* Capped, scrollable suggestions menu (10 rows max visible, then scroll). */
.snip-ac-menu {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  z-index: 40;
  width: max(100%, 240px);
  max-width: 340px;
  max-height: 252px;
  overflow-y: auto;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.32);
  padding: 4px;
}
.snip-ac-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  background: transparent;
  border: 0;
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-1);
  text-align: left;
  cursor: pointer;
}
.snip-ac-opt:hover { background: var(--bg-2); color: var(--text-0); }
.snip-ac-opt-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.snip-ac-opt-meta { flex-shrink: 0; color: var(--text-3); font-size: 10.5px; }

/* Detail-page private-stat chip in the topbar stats row. */
.snip-detail-stat--private {
  color: var(--accent) !important;
  border-color: var(--accent) !important;
}

/* Like-button hover popover — anchored to the like button, shows the
   avatar stack of users who upvoted. Replaces the previous full "Liked
   by" row in the meta strip so the discussion area stays cleaner. */
.snip-vote-wrap {
  position: relative;
  display: inline-flex;
}
.snip-vote-popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  min-width: 220px;
  padding: 10px 12px;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-2px);
  transition: opacity 140ms ease, transform 140ms ease, visibility 0s linear 140ms;
  pointer-events: none;
}
.snip-vote-wrap:hover .snip-vote-popover,
.snip-vote-wrap:focus-within .snip-vote-popover {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity 140ms ease, transform 140ms ease;
  pointer-events: auto;
}
.snip-vote-popover-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-3);
  margin-bottom: 6px;
}
/* Reset the upvoter-stack inside the popover (no top border / margin). */
.snip-vote-popover .snip-upvoters-stack {
  margin: 0;
  padding: 0;
  border: 0;
}

/* Liked By avatars sit in the same meta-row as Linked-to / Tags, so the
   default top-margin / top-border meant for a standalone block layout
   would push the avatars onto a line below the label. Reset those when
   the stack lives inside the meta-row layout. */
.snip-detail-meta-row .snip-upvoters-stack {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  align-items: center;
}
.snip-detail-meta-row { align-items: center; }
.snip-detail-meta-row .snip-detail-meta-label { padding-top: 0; }

/* Top action row: Share + Browse-all CTAs above the search input. Same chip
   styling as the (now removed) foot row, but no top-border since it sits at
   the very top of the pane. */
.snip-sidebar-actions {
  display: flex;
  gap: 6px;
  padding: 10px 10px 6px;
}
/* Legacy foot row (kept for back-compat in case any caller still uses it). */
.snip-sidebar-foot {
  display: flex;
  gap: 6px;
  padding: 8px 10px 14px;
  border-top: 1px solid var(--border);
  margin-top: 6px;
}
.snip-sidebar-cta {
  flex: 1;
  text-align: center;
  padding: 7px 10px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  border-radius: 6px;
  background: var(--bg-2) !important;
  color: var(--text-1) !important;
  border: 1px solid var(--border-strong);
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.snip-sidebar-cta:hover {
  background: var(--bg-3) !important;
  color: var(--text-0) !important;
  border-color: var(--accent);
}
.snip-sidebar-cta--primary {
  background: var(--accent) !important;
  color: #1a0e00 !important;
  border-color: var(--accent);
}
.snip-sidebar-cta--primary:hover { filter: brightness(1.05); }

/* ─── Reusable snippet grid card ─────────────────────────────────────
   Shared by the profile page and the snippet library list. Compact
   code-editor look: top bar with mac-style traffic lights + kebab,
   abstract code skeleton (decorative bars only — never the real
   source), metadata footer with title/desc/footer.

   Caller picks which extras render via include flags (see
   _snip_grid_card.html): show_kebab, show_author, show_views. */
.profile-snip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}

/* Owner-only "+ Share a snippet" CTA card. Sits at the end of the
   Shared snippets grid, same footprint as a real card so the row
   layout doesn't break, but visually quiet (dashed border, muted
   text) so it doesn't compete with real shared work. */
.profile-snip-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 280px;
  padding: 16px;
  background: transparent;
  border: 1.5px dashed var(--border);
  border-radius: 10px;
  color: var(--text-2);
  text-decoration: none;
  text-align: center;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease, transform 120ms ease;
}
/* When the share-CTA is the ONLY item in a grid (doc-page Community
 * Snippets card with no snippets yet), it shouldn't balloon to the full
 * 280px card-height — shrink to a compact prompt so the empty state
 * doesn't waste vertical space. With sibling snippet cards it keeps the
 * 280px so it lines up with their height. */
.snip-doc-card .snip-grid .profile-snip-cta:only-child {
  min-height: 120px;
}
.profile-snip-cta:hover {
  border-color: var(--accent);
  color: var(--text-0);
  background: var(--bg-2);
  transform: translateY(-2px);
}
.profile-snip-cta-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  font-size: 28px;
  font-weight: 300;
  line-height: 1;
  color: var(--text-3);
  border: 1.5px dashed var(--border);
  border-radius: 50%;
  transition: border-color 120ms ease, color 120ms ease;
}
.profile-snip-cta:hover .profile-snip-cta-plus {
  border-color: var(--accent);
  color: var(--accent);
}
.profile-snip-cta-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.profile-snip-cta-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
}
.profile-snip-cta:hover .profile-snip-cta-title { color: var(--text-0); }
.profile-snip-cta-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
}
.profile-snip-cta:hover .profile-snip-cta-meta { color: var(--accent); }
.profile-snip-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;          /* never let card content stretch its grid track */
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 10px;
  /* No `overflow: hidden` — that clipped the kebab dropdown. Inner
     blocks use border-radius on the corners they touch instead. */
  transition: border-color 120ms ease, transform 120ms ease;
}
.profile-snip-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.profile-snip-card-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  border-top-left-radius: 9px;
  border-top-right-radius: 9px;
}
/* Created date in the top bar, pushed to the right (kebab, when present,
   follows it). Small + mono so it fits without growing the bar. */
.profile-snip-card-bar-date {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: var(--text-3);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.profile-snip-card-dots { display: inline-flex; gap: 4px; }
.profile-snip-card-dots > span {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--bg-3);
  border: 1px solid var(--border);
}
.profile-snip-card-dots > span:nth-child(1) { background: #ff6b8a; border-color: #ff6b8a; }
.profile-snip-card-dots > span:nth-child(2) { background: #ffb454; border-color: #ffb454; }
.profile-snip-card-dots > span:nth-child(3) { background: #7ad9b9; border-color: #7ad9b9; }
.profile-snip-card .profile-snippet-kebab { position: static; }
.profile-snip-card .profile-snippet-kebab .snip-kebab-btn {
  width: 22px;
  height: 22px;
  font-size: 14px;
  background: transparent;
  border: 0;
}
.profile-snip-card-link {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  flex: 1 1 auto;
}

/* Real code preview: the snippet's first lines in tiny monospace,
   clipped to what fits. Plain (no hljs) so the same render is reused on
   the live grid, after an AJAX filter swap, and in the form preview with
   no per-card highlight cost and no drift. Uniform height keeps the grid
   aligned regardless of how many lines the body has; the bottom fade
   makes the cut-off read as intentional. */
/* Hero-image thumbnail variant: same fixed height as the code preview so
   the grid stays uniform whether a card shows an image or code. */
.profile-snip-thumb {
  height: 134px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
.profile-snip-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.profile-snip-code {
  position: relative;
  height: 134px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
.profile-snip-code-pre {
  margin: 0;
  padding: 11px 13px;
  height: 100%;
  display: flex;
  gap: 9px;
  overflow: hidden;          /* it's a title-preview thumbnail, never scroll */
  scrollbar-width: none;     /* Firefox */
  -ms-overflow-style: none;  /* old Edge */
  background: transparent;
}
.profile-snip-code-pre::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* Terminal-style gray line-number gutter. Same font metrics as the code so
   the numbers line up with their lines; not selectable, faintly dimmed. */
.profile-snip-code-gutter {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 7.5px;
  line-height: 1.5;
  color: var(--text-3);
  text-align: right;
  white-space: pre;
  user-select: none;
  opacity: 0.75;
  padding-right: 9px;
  border-right: 1px solid var(--border);
}
.profile-snip-code-body,
.profile-snip-code-body.hljs {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 7.5px;
  line-height: 1.5;
  color: var(--text-1);
  white-space: pre;
  overflow: hidden;
  tab-size: 4;
  -moz-tab-size: 4;
  /* Neutralise the hljs theme's own background + padding so the colored
     tokens render but the tiny preview layout stays intact. */
  background: transparent !important;
  padding: 0 !important;
}
/* Dark gradient veil over the code so the eye lands on the card title and
   description, not the preview. Denser toward the bottom (also masks the
   clipped last line). Lifts toward transparent on hover, when the user is
   actually focused on this card. */
/* Fade the preview toward the CARD surface, not toward black: a black
   wash on the light theme turned every code card into a murky brown slab.
   Light = porcelain veil, dark = the original charcoal vignette. */
.profile-snip-code::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(246, 241, 231, 0.28) 0%, rgba(246, 241, 231, 0.5) 58%, rgba(246, 241, 231, 0.78) 100%);
  pointer-events: none;
  transition: opacity 180ms ease;
}
.dark .profile-snip-code::after {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.34) 0%, rgba(0, 0, 0, 0.52) 58%, rgba(0, 0, 0, 0.74) 100%);
}
.profile-snip-card:hover .profile-snip-code::after { opacity: 0.18; }

.profile-snip-card-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px 8px;
  flex: 1 1 auto;   /* fill the card so the author chip can pin to the bottom */
}
.profile-snip-card-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-0);
  line-height: 1.2;
  display: flex;
  align-items: center;
  gap: 4px;
}
.profile-snip-card-lock { font-size: 11px; }
.profile-snip-card-desc {
  font-family: var(--font-sans);
  font-size: 11.5px;
  color: var(--text-2);
  line-height: 1.35;
  min-height: calc(11.5px * 1.35 * 2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.profile-snip-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-3);
  padding: 6px 12px 10px;
  border-bottom-left-radius: 9px;
  border-bottom-right-radius: 9px;
}
.profile-snip-card-foot-stats {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.profile-snip-card-foot-likes { color: var(--text-1); }

/* In-card upvote button. Lives in the card foot (OUTSIDE the
   stretched <a>), so a click on it never navigates to the detail
   page. Visually compact to match the surrounding 10px mono foot
   text — not the full pill treatment from `.snip-card-vote`. */
.profile-snip-card-vote {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-1);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.profile-snip-card-vote:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--bg-2);
}
.profile-snip-card-vote.is-active {
  background: var(--accent-glow);
  border-color: var(--accent);
  color: var(--accent);
}
.profile-snip-card-foot-date { color: var(--text-3); }
.profile-snip-card-lang {
  font-weight: 700;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  display: inline-flex;
  align-items: center;
}
/* Language badge icon (pyRevit / C# / Dynamo) replaces the text label. If
   the image is missing the browser shows the alt text (the language name),
   so this degrades gracefully before the icon files are added. */
.profile-snip-card-lang-icon {
  display: block;
  width: 18px;
  height: 18px;
  object-fit: contain;
  border-radius: 3px;
}

/* Right-side group in the footer — packs the optional "{N} APIs" chip
   together with the language label without breaking the card width. */
.profile-snip-card-foot-right {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* "{🔗 N} APIs" chip. Tiny by design — same font-size as the rest of
   the footer; just a 🔗 + count. Hover/focus reveals a small popover
   listing the page titles. The popover uses position: absolute on the
   chip (parent has overflow: visible because the card itself does
   not clip), so it can spill out above the footer. */
.snip-card-apis {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 5px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: var(--text-3);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: default;
  outline: none;
  transition: color 120ms ease, border-color 120ms ease;
}
.snip-card-apis:hover,
.snip-card-apis:focus-visible { color: var(--text-1); border-color: var(--text-3); }
.snip-card-apis-icon { font-size: 9px; line-height: 1; }
.snip-card-apis-count { font-weight: 700; }

.snip-card-apis-pop {
  position: absolute;
  bottom: calc(100% + 6px);
  right: 0;
  display: none;
  flex-direction: column;
  gap: 4px;
  width: max-content;
  max-width: 240px;
  padding: 8px 10px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--text-1);
  z-index: 10;
  pointer-events: none;
  text-align: left;
}
.snip-card-apis:hover .snip-card-apis-pop,
.snip-card-apis:focus-visible .snip-card-apis-pop {
  display: flex;
}
.snip-card-apis-pop-hd {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}
.snip-card-apis-pop-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.snip-card-apis-pop-item {
  font-size: 11px;
  color: var(--text-0);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.profile-snip-card-author {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;   /* stick to the bottom of the card, just above the footer */
  padding-top: 8px;
}
.profile-snip-card-author-avatar {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg-3);
  object-fit: cover;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--text-1);
}
.profile-snip-card-author-handle {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-2);
}
.profile-snip-card--private .profile-snip-card-bar { background: rgba(255, 107, 138, 0.06); }
