.timeline {
  padding: 8px 16px 40px;
}

.month-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 56px 0 24px;
  color: var(--black);
  font-family: "Cormorant Garamond", serif;
  font-size: 1.3rem;
  font-style: italic;
  letter-spacing: 0.04em;
}

.month-divider::before,
.month-divider::after {
  content: "";
  flex: 1;
  max-width: 64px;
  height: 1px;
  background: var(--hairline);
}

.month-divider:first-child {
  margin-top: 12px;
}

.place-divider {
  text-align: center;
  margin: 8px 0 18px;
  color: var(--grey-light);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.photo-card,
.message {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.photo-card {
  margin: 0 auto 20px;
  /* .photo-card has its own `transform` (the reveal animation below),
     which creates a new stacking context - that traps the child img/
     video's z-index:95 inside it, making it invisible to #ambient-canvas
     (z-index:90) in the outer context, so the rain art still bled through
     on top of photos despite that rule. Fixing it here, at the level
     that actually competes with the canvas, is what makes the img rule
     effective at all. */
  position: relative;
  z-index: 95;
}

.photo-card.visible,
.message.visible {
  opacity: 1;
  transform: translateY(0);
}

.photo-card img,
.photo-card video {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 0;
  border: 1px solid var(--hairline);
  background: var(--off-white);
  cursor: zoom-in;
  /* Above #ambient-canvas (z-index:90) - Malte: photos/videos must sit in
     front of the rain art, not be tinted by it. Everything else (text,
     empty background) stays behind it on purpose. */
  position: relative;
  z-index: 95;
}

/* Read-only note caption (issue #75) - a child of .photo-card, not the
   img/video itself, so it can sit visually on top via z-index within the
   card's own stacking context (established by .photo-card's `position:
   relative` + `transform`, see the comment above) without affecting the
   image element. Toggled globally via body.notes-hidden, not re-rendered
   per toggle - see script.js's applyNotesOverlayVisibility(). Visual rules
   shared with the lightbox caption live in .note-glass (style.css, search
   for it) - unlike the lightbox, a card's img is always exactly the
   card's width (width:100% + height:auto, no letterboxing), so plain
   left/right insets are accurate without script.js's JS-measured
   positioning. bottom:62px = above the badge below (bottom:12px, 40px
   tall -> top edge 52px) with a 10px gap, mirroring the lightbox's
   caption-above-trigger relationship. Clamped to fewer lines than the
   lightbox (see .note-glass's -webkit-line-clamp comment) - a card is a
   small thumbnail-scale caption, not the main focus the way the lightbox
   caption is. */
.card-note-overlay {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 62px;
  z-index: 96;
  -webkit-line-clamp: 4;
}

/* Read-only reaction badge on the timeline (mirrors the lightbox trigger's
   reacted-to look) - only rendered when a reaction exists, see
   renderCardReactionBadge() in script.js. Deliberately NOT a button (no
   interaction from the timeline - reacting stays a lightbox-only action)
   and deliberately NOT in the .notes-hidden selector below: that toggle is
   about notes, not reactions, and must not hide this too. No padding on
   the glyph, matching the fixed .reaction-toggle-emoji - see its comment
   for why padding was the actual centering bug. */
.card-reaction-badge {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #fff;
  color: #000;
  z-index: 96;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  line-height: 1;
  pointer-events: none;
}

.notes-hidden .card-note-overlay,
.notes-hidden #lightbox-note-overlay {
  display: none;
}

