/* ---------------------------------------------------------------------------
   Kitbash Feed — starter stylesheet
   This is structural scaffolding only. Real design happens later.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Crash Socratic — stylesheet

   Tokens live at the top. Colors and fonts are set up so rules that reference
   var(--color-*) get re-skinned automatically when dark mode is active via
   [data-theme="dark"] on the <html> element.
   --------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  --max-width: 640px;

  /* --- Colors (light mode) --- */
  --color-bg:       #F7F2E8;  /* aged paper, subtle warmth */
  --color-surface:  #FCF7E8;  /* cards, slight lift from bg */
  --color-text:     #1A1A1A;  /* neutral near-black for main text */
  --color-text-mono: #1A1612; /* warm near-black, reserved for mono */
  --color-muted:    #6B5E4A;  /* warm mid-tone */
  --color-border:   #D9D0BB;  /* warm hairline */
  --color-accent:   #1E3A5F;  /* ink blue, primary */
  --color-accent-2: #9B7A2C;  /* dull gold, reserve */

  /* --- Fonts --- */
  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:    'Work Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, "SF Mono", Menlo, monospace;
}

[data-theme="dark"] {
  color-scheme: dark;

  --color-bg:        #1C1915;
  --color-surface:   #26221B;
  --color-text:      #E8E8E8;
  --color-text-mono: #E8E0CE;
  --color-muted:     #9B9384;
  --color-border:    #3A342A;
  --color-accent:    #7EA3D1;
  --color-accent-2:  #C9A558;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.55;
  color: var(--color-text);
  background: var(--color-bg);
}

a { color: var(--color-accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Monospace elements keep the warm cast you liked — body text is neutral,
   but code/pre/kbd stay warm as a distinct typographic voice. */
code, pre, kbd, samp {
  color: var(--color-text-mono);
}

/* --- Layout --- */

.site-header {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1.75rem 1rem 1.25rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid var(--color-border);
}

.site-title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.75rem;
  letter-spacing: 0.04em;
  line-height: 1;
  text-transform: uppercase;
  color: var(--color-text);
  font-optical-sizing: auto;
}
.site-title:hover { text-decoration: none; }

/* --- VIBE toggle ---
   The one place our reserve gold shows up by default — the button that flips
   the world. Bodoni Moda SC for editorial flourish, dark text on gold in both
   modes so it reads like gold leaf stamping. Instant flip on click (no color
   transitions — animated theme swaps look like SaaS demos). */

.vibe-toggle {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  padding: 0.4rem 1rem;
  background: var(--color-accent-2);
  color: var(--color-bg);
  border: 1px solid var(--color-accent-2);
  border-radius: 999px;
  cursor: pointer;
  text-transform: uppercase;
  line-height: 1;
}
.vibe-toggle:hover {
  background: transparent;
  color: var(--color-accent-2);
}

.site-main {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1rem;
}

.site-footer {
  max-width: var(--max-width);
  margin: 3rem auto 2rem;
  padding: 1.5rem 1rem;
  text-align: center;
  color: var(--color-muted);
  font-size: 0.85rem;
  border-top: 1px solid var(--color-border);
}

/* --- Feed & post cards --- */

.feed {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.post {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 1rem 1.25rem;
}

.post-meta {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.75rem;
  font-size: 0.85rem;
  color: var(--color-muted);
}

.post-avatar {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  object-fit: cover;
  background: var(--color-border);
  /* Flat-top hexagon — wider than tall, reads as identity marker
     without being the same old circle every other platform uses. */
  clip-path: polygon(50% 0%, 79.39% 9.55%, 97.55% 34.55%, 97.55% 65.45%, 79.39% 90.45%, 50% 100%, 20.61% 90.45%, 2.45% 65.45%, 2.45% 34.55%, 20.61% 9.55%);
}

.post-identity {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  flex: 1;
  min-width: 0;
}

.post-username {
  color: var(--color-text);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.2;
}

.post-timeline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  line-height: 1.2;
}

.post-meta a { color: var(--color-muted); }

.post-meta-sep {
  color: var(--color-muted);
}

.post-title {
  margin: 0.25rem 0 0.5rem;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.005em;
}
.post-title a { color: var(--color-text); }

.post-subtitle {
  margin: 0 0 1rem;
  color: var(--color-muted);
  font-style: italic;
}

.post-body { margin-top: 0.25rem; }
.post-body p { margin: 0.5rem 0; }
.post-body p:first-child { margin-top: 0; }
.post-body p:last-child { margin-bottom: 0; }

/* Headings inside post bodies. Gentler than browser defaults so an H3
   doesn't visually outrank the card's own post-title. Weight 600 across
   the board — hierarchy comes from size, not weight. */
.post-body h1 { font-size: 1.4rem; font-weight: 600; line-height: 1.3; margin: 1.25rem 0 0.5rem; }
.post-body h2 { font-size: 1.2rem; font-weight: 600; line-height: 1.3; margin: 1.25rem 0 0.5rem; }
.post-body h3 { font-size: 1.05rem; font-weight: 600; line-height: 1.35; margin: 1rem 0 0.4rem; }
.post-body h4 { font-size: 1rem; font-weight: 600; line-height: 1.4; margin: 0.875rem 0 0.4rem; }
.post-body h1:first-child,
.post-body h2:first-child,
.post-body h3:first-child,
.post-body h4:first-child { margin-top: 0; }

.post-cover {
  width: 100%;
  border-radius: 4px;
  margin: 0.5rem 0 1rem;
}

/* --- Link callout ---
   "Readings"-inspired magazine-clipping treatment.
   CSS-drawn brackets wrap the callout content — skinny vertical bar with
   longer horizontal arms, like editorial citation brackets. Gold, paralleling
   the gold quote mark on Quote posts. */

.link-callout-wrap {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 0.9rem;
  padding: 0.5rem 0;
  margin: 0.25rem 0 0;
}

/* Brackets are drawn with borders, not glyphs. Width controls how far
   the horizontal arms extend; the single-pixel vertical keeps the bar skinny. */
.link-mark {
  flex-shrink: 0;
  width: 0.6rem;
  border-top: 2px solid var(--color-accent-2);
  border-bottom: 2px solid var(--color-accent-2);
}
.link-mark-open {
  border-left: 1px solid var(--color-accent-2);
}
.link-mark-close {
  border-right: 1px solid var(--color-accent-2);
}

.link-callout {
  flex: 1;
  min-width: 0;
  display: block;
  padding: 0.1rem 0;
  text-decoration: none;
  color: var(--color-text);
}
.link-callout:hover { text-decoration: none; }
.link-callout:hover .link-title { color: var(--color-accent); }

.link-title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0 0 0.5rem;
  color: var(--color-text);
  font-optical-sizing: auto;
}

.link-byline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.85rem;
  line-height: 1.3;
}

.link-author {
  font-style: italic;
  color: var(--color-muted);
}

.link-domain {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  flex-shrink: 0;
}

/* Commentary is the user's own voice — same body type as anywhere else. */
.link-commentary {
  margin-top: 1rem;
}

/* --- Quote callout ---
   Pull-quote treatment: ornamental opening quote as graphic element,
   Work Sans italic body at generous size, em-dash-led attribution.
   Hairlines top and bottom to sibling the link callout. */

.quote-callout {
  position: relative;
  margin: 0.25rem 0 0;
  padding: 0.5rem 0 0.5rem 2.5rem;
  border: 0;
}

.quote-mark {
  position: absolute;
  top: 0.55rem;
  left: -0.25rem;
  font-family: var(--font-display);
  font-size: 4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--color-accent-2);
  font-optical-sizing: auto;
  user-select: none;
}

.quote-text {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--color-text);
}

.quote-attribution {
  margin-top: 0.75rem;
  padding-left: 0;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--color-muted);
  font-style: normal;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
}

.quote-dash {
  color: var(--color-muted);
  margin-right: 0.1rem;
}

.quote-author {
  font-weight: 500;
  color: var(--color-text);
}

.quote-source {
  font-style: italic;
  color: var(--color-muted);
}
a.quote-source { color: var(--color-accent); }
a.quote-source:hover { text-decoration: underline; }

.quote-location {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-muted);
}
.quote-location::before {
  content: "· ";
  margin-right: 0.15rem;
}

.quote-commentary {
  margin-top: 1rem;
  font-style: normal;
}

/* --- Single post page --- */

.post-focused .post {
  border: 2px solid var(--color-text);
}

.continuation-divider {
  text-align: center;
  margin: 2rem 0 1rem;
  font-size: 0.85rem;
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  position: relative;
}
.continuation-divider::before,
.continuation-divider::after {
  content: "";
  position: absolute;
  top: 50%;
  width: calc(50% - 8rem);
  height: 1px;
  background: var(--color-border);
}
.continuation-divider::before { left: 0; }
.continuation-divider::after { right: 0; }

/* Continuation feed posts. Unlike the homepage feed they're not wrapped in
   .feed-item flex children, so we add explicit top margin to space them.
   Adjacent vertical margins collapse, so a single rule handles both
   divider-to-post and post-to-post spacing cleanly. */
.continuation > .post {
  margin-top: 1.25rem;
}

/* --- Project page --- */

.project-header {
  margin-bottom: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--color-border);
}

.project-meta {
  display: flex;
  gap: 0.75rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin-bottom: 0.5rem;
}

.project-status {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  padding: 0.15rem 0.5rem;
  border-radius: 2px;
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  color: var(--color-accent);
  border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent);
}
.status-active {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  color: var(--color-accent);
}

.project-title {
  margin: 0 0 0.5rem;
  font-size: 2rem;
  font-weight: 600;
}

.project-description {
  font-size: 1.1rem;
  color: var(--color-muted);
  margin: 0 0 1rem;
}

/* ---------------------------------------------------------------------------
   Media types
   --------------------------------------------------------------------------- */

/* --- Image --- */

.image-figure {
  margin: 0;
}

.post-image {
  width: 100%;
  height: auto;
  border-radius: 4px;
  display: block;
}

.image-caption {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--color-muted);
  font-style: italic;
}

.image-commentary {
  margin-top: 0.75rem;
}

/* --- Zine carousel --- */

.zine-carousel {
  position: relative;
  margin: 0.5rem 0;
}

.zine-track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  gap: 0;
  border-radius: 4px;
}
.zine-track::-webkit-scrollbar { display: none; }

.zine-slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
}

.zine-image {
  width: 100%;
  height: auto;
  display: block;
}

.zine-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  border: none;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.15s;
}
.zine-carousel:hover .zine-nav { opacity: 1; }
.zine-prev { left: 0.5rem; }
.zine-next { right: 0.5rem; }

.zine-dots {
  display: flex;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 0.5rem;
}

.zine-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #d0d0d0;
  border: none;
  padding: 0;
  cursor: pointer;
}
.zine-dot.is-active { background: var(--color-text); }

.zine-counter {
  position: absolute;
  bottom: 0.5rem;
  right: 0.5rem;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 12px;
}

.zine-caption {
  margin: 0.5rem 0 0;
  color: var(--color-muted);
  font-size: 0.9rem;
}

/* --- Voice --- */

.voice-player {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0.5rem 0;
}

.voice-player audio {
  flex: 1;
  min-width: 0;
}

.voice-duration {
  font-size: 0.85rem;
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

.voice-transcript {
  margin-top: 0.5rem;
  font-size: 0.95rem;
}

.voice-transcript summary {
  cursor: pointer;
  color: var(--color-muted);
  font-size: 0.85rem;
  padding: 0.25rem 0;
}

.voice-transcript[open] summary {
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--color-border);
}

/* --- Vertical video --- */

.video-vertical-wrap {
  position: relative;
  max-width: 400px;
  margin: 0 auto;
  aspect-ratio: 9 / 16;
  background: #000;
  border-radius: 4px;
  overflow: hidden;
}

.video-vertical-player {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.video-vertical-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0.75rem;
}

.video-vertical-overlay > * {
  pointer-events: auto;
}

.video-vertical-caption {
  color: white;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
  max-width: calc(100% - 60px);
}

.video-vertical-mute {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  color: white;
  border: none;
  font-size: 1rem;
  cursor: pointer;
}

.video-vertical-duration {
  position: absolute;
  bottom: 0.75rem;
  right: 0.75rem;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 12px;
  font-variant-numeric: tabular-nums;
}

/* --- Traditional video --- */

.video-traditional-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: 4px;
  overflow: hidden;
  margin: 0.5rem 0;
}

.video-traditional-wrap iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.video-traditional-thumb {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: block;
}

.video-traditional-poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.video-traditional-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  border-radius: 50%;
  font-size: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-left: 4px;
  transition: transform 0.15s;
}

.video-traditional-thumb:hover .video-traditional-play {
  transform: translate(-50%, -50%) scale(1.08);
}

.video-traditional-duration {
  position: absolute;
  bottom: 0.5rem;
  right: 0.5rem;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 12px;
  font-variant-numeric: tabular-nums;
}

.video-chapters {
  margin-top: 0.5rem;
  font-size: 0.9rem;
}

.video-chapters summary {
  cursor: pointer;
  color: var(--color-muted);
  padding: 0.25rem 0;
}

.video-chapters ul {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.video-chapters .chapter-time {
  display: inline-block;
  width: 4rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent);
}

/* ---------------------------------------------------------------------------
   Article inline embeds (direct media: ![[file.jpg]], etc.)
   Post embeds inside articles render as full .post cards and are already
   constrained by .post sizing, so they don't need rules here.
   --------------------------------------------------------------------------- */

.article-body picture,
.article-body img.article-inline-image,
.article-body .article-inline-image img,
.article-body .article-inline-video,
.article-body .article-inline-audio {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1rem 0;
  border-radius: 4px;
}

.article-body .article-inline-audio {
  width: 100%;
}

/* Drop cap on the first paragraph of an article body. Targets the first
   <p> element (regardless of whether a heading precedes it) so articles
   that open with `## section` still get the flourish on the first prose
   paragraph. Float-left wraps the surrounding text around the letter,
   line-height tightened so the cap sits flush with the first few lines. */
.article-body > p:first-of-type::first-letter {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 4.5em;
  line-height: 0.85;
  float: left;
  padding: 0 0.1em 0 0;
  color: var(--color-text);
  font-optical-sizing: auto;
}

/* ---------------------------------------------------------------------------
   Feed controls (sticky filter + order toggle)
   --------------------------------------------------------------------------- */

.feed-controls {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--color-bg);
  margin: -1rem -1rem 1rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.feed-controls-row {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  align-items: center;
}

.feed-controls-order {
  justify-content: flex-end;
}

.filter-chip,
.order-toggle {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.35rem 0.8rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-muted);
  cursor: pointer;
  white-space: nowrap;
  line-height: 1;
}

.filter-chip:hover:not(.is-active):not([aria-expanded="true"]),
.order-toggle:hover:not(.is-active) {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.filter-chip.is-active,
.order-toggle.is-active {
  background: var(--color-accent);
  color: var(--color-surface);
  border-color: var(--color-accent);
}

.order-toggle {
  font-size: 0.75rem;
  padding: 0.3rem 0.7rem;
}

/* Filtered-out posts */
.feed-item[hidden] {
  display: none;
}

/* Vertical divider between chip groups (type | projects) on the same row. */
.feed-controls-divider {
  display: inline-block;
  width: 1px;
  height: 1.1rem;
  background: var(--color-border);
  margin: 0 0.25rem;
  align-self: center;
}

/* Project chips use gold accent when active — distinguishes them from type
   chips (ink blue) without looking like a different component. */
.filter-chip--project.is-active {
  background: var(--color-accent-2);
  color: var(--color-bg);
  border-color: var(--color-accent-2);
}
.filter-chip--project:hover:not(.is-active):not([aria-expanded="true"]) {
  color: var(--color-accent-2);
  border-color: var(--color-accent-2);
}

/* ---------------------------------------------------------------------------
   Filter groups
   ---------------------------------------------------------------------------
   A filter group is a parent pill + a collapsible set of child option pills.
   `display: contents` makes the group's children participate directly in the
   controls row's flex layout, so options wrap inline alongside the parent
   when the group is open. This keeps the visual order correct (options follow
   their own parent, before the next parent pill) without any JS reordering. */

.filter-group {
  display: contents;
}

.filter-chip--parent {
  display: inline-flex;
  align-items: baseline;
}

/* Tiny chevron after the parent pill, rotates when the group is expanded. */
.filter-chip--parent::after {
  content: "\25BE"; /* ▾  */
  margin-left: 0.35em;
  font-size: 0.85em;
  display: inline-block;
  transition: transform 0.12s ease;
}
.filter-chip--parent[aria-expanded="true"]::after {
  transform: rotate(180deg);
}

/* Inline suffix on the parent pill when a filter is active. JS owns the
   full string including separator: ": Quip" for a single selection,
   " · 3" for multiple. */
.filter-chip-current {
  font-weight: 500;
}

/* Filled state for an expanded parent pill, so it's obvious which group's
   options are currently fanned out alongside it. */
.filter-chip--parent[aria-expanded="true"] {
  background: var(--color-accent);
  color: var(--color-surface);
  border-color: var(--color-accent);
}
.filter-chip--parent.filter-chip--project[aria-expanded="true"] {
  background: var(--color-accent-2);
  color: var(--color-bg);
  border-color: var(--color-accent-2);
}

/* Child options: hidden by default, become direct flex siblings of the
   parent pill when the group opens (display: contents propagation). */
.filter-group-options {
  display: none;
}
.filter-group.is-open .filter-group-options {
  display: contents;
}

/* Empty state when all posts are filtered out */
.feed-empty {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--color-muted);
}

/* The feed-item wrapper is transparent — the post card inside provides the styling */
.feed-item > .post {
  margin: 0;
}

.feed {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Override the earlier .feed { gap } rule — now applied to feed items so wrapper gap works */
.feed {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}


/* ---------------------------------------------------------------------------
   Collapsible blocks
   ---------------------------------------------------------------------------
   Shared mechanism for articles in feed, long bulletins, and project page
   headers. Markup:
     <div class="collapsible collapsible--X" data-collapsible>
       <div class="collapsible-content">...</div>
       <button data-collapsible-expand>Read more</button>
       <button data-collapsible-collapse>Collapse ↑</button>
     </div>

   JS toggles `.is-expanded` on click. Blocks that should default to open
   (project headers) render with `is-expanded` already on the wrapper.

   On `.post-focused` (single-post pages), the entire collapse mechanism is
   stripped so the focused post reads fully expanded with no toggles.
   --------------------------------------------------------------------------- */

.collapsible-content {
  position: relative;
  overflow: hidden;
}

.collapsible.is-expanded .collapsible-content {
  /* Fully revealed when open. */
  max-height: none;
  overflow: visible;
}

/* Per-context preview heights, tuned per use:
   article = enough to show drop cap + a few lines + tease;
   bulletin = quip-shaped (~3 lines);
   project header = a couple lines of description before the feed. */
.collapsible--article:not(.is-expanded) .collapsible-content      { max-height: 14em; }
.collapsible--bulletin:not(.is-expanded) .collapsible-content     { max-height: 5.5em; }
.collapsible--project-header:not(.is-expanded) .collapsible-content { max-height: 4em; }

/* Fade gradient at the bottom of the truncated content. Blends from
   transparent into the post surface so the cutoff feels soft. The 92%
   target color leaves a slight see-through at the bottom so the text
   underneath is still hinted at, signaling "there's more here". */
.collapsible:not(.is-expanded) .collapsible-content::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 3em;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--color-surface) 0%, transparent) 0%,
    var(--color-surface) 92%);
  pointer-events: none;
}

/* The bulletin and article live inside .post (surface color background);
   project header lives directly on page bg — fade has to match that. */
.collapsible--project-header:not(.is-expanded) .collapsible-content::after {
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--color-bg) 0%, transparent) 0%,
    var(--color-bg) 92%);
}

/* Both buttons share base styling — mono, pill-shaped, muted like the
   post-meta date/handle links. JS-visible state hides the other. */
.collapsible-expand,
.collapsible-collapse {
  display: none;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.9rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-muted);
  cursor: pointer;
  line-height: 1;
  margin-top: 0.5rem;
}

.collapsible-expand:hover,
.collapsible-collapse:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

/* Expand button: visible when collapsed, sits inline below the fade. */
.collapsible:not(.is-expanded) > .collapsible-expand {
  display: inline-block;
}

/* Collapse button: visible when expanded; sticks to viewport bottom while
   the parent extends below the fold, naturally settles at the end of the
   content as the user scrolls past. Right-aligned. Subtle backdrop blur so
   it's legible over any content underneath. */
.collapsible.is-expanded > .collapsible-collapse {
  display: block;
  position: sticky;
  bottom: 1rem;
  margin-left: auto;
  width: fit-content;
  background: color-mix(in srgb, var(--color-surface) 92%, transparent);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  box-shadow: 0 1px 6px color-mix(in srgb, var(--color-text) 12%, transparent);
}

/* On single-post pages the post is the whole point — strip the collapse
   mechanism entirely so articles (and any nested embedded long-form) open
   fully with no toggles in sight. Selectors written with enough specificity
   to beat the per-variant truncation rules above (4 classes vs. 3). */
.post-focused .collapsible--article:not(.is-expanded) .collapsible-content,
.post-focused .collapsible--bulletin:not(.is-expanded) .collapsible-content,
.post-focused .collapsible--project-header:not(.is-expanded) .collapsible-content {
  max-height: none;
  overflow: visible;
}
.post-focused .collapsible:not(.is-expanded) .collapsible-content::after,
.post-focused .collapsible:not(.is-expanded) > .collapsible-expand,
.post-focused .collapsible.is-expanded > .collapsible-collapse {
  display: none;
}


/* ---------------------------------------------------------------------------
   Engagement action row
   ---------------------------------------------------------------------------
   Lives at the bottom of every post card. Three buttons share .engagement-btn
   styling — clap, reply, share — distinguished by modifier classes only when
   needed (currently just for the clap-only count + floating combo number).

   Icons are inline Lucide SVGs with stroke=currentColor, so they pick up
   the button's color naturally on hover and in dark mode.
   --------------------------------------------------------------------------- */

.post-engagement {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--color-border);
}

.engagement-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  padding: 0.4rem 0.75rem;
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-muted);
  cursor: pointer;
  line-height: 1;
  user-select: none;
  -webkit-user-select: none;
}

.engagement-btn:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

/* Quick scale-down on click — makes spam-tapping the clap feel responsive. */
.engagement-btn:active {
  transform: scale(0.95);
}

.engagement-icon {
  display: inline-flex;
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}
.engagement-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.engagement-count {
  font-variant-numeric: tabular-nums;
}

/* Floating "+N" combo counter, anchored above the clap button. Fades up
   and out over 1.5s via CSS animation. Each click restarts the animation
   (JS removes/reflows/adds .is-animating); the burst counter only resets
   when the animation runs to completion uninterrupted. */
.engagement-float {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 0.3rem;
  opacity: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--color-accent-2);
  pointer-events: none;
  white-space: nowrap;
  font-optical-sizing: auto;
  font-variant-numeric: tabular-nums;
}

.engagement-float.is-animating {
  animation: engagement-float-up 1.5s ease-out forwards;
}

@keyframes engagement-float-up {
  0%   { opacity: 1; transform: translate(-50%, 0); }
  20%  { opacity: 1; transform: translate(-50%, -8px); }
  100% { opacity: 0; transform: translate(-50%, -56px); }
}


/* ---------------------------------------------------------------------------
   Toast notifier
   ---------------------------------------------------------------------------
   Single global element near the bottom of the viewport. JS sets textContent
   and restarts the fade animation — in for 0.25s, hold for ~2s, out for 0.5s.
   aria-live="polite" announces the message on text change for screen readers.
   --------------------------------------------------------------------------- */

.toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-text);
  color: var(--color-bg);
  padding: 0.85rem 1.6rem;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  line-height: 1.3;
  opacity: 0;
  pointer-events: none;
  z-index: 1000;
  max-width: calc(100vw - 2rem);
  text-align: center;
  box-shadow: 0 8px 28px color-mix(in srgb, var(--color-text) 35%, transparent);
}

.toast.is-showing {
  animation: toast-fade 2.5s ease-out forwards;
}

/* Center-frame toast animation: pop in (scale 0.95 → 1 + soft upward drift),
   hold steady through the dwell window, ease out with a slight shrink.
   Translate values are layered on top of the base translate(-50%, -50%)
   that anchors the toast in the middle of the viewport. */
@keyframes toast-fade {
  0%   { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(0.95); }
  10%  { opacity: 1; transform: translate(-50%, -50%)              scale(1);    }
  80%  { opacity: 1; transform: translate(-50%, -50%)              scale(1);    }
  100% { opacity: 0; transform: translate(-50%, calc(-50% - 4px)) scale(0.98); }
}

