/* =============================================================================
   SudaHub redesign — primitives (CLAUDE.md §12 Phase 4)
   Tile, Button, Input, Badge, Chip, RibbonProgress, Skeleton, Toast, Sheet,
   EmptyState, StaleChip, SourceCount. Every state. RateDelta (the original
   13th) was removed in Book Club Phase 2 along with Rates, its only
   consumer — see docs/REDESIGN_LOG.md.
   Gallery: www/dev/kitchen-sink.html.
   ========================================================================== */

/* ================= TILE ================= */
.tile {
    background: var(--surface);
    border: 1px solid var(--line-subtle);
    border-radius: var(--r-lg);
    box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.4);
    padding: var(--space-6);
    color: var(--text-primary);
}
[data-theme="light"] .tile { box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.08); }
.tile-raised {
    background: var(--surface-raised);
    border-color: var(--line);
    box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.5);
}
[data-theme="light"] .tile-raised { box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.10); }
.tile-interactive { cursor: pointer; transition: border-color var(--dur-base) var(--ease-out), transform var(--dur-micro) var(--ease-out); }
.tile-interactive:hover { border-color: var(--line-strong); }
.tile-interactive:active { transform: scale(0.99); }

/* Hero tile (§5.3) — a bare positioning base; the actual bento-grid sizing
   comes from a screen's own CSS (.rates-hero used it before Rates was
   removed in Book Club Phase 2; §3.1's Premieres board hero slot is the
   next real consumer). Plain, no corner brackets. */
.tile-hero { position: relative; }

/* ================= BUTTON ================= */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-family: var(--font-label);
    font-weight: 600;
    font-size: var(--fs-body-s);
    letter-spacing: 0.02em;
    border-radius: var(--r-md);
    padding: 10px 18px;
    min-height: 44px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform var(--dur-micro) var(--ease-out), opacity var(--dur-micro) var(--ease-out), background var(--dur-base) var(--ease-out);
}
[lang="ar"] .btn { letter-spacing: 0; }
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }

.btn-primary { background: var(--accent); color: var(--text-on-bright); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
/* The one hero CTA per view only — gradient budget is one element/viewport. */
.btn-primary-gradient { background: var(--grad-action); color: rgb(var(--text-on-accent-rgb)); }
.btn-primary-gradient:hover:not(:disabled) { filter: brightness(1.08); }

.btn-secondary { background: transparent; color: var(--text-primary); border-color: var(--line-strong); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-raised); }

.btn-ghost { background: transparent; color: var(--text-secondary); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-raised); color: var(--text-primary); }

.btn-danger { background: var(--danger-fill); color: rgb(var(--text-on-accent-rgb)); }
.btn-danger:hover:not(:disabled) { filter: brightness(1.08); }

.btn-sm { min-height: 32px; padding: 6px 12px; font-size: var(--fs-caption); }

.btn-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn-loading::after {
    content: '';
    position: absolute;
    width: 16px;
    height: 16px;
    border-radius: 999px;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    color: var(--text-on-bright);
    animation: btn-spin 700ms linear infinite;
}
.btn-secondary.btn-loading::after, .btn-ghost.btn-loading::after { color: var(--text-primary); }
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn-loading::after { animation-duration: 1400ms; } }

/* ================= INPUT ================= */
.input {
    display: block;
    width: 100%;
    min-height: 44px;
    padding: 10px 14px;
    background: var(--surface);
    color: var(--text-primary);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    font-size: var(--fs-body);
    transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.input::placeholder { color: var(--text-muted); }
.input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent); }
.input:disabled { opacity: 0.5; cursor: not-allowed; }
.input-error { border-color: var(--danger); }
.input-error:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 25%, transparent); }
.input-hint { font-family: var(--font-label); font-size: var(--fs-caption); color: var(--text-muted); margin-block-start: 6px; }
.input-hint-error { color: var(--danger); }

/* ================= BADGE ================= */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-family: var(--font-label);
    font-size: var(--fs-meta);
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 3px 8px;
    border-radius: var(--r-sm);
    line-height: 1.4;
}
[lang="ar"] .badge { letter-spacing: 0; }
[lang="en"] .badge { text-transform: uppercase; }
.badge-neutral { background: var(--surface-raised); color: var(--text-secondary); border: 1px solid var(--line); }
.badge-accent { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
.badge-warning { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
.badge-danger { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); }

/* ================= CHIP ================= */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-label);
    font-size: var(--fs-caption);
    font-weight: 600;
    padding: 6px 12px;
    min-height: 32px;
    border-radius: var(--r-full);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text-secondary);
    cursor: pointer;
    transition: background var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
}
.chip:hover { border-color: var(--line-strong); color: var(--text-primary); }
.chip-selected { background: var(--accent); border-color: var(--accent); color: var(--text-on-bright); }
.chip-selected:hover { color: var(--text-on-bright); }
.chip:disabled { opacity: 0.4; cursor: not-allowed; }

/* ================= RIBBON PROGRESS (§5.4) ================= */
/* Corner arc on a library cover. Never mirrored — it's a clock-style
   indicator, not a direction-carrying icon, so it stays visually identical
   under RTL (only its corner placement follows the logical inline-end). */
.ribbon-progress {
    position: absolute;
    inset-block-start: 8px;
    inset-inline-end: 8px;
    width: 30px;
    height: 30px;
    filter: drop-shadow(0 1px 2px rgba(var(--scrim-rgb), 0.4));
}
.ribbon-progress-track { stroke: rgba(var(--text-on-accent-rgb), 0.3); }
.ribbon-progress-fill { stroke: var(--accent); transition: stroke-dashoffset var(--dur-enter) var(--ease-out); }
.ribbon-progress-label { font-family: var(--font-mono); font-size: var(--fs-micro); font-weight: 700; fill: rgb(var(--text-on-accent-rgb)); }

/* ================= SKELETON ================= */
.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--surface-sunken);
    border-radius: var(--r-sm);
}
.skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, var(--line-strong), transparent);
    animation: skeleton-shimmer 1.6s ease-in-out infinite;
}
[dir="rtl"] .skeleton::after { transform: translateX(100%); animation-name: skeleton-shimmer-rtl; }
@keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }
@keyframes skeleton-shimmer-rtl { 100% { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }
[data-reduce-effects="true"] .skeleton::after { animation: none; }

/* ================= TOAST =================
   Layout and the resting appearance live in shell.css (.toast-host/.toast),
   since the toast is positioned relative to the tab bar. Only the leave
   animation and the per-type dot colour are here. */
.toast-leaving { animation: toast-out var(--dur-base) var(--ease-out) forwards; }
@keyframes toast-out { to { transform: translateY(8px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    .toast-leaving { animation: none; opacity: 0; }
}
.toast-success .toast-dot { background: var(--success); }
.toast-error .toast-dot { background: var(--danger); }
.toast-info .toast-dot { background: var(--color-accent); }

/* ================= SHEET ================= */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: rgba(var(--scrim-rgb), 0.5);
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease-out);
}
.sheet-backdrop.is-open { opacity: 1; }
.sheet {
    position: fixed;
    z-index: 91;
    background: var(--surface-overlay);
    border: 1px solid var(--line);
    box-shadow: 0 24px 64px rgba(var(--shadow-rgb), 0.5);
    transition: transform var(--dur-enter) var(--ease-out);
}
/* Rises from the bottom edge — at EVERY width.
   RESKIN Phase 3: the ≥768px "centred modal" variant is deleted. It
   contradicted the single phone silhouette (§2.1), and it made
   drag-to-dismiss incoherent: dragging a centred dialog downward means
   nothing, and the drag handler's translateY fought the centring
   transform, leaving the sheet floating mid-screen instead of docked.
   One sheet, one behaviour, one gesture.

   Constrained to the column so it lines up with the header and tab bar. */
.sheet {
    inset-inline: 0;
    inset-block-end: 0;
    width: 100%;
    max-width: var(--shell-max, 480px);
    margin-inline: auto;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    max-height: 85vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    transform: translateY(100%);
    padding: 20px;
    padding-block-end: max(20px, env(safe-area-inset-bottom));
}
.sheet.is-open { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
    .sheet, .sheet-backdrop { transition: none; }
}
/* The drag affordance AND the drag target (RESKIN §2.2). The padding is
   what makes it grabbable — the visual bar is 4px tall, which is nowhere
   near a usable touch target, so the hit area is padded out to 44px while
   the bar itself stays hairline-thin. */
.sheet-handle {
    display: block;
    width: 100%;
    padding-block: 20px;
    margin-block-end: 4px;
    cursor: grab;
    /* The sheet owns this gesture outright — no native scroll or
       rubber-band should compete with a drag on the handle. */
    touch-action: none;
}
.sheet-handle::before {
    content: '';
    display: block;
    width: 38px;
    height: 4px;
    margin-inline: auto;
    border-radius: var(--r-full);
    background: var(--line-strong);
}
.sheet-handle:active { cursor: grabbing; }

/* Shared sheet header — title on the start side, close on the end side.
   Both mirror on their own, since this is plain flex. */
.sheet-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-block-end: var(--space-4);
}
.sheet-title {
    flex: 1;
    min-width: 0;
    margin: 0;
    font-family: var(--font-ui);
    font-weight: var(--fw-medium);
    font-size: var(--fs-body-l);
    color: var(--color-neutral-100);
}
.sheet-close {
    flex: none;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-pill);
    border: none;
    background: transparent;
    color: var(--color-neutral-400);
    cursor: pointer;
}
.sheet-close:hover { background: var(--color-neutral-900); color: var(--color-neutral-100); }
.sheet-close svg { width: 16px; height: 16px; }
/* Kept on desktop now: the sheet is draggable at every width under the
   reskin, and hiding the handle would hide the affordance for a gesture
   that still works. */

/* ================= EMPTY STATE (§7.3, §11) ================= */
/* An invitation, not a dead end — always a title, a body sentence, and
   (when there's a real action) a button. Never just "NO ARCHIVES FOUND". */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 12px;
    padding: 48px 24px;
    color: var(--text-secondary);
}
.empty-state-icon { width: 40px; height: 40px; color: var(--text-muted); }
.empty-state-title { font-weight: 700; font-size: var(--fs-body-l); color: var(--text-primary); }
.empty-state-body { font-size: var(--fs-body-s); max-width: 40ch; line-height: 1.6; }

/* ================= STALE CHIP (§7.1) ================= */
.stale-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-family: var(--font-label);
    font-size: var(--fs-micro);
    font-weight: 700;
    letter-spacing: 0.08em;
    padding: 2px 7px;
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--warning) 16%, transparent);
    color: var(--warning);
    border: 1px solid color-mix(in srgb, var(--warning) 40%, transparent);
}
[lang="en"] .stale-chip { text-transform: uppercase; }
[lang="ar"] .stale-chip { letter-spacing: 0; }
.value-stale { opacity: 0.5; }

/* ================= SOURCE COUNT (§7.2) ================= */
/* Replaces "ACCURACY 100%". Never green — green reads as endorsing content
   rather than sourcing. Confirmed uses --text-secondary; single/unverified
   use --warning. */
.source-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-label);
    font-size: var(--fs-meta);
    font-weight: 700;
    letter-spacing: 0.04em;
}
[lang="en"] .source-count { text-transform: uppercase; }
[lang="ar"] .source-count { letter-spacing: 0; }
.source-count-dashes { display: inline-flex; gap: 2px; }
.source-count-dashes span { width: 10px; height: 2px; border-radius: var(--r-full); background: var(--line-strong); }
.source-count-confirmed { color: var(--text-secondary); }
.source-count-confirmed .source-count-dashes span { background: var(--text-secondary); }
.source-count-single, .source-count-unverified { color: var(--warning); }
.source-count-single .source-count-dashes span:nth-child(1),
.source-count-unverified .source-count-dashes span:nth-child(1) { background: var(--warning); }

/* ================= SCREENSHOT SECURITY OVERLAY =================
   Phase 8 audit: this was the last markup in the app still carrying
   Tailwind palette classes (bg-slate-950/70, text-red-600, dark:bg-white…)
   — colour literals in disguise, and §13 requires none outside the token
   file. It also meant this dialog ignored the app's own theme.

   --danger is used deliberately and sparingly here: §7.2 reserves that
   treatment for genuine civil-safety alerts, and a screenshot warning on
   war reporting is one of the few places it belongs. */
.screenshot-overlay {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-8);
    background: rgba(var(--scrim-rgb), 0.7);
}
.screenshot-dialog {
    width: 100%;
    max-width: 320px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-8);
    text-align: center;
    border-radius: var(--radius-lg);
    border: 1px solid var(--danger);
    background: var(--color-surface);
    box-shadow: var(--shadow-lg);
}
.screenshot-icon {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--danger) 18%, transparent);
    color: var(--danger);
}
.screenshot-icon svg { width: 24px; height: 24px; }
.screenshot-title {
    margin: 0;
    font-family: var(--font-ui);
    font-weight: var(--fw-semibold);
    font-size: var(--fs-body-l);
    color: var(--color-neutral-100);
}
.screenshot-body {
    margin: 0;
    font-size: var(--fs-body-s);
    line-height: var(--lh-body);
    color: var(--color-neutral-400);
}
.screenshot-dismiss { width: 100%; }
