/* ===========================================================================
   The in-story photo gallery, and the lightbox it opens.
   ===========================================================================

   WHY EVERY PHOTO IS AN <a> AROUND AN <img>.

   The story body passes through sanitizeStoryHtmlBlock(), which allows no
   data-* attributes and strips <button> outright. So the grid file is the
   <img> and the full-size file is the anchor's href, and the lightbox reads
   the URL off that href.

   Same reasoning as document-viewer.css, and the same benefit: with JavaScript
   off, before it loads, or if it throws, clicking a photograph still opens the
   photograph. A gallery whose pictures do nothing is worse than no gallery.

   TWO FILES PER PHOTOGRAPH. The grid loads a 700px copy; the 1600px one is
   fetched only when a reader opens a picture. Twelve full-size photographs is
   about three megabytes, and a reader on a phone should not pay that to find
   out whether they wanted to look.

   Scoped under .article-container because custom.css sets rules like
   `.article-container h3` at specificity (0,1,1), which silently outranks a
   bare class at (0,1,0). Two classes deep beats it.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   THE GALLERY IN THE STORY

   A LEAD PHOTOGRAPH, THEN A STRIP OF THUMBNAILS.

   This was a uniform grid of equal cells, and a uniform grid reads as the whole
   of what there is -- a reader looks at six tidy squares and has no reason to
   think anything else is behind them. One big photograph above a row of small
   ones reads as a stack: this is the picture, and there are more of them.

   Which is the truth. The grid is a doorway; the lightbox is the gallery.

   All of it is done here, in CSS, on markup that has not changed. Galleries
   already published pick this up without being re-edited, and a gallery filed
   next year still works if this stylesheet is replaced with another.
   ------------------------------------------------------------------------- */

.article-container .osc-gallery {
    margin: 38px 0;
    padding: 0;
    border-top: 3px solid #1f4e79;
    border-bottom: 1px solid #d8d2c4;
}

.article-container .osc-gallery-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 14px;
    margin: 0;
    padding: 14px 0 16px;
}

.article-container .osc-gallery-title {
    margin: 0;
    padding: 0;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 1.32rem;
    font-weight: 700;
    line-height: 1.25;
    color: #1a1a1a;
    letter-spacing: -.01em;
}

/* The count is written by the CMS, not counted in JavaScript, so it is right in
   a feed reader and in a printout as well as on the page. */
.article-container .osc-gallery-count {
    flex: 0 0 auto;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: #6b6257;
}

/* FLEX, NOT GRID, AND FOR ONE REASON: THE STRIP HAS TO FILL THE LINE.
   A grid of fixed tracks left five thumbnails sitting in the first half of the
   row with the rest of it empty, which reads as a layout that has gone wrong
   rather than as a strip. Flex lets them share whatever is there -- but with a
   ceiling, because a two-photograph gallery whose single "thumbnail" stretched
   to the full measure would be two lead photographs and no strip at all. */
.article-container .osc-gallery-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0 0 20px;
    padding: 0;
    list-style: none;
}

.article-container .osc-gallery-item {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.article-container .osc-gallery-item:first-child {
    flex: 0 0 100%;
    margin-bottom: 4px;
}

/* Share the line, between 56px and 150px each, and wrap when there are more
   than fit. Basis 0 so the sharing is even rather than weighted by content. */
.article-container .osc-gallery-item:not(:first-child) {
    flex: 1 1 0;
    min-width: 56px;
    max-width: 150px;
}

.article-container .osc-gallery-link {
    position: relative;
    display: block;
    overflow: hidden;
    background: #efece5;
    border: 1px solid #ddd6c8;
    border-radius: 2px;
    line-height: 0;
    text-decoration: none;
    transition: border-color .18s ease, box-shadow .18s ease;
}

.article-container .osc-gallery-link:hover,
.article-container .osc-gallery-link:focus-visible {
    border-color: #1f4e79;
    box-shadow: 0 6px 18px rgba(31, 46, 66, .16);
}

.article-container .osc-gallery-link:focus-visible {
    outline: 2px solid #1f4e79;
    outline-offset: 2px;
}

/* THE LEAD. Cropped to a fixed shape on purpose: it is a doorway, not the
   photograph, and a portrait hero at natural height would push the rest of the
   story off the screen. Nothing is lost -- opening it shows all of it. */
.article-container .osc-gallery-item:first-child .osc-gallery-img {
    aspect-ratio: 3 / 2;
    /* On a short screen 3:2 across the full measure is the whole window, and
       the reader loses the story around it. Cropping a little further is the
       cheaper loss -- the picture is intact behind the tap. */
    max-height: 58vh;
}

/* THE THUMBNAILS. Square, because a strip of mixed shapes is a ragged strip,
   and at this size the shape of the crop carries no information anyway. */
.article-container .osc-gallery-item:not(:first-child) .osc-gallery-img {
    aspect-ratio: 1 / 1;
}

.article-container .osc-gallery-img {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
    object-position: center;
    margin: 0;
    transition: transform .3s ease;
}

.article-container .osc-gallery-link:hover .osc-gallery-img {
    transform: scale(1.03);
}

/* THE INVITATION, AND IT IS INSIDE THE ANCHOR ON PURPOSE.
   A prompt that says "tap" has to be part of the thing you tap, or it is an
   instruction pointing somewhere else. Because it lives on the link, the whole
   bar opens the gallery, and it still works with the script blocked -- the
   anchor goes to the photograph either way.

   The text comes from a custom property the script sets, so it can carry the
   count and the right verb for the pointer. The fallback in the var() is what a
   reader sees when the script never arrives, which is why it says something
   complete rather than a placeholder. */
.article-container .osc-gallery-item:first-child .osc-gallery-link::after {
    content: var(--osc-gallery-label, "See all photos");
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 34px 14px 12px 44px;
    background: linear-gradient(180deg, rgba(13, 16, 22, 0) 0%, rgba(13, 16, 22, .82) 62%, rgba(13, 16, 22, .92) 100%);
    color: #ffffff;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .8rem;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: .05em;
    text-transform: uppercase;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .5);
    pointer-events: none;
}

/* The expand glyph, drawn rather than shipped: the sanitizer allows no <svg> in
   a story body, and a decorative <img> is one more request and one more thing
   to 404. Sits in the padding the label leaves for it. */
.article-container .osc-gallery-item:first-child .osc-gallery-link::before {
    content: "";
    position: absolute;
    left: 14px;
    bottom: 13px;
    z-index: 1;
    width: 18px;
    height: 18px;
    border: 2px solid rgba(255, 255, 255, .95);
    border-radius: 2px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .5);
    pointer-events: none;
}

/* A thumbnail carries no label -- the lead has said it once, and eleven copies
   of the same instruction is not eleven times as clear. */
.article-container .osc-gallery-item:not(:first-child) .osc-gallery-link::after,
.article-container .osc-gallery-item:not(:first-child) .osc-gallery-link::before {
    content: none;
}

/* Only the lead's caption is printed. A caption under a 74px square is
   unreadable, and every caption is on its own photograph in the stream. */
.article-container .osc-gallery-cap {
    margin: 9px 0 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .84rem;
    line-height: 1.45;
    color: #4a4540;
}

.article-container .osc-gallery-item:not(:first-child) .osc-gallery-cap {
    display: none;
}

/* A gallery of one is a photograph, not a stack: no crop, and the label says
   so instead of offering to show you the other none of them. */
.article-container .osc-gallery-grid > .osc-gallery-item:only-child .osc-gallery-img {
    aspect-ratio: auto;
}

@media (max-width: 599px) {
    .article-container .osc-gallery { margin: 28px 0; }
    .article-container .osc-gallery-grid { gap: 8px; }
    .article-container .osc-gallery-item:not(:first-child) {
        min-width: 52px;
        max-width: 100px;
    }
    /* Taller on a phone, where 3:2 across 340px is a letterbox rather than a
       lead photograph. */
    .article-container .osc-gallery-item:first-child .osc-gallery-img {
        aspect-ratio: 4 / 3;
    }
}
/* ---------------------------------------------------------------------------
   THE LIGHTBOX
   ------------------------------------------------------------------------- */

.osc-lightbox {
    position: fixed;
    inset: 0;
    z-index: 2147483000;
    display: flex;
    flex-direction: column;
    /* SOLID, NOT NEARLY SOLID. At 96% the masthead and the ticker showed
       faintly through -- they are the brightest things on the site, a white
       bar and a blue button, and 4% of that is enough to read. A photograph
       should be looked at in a dark room, not through the page it came from,
       and every other photo viewer a reader has used is opaque. */
    background: #0d1016;
    /* Fallback first: a browser without dvh still gets a full-height overlay. */
    height: 100vh;
    height: 100dvh;
}

.osc-lightbox[hidden] { display: none !important; }

.osc-lightbox-bar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 14px;
    color: #f4f1ea;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}

.osc-lightbox-counter {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .76rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: #cfc8bb;
}

.osc-lightbox-close {
    flex: 0 0 auto;
    min-width: 44px;
    min-height: 44px;
    padding: 6px 14px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #0d1016;
    background: #f4f1ea;
    border: 0;
    border-radius: 3px;
    cursor: pointer;
}

.osc-lightbox-close:hover { background: #ffffff; }
.osc-lightbox-close:focus-visible { outline: 2px solid #8fb6de; outline-offset: 2px; }

.osc-lightbox-stage {
    position: relative;
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;             /* or the image cannot shrink inside the flex row */
    padding: 10px 56px;
}

.osc-lightbox-figure {
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    max-width: 100%;
    max-height: 100%;
    min-height: 0;
}

.osc-lightbox-img {
    display: block;
    max-width: 100%;
    max-height: 100%;
    min-height: 0;
    object-fit: contain;
    background: #1a1f28;
    opacity: 1;
    transition: opacity .18s ease;
}

/* Hidden until the photograph it is captioned with has actually decoded. */
.osc-lightbox-img.is-loading {
    opacity: 0;
}

.osc-lightbox-cap {
    flex: 0 0 auto;
    max-width: 68ch;
    margin: 12px 0 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: .86rem;
    line-height: 1.5;
    color: #ded8cd;
    text-align: center;
}

.osc-lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 46px;
    height: 46px;
    padding: 0;
    color: #f4f1ea;
    background: rgba(24, 29, 38, .8);
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: 50%;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}

.osc-lightbox-nav:hover { background: rgba(44, 52, 66, .95); }
.osc-lightbox-nav:focus-visible { outline: 2px solid #8fb6de; outline-offset: 2px; }
.osc-lightbox-nav[disabled] { opacity: .3; cursor: default; }

.osc-lightbox-prev { left: 6px; }
.osc-lightbox-next { right: 6px; }

/* While the lightbox is open. Also fixes the iOS background-scroll leak. */
body.osc-lightbox-open { overflow: hidden; }

@media (max-width: 599px) {
    .osc-lightbox-stage { padding: 8px 8px 0; }
    /* Thumb-reachable at the bottom on a phone, rather than mid-screen where
       they sit over the photograph. */
    .osc-lightbox-nav {
        top: auto;
        bottom: 10px;
        transform: none;
    }
    .osc-lightbox-prev { left: 12px; }
    .osc-lightbox-next { right: 12px; }
    .osc-lightbox-cap { margin-bottom: 64px; }
}

@media (prefers-reduced-motion: reduce) {
    .osc-lightbox-img,
    .article-container .osc-gallery-img,
    .article-container .osc-gallery-link {
        transition: none;
    }
    .article-container .osc-gallery-link:hover .osc-gallery-img { transform: none; }
}
