/* =============================================
   Chronica - TikTok-Style News Feed
   Instagram-inspired light mode design system
   ============================================= */

:root {
  /* Typography */
  --font-display: 'Playfair Display', serif;
  --font-serif: 'Lora', serif;
  --font-sans: 'DM Sans', sans-serif;

  /* Neutrals */
  --color-warm-white: #FDFBF7;
  --color-white: #FFFFFF;
  --color-warm-grey: #E8E6E1;
  --color-medium-grey: #A8A5A0;
  --color-charcoal: #2D2D2D;
  --color-black: #1A1A1A;

  /* Accent colours */
  --color-burgundy: #8B2635;
  --color-forest: #2C5F2D;
  --color-navy: #1E3A5F;
  --color-orange: #D4711A;
  --color-purple: #6B4C8A;
  --color-terracotta: #C65D3B;

  /* Spacing */
  --space-xs: 8px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 32px;
  --space-xl: 48px;

  /* Border Radius */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);

  /* Card clearances (fixed header/nav heights + breathing room) */
  --card-top-clearance: 76px;
  --card-bottom-clearance: 90px;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 350ms ease;
  --easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --easing-spring: cubic-bezier(0.68, -0.55, 0.265, 1.55);

  /* Legacy aliases (used throughout, will migrate per-chunk) */
  --primary: #8B2635;
  --bg-dark: #FDFBF7;
  --bg-card: #FFFFFF;
  --bg-card-alt: #F5F5F5;
  --text-primary: #1A1A1A;
  --text-secondary: #2D2D2D;
  --text-muted: #A8A5A0;
  --glass: rgba(255, 255, 255, 0.95);
  --glass-border: #E8E6E1;
  --accent-pink: #8B2635;
  --accent-yellow: #D4711A;
  --secondary: #1E3A5F;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  height: 100%;
  overflow: hidden;
  font-family: var(--font-sans);
  background: var(--bg-dark);
  color: var(--text-primary);
  user-select: none;
  -webkit-user-select: none;
}

/* =============================================
   Splash Screen
   ============================================= */

.splash {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #FFFFFF;
  transition: opacity 0.5s ease, visibility 0.5s ease;
}

.splash.fade-out {
  opacity: 0;
  visibility: hidden;
}

.splash-content {
  text-align: center;
  animation: splashIn 0.6s ease-out;
}

.splash-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 12px;
}

.logo-icon {
  font-size: 48px;
  filter: drop-shadow(0 0 20px rgba(225, 48, 108, 0.3));
  animation: pulse 1.5s ease-in-out infinite;
}

.logo-text {
  font-family: var(--font-display);
  font-size: 42px;
  font-weight: 700;
  color: var(--color-black);
}

.splash-tagline {
  font-size: 16px;
  color: var(--text-muted);
  margin-bottom: 40px;
  letter-spacing: 0.5px;
}

.splash-loader {
  display: flex;
  gap: 8px;
  justify-content: center;
}

.loader-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-burgundy);
  animation: bounce 1.2s ease-in-out infinite;
}

.loader-dot:nth-child(2) {
  animation-delay: 0.15s;
  background: var(--color-orange);
}

.loader-dot:nth-child(3) {
  animation-delay: 0.3s;
  background: var(--color-navy);
}

@keyframes splashIn {
  from { opacity: 0; transform: scale(0.9) translateY(20px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes bounce {
  0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
  40% { transform: scale(1.2); opacity: 1; }
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.1); }
}

/* =============================================
   App Shell
   ============================================= */

.app {
  height: 100%;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
}

.app.hidden {
  display: none;
}

/* =============================================
   Top Bar
   ============================================= */

.top-bar {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.top-bar-left {
  display: flex;
  align-items: center;
  gap: 6px;
}

.top-bar-center {
  display: flex;
  gap: 4px;
  align-items: center;
  background: rgba(0, 0, 0, 0.04);
  border-radius: var(--radius-full);
  padding: 3px;
  border: 1px solid rgba(0, 0, 0, 0.06);
}

.logo-icon-sm {
  font-size: 20px;
}

.logo-text-sm {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--color-black);
}

.tab-btn {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 14px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: all 0.3s ease;
  white-space: nowrap;
}

.tab-btn.active {
  background: var(--color-white);
  color: var(--color-black);
  box-shadow: var(--shadow-sm);
}

/* Ranked/Latest ordering toggle for the For You feed (icon-only; the
   icon is the state: sparkle = ranked, clock = latest) */
.feed-mode-toggle:active {
  transform: scale(0.9);
}

.feed-mode-toggle.hidden {
  display: none;
}

.feed-mode-toggle svg.hidden {
  display: none;
}

.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  color: var(--color-black);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.06);
}

.icon-btn:active {
  transform: scale(0.9);
}

/* =============================================
   Feed Container
   ============================================= */

.feed-container {
  height: 100%;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior: none;
}

.feed-container::-webkit-scrollbar {
  display: none;
}

.feed-container.ptr-active {
  scroll-snap-type: none !important;
  overflow-y: hidden !important;
}

.feed-container.ptr-transitioning {
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* =============================================
   Pull-to-Refresh Indicator
   ============================================= */

.ptr-indicator {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 60px;
  z-index: 110;
  pointer-events: none;
  transform: translateY(-60px);
  opacity: 0;
}

.ptr-indicator.releasing {
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1),
              opacity 300ms cubic-bezier(0.4, 0, 0.2, 1);
}

.ptr-spinner {
  width: 36px;
  height: 36px;
}

.ptr-spinner-svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.ptr-spinner-circle {
  stroke: var(--color-charcoal);
  stroke-dasharray: 125.66;
  stroke-dashoffset: 125.66;
}

.ptr-indicator.refreshing .ptr-spinner-svg {
  animation: spin 0.8s linear infinite;
}

.ptr-indicator.refreshing .ptr-spinner-circle {
  stroke-dashoffset: 31.4;
}

/* =============================================
   Article Card
   ============================================= */

.article-card {
  height: 100vh;
  height: 100dvh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-dark);
}

.article-card-inner {
  /* Height of the photo, as a share of the card. The action rail is anchored
     to the same value so it starts exactly where the photo ends — change it
     in one place and both follow. */
  --card-image-share: 42%;
  flex: 1;
  min-height: 0;
  margin: var(--card-top-clearance) 16px var(--card-bottom-clearance);
  display: flex;
  flex-direction: column;
  position: relative;
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

.article-bg {
  position: relative;
  width: 100%;
  flex: 0 0 var(--card-image-share);
  overflow: hidden;
}

.article-bg-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.category-tags {
  position: absolute;
  bottom: 8px;
  left: 12px;
  right: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  z-index: 1;
}

.category-tag {
  position: static;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 6px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--color-charcoal);
  padding: 4px 8px;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

/* Vertical action rail.
   Runs down the right edge instead of sitting in flow above the headline:
   like/bookmark are the actions worth reaching, and the right-hand column is
   the part of a full-screen card a thumb reaches without a hand shuffle.
   It spans the whole white area — photo edge to card edge — and uses
   space-evenly rather than space-between so the gap above the first icon and
   below the last are the same as the gaps between icons. Anchoring it flush
   to both edges is what makes that work: any top/bottom inset would show up
   as two odd-sized end gaps. It is absolutely positioned, so .article-content
   carries a right padding wide enough to keep headline, summary and byline
   clear of it. */
.card-actions {
  position: absolute;
  right: 4px;
  top: var(--card-image-share);
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  /* Deliberately no gap: `gap` is added on top of the distributed free space
     between items but not at the ends, which would make the end gaps smaller
     than the rest — the exact thing space-evenly is here to avoid. */
  gap: 0;
  background: transparent;
  z-index: 3;
}

.card-action-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  width: 44px;
  border: none;
  color: var(--color-charcoal);
  font-family: var(--font-sans);
  cursor: pointer;
  padding: 2px 0;
  transition: all var(--transition-fast);
  background: transparent;
}

.card-action-btn svg {
  width: 22px;
  height: 22px;
  /* Rounded terminals across the whole set — the mitred corners the raw paths
     produce are what made these read as stock icons. */
  stroke-linecap: round;
  stroke-linejoin: round;
}

.card-action-btn .action-count {
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  opacity: 0.6;
}

/* The share button is the one action with no count under it — there is no
   articles.shares column to render, and the number that used to sit here was
   derived from likes. Reserve the line it no longer occupies: .card-actions
   is a space-evenly column, so a button 14px shorter than its siblings
   silently redistributes every other icon in the stack. Matches
   .action-count's 11px line box plus the 3px column gap. */
.card-action-btn.share-btn::after {
  content: '';
  height: 11px;
}

.card-action-btn.active {
  color: var(--color-burgundy);
}

.card-action-btn.active svg {
  fill: var(--color-burgundy);
  stroke: var(--color-burgundy);
}

.card-action-btn.bookmark-active {
  color: var(--color-orange);
}

.card-action-btn.bookmark-active svg {
  fill: var(--color-orange);
  stroke: var(--color-orange);
}

.article-content {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Right padding clears the absolutely-positioned .card-actions rail. */
  padding: 16px 56px 16px 16px;
  overflow: hidden;
  cursor: pointer;
  user-select: text;
  -webkit-user-select: text;
}

/* Article Text Wrapper */
.article-text {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* Article Title */
.article-title {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-black);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 8px;
}

/* Article Summary */
.article-summary {
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--color-charcoal);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Source & Meta */
.article-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 20px;
  flex-shrink: 0;
  margin-bottom: 14px;
}

.source-avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: white;
  flex-shrink: 0;
}

.source-info {
  flex: 1;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--color-charcoal);
  /* Publisher name + byline + time on one line: some publisher names are
     very long (Al Jazeera's is its whole meta description), so this must
     ellipsize rather than wrap and push the card's text sizing around. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.source-time {
  color: var(--color-medium-grey);
}

/* Swipe indicator */
.swipe-indicator {
  position: absolute;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 4px;
  padding: 5px 12px 5px 8px;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-sm);
  animation: swipeHint 2s ease-in-out infinite;
}

.swipe-indicator svg {
  width: 16px;
  height: 16px;
  stroke: var(--text-primary);
  stroke-width: 2;
  fill: none;
}

.swipe-indicator span {
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 1px;
  text-transform: uppercase;
}

@keyframes swipeHint {
  0%, 100% { transform: translateX(-50%) translateY(0); opacity: 0.5; }
  50% { transform: translateX(-50%) translateY(-8px); opacity: 1; }
}

/* Heart animation */
@keyframes heartPop {
  0% { transform: scale(1); }
  30% { transform: scale(1.4); }
  60% { transform: scale(0.9); }
  100% { transform: scale(1); }
}

.card-action-btn.pop {
  animation: heartPop 0.4s var(--easing-spring);
}

/* The vote arrows, which took the slot a read-only view count used to
   occupy. Two buttons in one wrapper rather than two rail items: .card-actions
   distributes its children with space-evenly, so wrapping them keeps the stack
   at five and leaves like/comment/bookmark/share exactly where they were. */
.card-vote {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

/* Each arrow is its own hit target. Width comes free from the 44px rail;
   height is the part that has to be bought, and 30px is the smallest that
   still separates the two reliably under a thumb. The pair stacks to 62px
   against the 36px a counted button occupies (22px glyph + 3px gap + 11px
   count) — a difference space-evenly absorbs without visibly moving the rest
   of the rail.

   No .action-count and no ::after spacer: these carry no number, and unlike
   .share-btn they are not competing to line up with a sibling that does. */
.card-action-btn.vote-btn {
  gap: 0;
  padding: 0;
  height: 30px;
  justify-content: center;
  /* Secondary to like/comment/bookmark — this is feed tuning, not the
     engagement the card is asking for. */
  opacity: 0.65;
}

/* Cast: the arrow fills. This is the affordance, not decoration — an outline
   reads as "you may vote" and a solid shape as "you have voted, tap to undo",
   which is the only signal the reader gets that the action is reversible.
   The inline fill="none" on the <svg> loses to a CSS `fill` property, the
   same way .active does it for the heart. */
.card-action-btn.vote-btn.vote-up-active,
.card-action-btn.vote-btn.vote-down-active {
  opacity: 1;
}

.card-action-btn.vote-up-active {
  color: var(--color-forest);
}

.card-action-btn.vote-up-active svg {
  fill: var(--color-forest);
  stroke: var(--color-forest);
}

.card-action-btn.vote-down-active {
  color: var(--color-navy);
}

.card-action-btn.vote-down-active svg {
  fill: var(--color-navy);
  stroke: var(--color-navy);
}

/* =============================================
   Bottom Navigation
   ============================================= */

.bottom-bar {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 10px 16px 20px;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.bottom-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  color: var(--color-medium-grey);
  font-size: 13px;
  font-family: var(--font-sans);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition-fast);
  padding: 4px 12px;
  min-width: 44px;
  min-height: 44px;
  justify-content: center;
}

.bottom-btn svg {
  width: 24px;
  height: 24px;
  stroke-width: 2;
}

.bottom-btn.active {
  color: var(--color-black);
}

.bottom-btn:active {
  transform: scale(0.9);
}

/* Add Article Button (TikTok-style center plus) */
.add-article-btn {
  padding: 0 !important;
}

.add-btn-icon {
  width: 44px;
  height: 30px;
  border-radius: 8px;
  background: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  box-shadow: var(--shadow-sm);
}

.add-btn-icon::before,
.add-btn-icon::after {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 8px;
  z-index: -1;
}

.add-btn-icon::before {
  background: var(--secondary);
  left: -3px;
}

.add-btn-icon::after {
  background: var(--accent-pink);
  right: -3px;
}

.add-btn-icon svg {
  color: white;
  position: relative;
  z-index: 1;
}

/* =============================================
   Comment Sheet
   ============================================= */

.comment-sheet {
  position: fixed;
  inset: 0;
  z-index: 300;
}

.comment-sheet.hidden {
  display: none;
}

.comment-sheet-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  animation: fadeIn 0.2s ease;
}

.comment-sheet-content {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  height: 60vh;
  background: var(--bg-card);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  display: flex;
  flex-direction: column;
  animation: slideUpSheet 0.3s ease;
  box-shadow: var(--shadow-lg);
}

@keyframes slideUpSheet {
  from { transform: translateX(-50%) translateY(100%); }
  to { transform: translateX(-50%) translateY(0); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.comment-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--glass-border);
}

.comment-sheet-title {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
}

.comment-sheet-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-size: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.comment-sheet-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
}

.comment-item {
  display: flex;
  gap: 12px;
  margin-bottom: 20px;
  animation: fadeIn 0.3s ease;
}

.comment-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
  flex-shrink: 0;
  /* Clips the avatar image to the circle — the initial-letter fallback
     didn't need this, a square <img> does. */
  overflow: hidden;
}

.comment-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.comment-body {
  flex: 1;
}

.comment-author {
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 3px;
  color: var(--text-primary);
}

.comment-text {
  font-family: var(--font-serif);
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.4;
  /* A pasted URL is one word as far as wrapping is concerned, and the sheet
     has no horizontal scroll to absorb it. */
  overflow-wrap: anywhere;
}

.comment-time {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
}

/* Shown only on your own comments. Low-contrast until hovered/tapped: it
   sits beside every one of your comments, and a full-strength destructive
   icon repeated down the list reads as an alarm. */
.comment-delete-btn {
  flex-shrink: 0;
  align-self: flex-start;
  padding: 2px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  opacity: 0.5;
  cursor: pointer;
  transition: opacity var(--transition-fast), color var(--transition-fast);
}

.comment-delete-btn:hover,
.comment-delete-btn:focus-visible {
  opacity: 1;
  color: var(--color-burgundy);
}

.comment-empty {
  color: var(--text-muted);
  text-align: center;
  padding: 40px 0;
}

.comment-sheet-input {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px 28px;
  border-top: 1px solid var(--glass-border);
}

.comment-sheet-input input {
  flex: 1;
  padding: 10px 16px;
  border-radius: var(--radius-full);
  border: 1px solid var(--glass-border);
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 14px;
  outline: none;
}

.comment-sheet-input input::placeholder {
  color: var(--text-muted);
}

.comment-sheet-input input:focus {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
}

.comment-send-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--color-black);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.comment-send-btn:active {
  transform: scale(0.9);
}

/* =============================================
   Share Sheet
   ============================================= */

.share-sheet {
  position: fixed;
  inset: 0;
  z-index: 300;
}

.share-sheet.hidden {
  display: none;
}

.share-sheet-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  animation: fadeIn 0.2s ease;
}

.share-sheet-content {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  background: var(--bg-card);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 20px;
  animation: slideUpSheet 0.3s ease;
  box-shadow: var(--shadow-lg);
}

.share-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}

.share-sheet-header span {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
}

.share-sheet-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-size: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.share-options {
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
  padding-bottom: 20px;
}

.share-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  border: none;
  background: none;
  color: var(--text-primary);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 12px;
  transition: all 0.2s ease;
}

.share-option:active {
  transform: scale(0.9);
}

.share-icon {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--clr);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: 700;
  color: white;
}

/* =============================================
   Toast
   ============================================= */

.toast {
  position: fixed;
  bottom: 100px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 300;
  padding: 10px 24px;
  background: var(--text-primary);
  color: white;
  border-radius: var(--radius-full);
  font-size: 14px;
  font-weight: 500;
  box-shadow: var(--shadow-md);
  animation: toastIn 0.3s ease;
  white-space: nowrap;
}

.toast.hidden {
  display: none;
}

@keyframes toastIn {
  from { opacity: 0; transform: translateX(-50%) translateY(20px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* =============================================
   Double-tap heart animation
   ============================================= */

.double-tap-heart {
  position: absolute;
  z-index: 10;
  pointer-events: none;
  animation: doubleTapHeart 0.8s ease forwards;
}

.double-tap-heart svg {
  width: 80px;
  height: 80px;
  fill: var(--accent-pink);
  filter: drop-shadow(0 0 20px rgba(225, 48, 108, 0.4));
}

@keyframes doubleTapHeart {
  0% { opacity: 1; transform: scale(0) translateY(0); }
  30% { opacity: 1; transform: scale(1.3) translateY(-10px); }
  60% { opacity: 1; transform: scale(1) translateY(-20px); }
  100% { opacity: 0; transform: scale(0.8) translateY(-60px); }
}

/* =============================================
   Skeleton loading
   ============================================= */

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: var(--radius-sm);
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* =============================================
   Fun Gradient Backgrounds (for articles without images)
   ============================================= */

.gradient-bg-1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.gradient-bg-2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.gradient-bg-3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
.gradient-bg-4 { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
.gradient-bg-5 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
.gradient-bg-6 { background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%); }
.gradient-bg-7 { background: linear-gradient(135deg, #fccb90 0%, #d57eeb 100%); }
.gradient-bg-8 { background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%); }

/* =============================================
   Responsive adjustments
   ============================================= */

@media (min-width: 481px) {
  .app {
    border-left: 1px solid var(--glass-border);
    border-right: 1px solid var(--glass-border);
  }
}

/* Small screens */
@media (max-width: 320px) {
  .card-actions {
    right: 2px;
  }

  /* The tallest item on the rail, so it gives back first. */
  .card-action-btn.vote-btn {
    height: 26px;
  }

  .article-title {
    font-size: 18px;
  }

  .article-content {
    padding: 12px 50px 12px 12px;
  }
}

/* Landscape phones: tighten chrome so text still has room */
@media (orientation: landscape) and (max-height: 500px) {
  :root {
    --card-top-clearance: 64px;
    --card-bottom-clearance: 78px;
  }

  .article-card-inner {
    --card-image-share: 30%;
  }

  /* Only ~180px of white to spread five items over, so the rail is size-bound
     rather than spacing-bound here: shrink the items so space-evenly still has
     free space left to hand out. */
  .card-action-btn {
    gap: 2px;
  }

  .card-action-btn svg {
    width: 16px;
    height: 16px;
  }

  .card-action-btn .action-count {
    font-size: 9px;
  }

  /* Track the smaller count line box so the share button keeps matching its
     siblings on the size-bound rail. */
  .card-action-btn.share-btn::after {
    height: 9px;
  }

  /* Same bargain as the icons: the vote pair is the tallest thing on a rail
     that has run out of height, so it shrinks with them. 22px still clears
     the 16px glyph. */
  .card-action-btn.vote-btn {
    height: 22px;
  }
}

/* =============================================
   Auth Sheet
   ============================================= */

.auth-sheet {
  position: fixed;
  inset: 0;
  z-index: 200;
}

.auth-sheet.hidden {
  display: none;
}

.auth-sheet-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  animation: fadeIn 0.2s ease;
}

.auth-sheet-content {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  background: var(--bg-card);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 0 0 28px;
  animation: slideUpSheet 0.3s ease;
  box-shadow: var(--shadow-lg);
}

.auth-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--glass-border);
}

.auth-sheet-title {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
}

.auth-sheet-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-size: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Auth Tabs (pill toggle) */
.auth-tabs {
  display: flex;
  gap: 4px;
  margin: 16px 20px;
  background: var(--bg-card-alt);
  border-radius: var(--radius-full);
  padding: 3px;
  border: 1px solid var(--glass-border);
}

.auth-tab {
  flex: 1;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  padding: 8px 16px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: all 0.3s ease;
}

.auth-tab.active {
  background: var(--text-primary);
  color: white;
  box-shadow: var(--shadow-sm);
}

/* Auth Forms */
.auth-form {
  padding: 0 20px;
}

.auth-form.hidden {
  display: none;
}

.auth-field {
  margin-bottom: 12px;
}

.auth-field input {
  width: 100%;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--glass-border);
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 15px;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.auth-field input::placeholder {
  color: var(--text-muted);
}

.auth-field input:focus {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
}

.auth-submit-btn {
  width: 100%;
  min-height: 44px;
  padding: 0 16px;
  border: none;
  border-radius: var(--radius-xl);
  background: var(--color-black);
  color: white;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition-fast);
  margin-top: 4px;
}

.auth-submit-btn:active {
  transform: scale(0.98);
  opacity: 0.9;
}

.auth-submit-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.auth-error {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: rgba(139, 38, 53, 0.1);
  color: var(--color-burgundy);
  font-size: 13px;
  margin-bottom: 12px;
  line-height: 1.4;
}

.auth-error.hidden {
  display: none;
}

/* Auth success message */
.auth-success {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: rgba(52, 168, 83, 0.1);
  color: #1a7a32;
  font-size: 13px;
  margin-bottom: 12px;
  line-height: 1.4;
}

.auth-success.hidden {
  display: none;
}

/* =============================================
   Auth Gate (full-screen, non-dismissible)
   ============================================= */

.auth-gate {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--bg-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 20px;
  overflow-y: auto;
}

.auth-gate.hidden {
  display: none;
}

.auth-gate-content {
  width: 100%;
  max-width: 400px;
}

.auth-gate-logo {
  text-align: center;
  margin-bottom: 40px;
}

.auth-gate-tagline {
  font-family: var(--font-serif);
  color: var(--text-secondary);
  margin-top: 8px;
  font-size: 16px;
}

.auth-gate-social {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.auth-gate-form {
  padding: 0;
}

.auth-gate-toggle {
  text-align: center;
  margin-top: 20px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-secondary);
}

/* Sits directly under the submit button, above the sign-up toggle. Quieter
   than .auth-gate-toggle's link — recovery is the rare path, and it should
   not compete with "Create Account" for the primary glance. */
.auth-gate-forgot {
  text-align: center;
  margin-top: 14px;
}

.auth-gate-forgot .auth-link-btn {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}

/* Sits inside the form, directly under the success/error message it answers,
   so it reads as the next step rather than as another way in. */
.auth-gate-resend {
  text-align: center;
  margin: -4px 0 14px;
}

.auth-gate-resend .auth-link-btn {
  font-size: 13px;
  font-weight: 500;
}

/* Cooldown state — still legible, clearly not pressable. */
.auth-gate-resend .auth-link-btn:disabled {
  color: var(--text-muted);
  text-decoration: none;
  cursor: default;
}

.auth-link-btn {
  background: none;
  border: none;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* =============================================
   Onboarding Screens
   ============================================= */

.onboarding-screen {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--bg-dark);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 60px 20px 120px;
  overflow-y: auto;
}

.onboarding-screen.hidden {
  display: none;
}

.onboarding-content {
  width: 100%;
  max-width: 400px;
}

.onboarding-heading {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 28px;
  line-height: 1.3;
}

.category-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 24px;
}

.category-pill {
  padding: 10px 16px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--glass-border);
  background: var(--bg-card-alt);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
}

.category-pill.selected {
  background: var(--text-primary);
  color: white;
  border-color: var(--text-primary);
}

.onboarding-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16px 20px 36px;
  background: var(--bg-dark);
}

.onboarding-footer .auth-submit-btn {
  max-width: 400px;
  margin: 0 auto;
  display: block;
}

/* Google OAuth button */
.auth-social {
  padding: 0 20px 4px;
}

.auth-google-btn {
  width: 100%;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  transition: all var(--transition-fast);
}

.auth-google-btn:active {
  transform: scale(0.98);
  opacity: 0.85;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px 8px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 13px;
}

.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--glass-border);
}

/* =============================================
   Profile Setup Sheet
   ============================================= */

.profile-setup-sheet {
  position: fixed;
  inset: 0;
  z-index: 270;
}

.profile-setup-sheet.hidden {
  display: none;
}

.profile-setup-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  animation: fadeIn 0.2s ease;
}

.profile-setup-content {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  background: var(--bg-card);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 0 0 28px;
  animation: slideUpSheet 0.3s ease;
  box-shadow: var(--shadow-lg);
}

.profile-setup-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--glass-border);
  min-height: 52px;
}

.profile-setup-close {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
}

.profile-setup-close:active {
  transform: scale(0.9);
}

.profile-setup-title {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
}

.profile-setup-form {
  padding: 0 20px;
}

.profile-setup-desc {
  color: var(--text-secondary);
  font-size: 14px;
  margin: 16px 0;
  line-height: 1.5;
}

/* Username field with @ prefix */
.username-field {
  position: relative;
  display: flex;
  align-items: center;
}

.username-prefix {
  position: absolute;
  left: 14px;
  color: var(--text-muted);
  font-size: 15px;
  font-weight: 500;
  z-index: 1;
  pointer-events: none;
}

.username-field input {
  padding-left: 32px;
  padding-right: 36px;
}

.username-status {
  position: absolute;
  right: 14px;
  font-size: 16px;
  line-height: 1;
}

.username-status.available::after {
  content: '\2713';
  color: var(--color-forest);
}

.username-status.taken::after {
  content: '\2717';
  color: var(--accent-pink);
}

/* =============================================
   Profile Panel
   ============================================= */

.profile-panel {
  position: fixed;
  inset: 0;
  z-index: 250;
}

.profile-panel.hidden {
  display: none;
}

.profile-panel-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  animation: fadeIn 0.2s ease;
}

.profile-panel-container {
  position: absolute;
  inset: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  animation: slideUpFull 0.35s cubic-bezier(0.32, 0.72, 0, 1);
}

.profile-panel-container.closing {
  animation: slideDownFull 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;
}

.profile-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--glass-border);
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  min-height: 52px;
  flex-shrink: 0;
}

.profile-panel-back {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
}

.profile-panel-back:active {
  transform: scale(0.9);
}

.profile-header-edit-btn {
  background: transparent;
  border: 2px solid var(--color-warm-grey);
  border-radius: 24px;
  padding: 6px 16px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-black);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.profile-header-edit-btn:active {
  transform: scale(0.96);
  background: var(--bg-card-alt);
}

.profile-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 32px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.profile-avatar-lg {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  background: var(--color-charcoal);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  font-weight: 700;
  color: white;
  margin-bottom: 16px;
}

.profile-display-name {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 700;
  color: var(--color-black);
  margin-bottom: 4px;
}

.profile-username {
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--color-medium-grey);
  margin-bottom: 12px;
}

.profile-bio {
  font-family: var(--font-serif);
  font-size: 14px;
  color: var(--text-secondary);
  text-align: center;
  line-height: 1.5;
  margin-bottom: 24px;
  max-width: 280px;
}

.profile-stats {
  display: flex;
  justify-content: space-evenly;
  width: 100%;
  padding: 16px 0;
  border-top: 1px solid var(--color-warm-grey);
  border-bottom: 1px solid var(--color-warm-grey);
  margin-bottom: 20px;
}

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

.profile-stat-count {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 700;
  color: var(--color-black);
}

.profile-stat-label {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--color-medium-grey);
}

.profile-follow-btn {
  width: 100%;
  max-width: 280px;
  height: 44px;
  background: var(--color-black);
  color: white;
  border: none;
  border-radius: 24px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition-fast);
  margin-bottom: 12px;
}

.profile-follow-btn:active {
  transform: scale(0.98);
  background: #000000;
}

.profile-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: 280px;
}

.profile-signout-btn {
  width: 100%;
  padding: 12px;
  border: none;
  border-radius: var(--radius-sm);
  background: rgba(139, 38, 53, 0.1);
  color: var(--color-burgundy);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.profile-signout-btn:active {
  transform: scale(0.98);
  opacity: 0.8;
}

.admin-portal-link {
  display: block;
  width: 100%;
  padding: 10px;
  border-radius: var(--radius-sm);
  text-align: center;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--color-medium-grey);
  text-decoration: none;
  transition: color var(--transition-fast);
}

.admin-portal-link:hover {
  color: var(--color-charcoal);
}

/* =============================================
   Publisher Profile Panel
   ============================================= */

/* 266 — above the article peek (265), because the peek's byline opens this
   panel and it would otherwise open behind the card that asked for it. The
   two open each other, so their order is a stack rather than a fixed pair:
   a peek raised out of THIS panel takes 267, see .article-peek--over-publisher.
   Still below the profile-setup sheet (270) and the three sheets (300). */
.publisher-profile {
  position: fixed;
  inset: 0;
  z-index: 266;
}

.publisher-profile.hidden {
  display: none;
}

.publisher-profile-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: fadeIn 0.25s ease;
}

.publisher-profile-container {
  position: absolute;
  inset: 0;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  animation: slideInRight 0.3s ease;
  overflow: hidden;
}

@keyframes slideInRight {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

.publisher-profile-container.closing {
  animation: slideOutRight 0.25s ease forwards;
}

@keyframes slideOutRight {
  from { transform: translateX(0); }
  to   { transform: translateX(100%); }
}

.publisher-profile-header {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--glass-border);
  flex-shrink: 0;
}

.publisher-profile-back {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--color-charcoal);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.publisher-profile-back:active {
  transform: scale(0.88);
}

.publisher-profile-body {
  flex: 1;
  overflow-y: auto;
  padding: 28px 20px 48px;
}

.publisher-profile-body::-webkit-scrollbar {
  display: none;
}

.publisher-avatar-lg {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: 28px;
  font-weight: 700;
  color: #fff;
  margin: 0 auto 14px;
  flex-shrink: 0;
}

.publisher-name-lg {
  text-align: center;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 4px;
}

.publisher-website {
  text-align: center;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 10px;
  min-height: 18px;
}

.publisher-website a {
  color: inherit;
  text-decoration: none;
}

.publisher-website a:hover {
  text-decoration: underline;
}

.publisher-desc {
  text-align: center;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-secondary, #555);
  line-height: 1.55;
  margin-bottom: 20px;
  padding: 0 8px;
  min-height: 20px;
}

.publisher-stats {
  display: flex;
  justify-content: center;
  gap: 36px;
  margin-bottom: 20px;
}

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

.publisher-stat-count {
  font-family: var(--font-sans);
  font-size: 20px;
  font-weight: 700;
  color: var(--text-primary);
}

.publisher-stat-label {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.publisher-follow-btn {
  display: block;
  width: 160px;
  margin: 0 auto 32px;
  padding: 10px 0;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-charcoal);
  background: transparent;
  color: var(--color-charcoal);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.publisher-follow-btn.following {
  background: var(--color-charcoal);
  color: #fff;
}

.publisher-follow-btn:active {
  transform: scale(0.95);
}

.publisher-follow-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

/* Publisher articles section label */
.publisher-articles-heading {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--glass-border);
}

.publisher-articles-list {
  display: flex;
  flex-direction: column;
}

.publisher-article-item {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 0;
  border-bottom: 1px solid var(--glass-border);
  cursor: pointer;
  transition: opacity var(--transition-fast);
}

.publisher-article-item:active {
  opacity: 0.65;
}

.publisher-article-thumb {
  width: 68px;
  height: 68px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--bg-card-alt);
  flex-shrink: 0;
}

.publisher-article-thumb-placeholder {
  width: 68px;
  height: 68px;
  border-radius: 8px;
  background: var(--bg-card-alt);
  flex-shrink: 0;
}

.publisher-article-info {
  flex: 1;
  min-width: 0;
}

.publisher-article-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 6px;
}

.publisher-article-time {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-muted);
}

.publisher-articles-loading,
.publisher-articles-empty {
  text-align: center;
  padding: 36px 20px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-muted);
}

/* =============================================
   Saved Articles Panel
   ============================================= */

.saved-panel {
  position: fixed;
  inset: 0;
  z-index: 250;
}

.saved-panel.hidden {
  display: none;
}

.saved-panel-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: fadeIn 0.25s ease;
}

.saved-panel-container {
  position: absolute;
  inset: 0;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  animation: slideInRight 0.3s ease;
  overflow: hidden;
}

.saved-panel-container.closing {
  animation: slideOutRight 0.25s ease forwards;
}

/* =============================================
   Article Peek
   ============================================= */

/* The tapped story's feed card, shown over whichever list opened it.
   z-index 265 is deliberate: above every panel that can open it (search 240,
   saved / profile 250, following 255, settings 260) so the list stays behind
   it and closing the peek returns to it — and below the comment, share and
   live sheets (300), which open on top of the card exactly as they do in the
   feed. */
.article-peek {
  position: fixed;
  inset: 0;
  z-index: 265;
}

/* The publisher profile is the one panel that is BOTH above and below this,
   because each opens the other: its byline opens the panel (266), and the
   panel's article list opens a peek, which has to come back over the top.
   That second case is exactly `peekOrigin === 'publisher'`, and it cannot
   recurse — a peek over a publisher's own list is already showing that
   publisher, so its byline closes the peek instead of opening the panel
   again. Escape reads the same flag to decide which of the two is on top. */
.article-peek--over-publisher {
  z-index: 267;
}

.article-peek.hidden {
  display: none;
}

.article-peek-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  animation: fadeIn 0.2s ease;
}

.article-peek-container {
  position: absolute;
  inset: 0;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg-dark);
  overflow: hidden;
  animation: peekIn 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes peekIn {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

/* Sits in the card's top clearance, where the feed's top bar would be. */
.article-peek-close {
  position: absolute;
  top: 22px;
  left: 16px;
  z-index: 4;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--color-charcoal);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.article-peek-close:active {
  transform: scale(0.88);
}

/* The peek is a scroller, not a slot for one card: it holds every article in
   the list it was opened from, so the reader can carry on past the story they
   tapped. The cards inside keep .article-card's own 100dvh box, clearances
   and snap rules, so each is laid out identically to a feed card and
   sizeCardText's fitting maths gives the same result.

   overscroll-behavior is `contain`, not the feed's `none`: reaching the end
   here must not start scrolling the search results or publisher list sitting
   behind the peek. */
.article-peek-feed {
  position: absolute;
  inset: 0;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior: contain;
}

.article-peek-feed::-webkit-scrollbar {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .article-peek-container,
  .article-peek-overlay {
    animation: none;
  }
}

.saved-unsave-btn {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
  margin-top: 4px;
}

.saved-unsave-btn:active {
  background: #e53e3e;
  color: #fff;
  transform: scale(0.88);
}

/* Make source area tappable */
.article-meta {
  cursor: pointer;
}

.article-meta:active .source-avatar {
  transform: scale(0.92);
}

/* =============================================
   Search / Discover
   ============================================= */

/* z-index 240 is deliberate: it sits UNDER .publisher-profile (250), because
   tapping a publisher result opens that panel over the top of search. The
   old .discover-overlay was 300 and would have covered it. */
.search-panel {
  position: fixed;
  inset: 0;
  z-index: 240;
}

.search-panel.hidden {
  display: none;
}

.search-panel-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: fadeIn 0.25s ease;
}

.search-panel-container {
  position: absolute;
  inset: 0;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg-dark);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: slideInRight 0.3s ease;
}

.search-panel-container.closing {
  animation: slideOutRight 0.25s ease forwards;
}

/* ---- Top bar ---- */

.search-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  padding: max(12px, env(safe-area-inset-top)) 12px 10px;
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--glass-border);
}

.search-back {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border: none;
  background: none;
  border-radius: 50%;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.search-back:active {
  transform: scale(0.88);
}

.search-field {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
}

.search-field-icon {
  position: absolute;
  left: 14px;
  color: var(--text-muted);
  pointer-events: none;
}

.search-field-input {
  width: 100%;
  box-sizing: border-box;
  padding: 11px 36px 11px 40px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-full);
  background: var(--bg-card-alt);
  color: var(--text-primary);
  font-family: var(--font-sans);
  /* 16px is load-bearing: anything smaller makes iOS Safari zoom the page on
     focus, which shoves the fixed panel off-screen. */
  font-size: 16px;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.search-field-input:focus {
  border-color: var(--color-charcoal);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
}

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

.search-field-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}

.search-clear {
  position: absolute;
  right: 8px;
  width: 22px;
  height: 22px;
  border: none;
  border-radius: 50%;
  background: var(--color-warm-grey);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.search-clear.hidden {
  display: none;
}

/* ---- Filter tabs ---- */
/* The underline idiom from .profile-gallery-tab rather than the pill
   .tab-btn: four tabs of uneven width read better underlined, and the row
   can scroll horizontally if more are added later. */

.search-tabs {
  display: flex;
  flex-shrink: 0;
  padding: 0 8px;
  background: var(--bg-card);
  border-bottom: 1px solid var(--glass-border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.search-tabs::-webkit-scrollbar {
  display: none;
}

.search-tab {
  position: relative;
  flex: 0 0 auto;
  height: 44px;
  padding: 0 16px;
  border: none;
  background: transparent;
  white-space: nowrap;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  transition: color var(--transition-fast);
}

.search-tab.active {
  color: var(--text-primary);
  font-weight: 700;
}

.search-tab.active::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 14px;
  right: 14px;
  height: 2px;
  background: var(--text-primary);
  border-radius: 2px 2px 0 0;
}

.search-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: calc(32px + env(safe-area-inset-bottom));
}

.search-body::-webkit-scrollbar {
  display: none;
}

.search-results.hidden,
.search-zero.hidden {
  display: none;
}

/* ---- Result grid ---- */

.s-grid {
  display: grid;
  /* minmax(0, 1fr) rather than 1fr: an unbreakable token in a headline would
     otherwise widen its track and blow out the grid. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 10px;
  padding: 14px 12px;
}

.s-tile {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.s-tile:active {
  transform: scale(0.97);
}

.s-tile-media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* Imageless articles (about 9% of the corpus) carry a .gradient-bg-N class
   instead of an <img>. Those classes are declared far earlier in this file, so
   a plain `background` on .s-tile-media would beat them on source order and
   flatten every one to grey — hence the :not(), which keeps the placeholder
   for the has-an-image case only. */
.s-tile-media:not([class*="gradient-bg-"]) {
  background: var(--bg-card-alt);
}

.s-tile-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.s-tile-title {
  margin: 9px 2px 0;
  font-family: var(--font-display);
  font-size: 15px;
  line-height: 1.24;
  font-weight: 600;
  color: var(--text-primary);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.s-tile-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 6px 2px 0;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--text-muted);
}

.s-tile-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

.s-tile-source {
  font-weight: 600;
  color: var(--text-secondary);
  max-width: 62%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.s-tile-time {
  flex-shrink: 0;
}

.s-tile-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  padding: 3px 7px;
  border-radius: var(--radius-full);
  background: var(--color-burgundy);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.s-tile-badge.paywall {
  background: var(--color-navy);
}

/* The top-ranked result gets a full-width hero, so the grid has some rhythm
   without resorting to a masonry layout that would break rank order. */
.s-tile--hero {
  grid-column: 1 / -1;
}

.s-tile--hero .s-tile-media {
  aspect-ratio: 16 / 9;
}

.s-tile--hero .s-tile-title {
  font-size: 21px;
  line-height: 1.2;
  -webkit-line-clamp: 2;
}

/* ---- Skeletons (composed with the .skeleton shimmer utility) ---- */

.s-skel-media {
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-md);
}

.s-skel-line {
  height: 12px;
  border-radius: 4px;
  margin-top: 8px;
}

.s-skel-line.short {
  width: 55%;
}

/* ---- Zero state ---- */

.search-zero {
  padding: 20px 16px 32px;
}

.search-zero-block {
  margin-bottom: 30px;
}

.search-zero-block.hidden {
  display: none;
}

.search-zero-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.search-zero-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 12px;
}

.search-zero-action {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--color-burgundy);
}

.search-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.search-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-full);
  background: var(--bg-card);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  transition: background var(--transition-fast), color var(--transition-fast),
              border-color var(--transition-fast), transform var(--transition-fast);
}

.search-chip:active {
  background: var(--text-primary);
  color: #fff;
  border-color: var(--text-primary);
  transform: scale(0.96);
}

.search-chip.recent {
  background: var(--bg-card-alt);
}

.search-chip-x {
  display: flex;
  align-items: center;
  color: var(--text-muted);
}

/* ---- Publisher rows ---- */

.search-pub-list {
  display: flex;
  flex-direction: column;
}

.search-results .search-pub-list {
  padding: 4px 16px;
}

.s-pub {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 2px;
  cursor: pointer;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.s-pub:active {
  background: var(--bg-card-alt);
}

.s-pub-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
}

.s-pub-info {
  flex: 1;
  min-width: 0;
}

.s-pub-name {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.s-pub-sub {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
}

.s-pub-follow {
  flex-shrink: 0;
  padding: 7px 16px;
  border: 1px solid var(--text-primary);
  border-radius: var(--radius-full);
  background: var(--text-primary);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast),
              transform var(--transition-fast);
}

.s-pub-follow.following {
  background: transparent;
  color: var(--text-primary);
}

.s-pub-follow:active {
  transform: scale(0.94);
}

/* ---- Topic rows ---- */

.search-topic-list {
  display: flex;
  flex-direction: column;
  padding: 4px 16px;
}

.s-topic-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 2px;
  cursor: pointer;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.s-topic-row:active {
  background: var(--bg-card-alt);
}

.s-topic-name {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.s-topic-count {
  flex-shrink: 0;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-muted);
}

/* ---- Loading / empty / error states ---- */

.search-empty {
  text-align: center;
  padding: 64px 32px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.6;
}

.search-empty-title {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.search-more {
  grid-column: 1 / -1;
  text-align: center;
  padding: 18px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
}

/* =============================================
   Accessibility: Reduced Motion
   ============================================= */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* =============================================
   Profile Redesign: Settings Gear Button
   ============================================= */

.profile-header-settings-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
}

.profile-header-settings-btn:active {
  transform: scale(0.9);
}

/* =============================================
   Settings Panel
   ============================================= */

.settings-panel {
  position: fixed;
  inset: 0;
  z-index: 260;
}

.settings-panel.hidden {
  display: none;
}

.settings-panel-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.settings-panel-container {
  position: absolute;
  inset: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  animation: slideUpFull 0.35s cubic-bezier(0.32, 0.72, 0, 1);
  overflow: hidden;
}

.settings-panel-container.closing {
  animation: slideDownFull 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;
}

.settings-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--glass-border);
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  min-height: 52px;
  flex-shrink: 0;
}

.settings-panel-back {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--bg-card-alt);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
}

.settings-panel-back:active {
  transform: scale(0.9);
}

.settings-panel-title {
  font-family: var(--font-sans);
  font-size: 17px;
  font-weight: 600;
  color: var(--text-primary);
}

.settings-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 20px 40px;
}

.settings-section-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 24px 0 8px;
}

.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 0;
  border-bottom: 1px solid var(--glass-border);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text-primary);
  transition: opacity 0.15s ease;
}

.settings-row:active {
  opacity: 0.65;
}

.settings-save-btn {
  width: 100%;
  height: 44px;
  background: var(--text-primary);
  color: white;
  border: none;
  border-radius: var(--radius-full);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 16px;
  margin-bottom: 8px;
  transition: all var(--transition-fast);
}

.settings-save-btn:active {
  transform: scale(0.97);
}

.settings-save-btn:disabled {
  opacity: 0.5;
}

.settings-signout-btn {
  width: 100%;
  padding: 14px;
  border: none;
  border-radius: var(--radius-sm);
  background: rgba(139, 38, 53, 0.1);
  color: var(--color-burgundy);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
  transition: all var(--transition-fast);
  margin-top: 4px;
}

.settings-signout-btn:active {
  background: rgba(139, 38, 53, 0.18);
}

.settings-admin-link {
  display: block;
  padding: 14px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-muted);
  text-decoration: none;
  margin-top: 4px;
}

.settings-admin-link.hidden {
  display: none;
}

/* =============================================
   Following Panel
   ============================================= */

.following-panel {
  position: fixed;
  inset: 0;
  z-index: 255;
}

.following-panel.hidden {
  display: none;
}

.following-panel-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

.following-panel-container {
  position: absolute;
  inset: 0;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  animation: slideInRight 0.3s ease;
  overflow: hidden;
}

.following-panel-container.closing {
  animation: slideOutRight 0.25s ease forwards;
}

.following-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--glass-border);
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.following-item:active {
  opacity: 0.65;
}

.following-item-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--color-charcoal);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 700;
  flex-shrink: 0;
}

.following-item-info {
  flex: 1;
  min-width: 0;
}

.following-item-name {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
}

.following-item-count {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 2px;
}

.following-unfollow-btn {
  flex-shrink: 0;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--color-warm-grey);
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.following-unfollow-btn:active {
  background: var(--bg-card-alt);
  transform: scale(0.96);
}

.following-unfollow-btn:disabled {
  opacity: 0.5;
}

/* "Follow" state — solid fill so it's clearly actionable */
.following-unfollow-btn--follow {
  background: var(--text-primary);
  color: white;
  border-color: var(--text-primary);
}

/* =============================================
   Profile Gallery (Liked / Saved / Read tabs)
   ============================================= */

.profile-gallery {
  width: 100%;
  margin-top: 20px;
  border-top: 1px solid var(--glass-border);
}

.profile-gallery-tabs {
  display: flex;
  border-bottom: 1px solid var(--glass-border);
}

.profile-gallery-tab {
  flex: 1;
  height: 44px;
  background: transparent;
  border: none;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  cursor: pointer;
  position: relative;
  transition: color 0.15s ease;
}

.profile-gallery-tab.active {
  font-weight: 700;
  color: var(--text-primary);
}

.profile-gallery-tab.active::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 10%;
  right: 10%;
  height: 2px;
  background: var(--text-primary);
  border-radius: 2px 2px 0 0;
}

.profile-gallery-list {
  padding: 4px 0;
}

/* =============================================
   Profile Avatar Edit Overlay
   ============================================= */

.profile-avatar-edit-wrap {
  position: relative;
  width: 80px;
  height: 80px;
  margin: 0 auto 20px;
}

.profile-avatar-edit-wrap .profile-avatar-lg {
  margin-bottom: 0;
  width: 80px;
  height: 80px;
}

.profile-avatar-edit-overlay {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--text-primary);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 2px solid var(--bg-card);
  transition: transform 0.15s ease;
}

.profile-avatar-edit-overlay:active {
  transform: scale(0.9);
}

/* Profile setup bio textarea */
.profile-setup-form textarea {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--glass-border);
  background: var(--bg-input, var(--bg-card-alt));
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--text-primary);
  resize: none;
  outline: none;
  transition: border-color 0.2s ease;
  box-sizing: border-box;
}

.profile-setup-form textarea:focus {
  border-color: var(--text-primary);
}

/* =============================================
   Live Blogs
   ============================================= */

/* LIVE chip above the headline on live-coverage cards. Sits in the text
   block so the measurement-based text sizing accounts for its height. */
.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  padding: 4px 10px 4px 8px;
  border-radius: 100px;
  background: rgba(139, 38, 53, 0.1);
  color: var(--accent-pink);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-pink);
  animation: live-pulse 2s ease-in-out infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(0.8); }
}

/* Coverage that has wrapped up keeps the chip but drops the urgency. */
.live-badge.ended {
  background: rgba(0, 0, 0, 0.06);
  color: var(--text-muted);
}

.live-badge.ended .live-dot {
  background: var(--text-muted);
  animation: none;
}

/* Brief highlight when the 60s poller swaps in a new update, so a change
   under the user's eyes doesn't happen silently. */
.live-just-updated .article-text {
  animation: live-flash 1.2s ease-out;
}

@keyframes live-flash {
  0%   { background: rgba(139, 38, 53, 0.12); }
  100% { background: transparent; }
}

/* The pulse is decorative; a persistent animation is exactly what
   reduced-motion users are asking not to see. */
@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
  .live-just-updated .article-text { animation: none; }
}

/* ---- Live timeline sheet ----
   Reuses .comment-sheet layout; only the row styling differs. */

.live-update {
  padding: 14px 0;
  border-bottom: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.07));
}

.live-update:last-child {
  border-bottom: none;
}

.live-update-time {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-pink);
  margin-bottom: 5px;
}

.live-update-headline {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text-primary);
  margin-bottom: 5px;
}

.live-update-text {
  font-family: var(--font-serif);
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-charcoal);
}

.live-sheet-footer {
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.07));
}

.live-sheet-open-btn {
  width: 100%;
  padding: 13px;
  border: none;
  border-radius: 12px;
  background: var(--accent-pink);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

/* =============================================
   Headline digest card
   =============================================
   Link-aggregator sources (Hacker News) have no publisher summary and
   often no image, so a full-screen card gave them a gradient and one
   headline. This card shows up to five, each opening its own article —
   closer to how the source is actually read.

   The gradient sits on .article-card-inner, and a frosted panel carries
   the rows so headlines stay legible over any of the eight gradients.
   Rows are flex-sized rather than fixed height: five long headlines and
   five short ones both have to fit 100vh without the card scrolling,
   which is why there is no dynamic text fitting here (sizeCardText
   deliberately no-ops on these — they have no .article-title). */

.article-card.is-digest .article-card-inner {
  padding: 0;
}

.digest-content {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 20px 16px 16px;
  gap: 14px;
}

.digest-header {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  padding: 10px 12px;
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.digest-header-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.digest-source {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: var(--color-charcoal);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.digest-count {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-medium-grey);
}

.digest-rows {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Top-aligned: a digest does not always hold five stories (a page
     boundary can leave two), and with rows capped below, a short list
     reads as a list under its header rather than floating mid-card. */
  justify-content: flex-start;
  gap: 8px;
  overflow: hidden;
}

/* Rows grow to share the leftover height so the card fills the screen,
   but `auto` basis means they never shrink BELOW their content. With
   `1 1 0` they did: a row got an equal 102px share while a three-line
   headline needed 109px, and overflow:hidden sliced the last line through
   the middle. The clamp below caps how much content there can be. */
.digest-row {
  /* Grows to share leftover height, never shrinks below its content, and
     never exceeds max-height. A full five rows divide the space and fill
     the card; a short digest keeps normal-sized rows instead of ballooning. */
  flex: 1 1 auto;
  max-height: 112px;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  border: none;
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  cursor: pointer;
  font: inherit;
  transition: transform 150ms var(--ease-spring, ease), background 150ms ease;
}

.digest-row:active {
  transform: scale(0.985);
  background: rgba(255, 255, 255, 1);
}

.digest-row-title {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-charcoal);
  /* Two lines holds ~80 characters at this width, which covers all but the
     longest aggregator headlines, and lets five rows plus the header fit
     100vh with room to spare. Three lines did not fit and was the reason
     headlines were being clipped. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.digest-row-meta {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  color: var(--color-medium-grey);
  flex-shrink: 0;
}

/* Shorter viewports: trade headline size for room rather than clipping. */
@media (max-height: 700px) {
  .digest-content { padding: 14px 14px 12px; gap: 10px; }
  .digest-rows { gap: 6px; }
  .digest-row { padding: 9px 12px; }
  .digest-row-title { font-size: 15px; }
}

@media (prefers-reduced-motion: reduce) {
  .digest-row { transition: none; }
  .digest-row:active { transform: none; }
}

/* =============================================
   Card chips (Live / Subscriber)
   =============================================
   Both chips share one wrapper. sizeCardText measures every non-title,
   non-summary child of .article-text and sums their heights to work out
   how much room the headline has — two inline-flex chips as siblings sit
   on ONE line box but would be counted as two, over-clamping the title.
   One block-level wrapper is measured once and is correct. */

.card-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
}

/* The live badge supplies its own bottom margin for the case where it is
   rendered alone; inside the wrapper that would double up. */
.card-chips .live-badge {
  margin-bottom: 0;
}

/* Navy rather than the live badge's burgundy: this is information, not
   urgency. A reader who only wants headlines should not feel warned off. */
.paywall-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px 4px 8px;
  border-radius: 100px;
  background: rgba(30, 58, 95, 0.09);
  color: var(--color-navy);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.paywall-badge svg {
  flex-shrink: 0;
}

