/* ═══════════════════════════════════════════════════════════════════════
   Markdown item embeds (#1417) — inline players and navigation cards.

   A paragraph whose only content is an item link is rewritten into one of
   these by ItemCardExtension. #1417 shipped that markup with NO stylesheet
   anywhere in the repo: a card rendered as a bare <a> with two <span>s, so
   its title and summary ran together on one line, and a player rendered as
   an unsized <video>. This file is that missing half.

   🛑 WEB ONLY, for now. AdminWeb article previews render the same embeds
   and are currently UNSTYLED there — its preview shows the right HTML but
   not the right appearance. Sharing one file between the two hosts needs
   more than a linked Content item: a linked file never enters the static
   web asset manifest (verified — app.css appears in it, a linked file does
   not), and AdminWeb must not reference the AppUI class library, which its
   ArchitectureTests pin. That is a small follow-up, not a silent gap.

   Plain element selectors and CSS variables only — this is loaded as an
   ordinary global stylesheet, never as scoped CSS.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Shared ─────────────────────────────────────────────────────────── */

.cs-md-embed {
    display: block;
    margin: 1.75rem 0;
}

/* ─── Navigation card ────────────────────────────────────────────────── */

/* The whole card is the anchor, not just the title: the reader is choosing
   a door, and the door should be the size of the door.

   🛑 The card carries its OWN measure rather than inheriting its container's.
   It was built for /i/{id}, where the page already caps at 56rem, so nothing
   here needed a width. Spec 193 D2 then made cards resolve for a signed-in
   viewer and they began rendering in the app view, whose description area has
   no measure at all — a card stretched the full window and read as a footer
   rule rather than a card. A component whose sizing lives in its container
   breaks the first time it meets a container that never agreed to it.

   38rem is narrower than the 56rem reading measure on purpose: a stack of
   these is a MENU, and a menu reads as a menu when the doors are visibly
   narrower than the prose around them. */
a.cs-md-embed--card {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    max-width: 38rem;
    padding: 1rem 1.125rem;
    border: 1px solid var(--neutral-stroke-rest, rgba(0, 0, 0, 0.15));
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

a.cs-md-embed--card:hover,
a.cs-md-embed--card:focus-visible {
    border-color: var(--accent-fill-rest, #0b6bcb);
    background-color: var(--neutral-fill-stealth-hover, rgba(0, 0, 0, 0.03));
}

a.cs-md-embed--card:focus-visible {
    outline: 2px solid var(--accent-fill-rest, #0b6bcb);
    outline-offset: 2px;
}

.cs-md-embed__title {
    font-size: 1.0625rem;
    font-weight: 600;
    line-height: 1.3;
}

/* The summary is one plain line of context. Clamped rather than wrapped
   without limit: a menu of cards is scanned, and a card that grows to five
   lines pushes every other door off the screen. */
.cs-md-embed__summary {
    font-size: 0.9375rem;
    line-height: 1.45;
    opacity: 0.8;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ─── Inline player ──────────────────────────────────────────────────── */

.cs-md-embed--video {
    /* It is a <figure>; browsers give those a default inline margin. */
    margin-inline: 0;
}

/* Same reasoning as the card, one size up. A player wants to be big, so this
   is the reading measure rather than the card's — which is exactly what it
   already got on /i/{id}, where the page capped it. The cap only changes what
   happens in the app view, where nothing was capping it at all. */
.cs-md-embed__player {
    display: block;
    width: 100%;
    max-width: 56rem;
    max-height: 70vh;
    border-radius: 8px;
    background: #000;
}

/* 🎯 Reported from prod 2026-09-05: "when I go to fullscreen, it doesn't seem
   to scale appropriately."

   In fullscreen the <video> IS the fullscreen element, but the two caps above
   still apply to it — so it stayed ~896px wide and 70% tall inside a black
   frame the size of the display. Nothing overrode them, because nothing here
   knew fullscreen existed.

   ⭐ Both of the reported video symptoms came from this ONE rule: the width cap
   also decides the streamed resolution, because hls.js's capLevelToPlayerSize
   picks a rung from the element's rendered size.

   PhotoLightbox already does this correctly (its :fullscreen block sizes to
   100% of the stage rather than to a viewport unit). This is the same move for
   the embedded player, which never got it. */
.cs-md-embed__player:fullscreen,
.cs-md-embed__player:-webkit-full-screen {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    border-radius: 0;
    /* contain, never cover: a 16:9 video on a 16:10 display must letterbox
       rather than crop the top and bottom off the content. */
    object-fit: contain;
}

.cs-md-embed__caption {
    display: block;
    margin-top: 0.5rem;
    font-size: 0.9375rem;
    line-height: 1.45;
    opacity: 0.8;
}

/* ─── Inline images in rendered markdown ─────────────────────────────── */

/*
   🐞 Brett, driving dev 2026-08-31: "embedded pic renders huge! needs to be
   appropriately sized."

   An `![](…)` embed renders as a BARE <img> with no class of its own — the
   rules above only ever covered the card and the player — so nothing had ever
   constrained one. A phone photo therefore rendered at its natural pixel size
   and pushed a horizontal scrollbar onto the page.

   🪤 The gap was invisible until spec 194 made inserting an image easy. 179
   built the syntax and 194 built the button; the sizing belonged to neither,
   so it fell between them.

   Both surfaces, because both render the same markdown through the same
   pipeline: `.cs-markdown` is the marker MarkdownText ALWAYS carries, and
   `.cs-public-item__description` is /i/{id}'s, which renders its MarkupString
   directly rather than through that component.

   🪤 This was first written against `.cs-markdown-text` — MarkdownText's
   DEFAULT CssClass — and missed the app view entirely, which is exactly where
   the huge photo was, because ItemCard passes `cs-page-subtitle`. Six of the
   eleven call sites override it. **A default is not an invariant**, so
   MarkdownText now emits an unoverridable `cs-markdown` alongside it.

   max-height matches the embedded PLAYER above rather than being picked
   independently — a photo and a video in one article should not disagree about
   how much of the screen an embed may take.
*/
.cs-markdown img,
.cs-public-item__description img {
    display: block;
    max-width: 100%;
    max-height: 70vh;
    /* Both dimensions auto so the pair of maxima scale it without distorting:
       fixing either one would let the other stretch the aspect ratio. */
    width: auto;
    height: auto;
    margin: 1rem 0;
    border-radius: 8px;
}

/* ═══════════════════════════════════════════════════════════════════════
   Spec 194 — full-screen view for an inline markdown image.
   Behaviour lives in js/cs-image-lightbox.js; that file explains why this is
   NOT the spec-034 PhotoLightbox (its CSS is Blazor-scoped, it needs an API
   client and a sibling set, and /i/ is anonymous SSR).

   🪤 SIZING, per the three traps recorded in [[reference-lightbox-js-module-traps]]:

   • NEVER `min(<viewport unit>, 100%)`. When the percentage cannot resolve
     against an indefinite-height parent CSS treats it as `none`, and that
     takes the WHOLE min() with it — the 034 photo computed
     `max-height: min(887.52px, 100%)` and still rendered 70px past the fold.
     Pick one. Here it is percentages all the way down.

   • "Fit the viewport" needs a DEFINITE HEIGHT AT EVERY LEVEL, or a child's
     `max-height: 100%` resolves circularly and the box grows to its content.
     So: the dialog is sized to the viewport, the stage is 100% of the dialog,
     and only then does the image cap against the stage. `min-height: 0` lets
     the flex child shrink below its intrinsic size.

   • The image caps against the STAGE, never in viewport units — vh cannot see
     sibling chrome, so a 100vh media element under a caption overruns the
     screen by exactly the caption's height.

   `dvh` rather than `vh` so mobile browser chrome collapsing does not clip it.
   ═══════════════════════════════════════════════════════════════════════ */

/* The affordance. Without a cursor change nothing suggests the image is
   clickable — the reason Brett had to ask whether it should be. */
.cs-markdown img,
.cs-public-item__description img {
    cursor: zoom-in;
}

/* A linked image is a LINK; the script skips it, so the cursor must not
   promise a zoom the click will not deliver. */
.cs-markdown a img,
.cs-public-item__description a img {
    cursor: pointer;
}

dialog.cs-img-lightbox {
    /* `margin: 0` is load-bearing: a dialog defaults to `margin: auto`, which
       would centre a viewport-sized box inside the viewport and produce a
       scrollbar. */
    margin: 0;
    padding: 0;
    border: 0;
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    background: transparent;
    overflow: hidden;
}

dialog.cs-img-lightbox::backdrop {
    /* Deliberately single-theme. A lightbox darkens the room in both themes —
       that is what the affordance means, not a palette choice. */
    background: rgba(0, 0, 0, 0.9);
}

.cs-img-lightbox__stage {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    min-height: 0;
    padding: 2rem;
    box-sizing: border-box;
}

.cs-img-lightbox__img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 4px;
}

.cs-img-lightbox__caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    padding: 0.75rem 1rem;
    text-align: center;
    color: rgba(255, 255, 255, 0.92);
    font-size: 0.875rem;
    background: rgba(0, 0, 0, 0.45);
}

.cs-img-lightbox__caption[hidden] {
    display: none;
}

.cs-img-lightbox__close {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    width: 2.5rem;
    height: 2.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.75rem;
    line-height: 1;
    color: #fff;
    background: rgba(0, 0, 0, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 50%;
    cursor: pointer;
}

.cs-img-lightbox__close:hover {
    background: rgba(0, 0, 0, 0.75);
}

.cs-img-lightbox__close:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}
