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

.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

:root {
    --bg: #1a1a1a;
    --bg-card: #252525;
    --accent: #c4a882;
    --text: #f0ece4;
    --text-muted: #a09888;
    --radius: 6px;
    /* Hairline used to rule off one section from the next. */
    --border-soft: #35322c;
    /* Scrollbar thumb. Muted, but held at ~3:1 against the darker
       track (bg-card) so it stays a findable control rather than a
       hint — the same bar people drag, not just a position readout. */
    --scroll-thumb: #707070;
    --scroll-thumb-hover: #8c8c8c;
    /* The page filter's second row: its height, and its distance from the top
       of the viewport (header padding + the input's own height). The TOC pill
       positions itself against these. */
    --filter-row2: 2rem;
    /* The filter column's width. The header reserves this much on its right
       so the out-of-flow filter never lands on the site title. */
    --filter-w: min(16rem, 60vw);
    /* How far the fixed right-hand controls (filter box, TOC pill, TOC
       panel) stand off the viewport edge: a touch clear of the scrollbar,
       no more. Declared once so the three stay aligned. */
    --filter-right: 0.75rem;
    --filter-row2-top: 4rem;
    /* Breathing room between the input and that row, so the two rounded
       pills never touch. */
    --filter-row2-gap: 0.4rem;
}

html {
    /* Reserve the scrollbar's space always: filtering a page down to a few
       cards otherwise removes the scrollbar and shifts the whole layout. */
    scrollbar-gutter: stable;
    /* Recoloured to sit quietly against the dark page, but deliberately NOT
       narrowed (no `scrollbar-width: thin`): the page scrollbar is a drag
       target, and on platforms that draw a full-width one, halving it costs
       precision for anyone who grabs it with a mouse. The TOC panel is a
       small overlay and can afford thin; the page cannot. */
    scrollbar-color: var(--scroll-thumb) var(--bg);
}
html::-webkit-scrollbar-track { background: var(--bg); }
html::-webkit-scrollbar-thumb {
    background: var(--scroll-thumb); border-radius: 6px;
    border: 3px solid var(--bg);
}
html::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }

body {
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, -apple-system, sans-serif;
    line-height: 1.6;
    min-height: 100vh;
}

header {
    /* Scrolls away with the page. It was `position: sticky; top: 0` for a
       while so the filter and its match count stayed reachable, but pinning
       the whole band -- site title, padding and all -- to keep two small
       controls on screen cost a large slice of every viewport. The controls
       are fixed on their own instead (see .page-filter), which is all that
       was ever wanted; the band itself is free to leave. */
    padding: 2rem 2rem 1rem;
    /* Reserve the fixed filter's column so a long site title cannot run
       underneath it -- the filter is out of flow, so the flex row can no
       longer make that room by itself. */
    padding-right: calc(var(--filter-w) + var(--filter-right) + 1rem);
    display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    background: var(--bg);
}
/* Narrow screens reserve the filter's space VERTICALLY instead. The
   horizontal reservation is most of a phone's width, which crushed the site
   title into a column a few characters wide -- a real title wraps to three
   or four lines there and still overflows it. Below this width the title
   gets the full line and simply starts under the fixed controls; the cost is
   some blank space at the very top of the page, which scrolls away. */
@media (max-width: 600px) {
    header { padding-right: 2rem; padding-top: 7rem; }
}
/* Breathing room when an anchor (a TOC entry, a dive-jump arrow) scrolls a
   section to the top. Small, now that nothing spans the top of the viewport:
   the fixed controls sit in the right-hand corner and a section heading runs
   along the left. DECLARED ONLY HERE -- .dive-section carried a second,
   later `scroll-margin-top` that silently won on source order, so the
   clearance this rule documented was never the one that shipped. */
.dive-section, [data-toc-target] { scroll-margin-top: 1.5rem; }
/* Two rows in a fixed-width column: the input on top, and under it a row
   holding the match count on the left and (positioned to match, see
   .dive-toc-pill) the table-of-contents pill on the right. The second row is
   reserved whether or not the count is showing, and the column's width never
   depends on it, so neither the input nor the pill moves as the count appears
   or gains a digit. */
.page-filter {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: auto var(--filter-row2);
    row-gap: var(--filter-row2-gap);
    align-items: center;
    width: var(--filter-w);
    /* Fixed, like the TOC pill that positions itself against this element's
       second row: these controls are the only part of the header worth
       keeping on screen, so they are pinned individually rather than by
       making the whole band sticky. Below .dive-toc-panel's z-index so an
       open TOC overlays them. */
    position: fixed; top: 2rem; right: var(--filter-right); z-index: 40;
}
#page-filter {
    background: var(--bg-card); color: var(--text); border: 1px solid #3a3a3a;
    border-radius: 20px; padding: 0.4rem 0.9rem; font-size: 0.9rem; width: 100%;
    font-family: system-ui, -apple-system, sans-serif;
}
#page-filter:focus { outline: none; border-color: var(--accent); }
.filter-count {
    font-size: 0.8rem; color: var(--text-muted); white-space: nowrap;
    font-variant-numeric: tabular-nums; justify-self: start; padding-left: 0.9rem;
}
.filter-empty { color: var(--text-muted); font-style: italic; margin: 1rem 0; }
/* !important because the UA's own [hidden] rule loses to any author rule that
   sets `display`. tests/test_style_contracts.py pins this in every stylesheet
   we serve; do not drop it as redundant. */
[hidden] { display: none !important; }

.site-title {
    font-family: Georgia, 'Times New Roman', serif;
    /* Bigger than a nav-bar brand would normally allow: the header scrolls
       away with the page now, so the size costs the reader nothing past the
       first screen. Still under the page h1's 2rem, so the hierarchy on that
       first screen is unchanged. */
    font-size: 1.75rem;
    color: var(--accent);
    text-decoration: none;
}

main {
    max-width: 1400px;
    margin: 0 auto;
    padding: 1rem 2rem 4rem;
}

footer {
    text-align: center;
    padding: 2rem;
    color: var(--text-muted);
    font-size: 0.85rem;
}

h1, h2 {
    font-family: Georgia, 'Times New Roman', serif;
    font-weight: 400;
    color: var(--accent);
}

h1 { font-size: 2rem; margin-bottom: 1.5rem; }
h2 { font-size: 1.3rem; margin-bottom: 1rem; }

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

/* Breadcrumbs */
.breadcrumbs {
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-bottom: 2rem;
}
.breadcrumbs a { color: var(--text-muted); }
.breadcrumbs a:hover { color: var(--accent); }

/* Card grid (homepage) */
.card-grid { margin-bottom: 3rem; }
/* The homepage's top-level sections (Trips / Locations / Photographers /
   Species / Timeline) must outrank the group headings nested INSIDE them
   (.linked-section h3: a trip, a location). They used to be the base h2 --
   1.3rem serif in the gold accent -- which reads smaller and quieter than
   the 1.25rem white sans h3 under it, so the hierarchy was inverted: a
   reader saw "Bonaire · March 2024" and scrolled past "Locations". Bigger,
   in the text colour (accent only on hover), with a rule between sections. */
.home-section ~ .home-section {
    border-top: 1px solid var(--border-soft);
    padding-top: 2.5rem;
}
.home-section > h2 {
    font-size: 1.9rem;
    color: var(--text);
    margin-bottom: 1.5rem;
}
.home-section > h2 a { color: var(--text); }
.home-section > h2 a:hover { color: var(--accent); }

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1.2rem;
}

.card {
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border-radius: var(--radius);
    overflow: hidden;
    text-decoration: none;
    transition: transform 0.15s;
}
.card:hover { transform: translateY(-3px); text-decoration: none; }

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

.card-label {
    padding: 0.6rem 0.8rem 0.2rem;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 1.1rem;
    color: var(--text);
}

.card-count {
    padding: 0 0.8rem 0.6rem;
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* Photo grid (dive/timeline/photographer pages): justified rows — every card
   in a row shares one height, widths follow each photo's aspect ratio, so
   mixed portrait/landscape rows pack with no gaps and no cropping. Each card
   grows and flex-bases proportionally to its --ar, which keeps final widths
   proportional to aspect and therefore row heights uniform. */
.photo-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.photo-grid::after {
    /* Greedy invisible filler: soaks up the last row's leftover space so a
       short final row keeps the target height instead of stretching. */
    content: "";
    flex-grow: 1000000;
    min-width: 0;
}

.photo-card {
    --ar: 3 / 2;
    display: block;
    aspect-ratio: var(--ar);
    flex-grow: calc(var(--ar) * 100);
    flex-basis: calc(var(--ar) * 13rem);
    min-width: 0;
    border-radius: var(--radius);
    overflow: hidden;
    position: relative;
    background: var(--bg-card);
}

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

.photo-card .play-badge {
    position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 3rem; height: 3rem; border-radius: 50%;
    background: rgba(0,0,0,0.55); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.1rem; padding-left: 0.2rem; pointer-events: none;
}
.photo-card .duration-pill {
    position: absolute; right: 0.4rem; bottom: 0.4rem;
    background: rgba(0,0,0,0.7); color: #fff;
    font-size: 0.75rem; padding: 0.1rem 0.35rem; border-radius: 0.2rem;
    pointer-events: none;
}
.photo-card .unconfirmed-badge {
    position: absolute; top: 0.4rem; right: 0.4rem;
    background: rgba(0,0,0,0.7); color: #fff;
    font-size: 0.7rem; padding: 0.1rem 0.35rem; border-radius: 0.2rem;
    pointer-events: none;
}

/* A stack tile: one cover photo with the rest of its burst peeking out from
   behind. The card keeps its exact grid footprint — the image is inset and the
   sheets fill the space that frees up — so the flex layout is untouched. */
.photo-card.is-stack {
    padding: 0 8px 8px 0;
    background: none;
}
.photo-card.is-stack::before,
.photo-card.is-stack::after {
    content: "";
    position: absolute;
    border-radius: var(--radius);
    background: var(--bg-card);
    border: 1px solid rgba(240, 236, 228, 0.16);
    transition: transform 0.15s ease;
}
.photo-card.is-stack::before { inset: 8px 0 0 8px; opacity: 0.6; }
.photo-card.is-stack::after { inset: 4px; }
.photo-card.is-stack:hover::after { transform: translate(1.5px, 1.5px); }
.photo-card.is-stack:hover::before { transform: translate(3px, 3px); }
.photo-card.is-stack img {
    position: relative;
    z-index: 1;
    border-radius: var(--radius);
}
.photo-card .stack-pill {
    position: absolute; left: 0.4rem; bottom: 0.4rem;
    background: rgba(0,0,0,0.7); color: #fff;
    font-size: 0.75rem; padding: 0.1rem 0.35rem; border-radius: 0.2rem;
    pointer-events: none; z-index: 2;
}
.photo-card.is-stack .play-badge,
.photo-card.is-stack .duration-pill,
.photo-card.is-stack .unconfirmed-badge { z-index: 2; }

.photo-card .hover-preview {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    pointer-events: none;
}

@media (max-width: 900px) { .photo-card { flex-basis: calc(var(--ar) * 10rem); } }
@media (max-width: 500px) { .photo-card { flex-basis: calc(var(--ar) * 8rem); } }

/* Photo detail */
.photo-detail {
    max-width: 1000px;
    margin: 0 auto;
}

.photo-main img {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: 80vh;
    margin-inline: auto;
    object-fit: contain;
    border-radius: var(--radius);
    cursor: pointer;
}

video.main-media {
    max-width: 100%;
    max-height: 80vh;
    display: block;
    margin: 0 auto;
    border-radius: var(--radius);
}

.photo-meta {
    margin-top: 1.5rem;
}

.photo-meta h1 {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
}

dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.3rem 1.5rem;
    margin-bottom: 1rem;
}

dt { color: var(--text-muted); font-size: 0.85rem; }
dd { color: var(--text-muted); }

.tags { display: flex; gap: 0.5rem; flex-wrap: wrap; }

.tag {
    font-size: 0.8rem;
    padding: 0.2rem 0.6rem;
    background: var(--bg-card);
    border-radius: var(--radius);
    color: var(--text-muted);
}

.photo-report {
    color: var(--text-muted);
    opacity: 0.65;
}
.photo-report:hover { opacity: 1; }

.chip-unconfirmed { display: inline-block; font-size: 0.7em; padding: 0 0.4em; border: 1px solid var(--text-muted); border-radius: 0.6em; color: var(--text-muted); vertical-align: middle; }
.scientific-name { margin-top: -0.6em; color: var(--text-muted); }

/* Lightbox */
.lightbox {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.95);
    z-index: 1000;
    overflow: hidden;
    cursor: none;
}
.lightbox.active { display: block; }

.lightbox img {
    display: block;
    transition: opacity 0.2s;
}

.lightbox.zoomed {
    overflow: auto;
    scrollbar-width: none;
}
.lightbox.zoomed::-webkit-scrollbar { display: none; }
.lightbox.zoomed img {
    max-width: none;
    max-height: none;
}

.lightbox-spinner {
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    width: 48px;
    height: 48px;
    margin: -24px 0 0 -24px;
    border: 3px solid rgba(255,255,255,0.15);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: lb-spin 0.8s linear infinite;
    z-index: 1002;
    pointer-events: none;
}
.lightbox.loading .lightbox-spinner { display: block; }
@keyframes lb-spin { to { transform: rotate(360deg); } }

.lightbox-close {
    position: fixed;
    top: 1rem;
    right: 1.5rem;
    background: none;
    border: none;
    color: var(--text);
    font-size: 2rem;
    cursor: pointer;
    z-index: 1001;
}

/* Photo prev/next navigation */
.photo-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1rem;
    padding: 0.5rem 0;
    font-size: 0.9rem;
}
.photo-nav a {
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.photo-nav a:hover { color: var(--accent); }
.photo-nav .nav-arrow { font-size: 1.2rem; }
.photo-nav .nav-spacer { flex: 1; }

/* Cluster filmstrip: a vertical rail right of the image; the boxed thumb is
   the page you are on. Below 600px it wraps to a horizontal strip. */
.photo-detail.has-cluster { display: grid; grid-template-columns: minmax(0, 1fr) 92px; gap: 1rem; }
.photo-detail.has-cluster .photo-meta { grid-column: 1 / -1; }
.cluster-strip { display: flex; flex-direction: column; gap: 0.5rem; max-height: 80vh; overflow-y: auto; }
.cluster-thumb { display: block; line-height: 0; border: 2px solid transparent; border-radius: 4px; }
.cluster-thumb img { width: 100%; border-radius: 2px; }
.cluster-thumb.current { border-color: var(--accent); }
@media (max-width: 600px) {
  .photo-detail.has-cluster { display: block; }
  .cluster-strip { flex-direction: row; overflow-x: auto; overflow-y: hidden; max-height: none; margin-top: 0.5rem; }
  .cluster-thumb { flex: 0 0 72px; }
}

/* Trip reports */
.trip-meta {
    color: var(--text-muted);
    font-size: 0.95rem;
    margin: -0.75rem 0 0.5rem;
}
.trip-meta .trip-count { margin-left: 0.5rem; opacity: 0.7; }
.trip-meta .trip-count::before { content: '· '; }
.trip-note {
    color: var(--text);
    margin-bottom: 0.5rem;
}
.trip-locations {
    color: var(--text-muted);
    font-size: 0.85rem;
    margin-bottom: 2rem;
}
.trip-day { margin-bottom: 3rem; }
.trip-day ~ .trip-day {
    border-top: 1px solid var(--border-soft);
    padding-top: 3rem;
}
.trip-day h2 {
    font-weight: 400;
    color: var(--text);
    margin-bottom: 1rem;
    font-size: 1.6rem;
}
.trip-day .section-count {
    font-size: 0.85rem;
    font-family: system-ui, -apple-system, sans-serif;
    margin-left: 0.5rem;
    opacity: 0.6;
}
/* A dive boundary has to out-shout the grid it interrupts. The header used
   to be QUIETER than the photos under it (weight 300, muted grey, 1.2rem)
   sitting in a 1rem gap identical to .photo-grid's own gutter, so a dive
   transition looked like nothing more than a wide row gap and readers
   scrolled straight past it. Three cues now mark it: a hairline rule, a
   bigger/darker heading, and asymmetric space — the padding above the
   header is several times the margin below it, so the header binds to the
   grid it labels instead of floating between two of them. */
.dive-section { margin-bottom: 3rem; }
/* General sibling, not :first-of-type: a page's first .dive-section is
   neither the first child of its container (a page heading precedes it) nor
   reliably the first <section> (.linked-section is one too), so the rule is
   hung on "has a dive section before it" instead. */
.dive-section ~ .dive-section {
    border-top: 1px solid var(--border-soft);
    padding-top: 3rem;
}
.dive-section h2 {
    font-weight: 400;
    color: var(--text);
    margin-bottom: 1rem;
    font-size: 1.6rem;
}
.dive-section h2 a { color: var(--text); }
.dive-section h2 a:hover { color: var(--accent); }
.dive-section .section-count,
.linked-section .section-count {
    font-size: 0.85rem;
    font-family: system-ui, -apple-system, sans-serif;
    margin-left: 0.5rem;
    opacity: 0.6;
}
.linked-section { margin-bottom: 3rem; }
/* Dimmer than the .home-section h2 above it: once the section heading went
   white, a white h3 under it made the homepage read as a wall of white. */
.linked-section h3 {
    font-weight: 400;
    color: var(--text-muted);
    margin-bottom: 1rem;
    font-size: 1.25rem;
}
.linked-section h3 a { color: var(--text-muted); }
.linked-section h3 a:hover { color: var(--accent); }
[data-toc-target] { scroll-margin-top: 4rem; }
.dive-jump { margin-left: 0.6rem; display: inline-flex; gap: 0.2rem; vertical-align: middle; }
.dive-jump-btn {
    width: 1.4rem; height: 1.4rem; display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid #3a3a3a; border-radius: 4px; color: var(--text-muted);
    text-decoration: none; font-size: 0.9rem; line-height: 1;
    font-family: system-ui, -apple-system, sans-serif;
}
.dive-jump-btn:hover { color: var(--accent); border-color: var(--accent); }
.dive-jump-btn.disabled { opacity: 0.3; pointer-events: none; }

.dive-toc-pill {
    /* Sits in the page filter's second row (see .page-filter): fixed so it
       survives scrolling, but placed on the header's right padding edge and
       at the filter's second row so the two read as one control. */
    position: fixed; top: calc(var(--filter-row2-top) + var(--filter-row2-gap));
    right: var(--filter-right); z-index: 50;
    background: var(--bg-card); border: 1px solid #3a3a3a; border-radius: 20px;
    padding: 0.4rem 0.85rem; font-size: 0.85rem; color: var(--accent); cursor: pointer;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
    font-family: system-ui, -apple-system, sans-serif;
}
.dive-toc-pill:hover { border-color: var(--accent); }
/* The pill names the dive currently on screen, so its width follows the
   dive site's name — capped, since a long one would otherwise run the pill
   across the header toward the filter box. */
#dive-toc-label {
    display: inline-block; max-width: 14rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    vertical-align: bottom;
}
.dive-toc-panel {
    position: fixed; top: calc(var(--filter-row2-top) + var(--filter-row2-gap) + 2.4rem);
    right: var(--filter-right); z-index: 50;
    width: 230px; max-height: 70vh; overflow-y: auto;
    background: var(--bg-card); border: 1px solid #3a3a3a; border-radius: 8px;
    padding: 0.35rem; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
    /* A default overlay scrollbar sits right beside the page's own and reads
       as a doubled bar, so the panel's is drawn as a thin, low-contrast
       thumb on a transparent track instead. */
    scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent;
}
.dive-toc-panel::-webkit-scrollbar { width: 6px; }
.dive-toc-panel::-webkit-scrollbar-track { background: transparent; }
.dive-toc-panel::-webkit-scrollbar-thumb {
    background: var(--scroll-thumb); border-radius: 3px;
}
.dive-toc-panel::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }
.dive-toc-panel a {
    display: flex; justify-content: space-between; gap: 0.5rem;
    font-size: 0.8rem; color: var(--text-muted); text-decoration: none;
    padding: 0.25rem 0.45rem; border-radius: 4px; border-left: 2px solid transparent;
}
.dive-toc-panel a:hover { color: var(--text); }
.dive-toc-panel a.on { color: var(--accent); background: #1e1c18; border-left-color: var(--accent); font-weight: 600; }
.dive-toc-panel a .toc-date { opacity: 0.6; font-variant-numeric: tabular-nums; white-space: nowrap; }
.dive-toc-year {
    font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-muted); opacity: 0.75; padding: 0.5rem 0.45rem 0.2rem;
    border-top: 1px solid #333; margin-top: 0.2rem;
}
.dive-toc-year:first-child { border-top: none; margin-top: 0; }
.dive-toc-panel a.toc-nested { padding-left: 1.1rem; font-size: 0.75rem; }

.variant-strip { display: flex; gap: .85rem; margin: .75rem 0 1rem; flex-wrap: wrap; }
.variant-thumb { padding: 0; border: 0; background: none; cursor: pointer; line-height: 1.2; display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.variant-frame { position: relative; display: block; border-radius: 6px; border: 2px solid transparent; padding: 2px; transition: border-color .12s ease, transform .12s ease; }
.variant-thumb img { width: 72px; height: 72px; object-fit: cover; display: block; border-radius: 4px; opacity: .55; filter: saturate(.7); transition: opacity .12s ease, filter .12s ease; }
.variant-thumb:hover img { opacity: 1; filter: none; }
.variant-name { font-size: .7rem; color: var(--text-muted); text-align: center; max-width: 82px; }
.variant-thumb.selected .variant-frame { border-color: var(--accent); transform: translateY(-1px); }
.variant-thumb.selected img { opacity: 1; filter: none; }
.variant-thumb.selected .variant-name { color: var(--accent); font-weight: 600; }
