/* ==========================================================================
   Cindy Thi Explorer — Global Stylesheet
   Tokens sourced from design.md
   ========================================================================== */

/* ---------- Fonts ---------- */

@font-face {
  font-family: "P22 Mackinac";
  src: url("../assets/fonts/P22MackinacProBold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "P22 Mackinac Book";
  src: url("../assets/fonts/P22MackinacProBook.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "North Stains";
  src: url("../assets/fonts/north-stains-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---------- Design tokens ---------- */

:root {
  /* Colors */
  --color-primary: #155e64;      /* headers, nav links, primary buttons */
  --color-body-text: #333333;    /* body text */
  --color-accent: #d36d11;       /* display text / accent */
  --color-background: #faf7f0;   /* page background */
  --color-header-bg: #ffd7b3;    /* header bg / contrast links */
  --color-mist: #e7f0f1;         /* light blue section wash */
  --color-white: #ffffff;
  --color-ink: #222222;          /* near-black for article headings */

  /* Fonts */
  --font-headers: "P22 Mackinac", Georgia, serif;
  --font-body: "Be Vietnam Pro", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body-2: "P22 Mackinac Book", Georgia, serif;
  --font-accent: "North Stains", cursive;

  /* Layout */
  --nav-height: 76px;
  --container: 1200px;
  --container-wide: 1360px;

  /* Spacing scale */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 40px;
  --space-5: 64px;
  --space-6: 96px;

  /* Misc */
  --radius-sm: 8px;
  --radius-md: 16px;
  --radius-pill: 999px;
  --shadow-card: 0 10px 30px rgba(21, 94, 100, 0.12);
  --transition: 0.25s ease;
}

/* ---------- Reset / base ---------- */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.7;
  color: var(--color-body-text);
  background-color: var(--color-background);
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: var(--color-primary);
  text-decoration: none;
  transition: color var(--transition);
}

h1, h2, h3, h4 {
  font-family: var(--font-headers);
  font-weight: 700;
  color: var(--color-primary);
  line-height: 1.15;
}

h1 { font-size: clamp(2.5rem, 5vw, 3.75rem); }
h2 { font-size: clamp(2rem, 4vw, 2.75rem); }
h3 { font-size: clamp(1.25rem, 2.5vw, 1.5rem); }

.container {
  width: min(var(--container), 100% - 48px);
  margin-inline: auto;
}

.container-wide {
  width: min(var(--container-wide), 100% - 48px);
  margin-inline: auto;
}

/* Visually hidden, still read by screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Accent script text (North Stains) */
.accent-script {
  font-family: var(--font-accent);
  font-weight: 600;
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  color: var(--color-accent);
  display: block;
  line-height: 1.2;
}

/* Oversized script headline (Where I've Been) */
.accent-script-lg {
  font-size: clamp(2.5rem, 5vw, 4rem);
}

/* Section eyebrow + intro */
.section {
  padding: var(--space-6) 0;
}

.section-intro {
  text-align: center;
  max-width: 640px;
  margin: 0 auto var(--space-5);
}

.section-intro p {
  margin-top: var(--space-2);
  font-family: var(--font-body);
  font-size: 1.125rem;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 2px;
  padding: 0.75rem 1.5rem;
  border-radius: 0;
  border: 1px solid transparent;
  cursor: pointer;
  transition: all var(--transition);
}

.btn-primary {
  background-color: var(--color-primary);
  color: var(--color-white);
}

.btn-primary:hover {
  background-color: #0e464b;
  transform: translateY(-2px);
}

.btn-outline {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: transparent;
}

.btn-outline:hover {
  background-color: var(--color-primary);
  color: var(--color-white);
}

/* Contrast button — for use on teal backgrounds */
.btn-contrast {
  background-color: var(--color-header-bg);
  color: var(--color-primary);
}

.btn-contrast:hover {
  background-color: var(--color-white);
  transform: translateY(-2px);
}

/* ---------- Navigation ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: var(--color-header-bg);
  box-shadow: 0 2px 12px rgba(21, 94, 100, 0.08);
}

.nav {
  height: var(--nav-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.nav-logo img {
  height: 26px;
  width: auto;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  list-style: none;
}

/* The one place the uppercase label treatment goes heavier: at 11px the nav
   sits above everything else on the page, and 600 is what holds it there. */
.nav-links a {
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-primary);
}

.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--color-accent);
}

/* Keep the nav CTA button legible regardless of link states */
.nav-links a.btn-primary,
.nav-links a.btn-primary:hover,
.nav-links a.btn-primary[aria-current="page"] {
  color: var(--color-white);
}

.nav-cta {
  white-space: nowrap;
}

/* Mobile nav toggle */
.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
}

.nav-toggle span {
  display: block;
  width: 26px;
  height: 3px;
  background: var(--color-primary);
  margin: 5px 0;
  border-radius: 2px;
  transition: all var(--transition);
}

/* ---------- Hero (Home) ---------- */

.hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: calc(100vh - var(--nav-height));
}

.hero-content {
  background-image: url("../assets/patterns/hero_bg_pattern.png");
  background-size: 640px;
  background-repeat: repeat;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: var(--space-6) clamp(24px, 6vw, 96px);
}

.hero-content .accent-script {
  font-size: 4rem;
}

.hero-content h1 {
  margin: var(--space-2) 0 var(--space-3);
}

/* The one line still set in the editorial serif — the home page's opening
   words. Every other subtext under a title reads in Be Vietnam. */
.hero-content .lede {
  font-family: var(--font-body-2);
  font-size: 1.2rem;
  max-width: 46ch;
  margin-bottom: var(--space-4);
}

.hero-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: center;
}

.hero-media {
  position: relative;
}

.hero-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 12px solid var(--color-white);
}

/* ---------- My Work cards ---------- */

.work-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}

/* The home page's collections cross the window rather than the container's
   measure — the row breaks out to the full width, a gutter short of each
   edge. The portfolio and shop indexes share the class and keep the plain
   three-up grid, so this is scoped to the section. */
#my-work {
  /* The row is as wide as the window; a scrollbar counted into 100vw must not
     turn that into a page that scrolls sideways. */
  overflow-x: clip;
}

/* The breakout is the wrapper's rather than the row's, so the arrow can ride
   the row's own right edge instead of the container's. */
.work-carousel {
  position: relative;
  margin-inline: calc(50% - 50vw + var(--space-3));
}

/* However many collections the CMS holds, they stay on the one line — an
   added page narrows the row rather than wrapping a card under it. Each tile
   is as wide as its own picture is: js/main.js grows a card by the ratio of
   the photo inside it, so a row of mixed shapes shares one height and fills
   the line with nothing cropped to a frame it wasn't shot for. */
#my-work .work-grid {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1);
  /* The tiles hold their width and run off the end of the line, so the line
     is what scrolls. The bar itself is nobody's idea of a nice edge under a
     row of photographs, and the arrow is the mouse's way in without it. */
  overflow-x: auto;
  /* A tile still rising into place hangs below the row for a moment; that
     is not a row to scroll up and down. */
  overflow-y: hidden;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

#my-work .work-grid::-webkit-scrollbar {
  display: none;
}

/* Reaching the row by keyboard scrolls it with the arrow keys, so it says
   where it is rather than taking focus invisibly. */
#my-work .work-grid:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 4px;
}

/* Every other collection rides lower than its neighbours, so the row reads
   as a stagger rather than a strip. The tiles are 450 wide and hold it —
   they don't shrink to make the row fit the window, they run off the end of
   it, which is what the section clips. */
#my-work .work-card {
  flex: 0 0 450px;
  aspect-ratio: auto;
  margin-top: var(--space-2);
}

#my-work .work-card:nth-child(even) {
  margin-top: var(--space-4);
}

/* Each tile rises into place as the row comes into view — js/main.js marks a
   card the moment it is scripted, and again once it has been seen, so a page
   without the script shows its collections where they stand. The delay is
   the script's too: the cards that arrive together come up one after the
   other, whichever they are. */
#my-work .work-card[data-reveal] {
  opacity: 0;
  transform: translateY(48px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

#my-work .work-card[data-reveal="in"] {
  opacity: 1;
  transform: none;
}

/* The photo sets the tile's height rather than being cut to fit one */
#my-work .work-card img {
  height: auto;
  object-fit: fill;
}

/* Unless it is a wide one — that gets the upright frame the row keeps, and
   fills it the way a cover always has. */
#my-work .work-card-cropped {
  aspect-ratio: 4 / 5;
}

#my-work .work-card-cropped img {
  height: 100%;
  object-fit: cover;
}

/* Nothing inside to take a shape from, so it keeps the portrait frame the
   galleries wear, which stands it as tall as the rest. */
#my-work .work-card-empty {
  aspect-ratio: 4 / 5;
}

/* A collection with no cover of its own — teal under the mark, so the white
   title on the overlay still reads. */
.work-card-empty {
  background-color: var(--color-primary);
}

.work-card-empty::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../assets/logos/favicon.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 96px;
  opacity: 0.22;
  transition: opacity var(--transition);
}

.work-card-empty:hover::before {
  opacity: 0.3;
}

/* The arrows ride the edges of the row, centred on the cards: peach over the
   photographs, because teal on the teal Video card is no button at all. JS
   takes each one out of the page whenever there is nothing its way left to
   reach — and both of them whenever the collections already fit. The cards
   are positioned too, and the back arrow comes before them in the markup, so
   without a level of its own it paints under the row it is meant to walk. */
.work-nav {
  position: absolute;
  z-index: 1;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--color-header-bg);
  color: var(--color-primary);
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: background var(--transition), transform var(--transition);
}

.work-nav svg {
  width: 26px;
  height: 26px;
}

.work-nav-prev {
  left: var(--space-2);
}

.work-nav-next {
  right: var(--space-2);
}

/* One arrow drawn, pointed the other way. */
.work-nav-prev svg {
  transform: scaleX(-1);
}

.work-nav:hover {
  background: var(--color-white);
  transform: translateY(-50%) scale(1.06);
}

.work-nav[hidden] {
  display: none;
}

.work-card {
  position: relative;
  border-radius: 0;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  box-shadow: var(--shadow-card);
  display: block;
}

.work-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease;
}

.work-card:hover img {
  transform: scale(1.05);
}

.work-card-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--space-3);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0) 55%);
}

.work-card-overlay h2,
.work-card-overlay h3 {
  color: var(--color-white);
  font-size: 1.6rem;
}

.work-card-overlay p {
  color: var(--color-header-bg);
  font-size: 0.95rem;
  margin-top: 4px;
}

/* ---------- Places / chips ---------- */

.stats-row {
  display: flex;
  justify-content: center;
  gap: clamp(24px, 6vw, 80px);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
  text-align: center;
}

.stat .stat-number {
  font-family: var(--font-headers);
  font-size: clamp(2rem, 4vw, 3rem);
  color: var(--color-accent);
  line-height: 1;
}

.stat .stat-label {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-top: 6px;
  color: var(--color-primary);
  font-weight: 600;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  max-width: 1000px;
  margin: 0 auto;
  list-style: none;
}

/* Two rows of countries sliding past each other in opposite directions.
   The rows pull back through the mist box's side padding so the chips run
   the full width of the box, appearing and disappearing at its edges. */
.chip-marquees {
  display: grid;
  gap: 12px;
  margin-inline: calc(-1 * var(--space-4));
  overflow: hidden;
}

.chip-marquee {
  display: flex;
  overflow: hidden;
}

/* Each row holds the same chips twice; a track slides left by its own full
   width and the copy behind it lands exactly where it started, so the loop
   never shows a seam. The right-running row plays the same slide in reverse. */
.chip-marquee .chip-track {
  flex: none;
  flex-wrap: nowrap;
  max-width: none;
  margin: 0;
  padding-right: 12px;
  animation: chip-marquee 85s linear infinite;
}

.chip-marquee-right .chip-track {
  animation-direction: reverse;
}

@keyframes chip-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

/* Standing still, the rows fall back to the wrapped, centred layout */
@media (prefers-reduced-motion: reduce) {
  #my-work .work-grid {
    scroll-behavior: auto;
  }

  #my-work .work-card[data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .work-nav:hover {
    transform: translateY(-50%);
  }

  .chip-marquees {
    margin-inline: 0;
  }

  .chip-marquee {
    overflow: visible;
  }

  .chip-marquee .chip-track {
    flex: 1;
    flex-wrap: wrap;
    padding-right: 0;
    animation: none;
  }

  .chip-marquee .chip-track[aria-hidden="true"] {
    display: none;
  }
}

/* Wanderlust in motion — the light blue wash is a rounded box sitting
   inside the content container, not a band running the page's full width.
   The section keeps a little cream around it in place of its usual padding. */
#places {
  padding: var(--space-4) 0;
}

#places .container {
  background-color: var(--color-mist);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-4);
}

/* The rows sit closer under the intro than the standard section gap */
#places .section-intro {
  margin-bottom: calc(var(--space-5) - 16px);
}

/* Country chip — circular photo + tracked uppercase label */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-body-text);
  background: rgba(255, 215, 179, 0.3);
  border: none;
  border-radius: var(--radius-pill);
  padding: 5px 16px 5px 5px;
  transition: all var(--transition);
}

/* Flag sits inside the circle, centred over the country photo */
.chip-flag {
  font-size: 18px;
  line-height: 1;
  letter-spacing: normal;
}

/* Falls back to a peach circle wherever a country photo is missing */
.chip-thumb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background-color: var(--color-header-bg);
  background-size: cover;
  background-position: center;
}

.chip:hover {
  background: var(--color-header-bg);
}

/* ---------- CTA section ---------- */

.cta {
  background-color: var(--color-primary);
  text-align: center;
  padding: var(--space-6) 0;
}

.cta .accent-script {
  color: var(--color-header-bg);
}

.cta h2 {
  color: var(--color-white);
  margin: var(--space-2) 0 var(--space-3);
}

.cta p {
  color: rgba(255, 255, 255, 0.85);
  max-width: 560px;
  margin: 0 auto var(--space-4);
  font-family: var(--font-body);
  font-size: 1.125rem;
}

/* ---------- Footer ---------- */

.site-footer {
  background-color: var(--color-primary);
  color: rgba(255, 255, 255, 0.85);
  padding: var(--space-5) 0 var(--space-3);
}

.cta + .site-footer {
  border-top: 1px solid rgba(255, 255, 255, 0.15);
}

.footer-grid {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.footer-brand img {
  height: 56px;
  width: auto;
  margin-bottom: var(--space-2);
}

.footer-brand p {
  font-size: 0.9rem;
  max-width: 32ch;
}

/* The sign-up sits under the description, in the brand column — the first
   thing in the footer, and the only thing in it that asks for anything. Held
   to the description's width so the column stays one shape. */
.footer-signup {
  margin-top: var(--space-2);
  max-width: 32ch;
}

/* The label wraps the field rather than pointing at it by id — the same form
   appears in the footer and, on a guide that asks, in the middle of the story,
   and one id can't belong to two fields on the same page. */
.footer-signup .signup-heading {
  display: block;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.85);
  margin-bottom: 8px;
}

.footer-signup .signup-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* The field takes the slack and the button takes what it needs; below about
   260px of column they stack rather than squeezing the address to nothing. */
.footer-signup .signup-row input {
  flex: 1 1 12ch;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--color-primary);
  background: var(--color-white);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 4px;
  padding: 0.55rem 0.7rem;
}

.footer-signup .signup-row input::placeholder { color: rgba(21, 94, 100, 0.45); }

.footer-signup .signup-row input:focus {
  outline: 2px solid var(--color-header-bg);
  outline-offset: 1px;
}

/* The footer is the primary teal, so the primary button would disappear into
   it — the header's warm contrast is what stands out down here. */
.footer-signup .signup-row .btn {
  flex: 0 0 auto;
  background-color: var(--color-header-bg);
  color: var(--color-primary);
  border-color: var(--color-header-bg);
  padding: 0.55rem 1.1rem;
}

.footer-signup .signup-row .btn:hover {
  background-color: var(--color-white);
  border-color: var(--color-white);
  color: var(--color-primary);
}

.footer-col h4 {
  color: var(--color-header-bg);
  font-size: 1rem;
  margin-bottom: var(--space-2);
}

.footer-col ul {
  list-style: none;
}

.footer-col li {
  margin-bottom: 8px;
}

.footer-col a {
  color: rgba(255, 255, 255, 0.85);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.footer-col a:hover {
  color: var(--color-header-bg);
}

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  padding-top: var(--space-3);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.6);
}

/* ---------- Page hero (interior pages) ---------- */

.page-hero {
  background-color: var(--color-header-bg);
  background-image: url("../assets/patterns/hero_bg_pattern.png");
  background-size: 640px;
  background-repeat: repeat;
  text-align: center;
  padding: var(--space-5) 0;
}

.page-hero p {
  max-width: 560px;
  margin: var(--space-2) auto 0;
  font-family: var(--font-body);
  font-size: 1.125rem;
}

/* ---------- Portfolio tabs + grid ---------- */

/* Segmented control — one joined bar, square corners */
.tabs {
  display: flex;
  justify-content: center;
  width: fit-content;
  margin: var(--space-4) auto;
  border: 1px solid rgba(21, 94, 100, 0.25);
}

.tab-btn {
  display: inline-block;
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-primary);
  background: transparent;
  border: none;
  border-right: 1px solid rgba(21, 94, 100, 0.25);
  border-radius: 0;
  padding: 10px 28px;
  cursor: pointer;
  transition: all var(--transition);
}

.tab-btn:last-child {
  border-right: none;
}

.tab-btn:hover {
  background: rgba(21, 94, 100, 0.07);
}

.tab-btn.active,
.tab-btn.active:hover {
  background: var(--color-primary);
  color: var(--color-white);
}

/* Portfolio landing: category cards. Three or fewer sit in a single row,
   however many there are; past three they fall into a 2-up grid, so a fourth
   page starts a second row rather than orphaning itself. */
.portfolio-index {
  margin: var(--space-4) 0 var(--space-6);
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}

.portfolio-index:has(.work-card:nth-child(4)) {
  grid-auto-flow: row;
  grid-template-columns: repeat(2, 1fr);
}

/* On the landing page the card's name is what you're picking between, so it
   reads first: the title and its line sit at the top, and the scrim falls
   from the top edge to hold them. (Home's My Work cards keep the bottom
   overlay — there the photo is the pitch.) */
.portfolio-index .work-card-overlay {
  justify-content: flex-start;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0) 55%);
}

/* Full-page-width gallery — three columns */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  padding: 0 var(--space-2) var(--space-6);
}

.photo-grid figure {
  position: relative;
  border-radius: 0;
  overflow: hidden;
  aspect-ratio: 4 / 5;
}

.photo-grid img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Location label, bottom-left, with a scrim so it reads on any photo */
.photo-grid figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-4) var(--space-2) var(--space-2);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-white);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%);
  pointer-events: none;
}

/* Drone video card */
.video-card {
  position: relative;
  display: block;
  border-radius: 0;
  overflow: hidden;
  max-width: 900px;
  margin: 0 auto var(--space-6);
  box-shadow: var(--shadow-card);
}

.video-card img {
  width: 100%;
  display: block;
  transition: transform 0.5s ease;
}

.video-card:hover img {
  transform: scale(1.03);
}

.video-card .play-badge,
.video-tile .play-badge,
.video-play .play-badge {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* No wash over the poster — the photo shows as shot, and the circle
     and label carry the play cue on their own. */
  background: none;
}

.play-badge .play-circle {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: var(--color-header-bg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.play-badge .play-circle::after {
  content: "";
  display: block;
  margin-left: 6px;
  border-style: solid;
  border-width: 14px 0 14px 24px;
  border-color: transparent transparent transparent var(--color-primary);
}

.play-badge span {
  color: var(--color-white);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: 12px;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.4);
}

/* Vertical video tiles — the photo grid at 9:16. Four across where there's
   room: portrait tiles are narrow, so they seat one more to a row than the
   photos do. They step down to three, then to two-by-two on a phone —
   never one, because a lone column of reels would scroll forever. */
.photo-grid.video-grid {
  grid-template-columns: repeat(4, 1fr);
}

.photo-grid.video-grid figure {
  aspect-ratio: 9 / 16;
}

.video-tile {
  position: absolute;
  inset: 0;
  display: block;
}

.video-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease;
}

.video-tile:hover img {
  transform: scale(1.03);
}

/* Same badge as the drone reel, scaled to the narrower column */
.video-tile .play-circle,
.video-tile-inline .play-circle {
  width: 56px;
  height: 56px;
}

.video-tile .play-circle::after,
.video-tile-inline .play-circle::after {
  margin-left: 4px;
  border-width: 9px 0 9px 16px;
}

/* ---------- Uploaded videos, playing in place ----------
   A video we host ourselves needs no lightbox — it plays in the card or the
   column it already occupies. The badge over the poster is the linked reels'
   badge, so a mixed grid still reads as one grid. */

.video-card-video {
  width: 100%;
  display: block;
  background: #000;
}

.video-tile-inline .video-tile-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Contain rather than cover: a reel that isn't quite 9:16 should be
     letterboxed, not cropped, once someone is actually watching it. */
  object-fit: contain;
  background: #000;
}

.video-play {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* The caption sits over the bottom of the video, the way the photos wear
   theirs — which is also where the controls arrive the moment one plays, so
   it steps aside for them and comes back when the player is parked. Lifted
   above the play badge, which spans the whole tile and would otherwise sit
   over the text — the badge is scripted in after the caption, so it wins
   the paint order without the nudge. */
.video-tile-inline figcaption {
  transition: opacity var(--transition);
  z-index: 1;
}

.video-tile-inline.is-playing figcaption {
  opacity: 0;
}

/* A tile with no video yet — holds its column's place in the grid */
.video-tile-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-mist);
  border: 1px dashed rgba(21, 94, 100, 0.35);
}

.video-tile-placeholder {
  font-family: var(--font-body);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-primary);
  opacity: 0.6;
  padding: 0 var(--space-2);
  text-align: center;
}

/* ---------- Video lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4) var(--space-3);
  background: rgba(12, 44, 47, 0.92);
  animation: lightbox-in 0.2s ease;
}

@keyframes lightbox-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.lightbox-frame {
  width: min(1100px, 100%);
  aspect-ratio: 16 / 9;
}

/* A vertical reel stands upright instead — sized by height, not width */
.lightbox-frame.portrait {
  width: auto;
  height: min(85vh, 100%);
  max-width: 100%;
  aspect-ratio: 9 / 16;
}

.lightbox-frame iframe,
.lightbox-frame video {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* An uploaded reel in the lightbox letterboxes rather than crops — same rule
   as playing in its tile. */
.lightbox-frame video {
  object-fit: contain;
  background: #000;
}

.lightbox-close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 44px;
  height: 44px;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--color-white);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.4);
  cursor: pointer;
  transition: all var(--transition);
}

.lightbox-close:hover {
  background: var(--color-white);
  color: var(--color-primary);
}

/* ---------- About ---------- */

/* Bio photo — container width, pulled up so it overlaps the page hero */
.about-banner {
  position: relative;
  z-index: 1;
  /* Overlaps the hero band, leaving 2rem of air below the hero paragraph */
  margin-top: calc(var(--space-5) * -1 + 2rem);
}

.about-banner img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center 30%;
  border: 12px solid var(--color-white);
  box-shadow: var(--shadow-card);
}

.about-grid {
  padding: var(--space-5) 0 var(--space-6);
}

.about-copy {
  max-width: 760px;
  margin-inline: auto;
  text-align: center;
}

.about-copy h2 {
  margin: var(--space-1) 0 var(--space-3);
}

.about-copy p {
  margin-bottom: var(--space-2);
  font-size: 1.05rem;
}

/* ---------- Work with me / form ---------- */

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: var(--space-5);
  padding: var(--space-6) 0;
  align-items: start;
}

.contact-info h2 {
  margin: var(--space-1) 0 var(--space-3);
}

.contact-info p {
  margin-bottom: var(--space-3);
  font-size: 1.05rem;
}

.contact-list {
  list-style: none;
  margin-top: var(--space-3);
}

.contact-list li {
  margin-bottom: 12px;
  font-weight: 500;
}

.contact-list a {
  color: var(--color-primary);
  margin-left: 4px;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: 12px;
  font-weight: 400;
}

.contact-list a:hover {
  color: var(--color-accent);
}

.contact-form {
  background: var(--color-white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.form-group {
  margin-bottom: var(--space-2);
}

.form-group label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-primary);
  margin-bottom: 6px;
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-body-text);
  background: var(--color-background);
  border: 1.5px solid rgba(21, 94, 100, 0.2);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  transition: border-color var(--transition);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--color-primary);
}

.form-note {
  font-size: 0.8rem;
  color: rgba(51, 51, 51, 0.6);
  margin-top: var(--space-2);
}

/* What happened to the message just sent. Hidden until there's something to
   say, so the form isn't carrying an empty line around. */
.form-status {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-primary);
  margin-top: var(--space-2);
}

.form-status.is-error {
  color: #a83232;
}

/* The honeypot: out of the layout, out of the tab order, and hidden from
   assistive technology — visible only to something filling in every field. */
.contact-honeypot {
  display: none;
}

/* ---------- Responsive ---------- */

@media (max-width: 1024px) {
  .photo-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .photo-grid.video-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 900px) {
  .hero {
    grid-template-columns: 1fr;
    min-height: 0;
  }

  .hero-media {
    order: -1;
    height: 55vh;
  }

  .hero-content {
    padding-top: var(--space-5);
    padding-bottom: var(--space-5);
  }

  .work-grid {
    grid-template-columns: 1fr;
    max-width: 480px;
    margin-inline: auto;
  }

  /* A phone takes the collections two by two, inside the container's measure
     and level with each other — the stagger wants more width than a phone
     has before it reads as anything but crooked. Two even columns can't also
     follow each photo's shape, so here the tiles share one frame and the
     photos fill it. */
  #my-work .work-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    max-width: none;
    overflow-x: visible;
  }

  /* Two even columns inside the container's measure — nothing runs off the
     end of the line, so there is nothing for an arrow to walk. */
  .work-carousel {
    margin-inline: 0;
  }

  .work-nav {
    display: none;
  }

  #my-work .work-card,
  #my-work .work-card-empty {
    aspect-ratio: 3 / 5;
  }

  #my-work .work-card img {
    height: 100%;
    object-fit: cover;
  }

  #my-work .work-card,
  #my-work .work-card:nth-child(even) {
    margin-top: 0;
  }

  /* A phone stacks three or fewer — a row of them would be thumbnails — but
     keeps the 2-up grid past three, so a long portfolio stays scannable
     rather than becoming an endless column. */
  .portfolio-index {
    grid-auto-flow: row;
    grid-template-columns: 1fr;
  }

  .portfolio-index:has(.work-card:nth-child(4)) {
    grid-template-columns: repeat(2, 1fr);
    max-width: none;
    gap: var(--space-2);
  }

  .contact-grid {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }

  /* Mobile nav */
  .nav-toggle {
    display: block;
  }

  .nav-links {
    position: fixed;
    top: var(--nav-height);
    left: 0;
    right: 0;
    background: var(--color-header-bg);
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) 0;
    transform: translateY(-130%);
    transition: transform 0.3s ease;
    box-shadow: 0 12px 24px rgba(21, 94, 100, 0.15);
    z-index: -1;
  }

  .nav-links.open {
    transform: translateY(0);
  }

}

@media (max-width: 640px) {
  .photo-grid {
    grid-template-columns: 1fr;
  }

  .photo-grid.video-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .form-row {
    grid-template-columns: 1fr;
  }

  .footer-grid {
    flex-direction: column;
  }
}

/* ==========================================================================
   Blog — index cards and post pages (rendered by netlify/functions/blog.mjs)
   ========================================================================== */

.post-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.post-card {
  display: flex;
  flex-direction: column;
}

.post-card-media {
  display: block;
  overflow: hidden;
}

.post-card-media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.post-card-media:hover img {
  transform: scale(1.05);
}

/* A guide without a cover keeps the card's shape — the airplane mark stands
   in for the photo rather than leaving the row ragged. Faded well back: it's
   a watermark on an empty slot, not something to click. */
.post-card-media-empty {
  aspect-ratio: 4 / 3;
  background-color: var(--color-mist);
  position: relative;
}

/* The mark carries its own opacity on a layer of its own, so the mist field
   underneath stays at full strength. */
.post-card-media-empty::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../assets/logos/favicon.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 64px;
  opacity: 0.18;
  transition: opacity var(--transition);
}

.post-card-media-empty:hover::before {
  opacity: 0.26;
}

.post-card-body {
  padding: var(--space-2) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1;
}

/* The card title carries the whole card now, and reads in the body face
   rather than the header serif the article's own title wears. Near-black
   at rest, teal under the cursor — the same green the nav links answer. */
.post-card h3 {
  font-family: var(--font-body);
  font-size: 1.2rem;
}

.post-card h3 a {
  color: var(--color-ink);
}

.post-card h3 a:hover {
  color: var(--color-primary);
}

.post-empty {
  text-align: center;
  padding: var(--space-6) 0;
  font-family: var(--font-body-2);
  font-size: 1.125rem;
}

/* Home page: link out to the full index */
.guides-more {
  text-align: center;
  margin-top: var(--space-4);
}

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

/* The article sits on white; the patterned band is painted behind the header
   only, so the cover image can cross the boundary between the two. */
.post {
  background: var(--color-white);
  position: relative;
}

/* Same measure as .container, so the title and the cover line up with the nav
   logo and the footer columns rather than sitting inset from them. */
.post-shell {
  width: min(var(--container), 100% - 48px);
  margin-inline: auto;
}

/* The body is the exception: 48rem, a line length that can be read, under a
   header and a cover that run the full width. Declared after .post-shell —
   same specificity, so the order is what settles it. */
.post-read {
  width: min(48rem, 100% - 48px);
}

.post-top {
  position: relative;
  /* 96px less 24px — tightens the gap between the nav and the title. */
  padding-top: calc(var(--space-6) - var(--space-3));
  z-index: 0;
}

/* Fixed-height band rather than a background on the header, so the cover can
   overlap it. If a very long title pushes the cover past the band the layout
   still reads correctly — the pattern just sits behind the text. */
.post-top::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: clamp(400px, 46vw, 660px);
  background-color: var(--color-background);
  background-image: url("../assets/patterns/hero_bg_pattern.png");
  background-size: 640px;
  background-repeat: repeat;
  z-index: -1;
}

/* No cover: the fixed band would leave a tall empty patterned area under the
   meta line, so it collapses to hug the header instead. */
.post-top-bare {
  padding-bottom: var(--space-4);
}

.post-top-bare::before {
  height: 100%;
}

.post-eyebrow {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  /* Every uppercase label on the site carries this tracking; untracked caps
     at this size would read as a different system. */
  text-transform: uppercase;
  letter-spacing: 2px;
  margin-bottom: var(--space-2);
  text-align: center;
}

.post-eyebrow a {
  color: var(--color-primary);
}

.post-eyebrow a:hover {
  color: var(--color-accent);
}

/* Full measure: the title runs the width of .post-shell, so it lines up with
   the cover beneath it rather than sitting inset from it. */
.post-title {
  font-size: clamp(2.25rem, 4.4vw, 3.5rem);
  color: var(--color-ink);
  letter-spacing: -0.01em;
  text-align: center;
}

.post-lede {
  max-width: 560px;
  margin-top: var(--space-3);
  margin-inline: auto;
  font-family: var(--font-body);
  font-size: clamp(1.125rem, 1.6vw, 1.4rem);
  line-height: 1.5;
  color: var(--color-ink);
  text-align: center;
}

/* Date and length, centred under the lede. Sentence case and unspaced, unlike
   the tracked-out uppercase on the cards: this line sits directly beneath the
   title and reads as part of it rather than as a label. */
.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  font-family: var(--font-body);
  font-size: 1rem;
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-body-text);
}

.post-cover {
  margin-top: var(--space-4);
}

.post-cover img {
  width: 100%;
  aspect-ratio: 5 / 3;
  object-fit: cover;
}

/* Article body on the left, "Latest posts" rail on the right. */

/* The body fills the shell — 48rem is the article's measure, and there's no
   rail beside it to leave room for. */
.post-body {
  /* 24px under the cover. 96 was holding the writing a screen away from the
     photo that introduces it. */
  margin-top: var(--space-3);
  padding-bottom: var(--space-6);
  font-size: 1.0625rem;
}

.post-body p,
.post-body ul,
.post-body ol {
  margin-bottom: var(--space-3);
}

.post-body ul,
.post-body ol {
  padding-left: 1.25rem;
}

.post-body li {
  margin-bottom: var(--space-1);
}

/* Drop cap on the opening paragraph only — :first-of-type so a guide that
   leads with a figure or a heading still gets it on the first real paragraph.
   The size and padding are tuned against the body's 1.7 line-height so the
   cap's own cap-height starts level with the first line's and its baseline
   lands on the second line's — two lines, both flush beside it. */
.post-body > p:first-of-type::first-letter {
  float: left;
  font-family: var(--font-headers);
  font-size: 3.27em;
  line-height: 0.83;
  color: var(--color-accent);
  padding: 0.19em 0.07em 0 0;
}

/* The headings inside a guide are set in the body face, not the display one:
   they're signposts through a long read rather than titles, and the serif at
   section size competes with the article's own. */
.post-body h2,
.post-body h3,
.post-body h4 {
  font-family: var(--font-body);
  font-weight: 700;
}

.post-body h2 {
  font-size: clamp(1.6rem, 3vw, 2rem);
  color: var(--color-ink);
  margin: var(--space-5) 0 var(--space-2);
}

.post-body h3 {
  color: var(--color-ink);
  margin: var(--space-4) 0 var(--space-1);
}

/* The third step down. Nothing sizes h4 globally, so without this it lands at
   the browser default and reads as bold body text rather than a heading —
   1.15rem is clear of the 1.0625rem it sits among, and clear of h3 above. */
.post-body h4 {
  font-size: 1.15rem;
  color: var(--color-ink);
  margin: var(--space-3) 0 var(--space-1);
}

.post-body a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.post-body a:hover {
  color: var(--color-accent);
}

/* space-3 rather than space-4: the blank caret-line after an insert is
   stripped on save, so this margin is the whole gap around media. */
.post-body figure {
  margin: var(--space-3) 0;
}

.post-body img {
  width: 100%;
}

.post-body figcaption {
  margin-top: var(--space-1);
  font-size: 0.85rem;
  color: rgba(51, 51, 51, 0.7);
}

.post-body blockquote {
  border-left: 3px solid var(--color-accent);
  padding-left: var(--space-3);
  margin: var(--space-4) 0;
  font-family: var(--font-body-2);
  font-size: 1.25rem;
  color: var(--color-primary);
}

.post-body hr {
  border: none;
  border-top: 1px solid rgba(21, 94, 100, 0.2);
  margin: var(--space-5) 0;
}

/* Table of contents, built at render time from the guide's own headings.
   Boxed like the other blocks that step out of the writing. */
.post-contents {
  border: 1px solid rgba(21, 94, 100, 0.25);
  border-radius: 6px;
  background: var(--color-background);
  padding: var(--space-3);
  /* Flush to what's above it, and a single 24px step to what follows — the
     border is what separates it, so the gap only has to keep the next section
     off the box, not hold it at arm's length. */
  margin: 0 0 var(--space-3);
}

/* The section heading before the sections, so it's set like one: the body
   face at h3's size and weight, in the same near-black the headings use. */
/* Scoped to the body, or `.post-body p` outranks the class and sets the gap
   under it to 24px along with every other paragraph's. */
.post-body .post-contents-title {
  font-family: var(--font-body);
  font-size: 1.35rem;
  font-weight: 700;
  color: #000;
  margin-bottom: var(--space-2);
}

/* The block's own margins being zero isn't enough on its own — what sits
   either side of it brings margins too, and a heading's is 64px. Both are
   closed here, so the box meets the writing it interrupts. */
.post-body :has(+ .post-contents) {
  margin-bottom: 0;
}

/* Scoped to the body so it outranks the heading margins set there — a bare
   `.post-contents + *` loses to `.post-body h2` on specificity, which is how
   the 64px under the box survived being told to go. */
.post-body .post-contents + * {
  margin-top: 0;
}

.post-contents ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.post-contents li {
  margin-bottom: 6px;
}

.post-contents li:last-child { margin-bottom: 0; }

.post-contents a {
  color: var(--color-primary);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.post-contents a:hover {
  border-bottom-color: var(--color-accent);
}

/* Depth by indent: a subheading sits under the heading it belongs to. */
.post-contents .contents-h3 { padding-left: var(--space-2); }
.post-contents .contents-h4 { padding-left: var(--space-3); }

/* The header is sticky, so a heading jumped to would otherwise land behind
   it. Clears its height and a little air. */
.post-body h2[id],
.post-body h3[id],
.post-body h4[id] {
  scroll-margin-top: 96px;
}

/* Callout — an aside inside the story, boxed so it reads as a step out of it
   rather than another paragraph. The accent edge is the blockquote's, which is
   the other block that interrupts the reading. */
.post-body .callout {
  border: 1px solid rgba(21, 94, 100, 0.25);
  border-left: 3px solid var(--color-accent);
  border-radius: 6px;
  background: rgba(231, 240, 241, 0.55);
  padding: var(--space-3);
  margin: var(--space-4) 0;
}

.post-body .callout > :first-child { margin-top: 0; }
.post-body .callout > :last-child { margin-bottom: 0; }

/* The sign-up box, built at render time from an empty marker in the body. The
   footer's version sits on teal and dresses accordingly; this one sits in the
   middle of the writing, so it takes the boxed treatment instead. */
.post-subscribe {
  border: 1px solid rgba(21, 94, 100, 0.25);
  border-radius: 6px;
  background: var(--color-mist);
  padding: var(--space-3);
  margin: var(--space-4) 0;
}

.post-signup .signup-heading {
  display: block;
  font-family: var(--font-heading);
  font-size: 1.15rem;
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}

.post-signup .signup-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.post-signup input[type="email"] {
  flex: 1 1 16ch;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-primary);
  background: var(--color-white);
  border: 1px solid rgba(21, 94, 100, 0.3);
  border-radius: 4px;
  padding: 0.6rem 0.75rem;
}

.post-signup input[type="email"]::placeholder { color: rgba(21, 94, 100, 0.45); }

.post-signup .btn {
  flex: 0 0 auto;
  padding: 0.6rem 1.2rem;
}

@media (max-width: 640px) {
  .post-lede {
    max-width: none;
  }
}

@media (max-width: 1024px) {
  .post-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .post-grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Preview bar (unpublished guide, visible only to its author) ---------- */

.preview-bar {
  position: sticky;
  top: var(--nav-height);
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  background: var(--color-accent);
  color: var(--color-white);
  padding: 0.75rem var(--space-3);
  font-family: var(--font-body);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.preview-bar strong {
  font-weight: 600;
}

.preview-bar a {
  color: var(--color-white);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.preview-bar a:hover {
  color: var(--color-header-bg);
}

/* ==========================================================================
   In-content galleries and video embeds
   ========================================================================== */

.gallery {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-4) 0;
}

/* Two, four, and six stack two-up; three sits in a single row. */
.gallery-2,
.gallery-4,
.gallery-6 {
  grid-template-columns: repeat(2, 1fr);
}

.gallery-3 {
  grid-template-columns: repeat(3, 1fr);
}

.gallery img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* Three across reads better in landscape. */
.gallery-3 img {
  aspect-ratio: 3 / 4;
}

/* Video sits in its own 16:9 box so the iframe can't dictate page height. */
.video-embed {
  margin: var(--space-4) 0;
}

.video-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--color-primary);
}

.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

@media (max-width: 640px) {
  /* Three-up is too tight on a phone; drop everything to two columns. */
  .gallery-3 {
    grid-template-columns: repeat(2, 1fr);
  }

  .gallery-3 img {
    aspect-ratio: 4 / 5;
  }
}

/* ==========================================================================
   Image loading skeletons
   ========================================================================== */

/* Photos are the page here, so a pending one holds its space as a tinted
   block instead of a hole. js/main.js sets data-loading until the image
   settles, and data-inview while it is near the viewport. */
img[data-loading] {
  background-color: var(--color-mist);
  /* Alt text would otherwise sit on top of the skeleton */
  color: transparent;
}

/* The sweep only runs on what the visitor can actually see — a portfolio
   page holds sixty lazy photos and none of them need to animate offscreen. */
img[data-loading][data-inview] {
  background-image: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0) 30%,
    rgba(255, 255, 255, 0.7) 50%,
    rgba(255, 255, 255, 0) 70%
  );
  background-repeat: no-repeat;
  background-size: 200% 100%;
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}

/* Images the layout can't size until they arrive — article body, video card —
   need a box to hold. The real ratio takes over the moment they load. */
.post-body img[data-loading],
.video-card img[data-loading] {
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

img[data-loaded] {
  animation: skeleton-fade 0.4s ease both;
}

@keyframes skeleton-sweep {
  from { background-position: 150% 0; }
  to { background-position: -50% 0; }
}

@keyframes skeleton-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  img[data-loading][data-inview],
  img[data-loaded] {
    animation: none;
  }
}

/* ==========================================================================
   Country page
   ========================================================================== */

/* The country page's band runs deeper than the other page heroes, and the
   guide grid rides up over its bottom edge — the same overlap the about
   page's photo uses, so the two pages feel related. */
.country-hero {
  padding-bottom: var(--space-6);
}

.country-grid {
  position: relative;
  z-index: 1;
  margin-top: calc(var(--space-4) * -1);
}

/* The country's flag above its name on the country page. A white disc so it
   reads as a badge against the hero's patterned band, sized well clear of the
   emoji so no flag's aspect ratio crops against the curve. */
.country-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 88px;
  margin: 0 auto var(--space-2);
  border-radius: 50%;
  background: var(--color-white);
  box-shadow: var(--shadow-card);
  font-size: 2.75rem;
  line-height: 1;
}



/* ---------- Shop ----------

   The product cards reuse the portfolio's card, with a price added. A 3-up
   grid rather than the portfolio's fill-the-row trick: a shop grows, and a
   row of eight would be thumbnails. */

.product-index {
  margin: var(--space-4) 0 var(--space-6);
}

/* Sits above the title in the overlay, so the price is read before the
   name — it's the thing being decided. */
.product-price {
  order: -1;
  align-self: flex-start;
  font-family: var(--font-body);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-white);
  background: rgba(0, 0, 0, 0.35);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  margin-bottom: 8px;
}

/* One product: the cover beside what it is, rather than above it — the two
   are read together, and a full-width cover would push the button off. */
.product {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  align-items: start;
  margin: var(--space-4) 0 var(--space-6);
}

.product-cover img {
  width: 100%;
  box-shadow: var(--shadow-card);
}

.product-detail h1 {
  margin-bottom: var(--space-1);
}

.product-blurb {
  font-family: var(--font-body);
  font-size: 1.25rem;
  margin-bottom: var(--space-2);
}

/* "PDF · 2.4 MB" — quiet, but there before the button is pressed. */
.product-file {
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(51, 51, 51, 0.65);
  margin-bottom: var(--space-2);
}

.product-price-large {
  font-family: var(--font-body-2);
  font-size: 2rem;
  line-height: 1.1;
  color: var(--color-accent);
  margin-bottom: var(--space-1);
}

.product-note {
  font-size: 0.95rem;
  color: rgba(51, 51, 51, 0.7);
  margin-bottom: var(--space-2);
}

.product-detail p + p {
  margin-top: var(--space-2);
}

.product-detail .btn {
  margin: var(--space-2) 0 var(--space-3);
}

.product-back {
  margin-top: var(--space-4);
  font-size: 0.95rem;
}

@media (max-width: 900px) {
  .product {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 700px) {
  .product-index {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-2);
  }
}
