/*
 * Dapestiri.ro — Phase 3 frontend styles.
 * Hand-built, no framework, mobile-first (CLAUDE.md: "lean hand-built
 * CSS/JS, mobile performance is a priority over convenience").
 *
 * Breakpoints are treated as three distinct layouts (dapestiri-dev-plan.md
 * §4), not just fluid scaling:
 *   - mobile   (default, <600px):  hamburger + off-canvas sidebar drawer,
 *                                   horizontal-scroll category pills
 *   - tablet   (>=600px):          same shell, wider feed column
 *   - desktop  (>=1024px):         persistent sidebar rail (no hamburger),
 *                                   category nav becomes a centered tabbar
 */

:root {
    --color-bg: #f4f5f7;
    --color-surface: #ffffff;
    --color-text: #17181c;
    --color-text-muted: #6b7078;
    --color-border: #e4e5e9;
    --color-accent: #e0393e;      /* red, direct/tabloid-adjacent tone */
    --color-accent-dark: #b82227;
    --color-tag-bg: #17181c;
    --color-tag-text: #ffffff;
    --color-tag-muted-bg: #eceef1;
    --color-tag-muted-text: #4b4f57;
    --color-scrim-top: rgba(0, 0, 0, 0);
    --color-scrim-bottom: rgba(0, 0, 0, 0.78);
    /* Hero card only (round 18 dark redesign, "mai mare impact") - the
       rest of the site has no dark-mode/theming system at all, this is a
       one-off intentional treatment for that one component. */
    --color-hero-bg: #121316;
    --color-hero-text-muted: #b5b8c0;

    --toolbar-height: 56px;
    --radius: 14px;
    --radius-sm: 8px;
    --gap: 12px;
    --feed-max-width: 640px;

    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
    box-sizing: border-box;
}

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

body {
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-sans);
    -webkit-font-smoothing: antialiased;
    padding-top: var(--toolbar-height);
}

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

img {
    max-width: 100%;
    display: block;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Top toolbar
   --------------------------------------------------------------------- */

.toolbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--toolbar-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    gap: var(--gap);
    padding: 0 var(--gap);
    z-index: 40;
}

.toolbar__menu-btn {
    appearance: none;
    border: 0;
    background: transparent;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    cursor: pointer;
    flex: 0 0 auto;
}

.toolbar__menu-btn:hover {
    background: var(--color-bg);
}

.toolbar__menu-btn svg {
    width: 20px;
    height: 20px;
}

/* flex: 0 0 auto (not 1 1 auto) is deliberate - .catnav below is also a
   flexible/shrinkable sibling in .toolbar, and with both set to shrink
   equally, catnav's much wider content (4+ category pills) claimed nearly
   all the shrink budget's counterpart space away from this on narrow
   phones, squeezing the brand link down toward 0 width and leaving the
   logo overlapped/hidden behind the first category pill. Locking brand to
   its natural size guarantees the logo always gets its full 36px, and
   catnav (which already scrolls internally) absorbs 100% of the
   remaining space instead. */
.toolbar__brand {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

.toolbar__logo {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
}

.toolbar__wordmark {
    display: none;
    font-weight: 800;
    font-size: 1.15rem;
    letter-spacing: -0.02em;
    white-space: nowrap;
}

.toolbar__wordmark span {
    color: var(--color-accent);
}

/* ---------------------------------------------------------------------
   Search box — CardRenderer::searchBox(). Two DOM instances, not one
   repositioned via CSS: search-box--sidebar (mobile/tablet, above the
   category list in .sidebar) and search-box--toolbar (desktop, in
   .toolbar next to the logo, or .read-topbar next to the home icon on
   the article page). Only one of the two is ever visible at a given
   width - toggled by the >=1024px override further down (same
   breakpoint the sidebar itself switches from off-canvas drawer to
   persistent rail at). A plain GET form to search.php, no JS.
   --------------------------------------------------------------------- */

.search-box {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 36px;
    padding: 0 12px;
    border-radius: 999px;
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text-muted);
    box-sizing: border-box;
}

.search-box:focus-within {
    border-color: var(--color-accent);
}

.search-box__icon {
    flex: none;
}

.search-box__input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    font-family: inherit;
    font-size: 0.9rem;
    color: var(--color-text);
}

.search-box__input:focus {
    outline: none;
}

.search-box__input::placeholder {
    color: var(--color-text-muted);
}

/* Toolbar/read-topbar variant - hidden below the desktop breakpoint (the
   sidebar variant covers mobile/tablet instead); see the >=1024px
   override further down. flex: 0 1 <basis> (shrinkable, not fixed) so it
   yields width back to .catnav on a narrower desktop/tablet-landscape
   window rather than forcing the category pills to scroll unnecessarily. */
.search-box--toolbar {
    display: none;
    flex: 0 1 220px;
    min-width: 140px;
}

/* Sidebar variant - visible by default (mobile/tablet, the off-canvas
   menu), hidden once the toolbar variant takes over at desktop widths;
   see the >=1024px override further down. */
.search-box--sidebar {
    margin: 0 0 var(--gap);
}

/* ---------------------------------------------------------------------
   Static info pages (termeni.php, confidentialitate.php, despre-noi.php)
   — simple prose pages, not the feed. Own minimal fixed header (logo +
   "Acasă" link only, no hamburger/categories) at the same
   --toolbar-height as .toolbar/.read-topbar so body's global
   padding-top still lines up.
   --------------------------------------------------------------------- */

.static-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--toolbar-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap);
    padding: 0 var(--gap);
    z-index: 40;
}

.static-topbar__brand {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.static-topbar__logo {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
}

.static-topbar__wordmark {
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.02em;
    white-space: nowrap;
}

.static-topbar__wordmark span {
    color: var(--color-accent);
}

.static-topbar__home-link {
    flex: 0 0 auto;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--color-text-muted);
    padding: 8px 4px;
}

.static-topbar__home-link:hover {
    color: var(--color-accent);
}

.static-page {
    max-width: 760px;
    margin: 0 auto;
    padding: 28px var(--gap) 8px;
}

.static-page__title {
    font-size: 1.5rem;
    font-weight: 800;
    line-height: 1.3;
    margin: 0 0 6px;
}

.static-page__updated {
    font-size: 0.82rem;
    color: var(--color-text-muted);
    margin: 0 0 24px;
}

.static-page__body {
    font-size: 0.95rem;
    line-height: 1.65;
    color: var(--color-text);
}

.static-page__body h2 {
    font-size: 1.1rem;
    font-weight: 800;
    margin: 28px 0 10px;
}

.static-page__body p {
    margin: 0 0 14px;
}

.static-page__body ul {
    margin: 0 0 14px;
    padding-left: 22px;
}

.static-page__body li {
    margin-bottom: 6px;
}

.static-page__body a {
    color: var(--color-accent);
    font-weight: 600;
}

.static-page__body a:hover {
    text-decoration: underline;
}

/* ---------------------------------------------------------------------
   Phase 8 — public accounts (public/cont/*.php): login/register/reset
   forms (.cont-*) and the account panel (.account-*, .avatar-picker__*).
   Reuses the same design tokens as everything else (--color-accent,
   --radius, --gap) rather than introducing a second visual language —
   this is still the same site, just with a form on it.
   --------------------------------------------------------------------- */

.cont-shell {
    max-width: 420px;
    margin: 0 auto;
    padding: 28px var(--gap) 40px;
}

.cont-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 24px 20px;
}

.cont-card__title {
    font-size: 1.3rem;
    font-weight: 800;
    line-height: 1.3;
    margin: 0 0 6px;
}

.cont-card__subtitle {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: 0 0 18px;
}

.cont-card__footer-link {
    font-size: 0.88rem;
    color: var(--color-text-muted);
    margin: 14px 0 0;
    text-align: center;
}

.cont-card__footer-link a {
    color: var(--color-accent);
    font-weight: 600;
}

.cont-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.cont-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

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

.cont-input {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 0.95rem;
    font-family: inherit;
    background: var(--color-bg);
    color: var(--color-text);
}

.cont-input:focus {
    outline: 2px solid var(--color-accent);
    outline-offset: 1px;
}

.cont-hint {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    margin: 0;
}

.cont-checkbox-field {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--color-text-muted);
}

.cont-checkbox-field input {
    margin-top: 3px;
    flex-shrink: 0;
}

.cont-checkbox-field a {
    color: var(--color-accent);
    font-weight: 600;
}

.cont-btn {
    display: inline-block;
    text-align: center;
    border: none;
    border-radius: var(--radius-sm);
    padding: 11px 16px;
    font-size: 0.92rem;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    text-decoration: none;
}

.cont-btn--primary {
    background: var(--color-accent);
    color: #fff;
}

.cont-btn--primary:hover {
    background: var(--color-accent-dark);
}

.cont-btn--secondary {
    background: var(--color-bg);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    align-self: flex-start;
}

.cont-btn--danger {
    background: #fff;
    color: var(--color-accent-dark);
    border: 1px solid var(--color-accent-dark);
}

.cont-btn--danger:hover {
    background: var(--color-accent-dark);
    color: #fff;
}

.cont-btn--ghost {
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    width: 100%;
}

.cont-flash {
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 0.85rem;
    line-height: 1.5;
    margin: 0 0 14px;
}

.cont-flash--error {
    background: #fdeceb;
    color: #a11f1f;
}

.cont-flash--success {
    background: #eaf6ee;
    color: #226b3c;
}

/* Avatar sizing — Avatar::render()'s base .avatar/.avatar__icon markup
   is otherwise unstyled here on purpose; the comments-section rules
   (Phase 8 comment CSS) define the default small size, this file only
   adds the larger account-panel variant. */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.avatar__icon {
    width: 100%;
    height: 100%;
    color: #fff;
}

.avatar--md {
    width: 48px;
    height: 48px;
}

.avatar--lg {
    width: 64px;
    height: 64px;
}

.avatar--xl {
    width: 96px;
    height: 96px;
}

/* ---------------------------------------------------------------------
   Public profile page (public/profil.php) - avatar, nickname, large
   animated stars, rank badge, comment count, "membru din" - Aug 2026
   request. A simple centered card, same visual language as
   .account-card/.cont-card (surface/border/radius tokens), not a new
   design system.
   --------------------------------------------------------------------- */

.profile-page {
    max-width: 480px;
    margin: 0 auto;
    padding: 40px var(--gap) 48px;
}

.profile-page__card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 32px 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
}

.profile-page__avatar {
    margin-bottom: 6px;
}

.profile-page__nickname {
    font-size: 1.4rem;
    font-weight: 800;
    margin: 0;
}

.profile-page__stars {
    margin: 4px 0;
}

/* ---------------------------------------------------------------------
   Star badge - unified single-star display (Aug 2026: "sa nu mai
   afisam ★☆☆☆☆... sa afisam ★, mai mare, golden... sa ii aratam ca a
   acumulat o stea deja - e mai pozitiv mesajul"). Replaces every prior
   ★★★☆☆-style "N of 5" rendering site-wide - always exactly one star,
   framed as an earned badge rather than a review score. The colored
   rank-badge pill (see .rank-badge below) still conveys tier via
   color+label, so no information is lost by dropping the star count.
   Shared by CommentRenderer::starBadge() everywhere an avatar box
   appears: the comment thread, the account panel header, the public
   profile page, the article-page composer avatar box, and the account
   panel's rank ladder.
   --------------------------------------------------------------------- */
.star-badge {
    display: inline-block;
    line-height: 1;
    color: #f2b400;
    text-shadow: 0 0 6px rgba(242, 180, 0, 0.5);
    font-size: 1.05rem;
    vertical-align: middle;
}

.star-badge--sm {
    font-size: 0.95rem;
}

/* The one place stars still repeat per-tier: the rank ladder's own
   per-node star count ("cu stele dedesupt, ca un award") - illustrating
   "this tier = this many stars" as part of the award graphic itself,
   not a live per-user rating. */
.rank-ladder__star {
    font-size: 0.85rem;
    text-shadow: none;
}

/* Big + a subtle, slow, non-distracting shimmer - "fa-le mai mari,
   golden... oamenilor le place aurul". prefers-reduced-motion turns the
   shimmer off entirely, same accessibility courtesy as any other
   decorative animation on the site. */
.star-badge--lg {
    font-size: 2.4rem;
    text-shadow: 0 0 12px rgba(242, 180, 0, 0.55);
    animation: star-badge-shimmer 3.2s ease-in-out infinite;
}

@keyframes star-badge-shimmer {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.78; }
}

@media (prefers-reduced-motion: reduce) {
    .star-badge--lg {
        animation: none;
    }
}

.profile-page__stats {
    display: flex;
    gap: 28px;
    margin-top: 14px;
}

.profile-page__stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}

.profile-page__stat-value {
    font-size: 1.15rem;
    font-weight: 800;
}

.profile-page__stat-label {
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

.account-panel {
    max-width: 560px;
    margin: 0 auto;
    padding: 28px var(--gap) 48px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.account-panel__header {
    display: flex;
    align-items: center;
    gap: 16px;
}

.account-panel__nickname {
    font-size: 1.25rem;
    font-weight: 800;
    margin: 0 0 4px;
}

.account-panel__meta {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin: 0;
}

/* Quick-nav row (Aug 2026 round 15, AccountRenderer::panelHeader()) -
   Contul meu / Comentariile mele / Notificări, shared across every
   account-realm page so switching between them doesn't require detouring
   through the sidebar drawer. */
.account-panel__nav {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.account-panel__nav-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 8px 14px;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.9rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
}

.account-panel__nav-link--active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.account-panel__nav-link--active .notif-badge {
    background: #fff;
    color: var(--color-accent);
}

/* "‹ Panou" back link - shown on every account-realm page except
   contul-meu.php itself (that page IS the panou). */
.account-panel__back {
    font-weight: 700;
    color: var(--color-text-muted);
}

.account-panel__back:hover {
    color: var(--color-accent);
}

.account-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 18px 18px 20px;
}

.account-card--warning {
    background: #fff8ea;
    border-color: #f0dca0;
}

.account-card--danger {
    border-color: #f2c6c6;
}

/* "Notificări" list (Aug 2026 round 15, public/cont/notificari.php) - one
   row per notification, unread ones visually distinguished with a left
   accent bar + slightly bolder text (same "weight, not color-only" cue
   used elsewhere so it still reads for color-blind users). */
.notificari-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.notificari-item {
    position: relative;
    border-radius: var(--radius-sm);
}

.notificari-item--unread {
    background: var(--color-tag-muted-bg);
}

.notificari-item--unread::before {
    content: "";
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 3px;
    border-radius: 2px;
    background: var(--color-accent);
}

/* Shared by both row shapes notificari.php can emit: an <a> (has a valid
   link target) or a plain <span> (rank_up, or a target that no longer
   exists) - see that file's own $isLink/$rowTag/$rowClass docblock. */
.notificari-item__row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px 10px 16px;
    color: inherit;
}

/* Hover/interactive affordance only applies to the actual <a> rows. */
.notificari-item__link:hover {
    background: var(--color-bg);
}

.notificari-item__icon {
    flex-shrink: 0;
    width: 22px;
    text-align: center;
    font-size: 1rem;
    color: var(--color-text-muted);
}

.notificari-item--unread .notificari-item__icon {
    color: var(--color-accent);
}

.notificari-item__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.notificari-item__text {
    font-size: 0.92rem;
    line-height: 1.4;
}

.notificari-item--unread .notificari-item__text {
    font-weight: 700;
}

.notificari-item__time {
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

/* Rank-up celebration (Aug 2026 round 15) - a handful of small CSS-only
   confetti pieces, staggered fall + rotate + fade, plays once when a
   rank_up row was still unread on this page load (notificari.php only
   renders .rankup-confetti on that condition - see the file's own
   docblock on read-then-mark-read ordering). No JS confetti library, per
   CLAUDE.md's "no frontend framework" rule - same restraint as the two
   existing keyframes below (star-badge-shimmer/comments-prompt-bounce),
   not a showy library-style burst. */
.rankup-confetti {
    position: absolute;
    top: 6px;
    right: 10px;
    width: 60px;
    height: 30px;
    pointer-events: none;
}

.rankup-confetti__piece {
    position: absolute;
    top: 0;
    width: 6px;
    height: 6px;
    border-radius: 1px;
    background: var(--color-accent);
    animation: rankup-confetti-fall 1.2s ease-out forwards;
}

.rankup-confetti__piece:nth-child(1) { left: 2%;  background: var(--color-accent); animation-delay: 0s; }
.rankup-confetti__piece:nth-child(2) { left: 18%; background: #f2b400; animation-delay: 0.05s; }
.rankup-confetti__piece:nth-child(3) { left: 34%; background: var(--color-accent); animation-delay: 0.1s; }
.rankup-confetti__piece:nth-child(4) { left: 50%; background: #f2b400; animation-delay: 0.02s; }
.rankup-confetti__piece:nth-child(5) { left: 66%; background: var(--color-accent); animation-delay: 0.12s; }
.rankup-confetti__piece:nth-child(6) { left: 82%; background: #f2b400; animation-delay: 0.07s; }
.rankup-confetti__piece:nth-child(7) { left: 96%; background: var(--color-accent); animation-delay: 0.15s; }

@keyframes rankup-confetti-fall {
    0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
    100% { transform: translateY(34px) rotate(140deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .rankup-confetti__piece {
        animation: none;
        opacity: 0;
    }
}

.account-card__title {
    font-size: 1rem;
    font-weight: 800;
    margin: 0 0 12px;
}

.account-card__hint {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    line-height: 1.55;
    margin: 12px 0 0;
}

/* "Nu forța obținerea artificială a punctelor" note (Aug 2026) - same
   size/rhythm as .account-card__hint, just visually set apart a touch
   so it doesn't blend into the mechanism paragraph above it. */
.account-card__hint--emphasis {
    font-style: italic;
    color: var(--color-text);
}

.account-progress__bar {
    background: var(--color-bg);
    border-radius: 999px;
    height: 10px;
    overflow: hidden;
}

.account-progress__fill {
    background: var(--color-accent);
    height: 100%;
    border-radius: 999px;
}

.account-progress__label {
    font-size: 0.85rem;
    margin: 8px 0 0;
}

/* ---------------------------------------------------------------------
   Rank ladder (Aug 2026 request: "aratam exact toate rankingurile in
   panoul utilizatorului... buline... cu stele dedesupt, ca un award...
   Vreau sa ii facem sa fie motivati"). One node per RankCalculator rank,
   connected by arrow glyphs - a flex row of alternating node/arrow
   children rather than an absolute-positioned line, so it reflows to a
   vertical stack on narrow screens for free (the arrow glyph itself
   rotates via the media query below).
   --------------------------------------------------------------------- */
/* nowrap + horizontal scroll rather than flex-wrap: at the account
   panel's own content width, 5 nodes + 4 arrows don't reliably fit one
   row, and wrapping mid-row orphans an arrow with nothing before it -
   scrolling reads as an intentional stepper instead of a layout bug.
   The <=480px media query below switches to a full vertical stack
   instead, where this never applies. */
.rank-ladder {
    display: flex;
    /* flex-start, not center (Aug 2026 fix): on desktop (flex-direction:
       row, below) this is the cross axis (vertical) - flex-start keeps
       every node's bubble top-aligned on the same line even though
       "Formator de opinie"'s two-line label makes that one node taller
       than the rest; centering there let the taller node's extra height
       push its bubble out of line with the others. On mobile (<=480px,
       flex-direction: column, further below) this becomes the cross
       axis in the OTHER direction (horizontal) - flex-start left-aligns
       the stacked node blocks instead of centering each one at its own
       (varying) width, which is what was breaking the "circles one
       under another" alignment there. */
    align-items: flex-start;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 10px;
    margin-top: 8px;
    /* The current-rank node's "RANGUL TĂU" tag sits at top:-10px, poking
       above the row (see .rank-ladder__tag) - without this, overflow-x:
       auto also clips that vertical overflow, cutting the tag off (Aug
       2026 bug report). */
    padding-top: 10px;
    padding-bottom: 4px;
}

.rank-ladder__node {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 4px;
    padding: 14px 8px 10px;
    border-radius: var(--radius);
    flex: 0 0 92px;
    box-sizing: border-box;
}

.rank-ladder__node--current {
    background: var(--color-bg);
    box-shadow: 0 0 0 2px var(--color-accent) inset;
}

.rank-ladder__tag {
    position: absolute;
    top: -10px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--color-accent);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}

.rank-ladder__bubble {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
}

.rank-ladder__node--current .rank-ladder__bubble {
    width: 52px;
    height: 52px;
    font-size: 1.3rem;
}

.rank-ladder__stars {
    line-height: 1;
}

.rank-ladder__label {
    font-size: 0.78rem;
    font-weight: 700;
}

.rank-ladder__threshold {
    font-size: 0.7rem;
    color: var(--color-text-muted);
}

.rank-ladder__arrow {
    color: var(--color-text-muted);
    font-size: 1.1rem;
    flex-shrink: 0;
}

@media (max-width: 480px) {
    .rank-ladder {
        flex-direction: column;
        overflow-x: visible;
    }

    .rank-ladder__node {
        /* Same width on every node (Aug 2026 fix, "toate trebuie sa aiba
           acelasi width") - width:100% up to a 300px cap, rather than
           each node's own varying content width, is what actually makes
           the circles line up "unul sub altul" (one under another); the
           .rank-ladder container's own align-items:flex-start (above)
           only matters once every node agrees on a width to align. */
        flex: 0 0 auto;
        width: 100%;
        max-width: 300px;
        box-sizing: border-box;
        flex-direction: row;
        text-align: left;
        justify-content: flex-start;
        gap: 12px;
        padding: 10px 14px;
    }

    .rank-ladder__arrow {
        transform: rotate(90deg);
    }
}

/* Aug 2026 request: .article-page__text (the article read-page excerpt
   text) reads at 1.15rem on desktop already - see its own rule inside
   the @media block right below - but every other .article-page__* rule,
   including this one, only ever existed inside that same desktop-only
   block, so mobile got no explicit font-size for it at all (just
   whatever it inherited). Confirmed with the site owner this size
   should apply on mobile too, not just desktop - base-scope duplicate
   here, same values, so mobile picks it up and the desktop rule below
   stays exactly as-is (identical values either way once >=1024px, so no
   cascade conflict). The rest of .article-page__* is left desktop-only,
   unchanged - out of scope for this ask. */
.article-page__text {
    font-size: 1.15rem;
    line-height: 1.6;
    color: var(--color-text);
    margin: 0 0 24px;
}

/* Aug 2026 request: "avem spatiu pe ecran, trebuie folosit" - widen the
   account panel on desktop only (mobile/tablet keep the original 560px,
   which already fits those viewports fine). At 800px the ladder's 5
   nodes + 4 arrows usually fit without the horizontal-scroll fallback
   above, so it's centered here rather than left-aligned in the extra
   space - desktop-only for the same reason, since the mobile vertical
   stack (<=480px, above) and the narrower in-between widths still rely
   on left-aligned/scrollable behavior. */
@media (min-width: 1024px) {
    .account-panel {
        max-width: 800px;
    }

    .rank-ladder {
        justify-content: center;
    }
}

/* ---------------------------------------------------------------------
   "Unde am comentat" - account panel list of articles the user has
   commented on (public/cont/contul-meu.php), AJAX-paginated by
   public/assets/js/account.js against public/cont/comentat-articole.php.
   Pager reuses .pagination/.pagination__link (index.php's feed pager) -
   .commented-articles__pager just trims that component's own bottom
   padding, which assumes it's the last thing on a full page rather than
   sitting inside an account-card.
   --------------------------------------------------------------------- */
.commented-articles__list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.commented-articles__item {
    border-bottom: 1px solid var(--color-border);
}

.commented-articles__item:last-child {
    border-bottom: none;
}

.commented-articles__link {
    display: block;
    padding: 10px 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-text);
    line-height: 1.4;
}

.commented-articles__link:hover {
    color: var(--color-accent);
}

.commented-articles__pager {
    padding: 12px 0 0;
}

/* ---------------------------------------------------------------------
   "Comentariile mele" - own account-panel page (Aug 2026 request,
   public/cont/comentariile-mele.php), one row per comment rather than
   "Unde am comentat"'s one row per article - shows the comment's own
   (truncated) text and vote counts, so it reuses .pagination/
   .pagination__link for its pager (real page links, no AJAX here) but
   needs its own row styling since each item carries more than a single
   title line.
   --------------------------------------------------------------------- */
.my-comments__list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.my-comments__item {
    border-bottom: 1px solid var(--color-border);
}

.my-comments__item:last-child {
    border-bottom: none;
}

.my-comments__link {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 12px 0;
    color: var(--color-text);
}

.my-comments__article-title {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--color-text-muted);
}

.my-comments__link:hover .my-comments__article-title {
    color: var(--color-accent);
}

.my-comments__body {
    font-size: 0.92rem;
    line-height: 1.45;
}

.my-comments__meta {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 0.76rem;
    color: var(--color-text-muted);
}

.my-comments__dot {
    opacity: 0.6;
}

.my-comments__pager {
    padding: 12px 0 0;
}

.avatar-picker-form {
    gap: 16px;
}

.avatar-picker__genders {
    display: flex;
    gap: 16px;
}

.avatar-picker__gender-option {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.88rem;
    cursor: pointer;
}

.avatar-picker__colors {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.avatar-picker__color-option {
    position: relative;
    cursor: pointer;
}

.avatar-picker__swatch {
    display: block;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: currentColor;
    border: 2px solid transparent;
}

.avatar-picker__color-option input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}

.avatar-picker__color-option input:checked + .avatar-picker__swatch {
    border-color: var(--color-text);
    box-shadow: 0 0 0 2px var(--color-surface);
}

/* ---------------------------------------------------------------------
   Article reader (read.php) — fixed topbar + full-height iframe embed of
   the original article, plus the "latest from this source" slide-down
   panel. Independent of .toolbar/.catnav (read.php doesn't render the
   main feed shell), but matches its height/style language so the two
   pages feel like one app rather than two.
   --------------------------------------------------------------------- */

.read-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--toolbar-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    gap: var(--gap);
    padding: 0 var(--gap);
    z-index: 40;
}

.read-topbar__back,
.read-topbar__original,
.read-topbar__home {
    appearance: none;
    border: 0;
    background: transparent;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    flex: 0 0 auto;
    cursor: pointer;
}

.read-topbar__back:hover,
.read-topbar__original:hover,
.read-topbar__home:hover {
    background: var(--color-bg);
}

/* .read-topbar__title (the old title <span>) is gone, and so is the old
   single .read-topbar__category-btn pill it was replaced by - the topbar
   now reuses .catnav (the same horizontal-scroll category strip
   index.php/sursa.php use) so a reader who lands directly on an article
   still sees every category, with theirs highlighted, instead of just
   one. .read-topbar__home no longer needs margin-left: auto to push
   itself right - .read-topbar__catnav is the flexible element now and
   fills/scrolls whatever space is left between home and the external-
   link icon. */
/* .catnav's base rule (further down this file) sets align-self: stretch,
   meant for the main toolbar where catnav fills the entire bar height.
   Here we give it a fixed 36px height instead (to match the icon
   buttons) - but align-self: stretch with a non-auto height doesn't
   resize the box, it just resolves to top-aligned instead of centered,
   which is what was making the category strip sit flush against the
   top of the bar instead of centered like everything else. The
   .read-topbar__catnav.catnav selector (both classes chained) is
   needed so this reliably outweighs .catnav's align-self: stretch
   regardless of which rule appears first in the file - a single-class
   selector here would tie on specificity and the outcome would depend
   on source order alone. */
.read-topbar__catnav {
    height: 36px;
}

.read-topbar__catnav.catnav {
    align-self: center;
}

/* Read-topbar's active category is a solid black pill (matching the old
   single-pill treatment this replaces), not the red accent .catnav uses
   on the main feed - a deliberate difference scoped to this page only,
   so it reads as "current context" rather than duplicating the feed's
   own selection color. */
.read-topbar__catnav .catnav__link--active {
    background: var(--color-tag-bg);
    color: var(--color-tag-text);
}

.read-topbar__source-btn {
    appearance: none;
    border: 0;
    background: var(--color-tag-bg);
    color: var(--color-tag-text);
    font-size: 0.8rem;
    font-weight: 700;
    padding: 8px 12px;
    border-radius: 999px;
    cursor: pointer;
    flex: 0 0 auto;
    white-space: nowrap;
}

.source-panel {
    position: fixed;
    top: var(--toolbar-height);
    left: 0;
    right: 0;
    max-height: 0;
    overflow: hidden;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
    z-index: 39;
    transition: max-height 0.2s ease;
}

.source-panel--open {
    max-height: min(60vh, 480px);
    overflow-y: auto;
}

.source-panel__list {
    list-style: none;
    margin: 0;
    padding: 8px;
}

.source-panel__link {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--gap);
    padding: 10px 8px;
    border-radius: var(--radius-sm);
}

.source-panel__link:hover {
    background: var(--color-bg);
}

.source-panel__link-title {
    font-size: 0.9rem;
    line-height: 1.3;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.source-panel__link-time {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.read-main {
    height: calc(100vh - var(--toolbar-height));
}

/* The article-page (non-iframe) branch renders real, taller-than-viewport
   content (hero + body + the "Mai multe" grid) inside this same <main> -
   the fixed 100vh height above is only correct for the iframe-embed
   branch (a <iframe> filling exactly one screen). Without this override,
   .read-main's box stays clipped to viewport height while its content
   overflows past it, and anything after </main> (the footer) gets
   positioned right after that short box instead of after the real
   (taller) content - visually landing on top of it. */
.read-main--article {
    height: auto;
}

.read-frame {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

.read-error {
    padding: 40px var(--gap);
    text-align: center;
    color: var(--color-text-muted);
    line-height: 1.6;
}

.read-error a {
    color: var(--color-accent);
    font-weight: 700;
}

/* ---------------------------------------------------------------------
   Article page — read.php's non-iframe reading mode ($skipEmbed): the
   content we actually have (image, title, teaser) laid out as a real
   page instead of just handing off to an <iframe>, with a "read the
   original" CTA and a grid of other recent articles from the same
   source. See read.php's docblock for when this mode is used.
   --------------------------------------------------------------------- */

.article-page {
    /* Same responsive container width as .feed (var(--feed-max-width),
       620/1040/1332px per breakpoint) - was hardcoded to 640px, which
       made this page read a lot narrower than the homepage at
       tablet/desktop widths. Any future full-width reader-facing page
       should use this same variable, not a bespoke number. */
    max-width: var(--feed-max-width);
    margin: 0 auto;
    padding-bottom: 40px;
}

.article-page__hero {
    position: relative;
    aspect-ratio: 16 / 9;
    background: var(--color-bg);
}

.article-page__hero-img {
    /* Without position:relative + z-index:1 here, the absolutely-
       positioned .card__placeholder gradient behind this <img> paints
       on top of it and hides a perfectly-loaded photo - same fix
       .card__img already has (the "Mai multe" grid below reuses that
       same card template now, not a bespoke thumb rule). */
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.article-page__body {
    /* Narrower reading column than the hero/More-from sections beside it
       (which keep the full .article-page width) - title/meta/text/CTA
       read better centered and capped, per explicit request. Children
       stay left-aligned (default block flow), only the box itself
       centers. */
    max-width: 800px;
    margin: 0 auto;
    padding: 20px var(--gap) 4px;
}

.article-page__title {
    font-size: 1.35rem;
    font-weight: 800;
    line-height: 1.32;
    margin: 0 0 10px;
}

.article-page__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: 16px;
}

.article-page__source {
    font-weight: 700;
    color: var(--color-text);
}

a.article-page__source:hover {
    color: var(--color-accent);
    text-decoration: underline;
}

.article-page__text {
    font-size: 1.15rem;
    line-height: 1.6;
    color: var(--color-text);
    margin: 0 0 24px;
}

/* Own-authored article body (Aug 2026 publishing feature) - the full
   sanitized body_html, in place of .article-page__text's excerpt, for
   an article whose source is Lang::OWN_SOURCE_NAME (see read.php's
   $isOwnArticle). Same base font-size/line-height/color as
   .article-page__text for a consistent article-reading feel; adds the
   handful of tags HtmlSanitizer::clean() actually allows through
   (headings, lists, blockquote, links) since a plain excerpt paragraph
   never needed them. */
.article-page__richtext {
    font-size: 1rem;
    line-height: 1.65;
    color: var(--color-text);
    margin: 0 0 24px;
}

.article-page__richtext p {
    margin: 0 0 16px;
}

.article-page__richtext h2,
.article-page__richtext h3 {
    margin: 28px 0 14px;
    line-height: 1.3;
}

.article-page__richtext h2 {
    font-size: 1.3rem;
}

.article-page__richtext h3 {
    font-size: 1.1rem;
}

.article-page__richtext a {
    color: var(--color-accent-dark);
    text-decoration: underline;
}

.article-page__richtext blockquote {
    margin: 16px 0;
    padding: 4px 18px;
    border-left: 3px solid var(--color-accent);
    color: var(--color-text-muted);
}

.article-page__richtext ul,
.article-page__richtext ol {
    margin: 0 0 16px;
    padding-left: 24px;
}

.article-page__richtext li {
    margin-bottom: 6px;
}

/* A pasted YouTube embed (Aug 2026 HTML-source toggle - see
   HtmlSanitizer::isSafeYoutubeEmbed()) ships with fixed width/height
   attributes (YouTube's own default 560x315) - overridden here to fill
   the article column at a proper 16:9 instead, so it stays responsive
   on mobile rather than either overflowing or sitting tiny and
   letterboxed. */
.article-page__richtext iframe {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    height: auto;
    border: 0;
    border-radius: var(--radius-sm);
    margin: 16px 0;
}

.article-page__cta {
    display: inline-flex;
    align-items: center;
    /* color-accent-dark, not color-accent - same white-text-on-red
       contrast fix as .catnav__link--active (not itself flagged by the
       PageSpeed run that caught the others, since that only audited the
       homepage, but it's the identical pattern and would fail the same
       check on an article page). Was also this button's own :hover color
       before this fix, so hover now darkens further (filter) to stay
       visibly different from the resting state. */
    background: var(--color-accent-dark);
    color: #fff;
    font-weight: 700;
    padding: 14px 22px;
    border-radius: 999px;
}

.article-page__cta:hover {
    filter: brightness(0.85);
}

/* --- article page, low-res-source variant --- */

/* Mobile vs desktop DOM-variant toggle for the low-res-source hero (see
   read.php's $useLowResHero branch) - CSS-toggled at the standard 1024px
   breakpoint, same pattern as .search-box--toolbar/--sidebar and the
   feed's own hero_lowres.php. Aug 2026 request: mobile now ignores the
   lowres treatment entirely and gets the plain full-bleed hero + normal
   title (identical markup to a non-lowres article); only desktop keeps
   the generic-photo-hero + compact-row treatment below. Both variants
   render unconditionally in the DOM - the hidden one's <img> is
   loading="lazy" so display:none keeps the browser from ever fetching
   it. */
.article-page__hero-variant--desktop {
    display: none;
}

@media (min-width: 1024px) {
    .article-page__hero-variant--mobile {
        display: none;
    }

    .article-page__hero-variant--desktop {
        display: block;
    }
}

/* Compact left-image row, shown *below* the generic curated hero photo,
   desktop-only (see the DOM-variant toggle above): the hero shows the
   curated generic stock photo here (never this item's own small/soft
   one), so the page still opens with a proper big image; the source's
   real low-res photo stays honestly small in this row instead of being
   stretched to hero size - same small-stays-small reasoning as the
   feed's own .card--hero-lowres (hero_lowres.php). Sits inside
   .article-page__body, same as the title/meta it replaces there - see
   read.php's $useLowResHero branch. */
.article-page__lowres-row {
    display: flex;
    align-items: stretch;
    gap: 14px;
    margin-bottom: 16px;
}

.article-page__lowres-media {
    position: relative;
    flex: 0 0 30%;
    align-self: center;
    aspect-ratio: 4 / 3;
}

.article-page__lowres-img {
    /* Same z-index fix as .article-page__hero-img - without it the
       placeholder gradient behind this <img> paints on top of a
       perfectly-loaded photo. */
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.article-page__lowres-text {
    flex: 1 1 auto;
    min-width: 0;
}

.article-page__hero-variant--desktop .article-page__title {
    font-size: 2rem;
    line-height: 1.2;
    margin-bottom: 6px;
}

.article-page__lowres-text .article-page__meta {
    margin-bottom: 0;
}

/* ---------------------------------------------------------------------
   Redacția AI (Sep 2026) - zona „Ultimele titluri din presă" de pe
   homepage, care ia locul zonei Dăpeștiri când modul „conținut propriu"
   e pornit. Deliberat mai discretă decât cea pe care o înlocuiește: aia
   scotea în evidență un articol propriu într-un flux străin, asta doar
   grupează trei titluri străine într-un flux propriu, deci n-are nevoie
   de fundal închis sau de card special - un cap de zonă și rândul
   obișnuit de 3 carduri.
   --------------------------------------------------------------------- */

/* Sep 2026: zona stă acum pe fundal propriu, cu aceeași rotunjire și
   același contur ca un card, ca să se citească drept o casetă aparte în
   flux, nu ca încă trei articole. Padding-ul propriu îl înlocuiește pe cel
   lateral al capului de zonă (vezi .press-zone__head mai jos), altfel
   titlul ar fi indentat față de cardurile de dedesubt. */
/* Sep 2026: fundal portocaliu-roșu (nuanța cerută de proprietar), în loc
   de alb - zona se citește dintr-o privire ca „altceva decât fluxul".
   Conturul e cu un pas mai saturat decât fundalul, cât să se vadă marginea
   pe fundalul gri al paginii. Cardurile dinăuntru rămân albe, deci ies în
   relief pe ea. */
.press-zone {
    margin: 8px 0;
    padding: 10px;
    background: #f2d6c9;
    border: 1px solid #e7c3b3;
    border-radius: var(--radius);
}

.press-zone__head {
    padding: 0 0 8px;
}

/* Linkul „mai multe titluri" stă acum sub carduri, aliniat la dreapta
   (Sep 2026) - vezi comentariul de la locul unde e randat, în index.php. */
.press-zone__foot {
    display: flex;
    justify-content: flex-end;
    padding: 8px 0 0;
}

.press-zone__label {
    font-size: 0.95rem;
    font-weight: 800;
}

.press-zone__more {
    flex: 0 0 auto;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--color-accent);
    text-decoration: none;
    white-space: nowrap;
}

.press-zone__more:hover {
    text-decoration: underline;
}

/* Tagurile din bara de navigare, lângă categorii (CardRenderer::navTags()).
   Aceeași înălțime și același ritm ca .catnav__link, dar rotunjite și cu
   contur, ca să se citească drept altceva decât o categorie - sunt
   subiecte trecătoare, calculate din cele mai citite articole, nu
   secțiuni permanente ale site-ului. */
.catnav__tag {
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 4px 10px;
}

.catnav__tag::before {
    content: '#';
    opacity: .55;
    margin-right: 1px;
}

/* ---------------------------------------------------------------------
   Redacția AI (Sep 2026) - elementele proprii unui articol scris de un
   jurnalist-bot: semnătura de sub titlu, creditul foto obligatoriu,
   lista de surse de la final și tagurile. Toate patru lipsesc complet
   de pe un articol preluat (read.php nici nu randează marcajul), deci
   nu au nevoie de nicio variantă „goală".
   --------------------------------------------------------------------- */

.article-page__byline {
    font-weight: 700;
    color: var(--color-text);
}

/* Semnătura e link spre /autor/<pseudonim> - păstrează exact aspectul de
   dinainte (text bold, culoarea textului), sublinierea apare doar la
   hover, ca să nu transforme linia de meta într-un rând de linkuri. */
a.article-page__byline {
    text-decoration: none;
}

a.article-page__byline:hover,
a.article-page__byline:focus-visible {
    text-decoration: underline;
}

/* Capul paginii de autor (/autor/<pseudonim>) - avatar + nume + domeniu.
   Un rând simplu, nu un card: pagina e o listă de articole cu un titlu,
   nu un profil. */
.author-head {
    display: flex;
    align-items: center;
    gap: 14px;
}

.author-head__text {
    min-width: 0;
}

.author-head .feed__page-heading {
    margin: 0;
}

.author-head__domain {
    margin: 2px 0 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.author-head__domain a {
    color: inherit;
}

/* Mențiunea despre modul de lucru, sub domeniul jurnalistului. Mai mică și
   mai ștearsă decât domeniul: e o precizare onestă, nu un avertisment -
   trebuie să fie citibilă de cine o caută, fără să domine pagina. */
.author-head__note {
    margin: 6px 0 0;
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--color-text-muted);
}

/* Secțiunea „Redacția" de pe /despre-noi - listă simplă, avatar + nume +
   domeniu, fără marcatori de listă. */
.newsroom-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.newsroom-list__item {
    display: flex;
    align-items: center;
    gap: 10px;
}

.newsroom-list__text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.newsroom-list__name {
    font-weight: 700;
}

.newsroom-list__domain {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* Lipit de imagine, nu de textul de dedesubt - creditul aparține pozei.
   Aceleași margini laterale ca .article-page__body, ca să se alinieze cu
   titlul, nu cu marginea full-bleed a imaginii. */
.article-page__image-credit {
    margin: 6px var(--gap) 0;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

/* Nota de paternitate de la finalul articolelor noastre (Sep 2026).
   Culoarea e fixată de proprietar (#6b7078), nu luată din variabile:
   e o notă discretă care trebuie să rămână identică indiferent ce se
   întâmplă cu paleta din jur. Aliniată dreapta, ca o semnătură. */
.article-page__ownership {
    margin-top: 24px;
    font-style: italic;
    font-size: 0.85rem;
    line-height: 1.6;
    text-align: right;
    color: #6b7078;
}

/* Când nota de mai sus e prezentă, sursele vin imediat sub ea - linia
   de sus a blocului de surse e deja separator suficient. */
.article-page__ownership + .article-page__sources {
    margin-top: 16px;
}

.article-page__sources {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
    font-size: 0.85rem;
    line-height: 1.7;
    color: var(--color-text-muted);
}

.article-page__sources-label {
    font-weight: 700;
    color: var(--color-text);
}

.article-page__sources a {
    color: var(--color-accent);
}

.article-page__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 16px;
}

.article-page__tag {
    display: inline-block;
    padding: 4px 10px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-text-muted);
    text-decoration: none;
}

.article-page__tag:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.article-grid-wrap {
    padding: 8px var(--gap) 8px;
    margin-top: 20px;
    border-top: 1px solid var(--color-border);
}

.article-grid__heading {
    font-size: 1rem;
    font-weight: 800;
    margin: 20px 0 14px;
}

/* The "Mai multe de la X" grid itself is no longer bespoke markup - it
   reuses the feed's own .zones/.zone-row + card templates (see
   read.php), so it needs no layout CSS of its own beyond the wrapper/
   heading above. */

/* ---------------------------------------------------------------------
   Phase 8 — comments (public/read.php's article-page mode, rendered by
   src/Layout/CommentRenderer.php). Plain responsive reflow only (no
   dual-DOM-variant split like the hero/search-box) - avatar/padding get
   smaller on mobile, nothing here is genuinely different content
   between breakpoints.
   --------------------------------------------------------------------- */

.comments-prompt-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin: 20px var(--gap) 0;
    padding: 12px 16px;
    background: var(--color-tag-bg);
    color: #fff;
    border-radius: var(--radius);
    font-weight: 700;
    font-size: 0.9rem;
    text-decoration: none;
}

.comments-prompt-bar svg {
    flex-shrink: 0;
    animation: comments-prompt-bounce 1.6s ease-in-out infinite;
}

@keyframes comments-prompt-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(3px); }
}

/* Site-identity card (Aug 2026 follow-up: owner noticed the article page
   never shows the logo/site name anywhere - "sa stie oamenii unde au
   ajuns si cu ce se mananca") - logo + wordmark + tagline, stacked and
   centered, right under .comments-prompt-bar above and before
   .comments-section below. Sized off .static-topbar__logo/__wordmark
   (the smaller of the two existing wordmark variants) rather than
   inventing a new scale, but laid out as a centered column instead of
   that variant's left-aligned row - see read.php's own comment at the
   render site for why this one spot, not a general page footer.
   .comments-section's own top border immediately below already reads as
   the divider under the tagline, so nothing extra is added here for
   that. */
.site-identity {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 8px;
    margin: 24px var(--gap) 0;
    text-decoration: none;
}

.site-identity__logo {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
}

.site-identity__wordmark {
    font-weight: 800;
    font-size: 1.2rem;
    letter-spacing: -0.02em;
    color: var(--color-text);
}

.site-identity__wordmark span {
    color: var(--color-accent);
}

.site-identity__tagline {
    display: block;
    max-width: 360px;
    font-size: 0.88rem;
    font-weight: 500;
    color: var(--color-text-muted);
}

.comments-section {
    /* Same reading-column cap as .article-page__body (800px) so the
       comments block doesn't visually flare wider than the article text
       above it - keep these two values in sync if either changes. */
    max-width: 800px;
    margin: 20px auto 0;
    padding: 20px var(--gap) 8px;
    border-top: 1px solid var(--color-border);
}

.comments-section__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 14px;
}

.comments-section__heading {
    font-size: 1.1rem;
    font-weight: 800;
    margin: 0;
}

.comments-section__sort {
    display: flex;
    gap: 6px;
}

.comments-section__sort-btn {
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text-muted);
    border-radius: 999px;
    padding: 5px 12px;
    font-size: 0.8rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
}

.comments-section__sort-btn--active {
    background: var(--color-tag-bg);
    color: #fff;
    border-color: var(--color-tag-bg);
}

.comments-section__login-prompt {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    padding: 14px;
    margin: 0 0 18px;
}

.comments-section__login-prompt a {
    color: var(--color-accent);
    font-weight: 700;
}

.comments-section__empty,
.comments-section__disabled {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    padding: 8px 0 4px;
}

/* ---------------------------------------------------------------------
   Composer avatar box (Aug 2026 request: "pune avatarul complet (nume,
   stelute, ranking) in stanga box-ului 'Scrie un comentariu'... fa tot
   boxul clickable catre profilul utilizatorului"). The identity block
   is its own <a> (avatar+name+star+rank, links to the commenter's own
   profile) sitting beside the <form> rather than wrapping it - a <form>
   can't legally nest inside an <a>, and keeping the textarea/button as
   plain interactive controls (not inside a link) means clicking in to
   type never risks a stray navigation.
   --------------------------------------------------------------------- */
.comment-composer-row {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    margin: 0 0 22px;
}

.comment-composer__identity {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    max-width: 160px;
    color: inherit;
}

.comment-composer__identity:hover .comment-composer__name {
    text-decoration: underline;
}

.comment-composer__identity-text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    min-width: 0;
}

.comment-composer__name {
    font-weight: 700;
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

.comment-composer {
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 480px) {
    .comment-composer-row {
        flex-direction: column;
    }

    .comment-composer__identity {
        max-width: none;
    }
}

.comment-composer__textarea,
.comment__reply-textarea {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 0.92rem;
    font-family: inherit;
    background: var(--color-bg);
    color: var(--color-text);
    resize: vertical;
    min-height: 72px;
}

.comment-composer__submit {
    align-self: flex-start;
}

.comments-section__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.comment {
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* Aug 2026 follow-up (owner: a notification's #comment-N link lands
       the target comment flush under the fixed .toolbar/.read-topbar -
       "bara de sus ascunde o parte din el si trebuie sa dau scroll in
       jos") - scroll-margin-top tells the browser's native fragment
       scroll (and any scrollIntoView() call, see comments.js) to stop
       this many px short of the very top instead of flush against it,
       so the comment lands fully clear of the fixed toolbar with a
       little breathing room. Applies to both .comment--top and
       .comment--reply (both always carry the base .comment class - see
       CommentRenderer::renderTopLevel()/renderReply()) and needs no JS -
       supported by every browser this site targets. */
    scroll-margin-top: calc(var(--toolbar-height) + 12px);
}

.comment__author-line {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    flex-wrap: wrap;
}

.avatar--sm {
    width: 32px;
    height: 32px;
}

.comment__avatar-link {
    display: inline-flex;
    color: inherit;
    flex-shrink: 0;
}

/* Name(+stars) on one line, the rank badge stacked directly under it
   ("sub nume pune si rankingul") - see CommentRenderer::authorLine(). */
.comment__author-info {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    min-width: 0;
}

.comment__author-name-line {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.comment__author-name {
    font-weight: 700;
    font-size: 0.88rem;
}

.comment__stars {
    font-size: 0.85rem;
}

.comment__deactivated-badge {
    font-size: 0.72rem;
    font-weight: 700;
    color: #8a4a7a;
    background: #f4e9f2;
    border-radius: 999px;
    padding: 2px 8px;
}

/* Rounded, tier-color-coded rank pill (RankCalculator::badgeColor()) -
   background/text colors are set inline per-instance (CommentRenderer::
   rankBadge()), this just handles shape/type. Reused as-is on the public
   profile page (public/profil.php). */
.rank-badge {
    display: inline-block;
    width: fit-content;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 2px 9px;
    border-radius: 999px;
    letter-spacing: 0.01em;
}

.comment__time {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    margin-left: auto;
}

.comment__body {
    font-size: 0.92rem;
    line-height: 1.55;
    margin: 0;
    padding-left: 40px;
    word-break: break-word;
}

.comment__mention {
    color: var(--color-accent);
    font-weight: 700;
}

.comment__actions {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-left: 40px;
    flex-wrap: wrap;
}

.comment__votes {
    display: flex;
    align-items: center;
    gap: 4px;
}

.comment__votes--own {
    font-size: 0.82rem;
    color: var(--color-text-muted);
}

.comment__vote-form {
    display: inline-block;
}

.comment__vote-btn {
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text-muted);
    border-radius: 999px;
    padding: 3px 9px;
    font-size: 0.78rem;
    font-weight: 700;
    cursor: pointer;
    font-family: inherit;
}

.comment__vote-btn--active {
    background: var(--color-accent);
    color: #fff;
    border-color: var(--color-accent);
}

.comment__report-form {
    display: inline-block;
}

.comment__report-btn {
    background: none;
    border: none;
    color: var(--color-text-muted);
    font-size: 0.78rem;
    text-decoration: underline;
    cursor: pointer;
    font-family: inherit;
    padding: 0;
}

.comment__report-btn--done {
    color: var(--color-text-muted);
    font-size: 0.78rem;
    text-decoration: none;
    cursor: default;
}

.comment__reply-toggle {
    font-size: 0.78rem;
}

/* Aug 2026 request: "box-ul Scrie un raspuns, fa-l mai larg -> sa umple
   restul spatiului pana in dreapta". Only widen once actually opened
   ([open], the <details> element's own state) - collapsed, the
   "Răspunde" summary stays inline and compact next to the vote/report
   buttons, same as before. Opened, it claims the full row width (via
   .comment__actions's existing flex-wrap: wrap, which pushes it onto
   its own line once nothing else fits beside a 100%-wide item) so the
   form filling that width reaches all the way to the comment's right
   edge instead of stopping at a fixed 480px. */
.comment__reply-toggle[open] {
    width: 100%;
}

.comment__reply-toggle summary {
    color: var(--color-text-muted);
    font-weight: 700;
    cursor: pointer;
    list-style: none;
}

.comment__reply-toggle summary::-webkit-details-marker {
    display: none;
}

.comment__reply-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 8px;
    width: 100%;
}

.comment__reply-submit {
    align-self: flex-start;
    padding: 7px 14px;
    font-size: 0.82rem;
}

.comment__replies {
    list-style: none;
    margin: 12px 0 0;
    padding: 0 0 0 22px;
    border-left: 2px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.comments-section__load-more {
    display: block;
    width: fit-content;
    margin: 6px auto 0;
}

@media (max-width: 480px) {
    .comment__body,
    .comment__actions {
        padding-left: 0;
    }

    .comment__replies {
        padding-left: 14px;
    }
}

/* ---------------------------------------------------------------------
   Category nav — lives inside .toolbar now (single Android-navbar-style
   row: hamburger, logo/wordmark, then categories scrolling in whatever
   width is left), not a second stacked bar. Horizontal scroll pills at
   every breakpoint.
   --------------------------------------------------------------------- */

.catnav {
    flex: 1 1 auto;
    align-self: stretch;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.catnav::-webkit-scrollbar {
    display: none;
}

.catnav__list {
    list-style: none;
    display: flex;
    gap: 8px;
    /* margin: 0 auto (not margin: 0) centers the pill row inside .catnav
       whenever it's narrower than the available space (few categories on
       a wide screen) - deliberately NOT done with justify-content:
       center on .catnav, because that would require .catnav itself to
       be display:flex, and a centered flex container combined with its
       overflow-x: auto scrolling has a well-known browser bug where the
       start of the overflowing content becomes unreachable by
       scrolling. margin:auto doesn't have that problem: when the list is
       WIDER than .catnav (the normal mobile case), both auto margins
       just resolve to 0 - nothing to distribute - so scrolling still
       starts exactly at the first pill, identical to before. */
    margin: 0 auto;
    padding: 0 var(--gap);
    height: 100%;
    align-items: center;
    width: max-content;
}

.catnav__link {
    display: inline-flex;
    align-items: center;
    height: 30px;
    padding: 0 14px;
    border-radius: 999px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
}

.catnav__link:hover {
    background: var(--color-tag-muted-bg);
}

.catnav__link--active {
    /* color-accent (#e0393e) under white text lands right at ~4.36:1 -
       just under WCAG's 4.5:1 for text this size (flagged by Lighthouse).
       color-accent-dark clears it comfortably (~6.4:1) while staying the
       same red hue - a deliberately small, same-brand-color fix rather
       than picking a new color. */
    background: var(--color-accent-dark);
    color: #fff;
}

/* ---------------------------------------------------------------------
   Sidebar — off-canvas drawer on mobile/tablet, persistent rail on desktop
   --------------------------------------------------------------------- */

.sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 17, 21, 0.45);
    z-index: 49;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s ease;
}

.sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(80vw, 300px);
    background: var(--color-surface);
    z-index: 50;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    display: flex;
    flex-direction: column;
    padding: var(--gap);
    overflow-y: auto;
}

body.sidebar-open .sidebar-backdrop {
    opacity: 1;
    pointer-events: auto;
}

body.sidebar-open .sidebar {
    transform: translateX(0);
    box-shadow: 8px 0 24px rgba(0, 0, 0, 0.12);
}

.sidebar__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: var(--gap);
    margin-bottom: var(--gap);
    border-bottom: 1px solid var(--color-border);
}

.sidebar__title {
    font-weight: 800;
    font-size: 1.05rem;
}

.sidebar__close-btn {
    appearance: none;
    border: 0;
    background: transparent;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.sidebar__close-btn:hover {
    background: var(--color-bg);
}

.sidebar__nav {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.sidebar__nav-link {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-weight: 600;
    color: var(--color-text);
}

.sidebar__nav-link:hover {
    background: var(--color-bg);
}

.sidebar__nav-link--active {
    background: var(--color-tag-muted-bg);
    color: var(--color-accent-dark);
}

/* ---------------------------------------------------------------------
   Sidebar — account/login state block. Sits near the TOP of the sidebar
   now (Aug 2026 request: "sa facem side menu mai personal" - right under
   the wordmark header, before the search box/category nav), rendered by
   the one shared CardRenderer::sidebarAccountBlock() for every page that
   has a sidebar - see that method's own docblock.
   --------------------------------------------------------------------- */

.sidebar__account {
    padding-bottom: var(--gap);
    margin-bottom: var(--gap);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sidebar__account-profile {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 12px 10px;
    border-radius: var(--radius-sm);
    color: var(--color-text);
}

.sidebar__account-profile:hover {
    background: var(--color-bg);
}

.sidebar__account-identity {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.sidebar__account-nickname {
    font-weight: 800;
    font-size: 1.05rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar__account-rank {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text-muted);
}

.sidebar__account-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.sidebar__account-nav-link {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    font: inherit;
    font-weight: 600;
    color: var(--color-text);
    appearance: none;
    border: 0;
    background: transparent;
    cursor: pointer;
}

.sidebar__account-nav-link:hover {
    background: var(--color-bg);
}

.sidebar__account-nav-link--logout {
    color: var(--color-text-muted);
}

.sidebar__account-prompt {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-weight: 600;
    color: var(--color-accent-dark);
    background: var(--color-tag-muted-bg);
}

.sidebar__account-logout-form {
    margin: 0;
}

.sidebar__account-logout {
    appearance: none;
    border: 0;
    background: transparent;
    width: 100%;
    text-align: left;
    padding: 6px 12px;
    font: inherit;
    font-weight: 600;
    color: var(--color-text-muted);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

/* Public-facing unread-notification count (Aug 2026 round 15) - sidebar
   account nav row + AccountRenderer::panelHeader()'s "Notificări" button.
   Same pill shape/weight as admin.css's .admin-badge--warning, kept as
   its own class since this renders on reader-facing pages, not admin. */
.notif-badge {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    font-weight: 700;
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
    background: var(--color-accent);
    color: #fff;
}

.sidebar__account-logout:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.sidebar__account-link {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-weight: 600;
    text-align: center;
    color: var(--color-text);
    border: 1px solid var(--color-border);
}

.sidebar__account-link:hover {
    background: var(--color-bg);
}

.sidebar__account-link--primary {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.sidebar__account-link--primary:hover {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
}

/* ---------------------------------------------------------------------
   Feed
   --------------------------------------------------------------------- */

.feed {
    max-width: var(--feed-max-width);
    margin: 0 auto;
    padding: var(--gap);
    display: flex;
    flex-direction: column;
    gap: var(--gap);
}

.feed__empty {
    text-align: center;
    color: var(--color-text-muted);
    padding: 48px 16px;
}

/* Heading above sursa.php's feed ("<source> — ultimele articole") - a
   source cuts across categories, so there's no nav pill to mark active
   here; this is what identifies the page instead. */
.feed__source-heading {
    font-size: 1.1rem;
    font-weight: 800;
    margin: 4px 0 4px;
}

/* index.php's own page heading (homepage/category/Ultima oră) - same
   small/discreet treatment as .feed__source-heading above, so this reads
   as a natural continuation of the existing toolbar/catnav chrome
   rather than a big new headline competing with the hero card below it.
   Exists on every load state (error/empty/paginated), see index.php. */
.feed__page-heading {
    font-size: 1.1rem;
    font-weight: 800;
    margin: 0;
}

/* Wraps .feed__page-heading + the "Arată sursele"/"Arată știrile" List-mode
   toggle (index.php) on the same line - wrap allowed (not nowrap) so a long
   category name + the button still stack cleanly on the narrowest phones
   instead of overflowing. */
.feed__heading-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 12px;
    margin: 4px 0;
}

.feed__view-toggle-btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    padding: 7px 14px;
    border-radius: 999px;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.85rem;
    font-weight: 700;
    text-decoration: none;
    white-space: nowrap;
}

/* --- category page, List mode (public/index.php's "Arată sursele") ---
   A different rendering of the category page: instead of the article
   feed, a simple alphabetical directory of that category's sources -
   narrower container (the android app's own source-list reads this way,
   not stretched full feed width) since a one-column list of short rows
   has nothing to gain from the feed's wider desktop measure. */
.feed--list-mode {
    max-width: 800px;
}

.source-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.source-list__row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: var(--color-text);
    text-decoration: none;
}

.source-list__avatar {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 10px;
    box-sizing: border-box;
    border-radius: 50%;
    background: #faf0eb;
    border: 2px solid #eda99d;
    color: var(--color-accent-dark);
    font-size: 1.15rem;
    font-weight: 800;
}

.source-list__name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 1.15rem;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.source-list__arrow {
    flex: none;
    color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------
   Site footer — CardRenderer::footer(), reused verbatim on index.php,
   read.php, sursa.php and the three legal/info pages themselves.
   --------------------------------------------------------------------- */

.site-footer {
    max-width: var(--feed-max-width);
    margin: 24px auto 0;
    /* Bottom padding well past what the links/copyright actually need
       (Aug 2026 request) - Google's AdSense floating consent icon pops
       up as soon as a reader scrolls to the very bottom of the page,
       which the old, tight footer made trivially easy to hit by
       accident right after the terms/privacy/about links. This extra
       breathing room is a stopgap (not a fix - the popup itself is
       Google's, outside our control) until that's looked at properly. */
    padding: 24px var(--gap) calc(280px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--color-border);
    text-align: center;
}

.site-footer__links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 8px 18px;
    /* Extra room under the links themselves, same "harder to
       accidentally hit page-bottom" reasoning as the footer's own
       bottom padding above. Bumped further by hand directly on the live
       site (Aug 2026) - 140px/48px wasn't enough to stop the AdSense
       popup from triggering; kept in sync here so a future update
       doesn't overwrite it back down. */
    margin-bottom: 100px;
}

.site-footer__links a,
.site-footer__consent-btn {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text-muted);
}

.site-footer__links a:hover,
.site-footer__consent-btn:hover {
    color: var(--color-accent);
    text-decoration: underline;
}

/* Opens Google's own consent-revocation dialog (consent.js, Aug 2026
   consent consolidation) - styled identically to the plain-text links
   beside it, just a <button> instead of an <a> since it triggers a JS
   action, not navigation. */
.site-footer__consent-btn {
    appearance: none;
    border: 0;
    background: transparent;
    padding: 0;
    font-family: inherit;
    cursor: pointer;
}

/* Fallback message (consent.js's maybeShowConsentFallback()) shown when
   Google's CMP script never loaded - most often Firefox's built-in
   tracking protection or an ad-blocking extension, see consent.js's
   docblock. Hidden until JS adds the --visible modifier on click. */
.consent-reopen-note {
    display: none;
    font-size: 0.78rem;
    color: var(--color-text-muted);
    flex-basis: 100%;
    margin-top: 4px;
}

.consent-reopen-note--visible {
    display: block;
}

.site-footer__copy {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    margin: 0;
}

/* ---------------------------------------------------------------------
   Google Funding Choices/AdSense CMP — Aug 2026 consent consolidation.
   The site's own cookie banner (previously here as .consent-banner*,
   removed) is gone in favor of Google's own IAB TCF-compliant CMP,
   which injects its own consent dialog and a persistent floating
   "reopen" icon (bottom-left, per Google's default placement) into the
   page itself - outside any markup this codebase controls. The user
   dislikes that icon sitting there permanently and already has the
   footer's own "Preferințe cookie-uri" link (consent.js) wired to
   Google's documented revocation-dialog API as the one visible control,
   so the floating icon is hidden here.

   BEST-EFFORT SELECTOR — NOT YET VERIFIED against the live DOM. Google's
   Funding Choices markup isn't officially documented and can change;
   #fc-consent-root is the container id Google's script is commonly
   observed to inject. Confirm the actual selector via a live Chrome
   inspection pass after deploying this and adjust here if it doesn't
   match what's actually rendered.
   --------------------------------------------------------------------- */

#fc-consent-root,
.fc-consent-root {
    display: none !important;
}

/* ---------------------------------------------------------------------
   Pagination — index.php, numbered ?page= links at the bottom of the feed
   --------------------------------------------------------------------- */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 0 24px;
}

.pagination__link,
.pagination__ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    height: 36px;
    padding: 0 10px;
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-text);
}

.pagination__link:hover {
    background: var(--color-tag-muted-bg);
}

.pagination__link--active {
    /* color-accent-dark, not color-accent - same white-text-on-red
       contrast fix as .catnav__link--active above. */
    background: var(--color-accent-dark);
    color: #fff;
}

.pagination__link--disabled {
    color: var(--color-text-muted);
    pointer-events: none;
    opacity: 0.5;
}

.pagination__ellipsis {
    color: var(--color-text-muted);
    font-weight: 700;
}

/* ---------------------------------------------------------------------
   Cards — shared base
   --------------------------------------------------------------------- */

.card {
    background: var(--color-surface);
    border-radius: var(--radius);
    overflow: hidden;
    border: 1px solid var(--color-border);
}

.card__link {
    display: block;
}

/* ---------------------------------------------------------------------
   Ad card — Phase 6 (AdSense in-feed native ad). Spliced into the feed
   every N articles by index.php and once into the article page's "Mai
   multe de la X" grid by read.php - same shared ad unit both places, see
   CardRenderer::adCard()/adsConfig(). Sits on top of the plain .card base
   above (border/background/radius already match every other card), just
   adds its own padding, a muted "Publicitate" label, and a reserved
   min-height so an ad that's still loading (or unfilled) doesn't cause a
   layout jump once it resolves - same "always paint something at the
   right size" idea as .card__placeholder elsewhere.
   --------------------------------------------------------------------- */

.card--ad {
    padding: 14px 14px 16px;
    min-height: 100px;
}

.card--ad__label {
    display: block;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    margin-bottom: 8px;
}

/* ---------------------------------------------------------------------
   Abstract placeholder backgrounds — 5 designs, picked per item by
   CardRenderer::placeholderIndex(). Shown instead of a photo when an item
   has no image_source_url, or as the graceful fallback if a hotlinked
   photo fails to load (see the onerror handlers in the media templates) -
   dark, soft radial glows so a missing photo still reads as "designed",
   not "broken". Sits inside any container that has its own aspect-ratio/
   sizing and position:relative; a same-sized <img> painted on top (when
   present) simply covers it until/unless it fails to load.
   --------------------------------------------------------------------- */

.card__placeholder {
    position: absolute;
    inset: 0;
}

/* Every real <img> sits after its .card__placeholder sibling in the
   markup, but absolutely-positioned elements always paint above static
   ones regardless of source order - without this, the placeholder would
   cover a perfectly-loaded photo instead of the other way around. */
.card__img {
    position: relative;
    z-index: 1;
}

.ph-1 {
    background:
        radial-gradient(circle at 52% 18%, #f2c53d 0%, rgba(242, 197, 61, 0) 58%),
        #0a0805;
}

.ph-2 {
    background:
        radial-gradient(circle at 22% 15%, #e0742a 0%, rgba(224, 116, 42, 0) 52%),
        radial-gradient(circle at 78% 88%, #7a3d10 0%, rgba(122, 61, 16, 0) 55%),
        #0a0e0d;
}

.ph-3 {
    background:
        radial-gradient(circle at 58% 58%, #2fd3ec 0%, rgba(47, 211, 236, 0) 55%),
        #04070f;
}

.ph-4 {
    background:
        radial-gradient(circle at 32% 22%, #23a878 0%, rgba(35, 168, 120, 0) 55%),
        radial-gradient(circle at 14% 74%, #2266c9 0%, rgba(34, 102, 201, 0) 45%),
        #04100c;
}

.ph-5 {
    background:
        radial-gradient(circle at 76% 24%, #d0499f 0%, rgba(208, 73, 159, 0) 50%),
        radial-gradient(circle at 68% 78%, #3568d4 0%, rgba(53, 104, 212, 0) 50%),
        #0d0714;
}

.card__tag {
    display: inline-block;
    /* Several card bodies (hero, feature) are flex columns, whose default
       align-items:stretch would otherwise blow this pill up to the full
       container width - it must always size to its own text. */
    align-self: flex-end;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--color-tag-bg);
    color: var(--color-tag-text);
    padding: 3px 8px;
    border-radius: 999px;
    margin-bottom: 8px;
}

.card__tag--muted {
    background: var(--color-tag-muted-bg);
    color: var(--color-tag-muted-text);
}

.card__title {
    font-size: 1.15rem;
    line-height: 1.32;
    font-weight: 700;
    margin: 0 0 8px;
    letter-spacing: -0.01em;
}

.card__title--small {
    font-size: 1.1rem;
    line-height: 1.35;
    margin-bottom: 6px;
}

.card__excerpt {
    font-size: 0.99rem;
    line-height: 1.45;
    color: var(--color-text-muted);
    margin: 0 0 8px;
}

.card__meta {
    font-size: 0.76rem;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    gap: 5px;
}

.card__dot {
    opacity: 0.6;
}

/* Semnătura jurnalistului pe cardul din flux: „de Elena Câmpeanu | acum
   23 min" (Sep 2026). Aceeași mărime ca restul liniei de meta, doar mai
   apăsată - e singura informație de acolo care spune că articolul e al
   nostru, nu preluat. Fără link, dinadins: vezi CardRenderer::byline().
   Se scurtează cu … pe carduri înguste, în loc să împingă ora pe rândul
   următor. */
.card__byline {
    font-weight: 700;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Pe cardurile cu text peste imagine (hero, overlay), linia de meta e
   deja albă pe fundal întunecat - semnătura moștenește culoarea aia, nu
   --color-text, care acolo ar fi negru pe negru. Rămâne doar mai apăsată
   decât restul liniei, prin font-weight. */
.card__meta--hero .card__byline,
.card__meta--on-image .card__byline {
    color: inherit;
}

.card__sep {
    opacity: 0.5;
    flex: 0 0 auto;
}

/* Ora nu se strânge niciodată: pe un card îngust, un pseudonim lung
   trebuie să cedeze el (are ellipsis mai sus), nu „acum 23 min", care ar
   deveni de necitit tăiat. */
.card__time {
    flex: 0 0 auto;
}

/* Comment-bubble icon + count, appended to .card__meta after source/time
   (Aug 2026 request) - only rendered at all when the article has
   comments, see CardRenderer::commentCountBadge(). Sits inline with the
   existing source/dot/time chain via the same flex+gap on .card__meta,
   no layout changes needed there. */
.card__comment-count {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex-shrink: 0;
}

.card__comment-icon {
    flex-shrink: 0;
    opacity: 0.75;
}

/* --- full-image, title below --- */

.card--full-below .card__media {
    position: relative;
    aspect-ratio: 16 / 9;
}

.card--full-below .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card--full-below .card__body {
    padding: 14px;
}

/* --- full-image top, title + description (the taller 3-column variant) --- */

.card--tall .card__media--tall {
    position: relative;
    aspect-ratio: 16 / 9;
}

.card--tall .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card--tall .card__body {
    padding: 14px;
}

/* --- full-image, title overlaid --- */

.card--full-overlay .card__media--overlay {
    position: relative;
    aspect-ratio: 16 / 13;
}

.card--full-overlay .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Also used by .card--own (the homepage own-article card, round 18) -
   same title-overlaid-on-scrim treatment as full_image_overlay.php,
   reused as-is inside that card's own media box. */
.card--full-overlay .card__scrim,
.card--own .card__scrim {
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(to bottom, var(--color-scrim-top) 40%, var(--color-scrim-bottom) 100%);
}

.card--full-overlay .card__overlay-body,
.card--own .card__overlay-body {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 3;
    padding: 16px 34px 14px;
}

.card__title--on-image {
    color: #fff;
}

.card__meta--on-image {
    color: rgba(255, 255, 255, 0.85);
}

.card__tag--on-image {
    /* color-accent-dark, not color-accent - see .catnav__link--active's
       comment; same white-text-on-red contrast fix. */
    background: var(--color-accent-dark);
}

/* --- thumbnail + title, left/right variants --- */

/* height:100% here is what actually fixes the vertical-centering gap
   (Aug 2026 report + screenshot): in a stretched grid row (intermediate/
   desktop - see .zone-row's grid default align-items:stretch) a thumb
   card can end up taller than its own content when a row-mate (e.g. a
   longer .card--text excerpt) is taller. Without this, .card__link--row
   just sat at its own natural (short) height, pinned to the top of the
   now-taller .card, leaving blank space below it. With it, the row fills
   the full stretched height, and its children's existing align-self:center
   (.card__thumb below) / justify-content:center (.card__body--thumb below)
   then center the thumb+title as a group - same fix .card--text already
   had for the identical reason (see that rule's own comment). Harmless at
   mobile: .card has no imposed height there (single stacked column), so
   100% just resolves to auto, same as before. */
.card__link--row {
    display: flex;
    align-items: stretch;
    height: 100%;
}

.card__thumb {
    position: relative;
    flex: 0 0 160px;
    align-self: center;
    aspect-ratio: 4 / 3;
}

.card__thumb .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card__body--thumb {
    flex: 1 1 auto;
    padding: 12px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Aug 2026: thumb_title_left.php's title/meta used to be right-aligned
   here (via a now-removed .card__body--right modifier) to visually
   balance the left-side thumb - changed to left-aligned per explicit
   request, so .card__body--thumb's own left-default styling above is now
   all every small 2-column card needs; no .card--thumb-left override
   left to write. */

/* --- thumb, vertical variant (image top, title below) --- */

/* TIER_THUMB_LOWRES's template (thumb_title_below.php) - a real image
   from a low-res-flagged source that would otherwise have qualified for
   the large tier (LayoutSelector's class docblock). Unlike the plain
   thumb_title_left row above, this one's image scales with aspect-ratio +
   object-fit:cover instead of a fixed 200px side thumb, so it looks
   proportionate at whatever height a grid row imposes - the horizontal
   thumb card looked broken (tiny image+title, huge blank gap) paired next
   to a full-size large-tier sibling in the same "big" 2-column row (Aug
   2026 report/screenshot: a GSP item next to a Golazo.ro full_image_below
   card). Same 16/9 media ratio as .card--full-below on purpose, so the two
   card families still read as one visual set when they land in the same
   row - title stays the normal .card__title size too (not the smaller
   --small variant thumb_title_left uses), per the request. This isn't
   really a "smaller/safer" treatment at all, just a plainer one (no
   scrim/overlay) - a source we don't fully trust the photo quality of. */
.card--thumb-below .card__media {
    position: relative;
    aspect-ratio: 16 / 9;
}

.card--thumb-below .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card--thumb-below .card__body {
    padding: 14px;
}

/* --- text-only variants --- */

/* No media slot at all - these items have no photo, so we don't reserve
   any image-shaped space or show placeholder art for it (see
   CardRenderer::placeholderIndex() - that's only ever for a real <img>
   that fails to load). Title carries the visual weight instead.
   In a stretched grid row (intermediate/desktop - see .zone-row's
   align-items:stretch below) these cards can end up taller than their own
   content, so both the link and the body are stretched to the full card
   height and centered as a flex column - short text sits in the middle of
   the card rather than pinned to the top. Harmless at mobile: .card has no
   imposed height there (single stacked column), so the 100% heights just
   resolve to auto. */
.card--text .card__link {
    height: 100%;
}

.card__body--text {
    padding: 14px 14px 16px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 100%;
}

.card--text-accent,
.card--text-accent-big {
    border: 4px solid #faf0eb;
}

/* Bigger, left-aligned, forced-multiline title - the padding-right keeps
   it from spanning the full card width so it wraps across more lines and
   fills the space an image would have. text_plain/text_accent are the
   only templates that are ever genuinely photo-free by design (compact
   position-14+ cards) - hero/feature/tall always show a real photo now,
   falling back to a local jpg rather than going text-only (see
   CardRenderer::heroFallbackSrc()). The excerpt is kept short here
   (CardRenderer.php's text_accent.php call) so it doesn't compete with
   the title for room.

   This base rule (mobile-first, applies at every width unless overridden
   below in the desktop media query) is shared by both text_accent and
   text_accent_big - unchanged since mobile always collapses to one
   column, so "top" vs "lower" position doesn't matter there (see the
   desktop-only split just below, same reasoning as text_plain_big's). */
.card--text-accent .card__title,
.card--text-accent-big .card__title {
    font-size: 1.4rem;
    line-height: 1.2;
    text-align: left;
    padding-right: 33%;
}

.card--text-plain,
.card--text-plain-big {
    background: #faf0eb;
    border: 3px dashed #eda99d;
}

/* ---------------------------------------------------------------------
   Rows — a single generic multi-column row wrapper, used for every row in
   the feed (public/index.php builds $rows from a fixed pattern -
   [1,2,1,2,1,3,1,2,2,2] then repeating 2s - one lead story, then a mix of
   full-width/2-up/3-up rows). Mobile-first: every row is just a single
   stacked column here, same as a plain card feed - it only becomes a real
   N-column CSS grid at the intermediate/desktop breakpoint below, where N
   comes from the --row-cols custom property index.php renders inline per
   row.
   --------------------------------------------------------------------- */

.zones,
.zone-row {
    display: flex;
    flex-direction: column;
    gap: var(--gap);
}

/* ---------------------------------------------------------------------
   Homepage trending zone (Aug 2026 request) — spliced into $rows right
   after article position 11 on the default landing view's page 1 only
   (public/index.php's dapestiri_trending_zone_html()/the $trendingZone
   build+splice above it). "Layout deosebit" per spec: a distinct
   background + dotted border sets it apart from the plain card rows
   around it. Up to 3 columns (Cele mai citite / Cele mai comentate /
   Comentatori în formă), each independently hidden server-side when it
   doesn't have enough data - flex-wrap (not CSS grid) is deliberate so
   a hidden column leaves no gap and the remaining ones reflow to fill
   the row, "flow liber", at every width down to mobile (a 240px flex-
   basis already forces a clean single-column stack on a phone-width
   screen with no extra media query needed).
   --------------------------------------------------------------------- */

.trending-zone {
    background: var(--color-surface);
    border: 2px dotted var(--color-border);
    border-radius: var(--radius);
    padding: var(--gap);
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
}

/* Baza a coborât de la 240 la 200px (Sep 2026) când zona a primit a patra
   coloană, „Subiectele zilei": la 240 cele patru nu mai încăpeau pe un
   rând la lățimea de 1024px și rupeau 2+2. Coloanele cresc oricum egal
   peste bază (flex-grow 1), deci pe ecrane mari arată la fel ca înainte. */
.trending-zone__col {
    flex: 1 1 200px;
    min-width: 0;
}

.trending-zone__heading {
    font-size: 0.95rem;
    font-weight: 800;
    margin: 0 0 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
}

.trending-zone__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
    counter-reset: trending-zone-counter;
}

.trending-zone__item {
    display: flex;
    align-items: baseline;
    gap: 6px;
    counter-increment: trending-zone-counter;
}

.trending-zone__item::before {
    content: counter(trending-zone-counter) ".";
    flex-shrink: 0;
    font-weight: 800;
    color: var(--color-accent);
    font-size: 0.78rem;
    min-width: 14px;
}

.trending-zone__link {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-text);
    line-height: 1.35;
    flex: 1 1 auto;
}

.trending-zone__link:hover {
    color: var(--color-accent);
    text-decoration: underline;
}

.trending-zone__count {
    flex-shrink: 0;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--color-text-muted);
    background: var(--color-tag-muted-bg);
    border-radius: 999px;
    padding: 1px 7px;
}

/* "Comentatori în formă" richer row (Aug 2026 follow-up: "sa punem
   aspect mai bogat la comentatori: icon, rang, steluta(e)") - avatar +
   name + star on one line, the tier-colored rank pill stacked directly
   under it, same two-line "name-line, then rank badge below" pattern
   already used in the comment thread (.comment__author-info) and
   reused here at the trending zone's smaller scale rather than
   inventing a new layout. .trending-zone__item's own baseline
   alignment is overridden to flex-start since this variant is no
   longer a single text line. */
.trending-zone__item--commenter {
    align-items: flex-start;
}

.trending-zone__commenter-avatar {
    display: inline-flex;
    flex-shrink: 0;
    color: inherit;
}

.trending-zone__commenter-info {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    min-width: 0;
    flex: 1 1 auto;
}

.trending-zone__commenter-name-line {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* Aug 2026 follow-up (owner: "aliniaza si dreapta si stanga... si
   centreaza tot blocul") - the plain flex layout above left every row's
   count badge trailing right after that row's own nickname, so with
   nicknames of different lengths the badges landed at 5 different x
   positions (ragged, not a clean column) and the whole list sat pinned
   to the column's left edge instead of reading as one contained block.
   --commenters makes .trending-zone__list itself size to its own
   widest row (width: max-content) rather than stretching to the full
   .trending-zone__col width, and centers that intrinsic-width block
   inside the column; because .trending-zone__list is a column flex
   container, every <li> then stretches (default align-items: stretch)
   to that same shared width, so a fixed left edge (avatar) and a
   fixed right edge (count, pushed out via margin-left: auto once the
   name-line itself is told to fill that shared row width) line up
   across all rows - a single flush rectangle, centered as a whole,
   matching the owner's green-oval mockup rather than the red one's
   ragged/left-pinned original. Scoped to this one modifier class so
   "Cele mai citite"/"Cele mai comentate" (which already intentionally
   use the column's full width, .trending-zone__link's flex: 1 1 auto
   pushing their own count flush right) are untouched. */
.trending-zone__list--commenters {
    width: max-content;
    max-width: 100%;
    margin: 0 auto;
}

.trending-zone__list--commenters .trending-zone__commenter-name-line {
    width: 100%;
}

.trending-zone__list--commenters .trending-zone__count {
    margin-left: auto;
}

/* ---------------------------------------------------------------------
   Homepage "Dăpeștiri" own-article card (Aug 2026 publishing feature;
   restructured round 18) - dapestiri_own_zone_html() in public/index.php,
   spliced into the feed right after article position 3 on the default
   landing view's page 1, same landmark as before. No longer its own
   standalone bordered zone: it's now one ordinary .zone-row (3 columns,
   same grid mechanism as every other multi-up row - see .zone-row/
   --row-cols above) whose first card is this bespoke, accent-bordered
   .card--own and whose other 2 cards are plain tall_feature.php cards
   borrowed from the real feed (see the splice site's own comment for
   why). Only THIS card carries the "branded" accent border/label - its
   2 row-mates render with their completely ordinary .card--tall styling,
   so only the own-article itself stands out as promoted content within
   the row, per the mockup (a red-bordered box next to 2 plain cards).
   --------------------------------------------------------------------- */

.card--own {
    border: 2px solid var(--color-accent);
    display: flex;
    flex-direction: column;
    padding: 12px;
}

.card--own__header {
    margin: 0 0 10px;
}

.card--own__label {
    font-size: 0.85rem;
    font-weight: 800;
    color: var(--color-text);
}

.card--own__label-accent {
    color: var(--color-accent);
}

/* Round 18, second pass: image on top, then a solid dark box below it
   (same --color-hero-bg/--color-hero-text-muted tokens the Hero redesign
   uses) instead of the title/time overlaid on the photo itself - per
   user feedback, the overlay left dead white space below it once this
   card stretched to match its 2 taller row-mates (.zone-row's default
   align-items:stretch), and read with less impact than intended. The
   link wraps both the image and the dark box as one flush unit
   (rounded/clipped together) so they read as a single connected block,
   same visual idea as the Hero card just stacked instead of side-by-side. */
.card--own__link {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.card--own__media {
    position: relative;
    aspect-ratio: 16 / 9;
    flex: none;
}

.card--own__media .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* flex: 1 1 auto - this is what actually fixes the dead-space report:
   the dark box grows to absorb whatever extra height the row's taller
   borrowed cards force onto this card, instead of leaving plain white
   card background below a fixed-height text block. */
.card--own__body {
    background: var(--color-hero-bg);
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 14px 16px;
}

.card--own__title {
    color: #ffffff;
}

.card--own__meta {
    color: var(--color-hero-text-muted);
}

.card--own__more {
    display: block;
    text-align: right;
    margin-top: 10px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--color-accent);
}

.card--own__more:hover {
    text-decoration: underline;
}

/* --- hero (the single opening story) --- */

.card--hero .card__media--hero {
    position: relative;
    aspect-ratio: 4 / 3;
}

.card--hero .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/* Round 18 ("bigger impact" request, mockup): dark box, white/light text
   - the one intentionally dark-themed component on an otherwise fully
   light site (see --color-hero-bg's own comment above). Only the body
   goes dark; the photo above it is untouched. hero_lowres.php carries
   both card--hero and card--hero-lowres and reuses card--hero's own
   inner classes wholesale (see that template's docblock), so it
   automatically gets this same dark treatment too - no separate rule
   needed, and no visual reason to exclude it (it's still the same "one
   opening story" card, just with a smaller/softer source photo).
   card__tag--hero's existing --color-accent-dark red pill + white text
   already reads correctly against this background unchanged - no
   contrast fix needed there. */
.card--hero .card__body--hero {
    background: var(--color-hero-bg);
    padding: 18px 16px;
}

.card__title--hero {
    font-size: 1.5rem;
    line-height: 1.22;
    letter-spacing: -0.015em;
    color: #ffffff;
}

.card__excerpt--hero {
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--color-hero-text-muted);
    margin: 0 0 10px;
}

.card__meta--hero {
    color: var(--color-hero-text-muted);
}

.card__tag--hero {
    /* color-accent-dark, not color-accent - see .catnav__link--active's
       comment; same white-text-on-red contrast fix. */
    background: var(--color-accent-dark);
}

/* --- hero, low-res-source variant (hero_lowres.php) - Aug 2026: no base/
   mobile-level rules here anymore (see this file's history if needed). The
   template now carries both card--hero and card--hero-lowres and reuses
   card--hero's own inner classes, so at mobile widths it's already
   identical to a plain hero card with zero extra CSS - the small
   side-by-side treatment only exists inside the >=1024px block below,
   scoped by the card--hero-lowres marker class. See hero_lowres.php's own
   docblock for why (small/soft source photo is fine at mobile sizes;
   only worth shrinking once there's room for a big stretched image to
   actually look blurry). */

/* --- feature (the full-width breakout story between pair rows) --- */

.card--feature .card__link--feature {
    display: flex;
    flex-direction: column;
}

.card--feature .card__media--feature {
    position: relative;
    aspect-ratio: 16 / 9;
    flex: none;
}

.card--feature .card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card--feature .card__body--feature {
    padding: 16px;
}

/* ---------------------------------------------------------------------
   Tablet (>=600px)
   --------------------------------------------------------------------- */

@media (min-width: 600px) {
    :root {
        --feed-max-width: 620px;
        --gap: 16px;
    }

    .feed {
        padding: 20px var(--gap) 40px;
    }

    .card__title {
        font-size: 1.15rem;
    }

    /* Enough room now to show the wordmark text next to the logo - stays
       hidden below this (logo alone) to keep the single-line navbar from
       crowding out the category pills on narrow phones. */
    .toolbar__wordmark {
        display: inline;
    }
}

/* ---------------------------------------------------------------------
   Intermediate (>=1024px) — a tier between tablet and the real desktop
   breakpoint below (>=1332px): persistent sidebar rail, tabbar-style
   category nav, and the row-based magazine layout already kick in here,
   just at a narrower container. The >=1332px block further down only
   changes container width and swaps the sidebar back to an off-canvas
   drawer - it does not redefine the row/grid rules below, which apply
   to both tiers via the normal cascade.
   --------------------------------------------------------------------- */

@media (min-width: 1024px) {
    :root {
        --sidebar-width: 240px;
        /* Wider than the mobile/tablet app-style column - the hero and the
           2-col/3-col zones below need real room to read as a proper
           magazine-style desktop homepage, not a stretched phone feed. */
        --feed-max-width: 1040px;
    }

    body {
        padding-left: var(--sidebar-width);
    }

    .toolbar {
        left: var(--sidebar-width);
    }

    .toolbar__menu-btn {
        display: none;
    }

    .sidebar-backdrop {
        display: none;
    }

    .sidebar {
        transform: none;
        width: var(--sidebar-width);
        border-right: 1px solid var(--color-border);
        box-shadow: none;
    }

    .sidebar__close-btn {
        display: none;
    }

    .feed {
        margin: 0 auto;
    }

    /* --- The intermediate/desktop magazine diversification --- */

    /* Every row becomes a real N-column grid here, N set per-row by
       index.php via the --row-cols custom property (falls back to 1 if
       it's ever missing). Rows of 1 (hero/feature) just render as a
       single full-width column, same as before. */
    .zone-row {
        display: grid;
        grid-template-columns: repeat(var(--row-cols, 1), 1fr);
        gap: 20px;
    }

    .zones {
        gap: 20px;
    }

    /* Hero: big banner, side-by-side image + text instead of stacked, so
       it reads as one wide opening block rather than a tall mobile card
       blown up to full width. */
    .card--hero .card__link {
        display: flex;
        align-items: stretch;
    }

    .card--hero .card__media--hero {
        flex: 0 0 58%;
        align-self: center;
    }

    .card--hero .card__body--hero {
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: 28px 32px;
        min-width: 0;
    }

    /* hero_lowres: only from this breakpoint up does it diverge from a
       plain hero card - a small, fixed ~30% image beside the title/excerpt,
       instead of card--hero's own 58%/42% split just above. A low-res
       photo doesn't get more flattering the bigger the viewport gets, so
       this proportion stays fixed rather than scaling with the next
       breakpoint the way card--hero's own 58% does. Below this breakpoint
       (mobile/tablet) hero_lowres.php renders with zero overrides at all -
       see this file's own "hero, low-res-source variant" comment above
       for why (Aug 2026 report: those images are fine at mobile sizes). */
    .card--hero-lowres .card__media--hero {
        flex: 0 0 30%;
    }

    .card--hero-lowres .card__body--hero {
        padding: 18px 22px;
    }

    .card--hero-lowres .card__title--hero {
        font-size: 1.1rem;
        line-height: 1.28;
        letter-spacing: -0.01em;
    }

    .card__title--hero {
        font-size: 2rem;
        line-height: 1.15;
    }

    /* Round 18 follow-up: same TIER_LARGE-position split as text_plain_big
       just below, now applied to text_accent too - this class-only bump
       used to apply to every occurrence of .card--text-accent regardless
       of where it landed in the feed (pre-dating round 18), same
       over-application bug the text_plain rule had before that fix. Only
       'text_accent_big' (FeedRows::buildRow()'s swap, TIER_LARGE-preferred
       "top" slots only - positions 2-3/5-6/12-13) keeps the bigger title;
       plain 'text_accent' drops to the rule right after this one. */
    .card--text-accent-big .card__title {
        font-size: 2rem;
        line-height: 1.15;
    }

    /* Plain text_accent (every other position - TIER_THUMB-preferred rows,
       position 14+, and the plain repeating fallback grid): back to the
       ordinary desktop card title size/line-height (matches the generic
       .card__title desktop rule above) instead of the big multi-line
       treatment reserved for text_accent_big. Base-rule padding-right/
       text-align stay as-is (shared, mobile-first) - only size is split. */
    .card--text-accent .card__title {
        font-size: 1.15rem;
        line-height: 1.32;
    }

    /* Round 18, second attempt: 'text_plain_big' (see that template's own
       docblock / FeedRows::buildRow()'s swap) is a genuinely distinct
       layout from plain 'text_plain', only ever chosen for the row
       pattern's TIER_LARGE-preferred "top" slots - so this bump is scoped
       to a class no other occurrence of the no-image card ever carries,
       unlike the first attempt (a class-only rule on .card--text-plain
       itself) which affected every no-image blurb throughout the whole
       feed. Desktop only, same as text_accent's own bump just above. */
    .card--text-plain-big .card__title {
        font-size: 2rem;
        line-height: 1.15;
    }

    /* Feature (the full-width 1-column rows between the multi-up ones):
       image-left, text-right breakout treatment. */
    .card--feature .card__link--feature {
        flex-direction: row;
        align-items: stretch;
    }

    .card--feature .card__media--feature {
        flex: 0 0 42%;
        align-self: center;
    }

    .card--feature .card__body--feature {
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: 22px 26px;
        min-width: 0;
    }

    .card--feature .card__title {
        font-size: 1.3rem;
    }

    /* Tall (the 3-up row): slightly smaller title than the 2-up rows so
       three of them sit comfortably side by side. */
    .card--tall .card__title {
        font-size: 0.98rem;
    }

    /* Small side-by-side thumb cards (position 14 on) get more visual
       weight at this width - same 16:9 crop, just a bigger box. */
    .card__thumb {
        flex: 0 0 300px;
    }

    /* Search box hands off from the sidebar to the toolbar/read-topbar
       here - see the base (mobile) rules above for why these are two
       separate DOM forms rather than one repositioned element. */
    .search-box--toolbar {
        display: flex;
    }

    .search-box--sidebar {
        display: none;
    }
}

/* ---------------------------------------------------------------------
   Desktop (>=1332px) — the real wide desktop tier. Wider container than
   the intermediate tier above, and the sidebar goes back to being an
   off-canvas drawer (hamburger-triggered) instead of a persistent rail,
   to free up the extra width for the feed itself. The row/grid rules
   from the intermediate block above still apply here unchanged - this
   block only touches container width and the sidebar/toolbar chrome.
   --------------------------------------------------------------------- */

@media (min-width: 1332px) {
    :root {
        --feed-max-width: 1332px;
    }

    body {
        padding-left: 0;
    }

    .toolbar {
        left: 0;
    }

    .toolbar__menu-btn {
        display: flex;
    }

    .sidebar-backdrop {
        display: block;
    }

    .sidebar {
        transform: translateX(-100%);
        border-right: 0;
        box-shadow: none;
    }

    body.sidebar-open .sidebar {
        transform: translateX(0);
        box-shadow: 8px 0 24px rgba(0, 0, 0, 0.12);
    }

    .sidebar__close-btn {
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

/* ===== Banda de identitate de pe prima pagină (Oct 2026) =====================
   O frază care spune ce face site-ul, un desen care o arată, cifrele zilei și
   publicațiile citite. Vezi dapestiri_identity_band_html() din index.php.

   Fundalul e --color-hero-bg, același cu al cardului hero: banda și hero-ul
   sunt singurele două suprafețe întunecate din site, iar folosirea aceleiași
   culori le leagă în loc să introducă un al treilea ton. */
.idband {
    background: var(--color-hero-bg);
    color: #fff;
    border-radius: var(--radius);
    overflow: hidden;
    margin: 0 var(--gap) var(--gap);
}

.idband__main {
    display: grid;
    grid-template-columns: 88px 1fr auto;
    gap: 16px;
    align-items: center;
    padding: 15px 16px 14px;
}

.idband__art { width: 88px; }
.idband__art svg { display: block; width: 100%; height: auto; }
.idband__ln { stroke: #3a3d44; stroke-width: 1.5; fill: none; }
.idband__ln--on { stroke: var(--color-accent); animation: idband-pulse 3.4s ease-in-out infinite; }
.idband__dot { fill: var(--color-accent); }
.idband__dot--src { fill: #575b63; }

@keyframes idband-pulse {
    0%, 100% { opacity: .25; }
    45% { opacity: 1; }
}

.idband__line {
    margin: 0 0 3px;
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.2;
    text-wrap: balance;
}
.idband__line em { font-style: normal; color: var(--color-accent); }

.idband__sub {
    margin: 0;
    font-size: 0.72rem;
    color: var(--color-hero-text-muted);
    line-height: 1.45;
}

.idband__counts { display: flex; gap: 18px; flex: none; }
.idband__count b {
    display: block;
    font-size: 1.2rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}
.idband__count span {
    font-size: 0.59rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-hero-text-muted);
}

/* Rândul cu publicațiile citite azi. Masca din dreapta face ca numele să se
   stingă în loc să fie tăiate brusc de marginea benzii. */
.idband__strip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 0 7px 16px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.idband__striplabel {
    flex: none;
    font-size: 0.59rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-hero-text-muted);
    font-weight: 700;
}

.idband__track {
    overflow: hidden;
    flex: 1;
    -webkit-mask-image: linear-gradient(90deg, #000 85%, transparent);
    mask-image: linear-gradient(90deg, #000 85%, transparent);
}

/* Lista e randată de două ori, deci -50% o aduce exact la început: bucla e
   continuă, fără salt vizibil. */
.idband__run {
    display: flex;
    gap: 20px;
    width: max-content;
    animation: idband-slide 26s linear infinite;
}
.idband__run span {
    font-size: 0.72rem;
    white-space: nowrap;
    color: #fff;
    opacity: .92;
}
.idband__run span::after { content: "·"; margin-left: 20px; color: var(--color-hero-text-muted); }

@keyframes idband-slide {
    to { transform: translateX(-50%); }
}

/* Pe telefon cade desenul: are nevoie de lățime ca să se citească, iar aici ar
   fura exact spațiul pe care banda trebuie să-l lase primei știri. */
@media (max-width: 620px) {
    .idband__main { grid-template-columns: 1fr; gap: 10px; }
    .idband__art { display: none; }
    .idband__line { font-size: 1rem; }
    .idband__counts { gap: 16px; }
}

@media (prefers-reduced-motion: reduce) {
    .idband__run,
    .idband__ln--on { animation: none; }
}

/* ===== „Cum s-a răspândit" — cronologia subiectului în articol (Oct 2026) ===
   Cine a scris primul și la cât timp au urmat ceilalți. Construit din
   pub_date-ul publicațiilor din grup; vezi GeneratedSourceRepository::
   timelineForArticle(). */
.spread {
    margin-top: 28px;
    padding: 14px 15px 12px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.spread__head {
    margin: 0 0 12px;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text);
}

.spread__total {
    display: block;
    margin-top: 3px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

.spread__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 7px;
}

/* Trei coloane: numele, bara proporțională, momentul. Numele au lățime fixă
   ca barele să pornească toate din același loc — altfel comparația vizuală,
   care e tot rostul blocului, nu funcționează. */
.spread__item {
    display: grid;
    grid-template-columns: 92px 1fr auto;
    gap: 10px;
    align-items: center;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.spread__who {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Publicațiile citite efectiv pentru articol — restul doar au titrat. */
.spread__item--read .spread__who { font-weight: 700; color: var(--color-text); }
.spread__item--read .spread__bar i { background: var(--color-accent); }

.spread__bar {
    display: block;
    height: 6px;
    background: var(--color-tag-muted-bg);
    border-radius: 999px;
    overflow: hidden;
    min-width: 0;
}

.spread__bar i {
    display: block;
    height: 100%;
    background: #b9bec9;
    border-radius: 999px;
}

.spread__when {
    font-variant-numeric: tabular-nums;
    font-size: 0.72rem;
    white-space: nowrap;
}

.spread__note {
    margin: 11px 0 0;
    font-size: 0.68rem;
    color: var(--color-text-muted);
}

@media (max-width: 420px) {
    .spread__item { grid-template-columns: 78px 1fr auto; gap: 8px; font-size: 0.75rem; }
}

/* ===== Pagina „Statistici" (Oct 2026) ======================================
   Tiparul săptămânii, ca pereche a benzii de pe prima pagină: banda spune
   cifrele zilei, pagina asta le arată pe șapte. */
.stats { max-width: var(--feed-max-width); margin: 0 auto; padding: 0 var(--gap) 32px; }
.stats__head { padding-top: 4px; }
.stats__intro { margin: 6px 0 0; color: var(--color-text-muted); font-size: 0.88rem; line-height: 1.6; }

.stats__tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: var(--gap);
    margin: 20px 0 26px;
}
.stat {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 13px 14px;
}
.stat b {
    display: block;
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.stat span {
    display: block;
    margin-top: 5px;
    font-size: 0.68rem;
    color: var(--color-text-muted);
    line-height: 1.35;
}

.stats__block { margin-bottom: 30px; }
.stats__title { margin: 0 0 3px; font-size: 1.05rem; font-weight: 800; letter-spacing: -0.02em; }
.stats__sub { margin: 0 0 14px; font-size: 0.78rem; color: var(--color-text-muted); line-height: 1.5; }
.stats__note { margin: 9px 0 0; font-size: 0.68rem; color: var(--color-text-muted); }
.stats__foot { margin: 26px 0 0; font-size: 0.8rem; color: var(--color-text-muted); }

/* Graficul pe zile. O singură serie, deci fără legendă — titlul o numește.
   Numărul de surse stă ca etichetă sub coloană, nu pe a doua axă. */
.chart {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 6px;
    align-items: end;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 14px 12px 11px;
}
.chart__col { display: grid; justify-items: center; gap: 5px; min-width: 0; }
.chart__value {
    font-size: 0.72rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    min-height: 1em;
}
.chart__track { width: 100%; height: 86px; display: flex; align-items: flex-end; }
.chart__bar {
    width: 100%;
    background: var(--color-accent);
    /* Capătul rotunjit doar sus: bara e ancorată în linia de bază. */
    border-radius: 4px 4px 0 0;
    min-height: 3px;
}
.chart__bar--empty { background: var(--color-tag-muted-bg); height: 3px !important; }
.chart__day { font-size: 0.68rem; color: var(--color-text-muted); }
.chart__sub {
    font-size: 0.62rem;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    opacity: .75;
}

/* Un singur număr se afișează ca număr, nu ca grafic. */
.stats__hero {
    margin: 0 0 12px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 15px 16px;
}
.stats__hero b {
    display: block;
    font-size: 1.7rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1.05;
    color: var(--color-accent);
}
.stats__hero span { display: block; margin-top: 4px; font-size: 0.75rem; color: var(--color-text-muted); }

.stats__pair { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.stats__pair li {
    display: grid;
    gap: 2px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
}
.stats__pairlabel {
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    font-weight: 700;
}
.stats__pair a { font-size: 0.85rem; font-weight: 600; color: var(--color-text); line-height: 1.3; }
.stats__pairtime { font-size: 0.72rem; color: var(--color-accent); font-weight: 700; }

/* Clasamentul publicațiilor. */
.ranks { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.ranks__item {
    display: grid;
    grid-template-columns: 104px 1fr auto;
    gap: 10px;
    align-items: center;
    font-size: 0.8rem;
}
.ranks__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ranks__bar {
    display: block;
    height: 6px;
    background: var(--color-tag-muted-bg);
    border-radius: 999px;
    overflow: hidden;
    min-width: 0;
}
.ranks__bar i { display: block; height: 100%; background: var(--color-accent); border-radius: 999px; }
.ranks__num { font-variant-numeric: tabular-nums; font-size: 0.75rem; white-space: nowrap; }
.ranks__seen { color: var(--color-text-muted); }

@media (max-width: 420px) {
    .ranks__item { grid-template-columns: 82px 1fr auto; gap: 8px; font-size: 0.75rem; }
    .chart { gap: 4px; padding: 12px 9px 9px; }
    .chart__track { height: 70px; }
}

/* „Statistici" în navigația de categorii: aceeași formă ca o categorie, dar
   accentuată discret — nu e o rubrică de știri, e o pagină despre cum lucrăm. */
.catnav__link--stats { color: var(--color-accent); font-weight: 600; }

/* Fraza de deschidere din paginile statice — un rând de poziționare, nu un
   titlu: spune în propoziție ce face site-ul, înaintea explicației. */
.static-page__lead {
    font-size: 1.05rem;
    line-height: 1.5;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--color-text);
}

/* ===== Banda de identitate în articol (Oct 2026) ============================
   Aceeași bandă ca pe prima pagină, mai mică, sub cronologia aparițiilor.
   Motivul e cititorul venit din Google direct pe articol: el nu vede
   niciodată prima pagină, deci n-ar afla niciodată ce face site-ul.

   Reia clasele benzii mari dinadins — fraza, negrul și desenul sunt
   aceleași. Diferă doar scara și, în locul contoarelor zilei, două linkuri:
   cifrele zilei n-au ce căuta sub un articol de săptămâna trecută. */
.idband--mini {
    margin: 26px 0 0;
    animation: dp-rise 0.5s ease both;
}

.idband--mini .idband__main {
    grid-template-columns: 56px 1fr auto;
    gap: 13px;
    padding: 12px 14px;
}

.idband--mini .idband__art { width: 56px; }
.idband--mini .idband__line { font-size: 0.98rem; margin-bottom: 2px; }
.idband--mini .idband__sub { font-size: 0.7rem; }

.idband__links {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: none;
    text-align: right;
}

.idband__links a {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--color-accent);
    text-decoration: none;
    white-space: nowrap;
}
.idband__links a:hover { text-decoration: underline; }

@media (max-width: 620px) {
    /* Desenul cade, ca la banda mare; linkurile trec pe un rând, sub frază. */
    .idband--mini .idband__main { grid-template-columns: 1fr; gap: 9px; }
    .idband__links { flex-direction: row; gap: 14px; text-align: left; }
}

/* ===== Contextul subiectului, la finalul articolului (Oct 2026) =============
   Un tag e un link; asta e o informație: câte articole am scris despre
   subiect și ce publicații am citit pentru ele. Apare numai când subiectul
   are deja o istorie la noi — vezi NewsroomStatsRepository::topicContext(). */
.topicnote {
    margin-top: 16px;
    padding: 12px 14px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-accent);
    border-radius: var(--radius-sm);
}

.topicnote__text {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--color-text-muted);
}

.topicnote__text b { color: var(--color-text); font-weight: 700; }

.topicnote__subject {
    font-weight: 800;
    color: var(--color-text);
    text-decoration: none;
    border-bottom: 2px solid var(--color-accent);
}
.topicnote__subject:hover { color: var(--color-accent); }

.topicnote__more {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-top: 8px;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--color-accent);
    text-decoration: none;
}
.topicnote__more svg { transition: transform 0.16s ease; }
.topicnote__more:hover svg { transform: translateX(3px); }

/* ===== „Subiectele zilei" pe pagina de statistici (Oct 2026) ================
   Aceeași formă ca clasamentul publicațiilor — nume, bară, cifră — fiindcă
   e același fel de informație: un clasament pe o singură măsură. Măsura e
   numărul de publicații care au scris despre subiect. */
.topics { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }

.topics__item {
    display: grid;
    grid-template-columns: minmax(0, 148px) 1fr auto;
    gap: 10px;
    align-items: center;
    font-size: 0.8rem;
}

.topics__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 700;
    color: var(--color-text);
    text-decoration: none;
}
.topics__name:hover { color: var(--color-accent); }

.topics__bar {
    display: block;
    height: 6px;
    background: var(--color-tag-muted-bg);
    border-radius: 999px;
    overflow: hidden;
    min-width: 0;
}
.topics__bar i { display: block; height: 100%; background: var(--color-accent); border-radius: 999px; }

.topics__num { font-variant-numeric: tabular-nums; font-size: 0.75rem; white-space: nowrap; }
.topics__unit { color: var(--color-text-muted); font-size: 0.68rem; }

@media (max-width: 480px) {
    .topics__item { grid-template-columns: minmax(0, 100px) 1fr auto; gap: 8px; font-size: 0.75rem; }
    .topics__unit { display: none; }
}

/* ===== Mișcări discrete (Oct 2026) =========================================
   Proprietarul: „animatiile sunt faine. aduc ceva. poti sa mai pui ceva
   discret pe ici pe colo."

   Discret înseamnă aici două reguli pe care le respectă tot ce urmează:
   1. nicio animație nu mută textul pe care cineva tocmai citește — doar
      bare, cifre și blocuri care intră o dată, la încărcare;
   2. nimic nu se repetă la infinit în afara benzii, care era deja așa.
   O animație care se tot mișcă lângă un articol e un ad, nu un detaliu.

   Barele cresc din zero prin scaleX, nu prin width: lățimea reală rămâne
   în atributul style, calculată în PHP, deci animația nu poate minți despre
   proporții — o oprești și barele sunt exact cât trebuie. */
@keyframes dp-rise {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: none; }
}

@keyframes dp-grow-x {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

@keyframes dp-grow-y {
    from { transform: scaleY(0); }
    to { transform: scaleY(1); }
}

/* Barele orizontale: cronologia din articol, clasamentul publicațiilor,
   subiectele. Decalajul pe --i le face să pornească una după alta, în
   ordinea în care se citesc. */
.spread__bar i,
.ranks__bar i,
.topics__bar i {
    transform-origin: left center;
    animation: dp-grow-x 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: calc(var(--i, 0) * 60ms);
}

/* Coloanele graficului cresc din linia de bază, iar cifra apare după ele. */
.chart__bar {
    transform-origin: bottom center;
    animation: dp-grow-y 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: calc(var(--i, 0) * 55ms);
}
.chart__value {
    animation: dp-rise 0.35s ease both;
    animation-delay: calc(var(--i, 0) * 55ms + 160ms);
}

/* Rândurile de clasament se luminează la trecerea cursorului: arată că sunt
   de citit pe rând, iar la subiecte chiar sunt linkuri. */
.ranks__item,
.topics__item {
    border-radius: var(--radius-sm);
    transition: background-color 0.15s ease;
    /* Padding negativ pe margini: fundalul de la hover respiră, dar coloanele
       rămân aliniate cu restul paginii — barele n-au voie să sară cu 6px. */
    padding: 3px 6px;
    margin: 0 -6px;
}
.ranks__item:hover,
.topics__item:hover { background: var(--color-tag-muted-bg); }

/* Tagurile din articol: un milimetru în sus la hover, cât să se simtă că
   sunt butoane. */
.article-page__tag {
    transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.article-page__tag:hover { transform: translateY(-1px); }

/* Blocul de cronologie și cel de context intră o dată, la încărcare. */
.spread,
.topicnote { animation: dp-rise 0.45s ease both; }

/* Cifrele: contoarele benzii de pe prima pagină și tilurile de pe pagina de
   statistici. Decalajul e în CSS, pe nth-child - sunt trei, fixe, deci n-are
   rost să ducem un --i prin PHP pentru ele. */
.idband__count,
.stats__tiles .stat { animation: dp-rise 0.5s ease both; }
.idband__count:nth-child(2),
.stats__tiles .stat:nth-child(2) { animation-delay: 0.07s; }
.idband__count:nth-child(3),
.stats__tiles .stat:nth-child(3) { animation-delay: 0.14s; }

@media (prefers-reduced-motion: reduce) {
    .spread,
    .topicnote,
    .idband--mini,
    .spread__bar i,
    .ranks__bar i,
    .topics__bar i,
    .chart__bar,
    .chart__value,
    .idband__count,
    .stats__tiles .stat {
        animation: none;
    }

    .article-page__tag:hover { transform: none; }
    .topicnote__more:hover svg { transform: none; }
}

/* ===== „Unde publicațiile nu sunt de acord" (Oct 2026) ======================
   Blocul pentru care există tot restul: întrebarea la care sursele răspund
   diferit și cine ce susține. Randat din generated_article_contradictions,
   nu din corpul articolului — vezi migrația 042.

   Bordura de sus accentuată, nu un fundal colorat: blocul trebuie să se vadă
   ca o secțiune a articolului, nu ca o casetă lipită lângă el. */
.disagree {
    margin-top: 28px;
    padding: 15px 16px 13px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-top: 3px solid var(--color-accent);
    border-radius: var(--radius);
    animation: dp-rise 0.45s ease both;
}

.disagree__head {
    margin: 0 0 13px;
    font-size: 0.78rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text);
}

.disagree__total {
    display: block;
    margin-top: 3px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

.disagree__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }

.disagree__item {
    animation: dp-rise 0.45s ease both;
    animation-delay: calc(var(--i, 0) * 80ms);
}

/* Întrebarea. Semnul întrebării se adaugă în PHP, nu din ::after: un ::after
   nu se poate uita la text, deci ar produce „??" la primul model care îl pune
   totuși. Aici e doar forma. */
.disagree__q {
    margin: 0 0 7px;
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.4;
    letter-spacing: -0.01em;
    color: var(--color-text);
}

/* Pozițiile: fiecare pe un rând, cu o linie verticală care le leagă — se
   citesc ca răspunsuri la aceeași întrebare, nu ca trei propoziții. */
.disagree__claims {
    list-style: none;
    margin: 0;
    padding: 0 0 0 13px;
    border-left: 2px solid var(--color-border);
    display: grid;
    gap: 6px;
}

.disagree__claims li {
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--color-text-muted);
}

.disagree__claims b {
    color: var(--color-text);
    font-weight: 700;
}
.disagree__claims b::after {
    content: " ·";
    color: var(--color-accent);
    font-weight: 800;
}

.disagree__note {
    margin: 12px 0 0;
    font-size: 0.68rem;
    color: var(--color-text-muted);
}

/* A treia linie din perechile de pe pagina de statistici: titlul articolului
   sub întrebare, mic și stins — întrebarea e informația, articolul e drumul. */
.stats__pairsource {
    font-size: 0.68rem;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .disagree,
    .disagree__item { animation: none; }
}

/* ===== Zona de dezacorduri de pe prima pagină (Oct 2026) ====================
   Sub banda neagră: întrebările la care presa a răspuns diferit AZI. Banda e
   afirmația, asta e dovada. Deschisă la culoare dinadins — două suprafețe
   întunecate una sub alta s-ar citi ca un singur bloc de reclamă. */
.disagreefeed {
    margin: 0 var(--gap) var(--gap);
    padding: 11px 14px 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-accent);
    border-radius: var(--radius);
    animation: dp-rise 0.5s ease both;
    animation-delay: 0.1s;
}

.disagreefeed__label {
    margin: 0 0 7px;
    font-size: 0.59rem;
    font-weight: 800;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.disagreefeed__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }

.disagreefeed__list a {
    display: flex;
    align-items: baseline;
    gap: 10px;
    justify-content: space-between;
    padding: 3px 0;
    text-decoration: none;
    color: var(--color-text);
}

.disagreefeed__q {
    font-size: 0.88rem;
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -0.01em;
}
.disagreefeed__list a:hover .disagreefeed__q { color: var(--color-accent); }

.disagreefeed__voices {
    flex: none;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .disagreefeed { animation: none; }
}
