/*!
 * AutoMedia Design System — Components
 * ----------------------------------------------------------------------
 * Depends on tokens.css. Every interactive component here has its JS
 * behavior in ui-kit.js — this file is presentation only. Class names
 * use a light BEM-ish convention (`.c-block__element--modifier`) scoped
 * with a `c-` prefix so nothing here collides with any existing inline
 * page styles during the page-by-page migration (Tahap 4-6).
 */

/* =======================================================================
   Layout primitives — container, grid, stack, cluster
   ======================================================================= */
.container {
    width: 100%;
    max-width: var(--container-wide);
    /* margin-left/right, not the margin-inline logical-property
       shorthand — this site has no RTL requirement, and this exact
       centering rule (max-width + margin auto) is the single most-used
       layout primitive on the entire site. Physical properties have
       been universally supported since the earliest CSS days; logical
       properties are newer and, while safe in real evergreen browsers,
       there's no upside to the shorthand here versus the guaranteed-
       compatible physical form for something this foundational. */
    margin-left: auto;
    margin-right: auto;
    padding-left: var(--space-4);
    padding-right: var(--space-4);
}
@media (min-width: 768px) {
    .container { padding-left: var(--space-6); padding-right: var(--space-6); }
}
.container--article { max-width: var(--container-article); }
.container--narrow { max-width: var(--container-md); }

.o-grid {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: repeat(1, minmax(0, 1fr));
}
@media (min-width: 640px)  { .o-grid--sm-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 768px)  { .o-grid--md-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .o-grid--md-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .o-grid--lg-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } .o-grid--lg-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.o-stack > * + * { margin-top: var(--stack-space, var(--space-4)); }
.o-stack--sm { --stack-space: var(--space-2); }
.o-stack--lg { --stack-space: var(--space-8); }

.o-cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

/* !important is deliberate here, not a shortcut: these are the only two
   show/hide utility classes in the system, and every real usage combines
   them with a component class (.btn, .share-rail, .floating-actions, ...)
   that also sets `display`. Both are single-class selectors with equal
   specificity (0,1,0) — without !important, whichever rule happens to
   come later in this file wins the cascade tie-break regardless of
   intent. That's a real, confirmed bug found this way: header.php's
   mobile hamburger button (.btn ... .u-hide-md-up) and "Masuk" link (.btn
   ... .u-hide-sm) both stayed visible at the wrong breakpoint because
   .btn's `display: inline-flex` (declared further down in this same
   file, in the Buttons section) was winning over these utility rules. */
.u-hide-sm { display: none !important; }
@media (min-width: 768px) {
    .u-hide-sm { display: revert !important; }
    .u-hide-md-up { display: none !important; }

    /* `display: revert` resets to the browser's UA-default display for
       the element's tag (block for <aside>, inline for <a>/<button>) —
       fine for plain elements, but it silently discards a component's
       OWN intended non-default display (flex/grid), which `revert`
       has no way to know about. Confirmed as a real regression from
       the fix above: .share-rail (needs flex-column) collapsed into
       plain block/inline flow at desktop width, turning its sticky
       vertical icon rail into a wrapped horizontal row. Every
       .u-hide-sm element that needs flex, not the reverted default,
       gets its own higher-specificity !important re-assertion here —
       .btn covers both header.php's search icon button and "Masuk"
       link in one rule; .share-rail has its own in article.css next
       to its definition. */
    .btn.u-hide-sm { display: inline-flex !important; }
}

/* =======================================================================
   Icons — sprite.svg symbols, used as <svg class="icon"><use href="...">.
   The outer <svg> carries no width/height/viewBox of its own (only the
   referenced <symbol> does), so without an explicit size here a browser
   falls back to the default replaced-element size (300×150) — every
   single icon sitewide (nav, buttons, badges, card meta, breadcrumbs,
   pagination, ...) rendering as a giant ~300px block. This rule was
   missing entirely until now; every Tahap 1-9 HTTP verification only
   checked markup/structure via curl, which can't catch a missing size on
   a replaced element — this needed an actual rendered page to surface.
   ======================================================================= */
.icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    vertical-align: middle;
}
.icon--sm { width: 16px; height: 16px; }
.icon--lg { width: 28px; height: 28px; }

/* =======================================================================
   Buttons
   ======================================================================= */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    line-height: 1;
    border-radius: var(--radius-md);
    border: var(--border-width) solid transparent;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--duration-fast) var(--ease-standard),
        border-color var(--duration-fast) var(--ease-standard),
        color var(--duration-fast) var(--ease-standard),
        transform var(--duration-instant) var(--ease-standard),
        box-shadow var(--duration-fast) var(--ease-standard);
}
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; pointer-events: none; }

.btn--primary { background: var(--color-primary); color: var(--color-text-on-primary); }
.btn--primary:hover { background: var(--color-primary-hover); }

.btn--secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border-strong); }
.btn--secondary:hover { background: var(--color-bg-subtle); }

.btn--ghost { background: transparent; color: var(--color-text-secondary); }
.btn--ghost:hover { background: var(--color-bg-subtle); color: var(--color-text); }

.btn--danger { background: var(--color-danger); color: white; }
.btn--danger:hover { filter: brightness(0.92); }

.btn--sm { padding: var(--space-1) var(--space-3); font-size: var(--text-xs); }
.btn--lg { padding: var(--space-3) var(--space-6); font-size: var(--text-base); }
.btn--icon { padding: var(--space-2); border-radius: var(--radius-full); }
.btn--block { width: 100%; }

/* =======================================================================
   Badge / Chip
   ======================================================================= */
.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0.1875rem var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    line-height: 1.2;
    border-radius: var(--radius-sm);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
}
.badge--primary { background: var(--color-primary-subtle); color: var(--color-primary-subtle-text); }
.badge--accent  { background: var(--color-accent-subtle); color: var(--color-accent-subtle-text); }
.badge--success { background: var(--color-success-subtle); color: var(--color-success-subtle-text); }
.badge--warning { background: var(--color-warning-subtle); color: var(--color-warning-subtle-text); }
.badge--danger  { background: var(--color-danger-subtle); color: var(--color-danger-subtle-text); }
.badge--info    { background: var(--color-info-subtle); color: var(--color-info-subtle-text); }
.badge--live {
    background: var(--color-danger);
    color: white;
}
.badge--live .badge__dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
    animation: pulse-dot 1.6s var(--ease-standard) infinite;
}
@keyframes pulse-dot {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* Small status dot for an icon-only button (e.g. the push-notification
   bell) — conveys on/off state without ever putting text inside a button
   sized for an icon. Button needs `position:relative` (set inline where
   used) and `data-subscribed` toggled by JS. */
.icon-badge-dot {
    display: none;
    position: absolute;
    top: 6px; right: 6px;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--color-danger);
    border: 2px solid var(--color-surface);
}
[data-subscribed="1"] .icon-badge-dot { display: block; }

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    border-radius: var(--radius-full);
    background: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
}
.chip:hover { border-color: var(--color-border-strong); color: var(--color-text); }
.chip[aria-pressed="true"], .chip--active {
    background: var(--color-primary-subtle);
    border-color: transparent;
    color: var(--color-primary-subtle-text);
}
.chip__remove {
    display: inline-flex;
    margin: 0 calc(var(--space-1) * -1) 0 0;
    background: none; border: 0; padding: 2px; cursor: pointer; color: inherit; border-radius: 50%;
}
.chip__remove:hover { background: hsl(0 0% 50% / 0.15); }

/* =======================================================================
   Card
   ======================================================================= */
.card {
    background: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: box-shadow var(--duration-base) var(--ease-standard),
        border-color var(--duration-base) var(--ease-standard),
        transform var(--duration-base) var(--ease-standard);
}
.card--interactive { cursor: pointer; }
.card--interactive:hover {
    box-shadow: var(--shadow-lg);
    border-color: var(--color-border-strong);
    transform: translateY(-2px);
}
.card__media {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: var(--color-bg-subtle);
}
.card__media img {
    width: 100%; height: 100%;
    object-fit: cover;
    transition: transform var(--duration-slow) var(--ease-decelerate);
}
.card--interactive:hover .card__media img { transform: scale(1.05); }
.card__media-badge {
    position: absolute;
    top: var(--space-3); left: var(--space-3);
}
.card__body { padding: var(--space-5); }
.card__eyebrow {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: var(--font-bold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-primary);
    margin-bottom: var(--space-2);
}
/* Same "small bold uppercase label" pattern as .card__eyebrow, reused for
   section labels outside a card (search overlay's "Topik Trending"/
   "Pencarian Terakhir Anda", the article inline-recommendation card).
   Moved here from article.css — header.php's search overlay uses it too,
   and article.css isn't loaded on every page. */
.inline-reco__label { font-size: var(--text-xs); font-weight: var(--font-bold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--color-primary); margin-bottom: var(--space-1); }
.card__title {
    font-size: var(--text-lg);
    line-height: var(--leading-lg);
    font-weight: var(--font-bold);
    color: var(--color-text);
}
.card__title a { transition: color var(--duration-fast) var(--ease-standard); }
.card__title a:hover { color: var(--color-primary); }
.card__excerpt {
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.card__meta {
    margin-top: var(--space-4);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}
.card__meta-sep { opacity: 0.5; }
.card__actions {
    margin-top: var(--space-4);
    display: flex;
    align-items: center;
    gap: var(--space-1);
    border-top: var(--border-width) solid var(--color-border);
    padding-top: var(--space-3);
}

.card--horizontal { display: flex; flex-direction: row; }
.card--horizontal .card__media { width: 40%; aspect-ratio: 4/3; flex-shrink: 0; }
.card--horizontal .card__body { flex: 1; padding: var(--space-4); }

/* Author card */
.author-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--color-primary-subtle);
    color: var(--color-primary-subtle-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    flex-shrink: 0;
    overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 28px; height: 28px; font-size: var(--text-xs); }
.avatar--lg { width: 64px; height: 64px; font-size: var(--text-lg); }
.author-card__name { font-weight: var(--font-semibold); font-size: var(--text-sm); color: var(--color-text); }
.author-card__meta { font-size: var(--text-xs); color: var(--color-text-tertiary); }

/* Icon buttons used for bookmark/like/share on cards */
.icon-action {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-md);
    border: 0;
    background: transparent;
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
.icon-action:hover { background: var(--color-bg-subtle); color: var(--color-text); }
.icon-action svg { width: 18px; height: 18px; transition: transform var(--duration-fast) var(--ease-spring); }
.icon-action[aria-pressed="true"] { color: var(--color-danger); }
.icon-action[aria-pressed="true"] svg { fill: currentColor; }
.icon-action--pop[aria-pressed="true"] svg { animation: icon-pop var(--duration-slow) var(--ease-spring); }
@keyframes icon-pop {
    0% { transform: scale(1); }
    40% { transform: scale(1.35); }
    100% { transform: scale(1); }
}

/* =======================================================================
   Alert
   ======================================================================= */
.alert {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4);
    border-radius: var(--radius-md);
    border: var(--border-width) solid transparent;
    font-size: var(--text-sm);
}
.alert svg { flex-shrink: 0; width: 20px; height: 20px; }
.alert--info    { background: var(--color-info-subtle); color: var(--color-info-subtle-text); }
.alert--success { background: var(--color-success-subtle); color: var(--color-success-subtle-text); }
.alert--warning { background: var(--color-warning-subtle); color: var(--color-warning-subtle-text); }
.alert--danger  { background: var(--color-danger-subtle); color: var(--color-danger-subtle-text); }
.alert__title { font-weight: var(--font-semibold); margin-bottom: var(--space-1); }

/* =======================================================================
   Toast (container fixed by ui-kit.js at document level)
   ======================================================================= */
.toast-region {
    position: fixed;
    bottom: var(--space-5);
    right: var(--space-5);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: min(24rem, calc(100vw - 2rem));
}
.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-surface-raised);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-sm);
    animation: toast-in var(--duration-base) var(--ease-decelerate);
}
.toast--leaving { animation: toast-out var(--duration-fast) var(--ease-accelerate) forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(4px) scale(0.98); } }
.toast__icon { flex-shrink: 0; }
.toast__body { flex: 1; }
.toast__close { background: none; border: 0; color: var(--color-text-tertiary); cursor: pointer; padding: var(--space-1); border-radius: var(--radius-sm); }
.toast__close:hover { background: var(--color-bg-subtle); }

/* =======================================================================
   Modal
   ======================================================================= */
.modal-overlay {
    position: fixed; inset: 0;
    background: var(--color-overlay);
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-standard);
}
.modal-overlay[data-state="open"] { opacity: 1; }
.modal {
    background: var(--color-surface-raised);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    width: 100%;
    max-width: 32rem;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    transform: scale(0.96) translateY(8px);
    transition: transform var(--duration-base) var(--ease-decelerate);
}
.modal-overlay[data-state="open"] .modal { transform: none; }
.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-5) var(--space-5) 0;
}
.modal__title { font-size: var(--text-xl); font-weight: var(--font-bold); }
.modal__close { background: none; border: 0; cursor: pointer; color: var(--color-text-tertiary); padding: var(--space-2); border-radius: var(--radius-full); }
.modal__close:hover { background: var(--color-bg-subtle); }
.modal__body { padding: var(--space-5); overflow-y: auto; }
.modal__footer { padding: 0 var(--space-5) var(--space-5); display: flex; justify-content: flex-end; gap: var(--space-2); }

/* =======================================================================
   Drawer (off-canvas panel — used by mobile nav + search overlay)
   ======================================================================= */
.drawer-overlay {
    position: fixed; inset: 0;
    background: var(--color-overlay);
    z-index: var(--z-overlay);
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-standard);
}
.drawer-overlay[data-state="open"] { opacity: 1; }
.drawer {
    position: fixed; top: 0; bottom: 0; right: 0;
    width: min(100%, 24rem);
    background: var(--color-surface-raised);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-overlay);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform var(--duration-base) var(--ease-decelerate);
}
.drawer[data-state="open"] { transform: translateX(0); }
.drawer--left { right: auto; left: 0; transform: translateX(-100%); }
.drawer--left[data-state="open"] { transform: translateX(0); }
.drawer--top { top: 0; left: 0; right: 0; bottom: auto; width: auto; height: auto; transform: translateY(-100%); }
.drawer--top[data-state="open"] { transform: translateY(0); }
.drawer__header { display: flex; align-items: center; justify-content: space-between; padding: var(--space-4) var(--space-5); border-bottom: var(--border-width) solid var(--color-border); }
.drawer__body { padding: var(--space-5); overflow-y: auto; flex: 1; }

/* =======================================================================
   Dropdown menu
   ======================================================================= */
.dropdown { position: relative; display: inline-block; }
.dropdown__panel {
    position: absolute;
    top: calc(100% + var(--space-2));
    right: 0;
    min-width: 12rem;
    background: var(--color-surface-raised);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2);
    z-index: var(--z-dropdown);
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
    pointer-events: none;
    transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard);
}
.dropdown__panel--left { right: auto; left: 0; }
.dropdown__panel[data-state="open"] {
    opacity: 1; transform: none; pointer-events: auto;
}
.dropdown__item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    border: 0;
    background: none;
    text-align: left;
    font-size: var(--text-sm);
    color: var(--color-text);
    cursor: pointer;
}
.dropdown__item:hover, .dropdown__item:focus-visible { background: var(--color-bg-subtle); }
.dropdown__separator { height: 1px; background: var(--color-border); margin: var(--space-2) 0; }

/* =======================================================================
   Accordion
   ======================================================================= */
.accordion-item { border-bottom: var(--border-width) solid var(--color-border); }
.accordion-trigger {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) 0;
    background: none;
    border: 0;
    text-align: left;
    font-weight: var(--font-semibold);
    font-size: var(--text-base);
    color: var(--color-text);
    cursor: pointer;
}
.accordion-trigger__icon { transition: transform var(--duration-base) var(--ease-standard); flex-shrink: 0; }
.accordion-trigger[aria-expanded="true"] .accordion-trigger__icon { transform: rotate(180deg); }
.accordion-panel {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--duration-base) var(--ease-standard);
}
.accordion-panel[data-state="open"] { grid-template-rows: 1fr; }
.accordion-panel__inner { overflow: hidden; }
.accordion-panel__content { padding-bottom: var(--space-4); color: var(--color-text-secondary); font-size: var(--text-sm); }

/* =======================================================================
   Tooltip
   ======================================================================= */
.tooltip { position: relative; display: inline-flex; }
.tooltip__bubble {
    position: absolute;
    bottom: calc(100% + var(--space-2));
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: var(--gray-900);
    color: var(--gray-50);
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-md);
    white-space: nowrap;
    box-shadow: var(--shadow-md);
    z-index: var(--z-tooltip);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard);
}
:root[data-theme="dark"] .tooltip__bubble { background: var(--gray-100); color: var(--gray-900); }
/* Tahap 9 fix: the rule above only fired for an *explicit* toggle click
   (literal [data-theme="dark"]). A visitor whose OS is in dark mode but
   who never clicked the toggle gets dark page tokens from tokens.css's
   own `@media (prefers-color-scheme: dark)` block, but this component
   override was being skipped — leaving a near-invisible dark-on-dark
   tooltip (gray-900 bubble on a gray-950 page). Mirrors tokens.css's own
   `:root:not([data-theme="light"])` pattern so both dark-mode entry
   points stay in sync. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .tooltip__bubble { background: var(--gray-100); color: var(--gray-900); }
}
.tooltip:hover .tooltip__bubble,
.tooltip:focus-within .tooltip__bubble {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* =======================================================================
   Skeleton loader
   ======================================================================= */
.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--color-bg-subtle);
    border-radius: var(--radius-md);
}
.skeleton::after {
    content: "";
    position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, hsl(0 0% 100% / 0.35), transparent);
    animation: skeleton-shimmer 1.6s infinite;
}
:root[data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, hsl(0 0% 100% / 0.06), transparent); }
/* Same Tahap 9 auto-dark fix as .tooltip__bubble above. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .skeleton::after { background: linear-gradient(90deg, transparent, hsl(0 0% 100% / 0.06), transparent); }
}
@keyframes skeleton-shimmer {
    100% { transform: translateX(100%); }
    0% { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
    .skeleton::after { animation: none; }
}
.skeleton--text { height: 0.9em; margin-bottom: 0.4em; }
.skeleton--title { height: 1.4em; width: 70%; }
.skeleton--avatar { width: 40px; height: 40px; border-radius: 50%; }
.skeleton--card { aspect-ratio: 16/9; border-radius: var(--radius-lg); }

/* =======================================================================
   Progress
   ======================================================================= */
.progress {
    height: 6px;
    background: var(--color-bg-subtle);
    border-radius: var(--radius-full);
    overflow: hidden;
}
.progress__bar {
    height: 100%;
    background: var(--color-primary);
    border-radius: inherit;
    transition: width var(--duration-base) var(--ease-standard);
}
.progress--accent .progress__bar { background: var(--color-accent); }

/* Reading progress bar — fixed to viewport top, driven by ui-kit.js */
.reading-progress {
    position: fixed;
    top: 0; left: 0;
    height: 3px;
    width: 0%;
    background: var(--color-primary);
    z-index: var(--z-fixed);
    transition: width var(--duration-instant) linear;
}

/* Spinner */
.spinner {
    width: 1.25em; height: 1.25em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    display: inline-block;
    animation: spin 0.7s linear infinite;
    opacity: 0.75;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* =======================================================================
   Pagination
   ======================================================================= */
.pagination {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    list-style: none;
}
.pagination__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    padding: 0 var(--space-2);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-secondary);
    transition: background-color var(--duration-fast) var(--ease-standard);
}
.pagination__link:hover { background: var(--color-bg-subtle); color: var(--color-text); }
.pagination__link[aria-current="page"] { background: var(--color-primary); color: var(--color-text-on-primary); }
.pagination__link[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }

/* =======================================================================
   Breadcrumb
   ======================================================================= */
.breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-tertiary); list-style: none; }
.breadcrumb__item { display: flex; align-items: center; gap: var(--space-2); }
.breadcrumb__item a:hover { color: var(--color-text); text-decoration: underline; }
.breadcrumb__sep { opacity: 0.6; }
.breadcrumb__item[aria-current="page"] { color: var(--color-text); font-weight: var(--font-medium); }

/* =======================================================================
   Tabs
   ======================================================================= */
.tablist {
    display: flex;
    gap: var(--space-1);
    border-bottom: var(--border-width) solid var(--color-border);
    overflow-x: auto;
}
.tab {
    padding: var(--space-3) var(--space-4);
    background: none;
    border: 0;
    border-bottom: var(--border-width-thick) solid transparent;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    color: var(--color-text-tertiary);
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
    margin-bottom: -1px;
}
.tab:hover { color: var(--color-text); }
.tab[aria-selected="true"] { color: var(--color-primary); border-color: var(--color-primary); }
.tabpanel { padding-top: var(--space-5); }
.tabpanel[hidden] { display: none; }

/* =======================================================================
   Timeline (used by Live Blog)
   ======================================================================= */
.timeline { position: relative; list-style: none; }
.timeline::before {
    content: "";
    position: absolute;
    left: 7px; top: 0.5rem; bottom: 0.5rem;
    width: 2px;
    background: var(--color-border);
}
.timeline-item { position: relative; padding-left: var(--space-8); padding-bottom: var(--space-6); }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-item::before {
    content: "";
    position: absolute;
    left: 0; top: 0.35rem;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--color-surface);
    border: var(--border-width-thick) solid var(--color-primary);
}
.timeline-item--live::before { border-color: var(--color-danger); animation: pulse-dot 1.6s var(--ease-standard) infinite; }
.timeline-item__time { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: var(--font-semibold); margin-bottom: var(--space-1); }
.timeline-item__content { font-size: var(--text-sm); color: var(--color-text); }

/* =======================================================================
   Card media placeholder (no featured-image system on `news` — a
   deterministic gradient + category initial, never a fabricated photo)
   ======================================================================= */
.media-placeholder {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, var(--ph-a, var(--ink-500)), var(--ph-b, var(--ink-800)));
    color: hsl(0 0% 100% / 0.92);
    font-size: var(--text-4xl);
    font-weight: var(--font-extrabold);
}

/* =======================================================================
   Comment thread
   ======================================================================= */
.comment { display: flex; gap: var(--space-3); padding: var(--space-4) 0; }
.comment + .comment { border-top: var(--border-width) solid var(--color-border); }
.comment__body { flex: 1; min-width: 0; }
.comment__meta { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); margin-bottom: var(--space-1); }
.comment__author { font-weight: var(--font-semibold); font-size: var(--text-sm); color: var(--color-text); }
.comment__time { color: var(--color-text-tertiary); }
.comment__text { font-size: var(--text-sm); color: var(--color-text); white-space: pre-line; }
.comment__actions { margin-top: var(--space-2); }
.comment__actions a, .comment__actions button { font-size: var(--text-xs); font-weight: var(--font-semibold); color: var(--color-text-tertiary); background: none; border: 0; cursor: pointer; padding: 0; }
.comment__actions a:hover, .comment__actions button:hover { color: var(--color-primary); }
.comment-replies { margin-left: var(--space-6); border-left: var(--border-width) solid var(--color-border); padding-left: var(--space-4); }

/* =======================================================================
   Gallery grid + Video embed (Fase 14 content, restyled)
   ======================================================================= */
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--space-3);
}
.gallery-grid__item { border-radius: var(--radius-lg); overflow: hidden; position: relative; aspect-ratio: 4/3; }
.gallery-grid__item img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--duration-slow) var(--ease-decelerate); }
.gallery-grid__item:hover img { transform: scale(1.06); }
.gallery-grid__caption {
    position: absolute; inset: auto 0 0 0;
    padding: var(--space-2) var(--space-3);
    background: linear-gradient(180deg, transparent, hsl(0 0% 0% / 0.75));
    color: white;
    font-size: var(--text-xs);
}

.video-embed { position: relative; aspect-ratio: 16/9; border-radius: var(--radius-lg); overflow: hidden; background: var(--gray-950); }
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* =======================================================================
   Forms — accessible by default (visible labels, clear focus, error text
   tied via aria-describedby at the markup level)
   ======================================================================= */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
.field__hint { font-size: var(--text-xs); color: var(--color-text-tertiary); }
.field__error { font-size: var(--text-xs); color: var(--color-danger); font-weight: var(--font-medium); }
.input, .textarea, .select {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    background: var(--color-surface);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    font-size: var(--text-base);
    color: var(--color-text);
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.input::placeholder, .textarea::placeholder { color: var(--color-text-tertiary); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--color-primary-subtle);
    outline: none;
}
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--color-danger); }
.textarea { resize: vertical; min-height: 6rem; }
.checkbox-field { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-sm); }
.checkbox-field input { margin-top: 0.2em; width: 1.05em; height: 1.05em; accent-color: var(--color-primary); }

/* =======================================================================
   Ripple (Micro Interaction — attached by ui-kit.js on [data-ripple])
   ======================================================================= */
[data-ripple] { position: relative; overflow: hidden; }
.ripple-effect {
    position: absolute;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.25;
    transform: scale(0);
    animation: ripple var(--duration-slow) var(--ease-decelerate);
    pointer-events: none;
}
@keyframes ripple { to { transform: scale(2.5); opacity: 0; } }

/* =======================================================================
   Simple entrance animation utilities (Tahap: Animation)
   ======================================================================= */
[data-animate] { opacity: 0; }
[data-animate].is-visible { animation-duration: var(--duration-slow); animation-timing-function: var(--ease-decelerate); animation-fill-mode: forwards; }
[data-animate="fade-up"].is-visible { animation-name: fade-up; }
[data-animate="fade"].is-visible { animation-name: fade-in; }
[data-animate="scale"].is-visible { animation-name: scale-in; }
@keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes scale-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
    [data-animate] { opacity: 1; animation: none !important; }
}
