/*
 * 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);

    --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: "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: 1rem;
    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;
}

.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); }
}

.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;
}

.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.05rem;
    line-height: 1.32;
    font-weight: 700;
    margin: 0 0 8px;
    letter-spacing: -0.01em;
}

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

.card__excerpt {
    font-size: 0.88rem;
    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;
}

/* 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;
}

.card--full-overlay .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 {
    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 200px;
    align-self: center;
    aspect-ratio: 16 / 9;
}

.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 {
    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. */
.card--text-accent .card__title {
    font-size: 1.4rem;
    line-height: 1.2;
    text-align: left;
    padding-right: 33%;
}

.card--text-plain {
    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);
}

.trending-zone__col {
    flex: 1 1 240px;
    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: 0.85rem;
    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;
}

/* ---------------------------------------------------------------------
   Homepage "Dăpeștiri" own-articles zone (Aug 2026 publishing feature) -
   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 scope/mechanism as .trending-zone above). A solid border (not
   the trending zone's dotted one) and the accent color on the header -
   deliberately a bit more "branded" than the trending zone, since this
   one is promoting the site's own content rather than just ranking
   existing feed items. Flexible 1-3 item grid (--own-zone-cols, set
   inline per how many featured articles actually exist) rather than a
   hardcoded 3-column grid, so 1 or 2 featured articles still lay out
   cleanly instead of leaving empty cells.

   No .own-zone__item/thumb/body/etc. styling here (Aug 2026, second
   pass) - each grid cell is now a real CardRenderer::render() card
   (.card, .card--feature/.card--full-below/.card--tall - see
   OwnArticleRepository::featuredForHomepage()'s docblock for which one
   per count), which already brings its own complete styling from
   elsewhere in this file. This block only owns the border/header/grid/
   more-link wrapper around them - the one part the user explicitly
   asked to keep unchanged ("bordura si restul zonei imi plac").
   --------------------------------------------------------------------- */

.own-zone {
    background: var(--color-surface);
    border: 2px solid var(--color-accent);
    border-radius: var(--radius);
    padding: var(--gap);
}

.own-zone__header {
    text-align: left;
    margin: 0 0 14px;
}

.own-zone__title {
    display: inline-block;
    font-size: 1.15rem;
    font-weight: 800;
    margin: 0;
    color: var(--color-text);
}

.own-zone__title-accent {
    color: var(--color-accent);
}

.own-zone__grid {
    display: grid;
    grid-template-columns: repeat(var(--own-zone-cols, 3), 1fr);
    gap: var(--gap);
}

/* The reused .card templates (feature_wide/full_image_below/tall_feature
   - see OwnArticleRepository::featuredForHomepage()) each carry the
   ordinary 1px .card border, which visually fights with .own-zone's own
   thicker accent border right around it (Aug 2026 report - "se cam bate
   cam in cap cu cel rosu"). Dropped here, scoped to cards inside this
   zone only - every other use of these same templates elsewhere in the
   feed keeps its normal border. */
.own-zone .card {
    border: none;
}

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

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

/* Mobile: single column, same as every other multi-column zone on the
   site collapsing at this breakpoint. */
@media (max-width: 680px) {
    .own-zone__grid {
        grid-template-columns: 1fr;
    }
}

/* --- 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;
}

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

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

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

.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;
    }

    /* text_accent's big multi-line title is only "normal" size on mobile
       (see the base .card--text-accent .card__title rule near the top of
       this file) - restored to its original 2rem here, unchanged from
       before, once there's enough width for it not to dominate the
       screen. */
    .card--text-accent .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;
    }
}
