/* ============================================================
   Article lightbox — .art-lightbox
   ------------------------------------------------------------
   Click any article image to see it big. Driven by
   js/v2/article-lightbox.js; loaded by all four article shells.

   Extracted from guide.css, where it started life welded to the
   carousel. It is not a carousel component and never was: the
   reason it exists is that every screenshot in this system is a
   dense product UI shrunk into a reading column, and at that size
   the control the sentence is naming is too small to read.
   Enlarging is the difference between the picture proving the
   sentence and merely illustrating it — as true of a lone figure
   in a news post as of a slide in a deck.

   SELF-CONTAINED ON PURPOSE. This is the one article stylesheet
   the split-hero news shell loads, and that shell deliberately
   loads none of the rest — it is a competing layout system in the
   .nsh-* vocabulary with no .art / .art-body / --gutter anywhere
   (see the comment in tools/news_article_split_hero.html). Mixing
   the two is exactly the failure check_articles' vocabulary check
   exists to catch. This file is safe there because it is not
   layout: it styles one <dialog> that the script appends to
   <body> and the browser renders in the TOP LAYER, outside the
   page's stacking and flow entirely. Nothing here selects a page
   element except the zoom affordance on [data-zoomable], and
   nothing here reads a token the article layer owns without a
   fallback beside it.

   Built on <dialog> rather than a hand-rolled overlay: showModal()
   brings the focus trap, the inert background, Esc-to-close and
   the top-layer stacking with it, and every one of those is a
   thing hand-rolled lightboxes get wrong. The script feature-
   detects showModal and simply never wires the click if it is
   missing, so a browser without it gets the page it would have had.

   The zoom is a FLIP: the image starts drawn at the thumbnail's
   exact rect and animates to its final one, so the picture appears
   to grow out of the figure the reader clicked rather than being
   replaced by a different picture somewhere else. Under
   prefers-reduced-motion it just fades.
   ============================================================ */

/* ---------- the trigger ----------
   Set by the script on each image it claims, so the cursor only ever
   promises a zoom that actually works. The images become buttons in
   all but name (role + tabindex, see article-lightbox.js), which makes
   them tab stops — hence a focus ring, which an <img> has no business
   having until the moment it is interactive. */
img[data-zoomable] { cursor: zoom-in; }
img[data-zoomable]:focus-visible {
    outline: 2px solid var(--accent, #4e964f);
    outline-offset: 2px;
}

.art-lightbox {
    border: 0;
    padding: 0;
    margin: 0;
    max-width: 100vw;
    max-height: 100vh;
    width: 100vw;
    height: 100vh;
    background: transparent;
    overflow: hidden;
}
.art-lightbox::backdrop {
    background: rgba(10, 14, 11, .88);
}

/* The stage centres the picture; the caption is taken OUT of the flow and
   pinned to the bottom bar. In the flow it plus the padding cost ~130px of
   height, and on a laptop (inner height ~680) that held the "enlarged"
   image to barely 20% bigger than the thumbnail it grew from — the whole
   point of the interaction, spent on chrome. Overlaying it costs nothing:
   the image is centred and the strip below it is empty backdrop. */
.art-lightbox-stage {
    margin: 0;
    width: 100%;
    height: 100%;
    display: flex;
    /* SCROLLS, in both axes, because the picture is allowed to be bigger
       than the screen — see .art-lightbox-img below.

       Centring is done with `margin: auto` ON THE IMAGE rather than
       align-items/justify-content here, and that is not a style preference.
       A flex item centred by its container and larger than it overflows
       equally past BOTH edges, and overflow past the start edge is not
       reachable by scrolling — the top of a tall invoice and the left third
       of a wide dispatch console would simply be gone. Auto margins collapse
       to zero the instant the item stops fitting, so every pixel stays
       reachable, and they still centre it perfectly while it does fit. */
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    /* The bottom padding is the caption's lane. Centring the image in the
       FULL height and trusting the leftover slack to clear an absolutely
       positioned caption does not work: the slack splits evenly top and
       bottom, so a 40px caption lands on the last ~20px of the picture.
       Reserving the lane in the stage's own box pushes the image up by
       exactly as much as the caption needs. */
    padding: 8px 8px 62px;
    box-sizing: border-box;
}

/* NEVER SCALED DOWN. This is the whole sizing rule, and it replaced two
   separate fit-to-screen rules (one desktop, one phone) that each had the
   same hole in them.

   Fitting cannot be trusted to enlarge anything. The thumbnail is already
   as wide as the column — full-bleed at 100vw on a phone — so "fit the
   image to the viewport" lands within a few percent of the size it was
   already, and lands BELOW it whenever the picture is taller than it is
   wide. Measured, on a 632x613 window: a 1200x1324 invoice shrank to
   485x535 against a 600px thumbnail. The reader clicked to enlarge and got
   something smaller. The phone rule had already been rewritten once for the
   same reason and only fixed the landscape half of it.

   Rendering at natural size instead makes the guarantee structural rather
   than lucky: a fragment never displays an image above its intrinsic width
   (that would just be blur), so intrinsic width is by definition >= the
   thumbnail's — and where it does not fit, the stage pans. It is also
   sharper than the old phone rule, which sized to viewport HEIGHT and so
   upscaled a 1200px-wide screenshot to ~1354 on a tall phone: bigger, and
   blurrier than the file it came from.

   1600px is a ceiling for a freakishly large export, not a target; nothing
   in this content system ships wider than 1200. */
.art-lightbox-img {
    display: block;
    margin: auto;
    max-width: 1600px;
    width: auto;
    height: auto;
    flex: none;   /* or flex-shrink squeezes it back down to fit, undoing all of the above */
    object-fit: contain;
    border-radius: 6px;
    background: var(--surface, #ffffff);
    box-shadow: 0 18px 60px rgba(0, 0, 0, .45);
    /* the FLIP transform is written by the script; keeping the
       property here documents that this element is the animated one */
    transform-origin: center center;
    cursor: zoom-out;
}

.art-lightbox-cap {
    position: absolute;
    left: 50%;
    bottom: 14px;
    transform: translateX(-50%);
    max-width: min(760px, 78vw);
    text-align: center;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .6);
    color: rgba(255, 255, 255, .82);
    font-size: 13px;
    line-height: 1.6;
}
.art-lightbox-cap b {
    color: #fff;
    display: block;
    font-size: 14.5px;
    margin-bottom: 2px;
}

/* ---------- controls ---------- */
.art-lightbox-btn {
    position: absolute;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: 50%;
    background: rgba(22, 26, 23, .72);
    color: #fff;
    cursor: pointer;
    padding: 0;
    transition: background .18s ease, border-color .18s ease, opacity .18s ease;
}
/* A gallery of one hides its arrows, and the script does that with the
   `hidden` property. display:inline-flex above outranks the UA stylesheet's
   [hidden] { display: none }, so without this line hiding them does nothing
   visible at all — the classic "why is my hidden element still on screen". */
.art-lightbox-btn[hidden] { display: none; }
.art-lightbox-btn:hover:not(:disabled) {
    background: rgba(52, 60, 54, .92);
    border-color: rgba(255, 255, 255, .45);
}
.art-lightbox-btn:disabled { opacity: .25; cursor: default; }
.art-lightbox-btn:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.art-lightbox-close {
    top: 14px;
    right: 14px;
    width: 38px;
    height: 38px;
    font-size: 15px;
}

.art-lightbox-arrow {
    top: 50%;
    transform: translateY(-50%);
    width: 42px;
    height: 42px;
    font-size: 17px;
}
.art-lightbox-arrow--prev { left: 14px; }
.art-lightbox-arrow--next { right: 14px; }

/* Counter, top-left, mono so it does not jitter. The fallback stack is not
   decoration: --art-font-mono is defined in css/v2/article/base.css, which
   the split-hero shell does not load. */
.art-lightbox-count {
    position: absolute;
    top: 22px;
    left: 18px;
    font-family: var(--art-font-mono, 'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace);
    font-size: 12px;
    color: rgba(255, 255, 255, .7);
}

/* On a phone the arrows would sit on top of the picture, which is the
   thing the reader opened this to look at. Drop them to the bottom bar
   beside the caption. */
@media (max-width: 599.98px) {
    /* Only the chrome changes on a phone. The image sizing does not: the
       natural-size rule above is already the "pan, don't fit" behaviour this
       breakpoint used to hand-roll, and it does it without upscaling.
       The lane is taller here because it holds the arrows as well as the
       caption. */
    .art-lightbox-stage {
        padding: 4px 4px 96px;
    }
    .art-lightbox-arrow {
        top: auto;
        bottom: 14px;
        transform: none;
        width: 38px;
        height: 38px;
    }
    .art-lightbox-arrow--prev { left: 18px; }
    .art-lightbox-arrow--next { right: 18px; }
    .art-lightbox-cap { padding-bottom: 46px; }
}
