/* Styles specific to the public pages (/browse, /tag/NAME, /bookmark/:id).
   Everything else — .toolbar, .count, .list, .row, .row-title-line,
   .row-url-line, .row-favicon, .row-sitename, .row-description, .row-thumb,
   .empty, .btn, .tag-pill, .tag-banner — comes from styles.css, which the
   dashboard already established these read-only rows (and its own tag
   filter banner) are visually a variant of. */

.row-tags-line {
  margin-top: 6px;
}

/* ---- ad slots (public/ads.js) ---- */

/* Shared by every page with an ad slot — originally just #adSlot on
   /view/:id's video-embed mode (frame.html/frame.js), moved here now that
   /browse (and /tag/NAME, /:username), /tags, and the bookmark permalink
   page each got one of their own (lib/adSlots.js's browse_top/tags_top/
   bookmark_top) — all of them already load this file, so one shared rule
   covers every placement instead of repeating it per page. No border/
   background/min-height of its own on purpose: whatever the ad network's
   embed code renders supplies its own look, and an empty, unconfigured slot
   (the common case until an admin sets one up — see /admin) should take up
   no visible space at all rather than showing as a blank box. */
.ad-slot {
  max-width: 640px;
  margin: 0 auto;
  /* margin:auto above only ever centers the ad-slot BOX itself — the ad
     network's own markup rendered inside it (an <ins>, an <iframe>, a
     <div> the network's script builds) doesn't necessarily fill that box's
     full width, and needs its own centering. `text-align: center` was the
     first thing tried here, but it only affects INLINE content — most real
     ad markup (an <ins> with a script-assigned fixed pixel width, or any
     <div>/<iframe> a network's own JS ends up rendering, both block-level
     by default) sat flush left regardless, confirmed by actually testing a
     saved embed rather than assuming. Flex + align-items:center centers a
     child regardless of whether that child turns out to be inline or
     block, which is what actually fixes it for arbitrary third-party
     markup instead of only the inline case. */
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ad-slot:not(:empty) {
  margin-bottom: 24px;
}

/* ---- tag pill expand/collapse (public/browse.js's buildTagsHtml) ---- */

/* .tags-extra is a plain inline wrapper around the overflow .tag-pill
   links — no layout rules of its own needed, its children already flow
   exactly like the always-visible pills before it; [hidden] (the native
   HTML attribute, toggled by initTagToggles) is what actually shows/hides
   it. */
.tag-toggle {
  display: inline-flex;
  align-items: center;
  background: none;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  margin: 2px 4px 2px 0;
  vertical-align: middle;
}

.tag-toggle:hover {
  background: var(--accent-soft);
  color: var(--text);
  border-color: var(--accent-soft);
}

.row-meta {
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-muted);
}

/* ---- thumbnail placeholder ---- */

/* Shown in a bookmark's .row-thumb slot instead of a real <img> whenever it
   has no captured image_url, or a captured one fails to load in the
   browser — public/browse.js's thumbPlaceholderHtml() and
   lib/publicPages.js's own copy for the permalink page. Same .row-thumb
   class as a real image, so it automatically picks up identical
   sizing/positioning in both list view (72px square, styles.css) and grid
   view (full-width bled card header, this file's .list.grid-view
   .row-thumb) — this rule only adds the placeholder's own look on top. */
.row-thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--text-muted);
}

/* This element's own `display: flex` above is an author style, which wins
   the cascade over the browser's default `[hidden]{display:none}` (a
   user-agent style) at equal specificity — so without this rule, setting
   the `hidden` attribute (as the broken-image fallback in
   public/browse.js's renderList does) would do nothing and the
   placeholder would stay visibly flexed even while hidden. */
.row-thumb-placeholder[hidden] {
  display: none;
}

.row-thumb-placeholder svg {
  width: 40%;
  height: 40%;
  max-width: 32px;
  max-height: 32px;
}

/* ---- profile follow bar (/:username only) ---- */

.profile-meta {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
}

/* Wraps memberSince + profileCounts so they stack as their own two-line
   column (X.com-style: join date above, follower/following counts below)
   instead of competing with the Follow button for the same row. */
.profile-meta-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.profile-counts {
  font-size: 13px;
  color: var(--text-muted);
}

.profile-joined {
  font-size: 13px;
  color: var(--text-muted);
}

.profile-joined:empty {
  display: none;
}

/* Unfollowed state reuses .btn-primary (solid accent) so it reads as the
   inviting default action; the "following" modifier below flips that to an
   outline once clicked, the common social-app convention for "you're
   already doing this, click to undo" rather than looking like a second
   equally-weighted call to action. */
#followBtn.following {
  background: none;
  border: 1px solid var(--border);
  color: var(--text);
}

/* ---- sort toggle (Newest / Top / Following) ---- */

.sort-toggle {
  display: flex;
  gap: 4px;
  flex: 0 0 auto;
}

.sort-btn {
  padding: 9px 14px;
  font-size: 13px;
}

.sort-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

/* ---- List/Grid layout toggle ---- */

/* Same active-state look as .sort-btn above, just narrower — these two
   buttons are icons, not words, so they don't need .sort-btn's wider
   horizontal padding. */
.view-toggle {
  display: flex;
  gap: 4px;
  flex: 0 0 auto;
}

.view-toggle-btn {
  padding: 9px 12px;
  font-size: 14px;
  line-height: 1;
}

.view-toggle-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

/* Grid view reuses the exact same .row markup list view renders
   (browse.js's renderList never branches on view mode) — everything below
   is pure reflow. flex `order` moves the thumbnail from its list-view spot
   on the right to the top of the card, and drops the vote widget out of
   the left margin into its own row underneath the text. A card with no
   thumbnail (item.image_url unset) just renders shorter, the same as a
   thumbnail-less row already does in list view. */
.list.grid-view {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}

.list.grid-view .row {
  flex-direction: column;
  align-items: stretch;
}

/* Bleeds the thumbnail to the card's edges using the row's own padding
   (12px 14px, from styles.css's .row) as the bleed amount, so it reads as
   a real image card header rather than an inset picture. */
.list.grid-view .row-thumb {
  order: -1;
  width: calc(100% + 28px);
  height: 180px;
  margin: -12px -14px 10px;
  border-radius: 10px 10px 0 0;
  border: none;
}

.list.grid-view .row-main {
  order: 1;
}

.list.grid-view .vote-widget {
  order: 2;
  flex-direction: row;
  justify-content: flex-start;
  gap: 8px;
  margin-top: 10px;
}

/* ---- up/down vote widget ---- */

/* Shown wherever a public bookmark is listed — /browse, /tag/:name,
   /:username row lists (public/browse.js) and the /bookmark/:id permalink
   page (lib/publicPages.js's renderVoteWidget, with the .horizontal
   modifier below). Not shown on the dashboard — voting is a public/social
   action on someone ELSE's content; you can't vote on your own bookmark
   anyway (routes/votes.js rejects it, and app.js hides the widget for it).
   Colors are fixed hex, not var(--accent) — --accent flips to a pink/red in
   dark mode (see styles.css's :root[data-theme="dark"]), which would read
   oddly next to a red downvote, the same reason .row-delete's hover
   (styles.css) uses a fixed red rather than the theme accent. */
.vote-widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
}

.vote-widget.horizontal {
  flex-direction: row;
  gap: 8px;
  margin-bottom: 14px;
}

.vote-btn {
  background: none;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1;
  border-radius: 4px;
  font-family: inherit;
}

.vote-btn:hover:not(:disabled) {
  background: var(--accent-soft);
}

.vote-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.vote-up.vote-active {
  color: #2e7d32;
}

.vote-down.vote-active {
  color: #c62828;
}

.vote-score {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  min-width: 1.4em;
  text-align: center;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 24px 0 8px;
}

.pagination button {
  cursor: pointer;
}

.pagination button:disabled {
  opacity: 0.4;
  cursor: default;
}

#pageInfo {
  font-size: 13px;
  color: var(--text-muted);
}

/* ---- tag directory (/tags) ---- */

/* CSS multi-column rather than a grid/flex-wrap — the tag list is a single
   alphabetical sequence (not a set of independent cards), and columns keep
   it reading top-to-bottom-then-across, like a phone book or an index,
   which is the order someone scanning for a specific letter expects.
   column-width (not column-count) lets the browser pick however many
   columns actually fit at the current window size — 2 on a narrow phone,
   4+ on a wide desktop — rather than a fixed count that's cramped or wastes
   space. */
.tag-index {
  column-width: 200px;
  column-gap: 28px;
}

.tag-index-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: var(--text);
  font-size: 13.5px;
  /* Keeps one tag's name+count together rather than splitting across the
     column break — the single biggest thing that makes a CSS-columns list
     look broken if left to the default. */
  break-inside: avoid;
}

.tag-index-item:hover .tag-index-name {
  text-decoration: underline;
}

.tag-index-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.tag-index-count {
  flex: 0 0 auto;
  color: var(--text-muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* ---- bookmark permalink page (/bookmark/:id) ---- */

/* Same 640px column and centering as .bm-card below, so the two read as
   stacked rows rather than a toolbar off on its own — "Back to browse" on
   the left (where you came from), "Random" on the right (where to go
   next), both above the fold regardless of how tall the card's own
   thumbnail/description/tags turn out to be. */
.bm-topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 640px;
  margin: 16px auto 0;
}

/* .tag-banner is shared with /tag/NAME (browse.html), which is full-width
   within its own container — scoped to just after .bm-topbar so it picks
   up this page's narrower 640px column instead of widening unexpectedly. */
.bm-topbar + .tag-banner {
  max-width: 640px;
  margin: 12px auto 0;
}

.bm-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  margin: 16px auto 24px;
  max-width: 640px;
}

.bm-image {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
  margin-bottom: 16px;
}

/* No real max-height to derive from (the placeholder isn't an <img>, so it
   has no natural aspect ratio) — a fixed height stands in for one, smaller
   than .bm-image's own max-height since a big empty icon reads as more
   "broken" than a smaller, quieter one would. */
.bm-image-placeholder {
  height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--text-muted);
}

/* Same cascade gotcha and fix as .row-thumb-placeholder[hidden] above —
   this element's own `display: flex` would otherwise beat the browser's
   default `[hidden]{display:none}` and defeat the broken-image fallback
   toggle in lib/publicPages.js's renderBookmarkPage. */
.bm-image-placeholder[hidden] {
  display: none;
}

.bm-image-placeholder svg {
  width: 48px;
  height: 48px;
}

.bm-title {
  font-size: 22px;
  margin: 0 0 10px;
}

.bm-url-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.bm-favicon {
  width: 16px;
  height: 16px;
  border-radius: 3px;
  flex: 0 0 auto;
}

/* The bookmark permalink page's one deliberate way to open a new tab — a
   real link straight to the external page (see lib/publicPages.js's own
   comment on thumb, just above where this is used, for the full picture of
   why only this one thing on that page opens a new tab). Accent-colored
   like any other link, since it is one there. */
.bm-url {
  color: var(--accent);
  font-size: 13.5px;
  word-break: break-all;
}

/* The toolbar viewer's own copy of this same card (public/frame.js's
   buildVideoCardHtml) renders bm-url as plain text, not a link — that
   page's one new-tab opener is its own "Open in a tab" button up in the
   toolbar, not a second link buried in this card. Overridden back to muted,
   non-link styling specifically inside #videoCard so it doesn't read as
   clickable there, without touching the real link .bm-url still is on the
   permalink page above. */
#videoCard .bm-url {
  color: var(--text-muted);
}

.bm-sitename {
  font-size: 12px;
  color: var(--text-muted);
  background: var(--accent-soft);
  border-radius: 6px;
  padding: 2px 8px;
}

.bm-description {
  font-size: 14.5px;
  color: var(--text);
  line-height: 1.5;
  margin: 0 0 14px;
}

.bm-tags {
  margin-bottom: 14px;
}

.bm-meta {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.bm-notfound {
  text-align: center;
  padding: 48px 24px;
}

.bm-notfound h1 {
  font-size: 20px;
  margin: 0 0 8px;
}

.bm-notfound p {
  color: var(--text-muted);
  margin: 0 0 20px;
}

/* ---- comments (public/comments.js) — the bookmark permalink page
   (.comments-section sits right after .bm-card, both server-rendered, see
   lib/publicPages.js) and the toolbar viewer's video-embed mode
   (.comments-section is built client-side after #videoCard, see frame.js).
   Same 640px column as .bm-card above in both places, so it reads as a
   continuation of the card rather than a separate, differently-proportioned
   block. */
.comments-section {
  max-width: 640px;
  margin: 0 auto 24px;
}

.comments-heading {
  font-size: 16px;
  margin: 0 0 12px;
}

.comments-loading {
  color: var(--text-muted);
  font-size: 13px;
}

.comments-login-prompt {
  color: var(--text-muted);
  font-size: 13.5px;
  margin: 0 0 16px;
}

.comment-form {
  margin-bottom: 20px;
}

.comment-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  resize: vertical;
}

.comment-input:focus {
  outline: none;
  border-color: var(--accent);
}

.comment-form-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
}

.comment-form-error {
  color: #d33;
  font-size: 12.5px;
}

.comment-row {
  padding: 12px 0;
  border-top: 1px solid var(--border);
}

.comment-row:first-child {
  border-top: none;
  padding-top: 0;
}

.comment-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin-bottom: 4px;
}

.comment-author {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.comment-author:hover {
  text-decoration: underline;
}

.comment-date {
  font-size: 12px;
  color: var(--text-muted);
}

.comment-delete {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
}

.comment-delete:hover {
  color: #d33;
}

.comment-body {
  font-size: 14px;
  color: var(--text);
  line-height: 1.5;
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
