/* static/css/campaign-responsive.css */
/*
   Shared responsive layer for the Doherty campaign site.

   WHY THIS FILE EXISTS:
   Most of base.html / event.html / home.html style structural layout
   (display:flex, grid-template-columns, etc.) with INLINE styles.
   Inline styles cannot be overridden by @media queries in an external
   stylesheet unless we win the cascade deliberately, so this file:
     1. Provides classes for the layout-critical properties (flex/grid),
        which the templates now use INSTEAD OF inline layout rules.
     2. Only reaches for !important where an inline rule genuinely can't
        be avoided (e.g. section padding kept for brand consistency).

   Load this AFTER the <style> block already in base.html, right before
   </head>, so it wins on equal-specificity selectors like .nav-toggle.
*/

:root {
  --color-ink: oklch(0.145 0.012 40);
  --color-ink-light: oklch(0.18 0.013 40);
  --color-paper: oklch(0.965 0.014 85);
  --color-gold: oklch(0.78 0.12 85);
  --color-red: oklch(0.52 0.19 27);
  --color-border: oklch(0.4 0.02 40 / 0.3);
}

/* ============================================================
   SCROLL-REVEAL
   ============================================================ */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   EVENT ROW
   ============================================================ */
.event-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 32px;
  padding: 28px 0;
  border-bottom: 1px solid var(--color-border);
}
.event-row__date {
  flex: 0 0 84px;
  width: 84px;
  text-align: center;
}
.event-row__body {
  flex: 1 1 auto;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.event-row__body h3,
.event-row__body p {
  overflow-wrap: break-word;
}
.event-row__rsvp {
  flex: 0 0 auto;
}

@media (max-width: 640px) {
  .event-row {
    flex-wrap: wrap;
    gap: 14px;
    padding: 22px 0;
  }
  .event-row__date {
    flex: 0 0 auto;
    width: auto;
    text-align: left;
    display: flex;
    align-items: baseline;
    gap: 8px;
    order: 1;
  }
  .event-row__body { flex: 1 1 100%; order: 2; }
  .event-row__rsvp { flex: 1 1 100%; order: 3; }
  .event-row__rsvp button { width: 100%; }
}

/* ============================================================
   RESPONSIVE GRIDS
   ============================================================ */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; }
.grid-2-asym { display: grid; grid-template-columns: 0.85fr 1.15fr; }
.grid-2-asym-rev { display: grid; grid-template-columns: 1.15fr 0.85fr; }
.grid-2-asym-wide { display: grid; grid-template-columns: 0.8fr 1.2fr; }
.grid-2-wide { display: grid; grid-template-columns: 1fr 1.15fr; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); }

@media (max-width: 900px) {
  .grid-2,
  .grid-2-asym,
  .grid-2-asym-rev,
  .grid-2-asym-wide,
  .grid-2-wide {
    grid-template-columns: 1fr !important;
  }
  .grid-3,
  .grid-4 {
    grid-template-columns: repeat(2, 1fr) !important;
  }
  /* Once .grid-2-asym-wide collapses to one column above, a sticky
     sidebar (candidate profile page's portrait/contact card) would stay
     pinned mid-scroll over the stacked bio text below it — turn sticky
     off so it just flows in document order like everything else. */
  .profile-bio__sidebar {
    position: static !important;
    top: auto !important;
  }
  /* Homepage hero: .home-hero (a .grid-2) just collapsed to one column
     above. .home-hero__slides holds only position:absolute images, which
     don't contribute intrinsic height, so its row would collapse to 0
     without an explicit height here. `order: -1` puts the photo band
     first (top) despite .home-hero__content coming first in the markup
     (markup order matches the desktop text-left/image-right layout). */
  .home-hero__slides {
    order: -1;
    min-height: 50vh;
    max-height: 560px;
  }
}
@media (max-width: 560px) {
  .grid-3,
  .grid-4 {
    grid-template-columns: 1fr !important;
  }
}

.grid-timeline {
  display: grid;
  grid-template-columns: 220px 1fr;
}
@media (max-width: 640px) {
  .grid-timeline { grid-template-columns: 1fr !important; }
}

.grid-timeline-divider {
  display: grid;
  grid-template-columns: 110px 1px 1fr;
}
@media (max-width: 640px) {
  .grid-timeline-divider { grid-template-columns: 1fr !important; }
}

@media (max-width: 520px) {
  .candidate-bullet-grid { grid-template-columns: 1fr !important; }
}

.past-events-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
@media (max-width: 900px) {
  .past-events-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .past-events-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .stat-item { padding: 0 24px !important; }
}
@media (max-width: 420px) {
  .stat-item { padding: 0 14px !important; }
  .stat-item div:first-child { font-size: 30px !important; }
}

/* ============================================================
   NAV — responsive
   ============================================================ */
nav {
  padding: 16px 48px;
}

.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  line-height: 1;
}

@media (max-width: 1180px) {
  nav .nav-links { gap: 20px; }
  nav .nav-links a { font-size: 13px; }
}

@media (max-width: 900px) {
  nav {
    padding: 14px 20px !important;
  }
  nav .nav-links {
    display: none !important;
  }
  .nav-toggle {
    display: flex !important;
  }
}

@media (min-width: 901px) {
  .mobile-nav-overlay {
    display: none !important;
  }
}

/* ============================================================
   SECTION SPACING — scale down the large desktop paddings.
   The original file only trimmed left/right padding on mobile,
   leaving every section at the full desktop 90px top/bottom —
   that reads as huge dead space once the content itself has
   already reflowed to a single narrow column. This scales
   vertical section padding down in two steps.
   ============================================================ */
@media (max-width: 640px) {
  section { padding-left: 24px !important; padding-right: 24px !important; }
}
@media (max-width: 900px) {
  section { padding-top: 64px !important; padding-bottom: 64px !important; }
}
@media (max-width: 560px) {
  section { padding-top: 48px !important; padding-bottom: 48px !important; }
}
@media (max-width: 640px) {
  .page-hero { padding-top: 140px !important; padding-bottom: 56px !important; }
}

@media (max-width: 560px) {
  .hero-cta-row { flex-direction: column; align-items: stretch; }
  .hero-cta-row a { text-align: center; width: 100%; }
}

/* Portrait photos (About / Running Mate): the 360px/340px desktop
   min-heights are fine at full width but eat most of the viewport
   on a small phone, pushing all the copy below the fold. Shrink the
   floor on narrow screens; object-fit:cover keeps the crop sane. */
@media (max-width: 640px) {
  .about-portrait { min-height: 240px !important; max-height: 360px !important; }
  .runningmate-portrait { min-height: 220px !important; max-height: 340px !important; }
}

/* ============================================================
   HOME HERO — MOBILE (fine-tuning within the stacked layout)
   The actual stacking (image band on top, text below — see `order: -1`
   on .home-hero__slides) happens at the 900px .grid-2 collapse above;
   this block just tightens padding/spacing for narrower phone screens.
   ============================================================ */
@media (max-width: 640px) {
  .home-hero__content {
    padding: 32px 24px 56px !important;
  }
  .home-hero__eyebrow-line {
    display: none !important;
  }
  .home-hero__eyebrow-row {
    gap: 0 !important;
  }
  .home-hero__scroll-indicator {
    display: none !important;
  }
}

/* RSVP modal padding on small screens */
@media (max-width: 480px) {
  .rsvp-modal-panel { padding: 28px 20px !important; }
}

@media (max-width: 480px) {
  .responsive-media-grid { grid-template-columns: 1fr !important; }
}

/* ============================================================
   MASONRY GALLERY
   ============================================================ */
.masonry-gallery {
  columns: 3 240px;
  column-gap: 20px;
}
.masonry-gallery__item {
  break-inside: avoid;
  margin-bottom: 20px;
  width: 100%;
  display: inline-block;
}
@media (max-width: 900px) {
  .masonry-gallery { columns: 2 220px; }
}
@media (max-width: 560px) {
  .masonry-gallery { columns: 1 220px; }
}

/* ============================================================
   SEGMENTED-CONTROL RADIO GROUPS
   ============================================================ */
.segmented-control__option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  box-sizing: border-box;
  border: 1.5px solid oklch(0.2 0.01 40 / 0.2);
  border-radius: 4px;
  padding: 12px 16px;
  cursor: pointer;
  background: white;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.segmented-control__option:hover {
  border-color: var(--segment-accent, oklch(0.52 0.19 27));
}
.segmented-control__option.is-selected {
  border-color: var(--segment-accent, oklch(0.52 0.19 27));
  background: color-mix(in oklch, var(--segment-accent, oklch(0.52 0.19 27)) 7%, white);
}
.segmented-control__option input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}
.segmented-control__check {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid oklch(0.2 0.01 40 / 0.35);
  box-sizing: border-box;
  position: relative;
}
.segmented-control__option.is-selected .segmented-control__check {
  border-color: var(--segment-accent, oklch(0.52 0.19 27));
}
.segmented-control__option.is-selected .segmented-control__check::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--segment-accent, oklch(0.52 0.19 27));
}
@media (max-width: 560px) {
  .segmented-control { grid-template-columns: 1fr !important; }
}

@media (max-width: 900px) {
  #about > div:first-child > div:first-child { display: none; }
  #runningmate > div:last-child > div:first-child { display: none; }
}

html { overflow-x: hidden; }

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
}
@media (max-width: 900px) {
  .footer-grid { grid-template-columns: 1fr 1fr !important; gap: 36px !important; }
}
@media (max-width: 560px) {
  .footer-grid { grid-template-columns: 1fr !important; }
}
@media (max-width: 640px) {
  footer { padding: 56px 24px 28px !important; }
}

.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

/* ============================================================
   LIGHTBOX MODAL
   ============================================================ */
.lightbox-modal {
  z-index: 400;
}
.lightbox-image-wrap {
  max-width: 100%;
  max-height: 70vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none;
}
.lightbox-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
  cursor: zoom-in;
  transition: transform 0.15s ease-out;
  user-select: none;
  -webkit-user-select: none;
}
.lightbox-image.is-zoomed {
  cursor: grab;
  transition: none;
}
.lightbox-nav-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  background: oklch(0.15 0.01 40 / 0.55);
  border: 1px solid oklch(1 0 0 / 0.35);
  color: white;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-nav-btn--prev { left: 16px; }
.lightbox-nav-btn--next { right: 16px; }
.lightbox-zoom-btn {
  background: none;
  border: 1px solid oklch(1 0 0 / 0.4);
  color: white;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
}
@media (max-width: 640px) {
  .lightbox-nav-btn { width: 40px; height: 40px; font-size: 16px; }
}

.milestone-row--clickable:hover {
  background: oklch(0.78 0.12 85 / 0.06);
}

.milestone-row--clickable:focus-visible,
.lightbox-trigger:focus-visible {
  outline: 2px solid oklch(0.78 0.12 85 / 0.8);
  outline-offset: -2px;
}

/* ============================================================
   HOME HERO SLIDESHOW — split layout
   .home-hero (a .grid-2) has two cells: .home-hero__content (text) and
   .home-hero__slides (the image column) — see core/templates/core/home.html.
   .home-hero__slides is `position:relative`, sized to fill its OWN grid
   cell (not the whole section — this used to be a full-bleed overlay
   behind the text; it's a dedicated narrower column now). Every
   .home-hero__image inside it stays position:absolute;inset:0 relative
   to that cell at ALL breakpoints so Alpine's x-show is never fought by
   a conflicting !important display/position rule.

   object-fit:contain — the whole uploaded photo must show, uncropped,
   full stop. cover was tried in this same narrower column and still
   sliced the top/bottom off portrait photos whenever their aspect ratio
   didn't exactly match the column's — contain never crops; instead it
   letterboxes against .home-hero's own dark background (set inline on
   the section), which reads as intentional rather than a black void
   since the column is already close to the photo's own proportions.

   will-change + backface-visibility + translateZ(0) force each slide
   onto its own GPU compositing layer during the slow heroZoom pan and
   the crossfade — without these, Chrome/Safari can fall back to
   software-compositing a scaling image, which reads as soft/blurry
   even though the source asset itself is sharp. The zoom amplitude
   was also pulled back from 1.07 to 1.05 (see the @keyframes in
   base.html) so the motion is subtler and less noticeable as "blur".
   ============================================================ */
.home-hero__slides {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.home-hero__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 50%;
  animation: heroZoom 26s ease-in-out infinite alternate;
  will-change: transform, opacity;
  backface-visibility: hidden;
  transform: translateZ(0);
}
@media (prefers-reduced-motion: reduce) {
  .home-hero__image { animation: none; }
}

/* Alpine crossfade transition — 400ms. Was 1000ms, which against the
   current 1500ms autoplay interval meant slides spent roughly two-thirds
   of their visible time mid-fade, reading as a blended "double exposure"
   rather than distinct photos. 400ms leaves each slide over a second of
   settled, unblended time before the next crossfade starts. */
.home-hero__fade-enter { transition: opacity 400ms ease; }
.home-hero__fade-enter-start { opacity: 0; }
.home-hero__fade-enter-end { opacity: 1; }
.home-hero__fade-leave { transition: opacity 400ms ease; position: absolute; }
.home-hero__fade-leave-start { opacity: 1; }
.home-hero__fade-leave-end { opacity: 0; }

.home-hero__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18px;
  z-index: 3;
  display: flex;
  justify-content: center;
  gap: 10px;
}
.home-hero__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1px solid oklch(1 0 0 / 0.6);
  background: oklch(1 0 0 / 0.25);
  padding: 0;
  cursor: pointer;
  min-width: 24px;
  min-height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.home-hero__dot::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
}
.home-hero__dot.is-active {
  background: oklch(0.78 0.12 85);
  border-color: oklch(0.78 0.12 85);
}

.home-hero__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid oklch(1 0 0 / 0.4);
  background: oklch(0.1 0.01 40 / 0.4);
  color: white;
  font-size: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.2s ease, background-color 0.2s ease;
}
.home-hero:hover .home-hero__arrow {
  opacity: 1;
}
.home-hero__arrow:hover {
  background: oklch(0.1 0.01 40 / 0.7);
}
.home-hero__arrow--prev { left: 24px; }
.home-hero__arrow--next { right: 24px; }

@media (max-width: 900px) {
  .home-hero__arrow { display: none !important; }
}

