/* ==========================================================================
   Case content — single case page (hero meta · media grid · about · related)
   ========================================================================== */

/* ── Hero video (mp4 header) — fills the .expertise-hero like the image ────── */
.case-hero-x__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}
/* Hero — Bunny Stream embed. Over-size the 16:9 player with container-query
   units so it COVERS the wide header (no letterbox); overflow:hidden crops. */
.case-hero-x__embed { position: absolute; inset: 0; z-index: 0; overflow: hidden; container-type: size; }
.case-hero-x__embed iframe {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max(100cqw, 177.78cqh);
  height: max(100cqh, 56.25cqw);
  border: 0;
  pointer-events: none;
}
/* Hero set to Click to play / Player: the wrapper fills the header the way the
   bare <video> does, the play button sits above the gradient, and the media
   takes pointer events so the controls work. An interactive embed fits the
   header instead of being over-sized, so its controls aren't cropped off. */
.case-hero-x .video-click { position: absolute; inset: 0; z-index: 0; }
.case-hero-x .video-click__btn { z-index: 3; }
.case-hero-x__video.js-video { pointer-events: auto; }
.case-hero-x__embed.is-interactive iframe {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
  pointer-events: auto;
}

/* ── Hero meta (Område tags + year) — on the dark .expertise-hero ─────────── */
.case-hero-x__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}
/* Glass tag/eyebrow — unified size (Figma 3190:9242): 46px tall, 24px sides,
   20px corners, 20px/regular text. Keep in sync with .service-hero__eyebrow.
   Glass look via .glass-card + --fx-filter. */
.case-hero-x__tag {
  display: inline-flex;
  align-items: center;
  min-height: 46px;
  padding: 0 24px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-light);
  /* Body/Small, all three properties from the same token family (Kajsa
     2026-10-12). line-height was a bare 1.2 ratio before, which is not what
     the scale says: --body-sm-lh is a flat 24px at every width. The size
     token steps itself (16 -> 14 on phone), so the mobile override that used
     to sit in the 767 block below is gone - the token does that job. */
  font-size: var(--body-sm);
  font-weight: 400;
  line-height: var(--body-sm-lh);
  letter-spacing: var(--body-sm-ls);
  white-space: nowrap;
}

/* ── Media — per item: full (full-bleed) / landscape (half) / portrait (half) ── */
.case-media { padding-block: var(--space-60); display: flex; flex-direction: column; gap: 20px; }
/* Rows that are .container carry its padding-bottom (50px desktop / 16px
   mobile) — inside the media block the flex gap owns ALL row spacing, so that
   padding is neutralised; otherwise rows ending in a grid get uneven gaps and
   the section's bottom padding stops being exactly 60px. */
.case-media > .container { padding-bottom: 0; }
/* A media block that ENDS the page with an edge-to-edge full row (no rounded
   corners) docks flush against the contact section — no bottom padding, the
   image/video meets the dark section directly. Contained (rounded) rows and
   blocks followed by more content keep their breathing room. */
.case-media:has(> .case-media__full:last-child:not(.case-media__full--contained)):has(+ .section-contact) {
  padding-bottom: 0;
}

/* Full-width "helskärm" item — full-bleed, tall, no radius */
.case-media__full { width: 100%; }
.case-media__full .case-mediagrid__item { border-radius: 0; max-height: 100vh; }
/* Optional "contained" look for a full item (toggle in the builder): rounded
   corners + the SAME geometry as .container, so the image tops out at the
   content column (1340px) and lines up with every other row — instead of
   running viewport-wide with a token margin on huge screens. */
.case-media__full--contained {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--margin-primary);
}
.case-media__full--contained .case-mediagrid__item { border-radius: var(--radius-image); }
.case-media__full .case-mediagrid__item img,
.case-media__full .case-mediagrid__item video { width: 100%; height: auto; max-height: 100vh; object-fit: cover; display: block; }

/* Half items — landscape/portrait in a centered 2-column grid */
.case-mediagrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; align-items: start; }
.case-mediagrid__item { margin: 0; overflow: hidden; border-radius: var(--radius-image); position: relative; }
.case-mediagrid__item img,
.case-mediagrid__item video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Bunny Stream embed — COVER the tile like the images do. The player keeps the
   video's own aspect, so over-size it with container-query units (per shape)
   and let the tile's overflow:hidden crop the excess. No letterbox. */
/* overflow: the iframe is scaled to COVER the tile, so a 16:9 film in a 3:2
   box is wider than its cell (711px in a 600px cell, measured 2026-10-02).
   The figure clips too, but the embed must clip its own player as well —
   otherwise the Bunny chrome can show past the edge. */
.case-embed { position: absolute; inset: 0; container-type: size; overflow: hidden; }
.case-embed iframe {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* --embed-r: the film's own width/height ratio, printed by media-render.php
     from the Bunny API. 16:9 when unknown. */
  width: max(100cqw, calc(100cqh * var(--embed-r, 1.7778)));
  height: max(100cqh, calc(100cqw / var(--embed-r, 1.7778)));
  border: 0;
  pointer-events: none;
}
.case-embed--square iframe { width: max(100cqw, 100cqh); height: max(100cqw, 100cqh); }        /* 1:1 */
.case-embed--portrait iframe { width: max(100cqw, 80cqh); height: max(100cqh, 125cqw); }      /* 4:5 (portrait) */
/* Full items take their natural height (no fixed-height crop). Embeds have no
   intrinsic size, so give the tile a ratio box matching the video's shape. */
.case-media__full .case-mediagrid__item:has(.case-embed)           { aspect-ratio: var(--embed-r, 16 / 9); }
.case-media__full .case-mediagrid__item:has(.case-embed--square)   { aspect-ratio: 1 / 1; }
.case-media__full .case-mediagrid__item:has(.case-embed--portrait) { aspect-ratio: 4 / 5; }

/* ── Full-BLEED full items (no radius/padding) → full width ───────────────────
   The WIDTH owns the geometry: the tile is always 100vw and the height follows
   the media's own ratio. On tall screens the row simply lands shorter than one
   screen; on wide screens it is allowed to grow PAST 100vh instead of being
   capped. Never a crop, never a margin — that is the difference to --natural,
   which caps at 100vh and pillarboxes on wide screens.
   Embeds have no intrinsic size, so the tile gets a ratio box per shape — with
   the box on ratio the iframe's cover formula resolves to an exact fit, so
   nothing is over-sized. Phones use the very same rules (no separate block). */
.case-media__full:not(.case-media__full--contained) .case-mediagrid__item { height: auto; max-height: none; }
.case-media__full:not(.case-media__full--contained) .case-mediagrid__item:has(.case-embed) { aspect-ratio: var(--embed-r, 16 / 9); }
/* …and the player simply fills that box — no shape variants, no cover
   over-sizing. The builder HIDES the per-item Shape on a Full width row, so
   honouring a stored one lets an invisible value decide how tall the page is;
   that is exactly how a 16:9 film ended up rendered as a 100vw square. 16:9 is
   the only shape a 100vw row can sensibly take (a 4:5 video would be 125vw
   tall) — square and portrait videos belong in a contained row or a collage
   tile, where the Shape control is visible. */
.case-media__full:not(.case-media__full--contained) .case-embed iframe {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
}
/* No crop happens here any more, so Fit/Position have nothing to act on in a
   full-bleed row — both keep working in every other pattern. */
.case-media__full:not(.case-media__full--contained) .case-mediagrid__item img,
.case-media__full:not(.case-media__full--contained) .case-mediagrid__item video { height: auto; max-height: none; }
.case-mediagrid__item--landscape { aspect-ratio: 3 / 2; }
.case-mediagrid__item--portrait  { aspect-ratio: 3 / 4; }
.case-mediagrid__item--square    { aspect-ratio: 1 / 1; }
/* Landscape 4:3 — the classic photo ratio (660×495 at full content width,
   the Figma two-up pair height). */
.case-mediagrid__item--landscape43 { aspect-ratio: 4 / 3; }
/* Instagram-format tiles (4:5) — the Bunny portrait player is 4:5 too, so an
   insta video in this shape shows with zero crop. */
.case-mediagrid__item--portrait45 { aspect-ratio: 4 / 5; }
/* "Original — no crop": the tile takes the media's own ratio, so designed
   cards (e.g. 4:3 exports) are never cropped. Two tiles with the same source
   ratio line up perfectly; mixed ratios sit top-aligned. */
.case-mediagrid__item--natural { aspect-ratio: auto; }
.case-mediagrid__item--natural img,
.case-mediagrid__item--natural video { height: auto; }

/* Two-up vertical alignment: how the SHORTER tile sits beside the taller one.
   Matches the Figma pairs — top (default), centred (equal air above/below),
   or bottom-lined. Replaces guessing with fixed pixel offsets. */
.case-collage--valign-center { align-items: center; }
.case-collage--valign-end    { align-items: end; }
/* Per-TILE override (set on the item in the builder): pins one tile to the
   top or bottom edge of its row regardless of the row-level alignment. */
.case-valign-start { align-self: start; }
.case-valign-end   { align-self: end; }
/* Per-tile HORIZONTAL alignment (Justify in the builder). Only visible when a
   tile is narrower than its cell — the square card in a feature stack, a
   case-card-half tile, or an item with a custom width. */
.case-halign-start  { margin-right: auto; }
.case-halign-center { margin-inline: auto; }
.case-halign-end    { margin-left: auto; }

/* Fit + Position. Fit says whether the media fills the cell (cover, the default
   — crops the overflow) or shows its whole frame (contain — letterboxes).
   Position says which part survives: with cover it picks the part that is KEPT,
   with contain it picks the edge the media hugs. They are separate so a video
   can be cover-cropped towards its top edge, which the old single "align" list
   made impossible (any edge choice forced contain). */
.case-fit-contain img,
.case-fit-contain video { object-fit: contain; }
.case-pos-top    img, .case-pos-top    video { object-position: center top; }
.case-pos-bottom img, .case-pos-bottom video { object-position: center bottom; }
.case-pos-left   img, .case-pos-left   video { object-position: left center; }
.case-pos-right  img, .case-pos-right  video { object-position: right center; }

/* ── Subtle parallax on cropped tiles ─────────────────────────────────
   The image is taller than its tile by --parallax-room at the top AND the
   bottom; the tile's overflow: hidden keeps that surplus out of sight, and
   main.js slides the image upwards across the tile as the page scrolls. The
   travel is exactly --parallax-room, so an edge can never come into view
   (Kajsa 2026-10-05).

   .is-parallax is added by main.js, NOT in the markup — it is only put on
   tiles that actually crop. A tile with Shape "Original — no crop", a
   full-bleed row, a contain-fitted tile or a film has no surplus to move, and
   on a film it would drag Bunny's player chrome into the frame.

   Desktop only, and never with reduced motion: on touch the scroll events are
   throttled during momentum scrolling, so the image lags behind the page
   instead of gliding with it. Without JS nothing is added and the tile keeps
   its ordinary 100%-height image. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .case-mediagrid__item.is-parallax {
    --parallax-room: 7%;
  }
  .case-mediagrid__item.is-parallax img {
    position: absolute;
    left: 0;
    width: 100%;
    top: calc(-1 * var(--parallax-room));
    height: calc(100% + 2 * var(--parallax-room));
    will-change: transform;
  }
}

/* ── Full-bleed: "Show the whole video" ─────────────────────────────────────
   The alternative to filling the viewport. The item takes the media's own
   proportions instead of a fixed 100vh, so nothing is cropped no matter how
   wide or tall the screen is — the fix for videos being cut off on large
   displays. Capped at the viewport height and centred. */
/* The :not() is what the fill rules above are scoped with — matching it keeps
   these at the same specificity, and being later in the file they win. */
.case-media__full--natural:not(.case-media__full--contained) .case-mediagrid__item {
  height: auto;
  max-height: 100vh;
  margin-inline: auto;
  background: #000;
}
.case-media__full--natural:not(.case-media__full--contained) .case-mediagrid__item img,
.case-media__full--natural:not(.case-media__full--contained) .case-mediagrid__item video {
  width: 100%;
  height: auto;
  max-height: 100vh;
  object-fit: contain;
}
/* Embeds have no intrinsic size, so the tile needs a ratio box — 16:9, the same
   one every full-bleed row uses and for the same reason (see above). max-width
   keeps the box exactly on ratio once max-height kicks in, so there is never a
   letterbox, only the black pillars this mode is chosen for. */
.case-media__full--natural:not(.case-media__full--contained) .case-mediagrid__item:has(.case-embed) {
  aspect-ratio: var(--embed-r, 16 / 9);
  max-width: calc(100vh * var(--embed-r, 1.7778));
}
.case-media__full--natural .case-embed iframe {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
}

/* ── Full-bleed: "Full width, cropped at one screen" ──────────────────────
   The third height mode (Kajsa 2026-10-05). Width still owns the geometry — the
   row is 100vw — but it is never taller than the screen: the tile caps at 100svh
   and the media COVERS it, so whatever is over the cap is cut off the top and
   bottom instead of making the row scroll past one screenful.
   The difference to the other two: --fill may grow past 100svh, --natural caps
   at 100svh and CONTAINS (black pillars, nothing lost), this one caps and CROPS
   (nothing letterboxed, the frame loses its edges). A row shorter than the
   screen is untouched by any of them.
   The cap is 100svh, not 100vh (Kajsa 2026-10-08): on a phone 100vh is the
   TALLEST the viewport ever gets, so with the browser chrome showing, a row
   capped at 100vh still runs past the screen - which is the one thing this
   mode exists to prevent. svh is the SMALL viewport, so the row fits even
   with the chrome down, and the tile's overflow crops whatever is over.
   Matches the fill rules' :not() so the specificity ties and source order wins,
   the same trick --natural uses just above. */
.case-media__full--crop:not(.case-media__full--contained) .case-mediagrid__item {
  height: auto;
  max-height: 100svh;
}
.case-media__full--crop:not(.case-media__full--contained) .case-mediagrid__item img,
.case-media__full--crop:not(.case-media__full--contained) .case-mediagrid__item video {
  width: 100%;
  height: auto;
  max-height: 100svh;
  object-fit: cover;
}
/* Embeds have no intrinsic size, so the tile keeps the 16:9 ratio box every
   full-bleed row uses — but with the cap on top, so on a wide screen the box
   stops being 16:9 and becomes 100vw × 100svh. */
.case-media__full--crop:not(.case-media__full--contained) .case-mediagrid__item:has(.case-embed) {
  /* width:100% is load-bearing. With only aspect-ratio + max-height the
     cap shrinks the WIDTH to keep 16:9 instead of letting the box go
     off-ratio - measured 1067x600 on a 1440x600 screen, so the row sat
     in from both edges with the page showing beside it. A definite width
     makes aspect-ratio set the HEIGHT, which max-height then clamps, and
     the box ends up the 100vw x 100svh this mode promises. */
  width: 100%;
  aspect-ratio: var(--embed-r, 16 / 9);
  max-height: 100svh;
}
/* Once the box is off-ratio the fill rules' stretch-to-fit iframe would squash
   the picture, so the player goes back to the cover formula the collage tiles
   use: over-size on whichever axis is short and let .case-embed's overflow do
   the cropping. */
.case-media__full--crop:not(.case-media__full--contained) .case-embed iframe {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max(100cqw, calc(100cqh * var(--embed-r, 1.7778)));
  height: max(100cqh, calc(100cqw / var(--embed-r, 1.7778)));
}

/* An interactive embed (Click to play / Player) shows the player's own controls,
   so it must NOT be over-sized to cover the cell — the controls would be cropped
   off. It fits the cell instead, letterboxing if the shapes differ. */
.case-embed.is-interactive iframe {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
  pointer-events: auto;
}

/* ── Collage rows (row-based media builder) ─────────────────────────────────
   Each row is one pattern. `full` and `two-up` reuse the classes above; the
   patterns below get their own grids. Air = how much breathing room a row has:
   tight pulls the tiles together, airy indents the row and opens the gap. */
.case-collage { display: grid; gap: 20px; }
.case-collage--air-tight { gap: 12px; }
.case-mediagrid.case-collage--air-tight { gap: 12px; }
.case-collage--air-airy { gap: 32px; padding-inline: calc(var(--margin-primary) + 6%); }
.case-mediagrid.case-collage--air-airy { gap: 32px; padding-inline: calc(var(--margin-primary) + 6%); }

/* Feature: one tall tile + two stacked beside it. Matches the Figma component:
   equal halves (2+2 columns of the 4-col grid), the big tile's 3:4 ratio
   drives the row height, and the stack splits it 532/332 (1.6:1) like the
   design — a landscape tile on top, a smaller card below. */
.case-collage--feature-left,
.case-collage--feature-right {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* minmax(0, …): a tall (portrait) image in the stack must not feed its own
     height back into the row and push the stack past the big tile. */
  grid-template-rows: minmax(0, 1.6fr) minmax(0, 1fr);
}
/* The design draws the small bottom card at HALF the column width — items
   opt in via the media builder's CSS class field ("case-card-half"). */
.case-collage__stack .case-mediagrid__item.case-card-half { width: 50%; }
.case-collage__big { grid-row: 1 / 3; }
/* The big CELL owns its 3:4 shape; the item covers it. Without this the item
   takes width from its own aspect-ratio and pokes past the container on
   screens wider than 1440 (the cell can't shrink it). */
.case-collage--feature-left  .case-collage__big,
.case-collage--feature-right .case-collage__big { aspect-ratio: 3 / 4; }
.case-collage--feature-right .case-collage__big { grid-column: 2; grid-row: 1 / 3; }
.case-collage--feature-right .case-collage__stack { grid-column: 1; }
.case-collage--feature-right .case-collage__stack:nth-child(2) { grid-row: 1; }
.case-collage--feature-right .case-collage__stack:nth-child(3) { grid-row: 2; }
.case-collage__big .case-mediagrid__item { width: 100%; height: 100%; aspect-ratio: auto; }
.case-collage__stack { min-height: 0; }
/* Two-column feature rows: the stack cells take their height from the big
   tile's rows only. contain:size stops a tall (portrait) image from feeding
   its own height into the rows; phones stack the cells and keep their size. */
@media (min-width: 768px) {
  .case-collage--feature-left  .case-collage__stack,
  .case-collage--feature-right .case-collage__stack { contain: size; }
}
.case-collage__stack .case-mediagrid__item { aspect-ratio: auto; height: 100%; }
/* A SQUARE item in the stack keeps its shape: it takes the row's height and
   sizes its width to match, sitting flush left — the design's "smaller card
   below" without needing the case-card-half class. */
.case-collage__stack { position: relative; }
.case-collage__stack .case-mediagrid__item--square {
  /* Absolute so the square can't feed its own height back into the fr rows
     (a static block with aspect-ratio would size the row to the column width). */
  position: absolute; top: 0; left: 0; bottom: 0;
  height: 100%; width: auto; max-width: 100%; aspect-ratio: 1 / 1;
}
.case-collage__stack .case-mediagrid__item--square.case-halign-center { left: 50%; transform: translateX(-50%); }
.case-collage__stack .case-mediagrid__item--square.case-halign-end    { left: auto; right: 0; }

/* Collage 1+3: big tile on one side; a wide tile + two small ones on the
   other. Here the RIGHT-hand stack drives the row height and the big tile
   stretches to match (cover-cropped), so it can never blow up the row —
   matches the Nordstan "Bäst klädda" reference. */
.case-collage--quad-left  { grid-template-columns: 0.9fr 0.5fr 0.5fr; grid-template-rows: auto auto; }
.case-collage--quad-right { grid-template-columns: 0.5fr 0.5fr 0.9fr; grid-template-rows: auto auto; }
.case-collage--quad-left  .case-collage__big  { grid-column: 1; grid-row: 1 / 3; }
.case-collage--quad-left  .case-collage__wide { grid-column: 2 / 4; grid-row: 1; }
.case-collage--quad-right .case-collage__big  { grid-column: 3; grid-row: 1 / 3; }
.case-collage--quad-right .case-collage__wide { grid-column: 1 / 3; grid-row: 1; }
.case-collage--quad-right .case-collage__tile:nth-child(3) { grid-column: 1; grid-row: 2; }
.case-collage--quad-right .case-collage__tile:nth-child(4) { grid-column: 2; grid-row: 2; }

/* Flip (row switch): swap the two rows so the small pair sits ABOVE the wide
   tile. The big tile spans both rows and is unaffected. quad-left places its
   pair implicitly, so the flipped rule has to name their columns too. */
.case-collage--quad-left.is-flipped .case-collage__wide { grid-column: 2 / 4; grid-row: 2; }
.case-collage--quad-left.is-flipped .case-collage__tile:nth-child(3) { grid-column: 2; grid-row: 1; }
.case-collage--quad-left.is-flipped .case-collage__tile:nth-child(4) { grid-column: 3; grid-row: 1; }
.case-collage--quad-right.is-flipped .case-collage__wide { grid-column: 1 / 3; grid-row: 2; }
.case-collage--quad-right.is-flipped .case-collage__tile:nth-child(3) { grid-column: 1; grid-row: 1; }
.case-collage--quad-right.is-flipped .case-collage__tile:nth-child(4) { grid-column: 2; grid-row: 1; }
.case-collage--quad-left .case-collage__big .case-mediagrid__item,
.case-collage--quad-right .case-collage__big .case-mediagrid__item { aspect-ratio: auto; height: 100%; }
.case-mediagrid__item--wide { aspect-ratio: 2 / 1; }

/* The chosen PATTERN owns the 1+3 collage geometry — cells keep the layout
   icon's proportions and items always cover their cell; a per-item shape can
   never shrink a tile below its cell (Figma: layout first, crop follows). */
.case-collage--quad-left  .case-collage__wide,
.case-collage--quad-right .case-collage__wide { aspect-ratio: 2 / 1; }
.case-collage--quad-left  .case-collage__tile,
.case-collage--quad-right .case-collage__tile { aspect-ratio: 330 / 453; }
.case-collage--quad-left  .case-collage__wide .case-mediagrid__item,
.case-collage--quad-right .case-collage__wide .case-mediagrid__item,
.case-collage--quad-left  .case-collage__tile .case-mediagrid__item,
.case-collage--quad-right .case-collage__tile .case-mediagrid__item {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  max-height: none;
}

/* Equal halves: the stacked side is ONE module split evenly. The big tile's
   3:4 cell drives the module height (same principle as the feature patterns)
   and the two rows share what is left 50/50, so the wide tile and the small
   pair always come out exactly level.

   Before this the cells carried fixed 2:1 / 330:453 ratios, which made the
   wide tile ~43% of the module (321px against 426px, measured 2026-10-02) —
   visibly short against the full-height tile beside it. 3:4 keeps the module
   the same total height it had (825px at content width), only the split
   changes.

   The items are taken out of flow so they cannot feed a height back into the
   row: a flexible track gives `height: 100%` nothing definite to resolve
   against and the media falls back to its own ratio. Same trick as the narrow
   tile in .case-collage--stagger.

   Must stay AFTER the pattern-geometry rules above — same specificity, so
   source order is what lets it drop their aspect-ratios. Desktop/tablet only;
   below 768px the collage stacks and the cells keep their own shapes. */
@media (min-width: 768px) {
  .case-collage--quad-left,
  .case-collage--quad-right { grid-template-rows: 1fr 1fr; }
  .case-collage--quad-left  .case-collage__big,
  .case-collage--quad-right .case-collage__big { aspect-ratio: 3 / 4; }
  .case-collage--quad-left  .case-collage__wide,
  .case-collage--quad-right .case-collage__wide,
  .case-collage--quad-left  .case-collage__tile,
  .case-collage--quad-right .case-collage__tile {
    aspect-ratio: auto;
    position: relative;
    min-height: 0;
  }
  .case-collage--quad-left  .case-collage__wide .case-mediagrid__item,
  .case-collage--quad-right .case-collage__wide .case-mediagrid__item,
  .case-collage--quad-left  .case-collage__tile .case-mediagrid__item,
  .case-collage--quad-right .case-collage__tile .case-mediagrid__item {
    position: absolute;
    inset: 0;
  }
}

/* Offset pair: two tiles, one pushed down for the staggered editorial look */
.case-collage--offset-left,
.case-collage--offset-right {
  grid-template-columns: 1fr 1fr;
  align-items: start;
}
.case-collage--offset-left  .case-collage__cell:first-child,
.case-collage--offset-right .case-collage__cell:last-child { margin-top: clamp(48px, 8vw, 130px); }
/* A per-tile Align choice OVERRIDES the pattern's built-in push: Top switches
   the stagger off for that tile, Bottom aligns to the row's bottom edge
   instead of using the fixed offset. Align wins over the pattern everywhere. */
.case-collage--offset-left  .case-collage__cell.case-valign-start,
.case-collage--offset-right .case-collage__cell.case-valign-start { margin-top: 0; }
.case-collage--offset-left  .case-collage__cell.case-valign-end,
.case-collage--offset-right .case-collage__cell.case-valign-end { margin-top: 0; align-self: end; }

/* Solo: one tile at reduced width, centred, with air around it */
.case-collage--solo { grid-template-columns: 1fr; }
/* Stagger: ONE grid, two rows, four tiles — a wide one and a narrow one per
   row, diagonally opposite, with the empty column as the air between them.
   No 1fr 1fr pattern can do unequal widths, which is why this exists.

   Equal heights: the WIDE tile keeps its own Shape and therefore sets the
   row height; the narrow tile drops its ratio and stretches to fill the row.
   Same mechanism as .case-collage__big / __stack in the feature patterns.

   Mirror is a row switch, not a second pattern: it flips the columns so the
   wide tile changes side on both rows. */
.case-collage--stagger {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: auto auto;
}
.case-collage--stagger .case-collage__cell:nth-child(1) { grid-column: 1 / 3; grid-row: 1; }
.case-collage--stagger .case-collage__cell:nth-child(2) { grid-column: 4;     grid-row: 1; }
.case-collage--stagger .case-collage__cell:nth-child(3) { grid-column: 2;     grid-row: 2; }
.case-collage--stagger .case-collage__cell:nth-child(4) { grid-column: 3 / 5; grid-row: 2; }

/* Mirrored: every column span flips about the centre line. */
.case-collage--stagger.is-mirrored .case-collage__cell:nth-child(1) { grid-column: 3 / 5; }
.case-collage--stagger.is-mirrored .case-collage__cell:nth-child(2) { grid-column: 1; }
.case-collage--stagger.is-mirrored .case-collage__cell:nth-child(3) { grid-column: 3; }
.case-collage--stagger.is-mirrored .case-collage__cell:nth-child(4) { grid-column: 1 / 3; }

/* The narrow tile must never set the row height — the WIDE one does, and the
   pair has to come out exactly level.

   height:100% alone does not do it: the grid rows are auto, so the percentage
   has nothing definite to resolve against and the image falls back to its own
   ratio (a 667x1000 portrait became 428px tall beside a 393px landscape,
   measured 2026-10-02). Taking the figure out of flow breaks that circle: it
   can no longer feed a height back into the row, so it simply fills whatever
   the wide tile decided. Same trick as the square in a feature stack. */
.case-collage--stagger .case-collage__narrow {
  position: relative;
  min-height: 0;
}
.case-collage--stagger .case-collage__narrow .case-mediagrid__item {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
}

.case-collage--solo .case-collage__cell { width: min(100%, 62%); margin-inline: auto; }
.case-collage--solo.case-collage--air-tight .case-collage__cell { width: min(100%, 78%); }

/* ── More about the client — accordion (native <details>) ─────────────────── */
.case-about-section { padding-block: 24px var(--space-60); }
/* Figma: 60px total between this section and the next heading — the section's
   own bottom padding IS that gap, so the neighbours contribute nothing. Also
   keeps the accordion from shoving the next section far down when it opens.
   (Desktop only: mobile has its own 24/20/32 spec further down.) */
@media (min-width: 768px) {
  /* Figma 1773:8262 (Aluwave) and the Carplus examples in 2130:6559 annotate a
     text section with 100 / 100 / 100: the media block owns 100 BELOW itself,
     the text section owns 100 ABOVE its heading and 100 below its body.
     Measured out of the Figma render at 1440: 200px from the image edge to the
     heading BOX (213 to its first ink row - the 80/86 heading carries 13px of
     leading above the cap), and 100px from the body box to the next image.
     We sat at 80 above and 160 below, so the whole section hung too low in its
     own gap. The 160 came from .case-media's padding-TOP, which mobile already
     sets to 0 (line ~702) - desktop's 60 was the odd one out.
     The accordion keeps its own 60px bottom, so that FIRST heading lands at
     Figma's 182 rather than the 213 the media sections get; the design does
     treat the two differently, which is why this is not one shared number. */
  .single-case .case-media { padding-block: 0 72px; }
  .single-case .expertise-text { padding-top: 72px; }
  .case-about .case-about__body { padding-bottom: 0; }
}
/* The shell already steps its own rhythm 100 / 72 / 40 across the breakpoints
   (page-hero.css), so the media block mirrors it instead of holding 100 all the
   way down to 768 - that gave tablet 200px above the heading against only 72
   below. Above the heading is always twice the step: 200 desktop, 144 tablet. */
@media (min-width: 1025px) {
  .single-case .case-media { padding-block: 0 var(--space-100); }
  .single-case .expertise-text { padding-top: var(--space-100); }
}
/* Row: accordion on the left, company / case title on the right (static) */
.case-about-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 40px;
  padding-bottom: 0;   /* .container's own 50px — the section's 60px is the gap */
}
.case-about { border: none; flex: 1 1 auto; min-width: 0; }   /* no dividers / borders */
.case-about__client {
  flex: 0 0 auto;
  text-align: right;
  /* Body.Large straight from tokens.css — size, leading AND tracking; the
     tokens are responsive themselves (24/36 desktop → 20/28 mobile). */
  font-size: var(--body-lg);
  line-height: var(--body-lg-lh);
  letter-spacing: var(--body-lg-ls);
  color: var(--text-title);
}
.case-about__summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;        /* icon sits right next to the text */
  align-items: center;
  gap: 12px;
  padding: 0 0 24px;
  font-family: var(--font);
  font-size: var(--body-sm);
  line-height: var(--body-sm-lh);
  color: var(--text-title);
}
.case-about__summary::-webkit-details-marker { display: none; }

/* Revealed content — main.js animates the panel's HEIGHT on open/close, so
   the client name and everything below glide down/up instead of jumping.
   The motion is a combo: the text block RISES slightly (transform on the
   direct children, subheading a beat before the body) while a soft gradient
   curtain wipes the text in top-down (the mask rules below). Closing
   reverses fast so the collapse never outruns the text. */
/* overflow:hidden is PERMANENT, not just while animating: it keeps the last
   child's bottom margin inside the panel's height. Without it the settled
   auto-height is 20px shorter than the animated height (margin collapses out
   of the box the moment the inline overflow is cleared) and everything below
   jumps up 20px at the end of the open animation. */
.case-about__reveal { will-change: height; overflow: hidden; position: relative; }   /* offsetTop anchor for the shared sweep canvas */
/* Applied for a single frame while main.js reads scrollHeight: the children's
   pre-reveal translateY inflates the scrollable overflow, and measuring with
   it in place makes the panel animate ~24px too tall and snap at the end. */
.case-about.is-measuring .case-about__reveal > * { transform: none !important; transition: none !important; }
.case-about__reveal > * {
  transform: translateY(16px);   /* subtle — the horizontal sweep is the star */
  transition: transform 0.3s ease;
}
.case-about.is-revealed .case-about__reveal > * {
  transform: none;
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.case-about.is-revealed .case-about__reveal > *:nth-child(1) { transition-delay: 0.05s; }
.case-about.is-revealed .case-about__reveal > *:nth-child(2) { transition-delay: 0.12s; }
/* The GLINT IS THE REVEAL: each text element carries a background-clip:text
   gradient reading [transparent → light band → solid colour] with a diagonal
   edge. Sliding the gradient sweeps the shining band across the glyphs, and
   every letter lights up as the band passes — the text grows in reading order
   with the sheen as its front edge.
   DIRECTION (Kajsa 2026-10-05): the front travels from the TOP LEFT to the
   BOTTOM RIGHT. 345deg puts the solid end of the gradient toward the top left,
   and main.js slides the canvas DOWNWARDS, so the solid zone enters from above
   and the left edge of each line reaches it first. It used to be 165deg slid
   upwards, which ran bottom-up and right-to-left — the exact mirror. Reversing
   it means flipping BOTH: the angle by 180deg AND the parked ends in main.js.
   Changing only one of them just moves the band back the way it came.
   The image is 3× the element width: parked at 100% only the transparent
   tail covers the text (invisible); parked at 0% only the solid head does
   (plain type). One pass per open, never loops; closing slides it back out. */
/* ONE continuous front through the whole block: both elements carry the same
   gradient on a shared canvas — main.js sizes it to 5× the panel height (px)
   and offsets each element's window by its own position in the panel, so the
   rising band crosses body AND subheading as a single motion at a single
   speed (no per-element clocks, nothing lags). JS also drives the sweep by
   transitioning background-position between the parked ends.
   Gated on .has-anim (set by main.js): without JS the text stays plain and
   visible — the parked-transparent state must never apply then. */
/* The sweep's ink is a KNOB (--sweep-ink / --sweep-body) with the original
   values as fallbacks, so the cases are untouched. Services reuses this very
   accordion on DARK blocks, where a gradient built from --text-title would
   paint dark text on a dark ground. Flip the variables in the context, never
   these rules. */
.case-about.has-anim .case-about__subheading,
.case-about.has-anim .case-about__body {
  background-image: linear-gradient(345deg,
    transparent 44%,
    color-mix(in srgb, var(--sweep-ink, var(--text-title)) 40%, #fff) 50%,
    var(--sweep-ink, var(--text-title)) 56%);
  background-size: 100% 500%;
  background-position: 50% 0;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.case-about.has-anim .case-about__body {
  background-image: linear-gradient(345deg,
    transparent 44%,
    color-mix(in srgb, var(--sweep-body, var(--text-body)) 40%, #fff) 50%,
    var(--sweep-body, var(--text-body)) 56%);
}
@media (prefers-reduced-motion: reduce) {
  .case-about__reveal > *,
  .case-about.is-revealed .case-about__reveal > * { transition: none; transform: none; }
  .case-about.has-anim .case-about__subheading,
  .case-about.has-anim .case-about__body {
    background-image: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    -webkit-text-fill-color: currentColor;
    animation: none;
    transition: none;
  }
  .case-about.has-anim .case-about__subheading { color: var(--sweep-ink, var(--text-title)); }
  .case-about.has-anim .case-about__body { color: var(--sweep-body, var(--text-body)); }
}
.case-about__subheading {
  margin: 0 0 16px;
  font-size: var(--title-xs);
  line-height: var(--title-xs-lh);
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--text-title);
}
.case-about__icon {
  position: relative;
  width: 16px; height: 16px;
  flex-shrink: 0;
  color: var(--text-title);
  transition: transform 0.25s ease;
}
.case-about__icon::before,
.case-about__icon::after {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 14px; height: 2px;
  background: currentColor;
  transform: translate(-50%, -50%);
}
.case-about__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
/* + rotates to × (matches the side nav-drawer expand toggle); keeps its colour */
.case-about[open] .case-about__icon { transform: rotate(45deg); }
.case-about__body {
  padding-bottom: 32px;
  max-width: 760px;
  font-size: var(--body-sm);
  line-height: var(--body-sm-lh);
  color: var(--text-body);
}
.case-about__body p { margin: 0 0 1em; }
.case-about__body p:last-child { margin-bottom: 0; }

@media (max-width: 767px) {
  /* Figma mobile: media blocks have NO top padding — the preceding text
     widget's 40px bottom is the whole gap — and exactly 60px below. */
  .case-media { padding-block: 0 60px; }

  .case-mediagrid { grid-template-columns: 1fr; }
  .case-mediagrid__item--landscape,
  .case-mediagrid__item--portrait { aspect-ratio: 4 / 3; }

  /* Collage rows collapse to one column; offsets and indents reset. The 1+3
     collage keeps its two small tiles side by side (big + wide span both). */
  .case-collage--feature-left,
  .case-collage--feature-right,
  .case-collage--offset-left,
  .case-collage--offset-right,
  .case-collage--stagger { grid-template-columns: 1fr; grid-template-rows: none; }
  .case-collage--stagger .case-collage__cell,
  .case-collage--stagger.is-mirrored .case-collage__cell { grid-column: 1; }
  .case-collage--quad-left,
  .case-collage--quad-right { grid-template-columns: 1fr 1fr; grid-template-rows: none; }
  .case-collage--quad-left  .case-collage__big,
  .case-collage--quad-left  .case-collage__wide,
  .case-collage--quad-right .case-collage__big,
  .case-collage--quad-right .case-collage__wide { grid-column: 1 / 3; grid-row: auto; }
  .case-collage--quad-right .case-collage__tile:nth-child(3),
  .case-collage--quad-right .case-collage__tile:nth-child(4) { grid-column: auto; grid-row: auto; }
  /* A FLIPPED 1+3 otherwise keeps its DESKTOP placement here: the tiles stay
     pinned to columns 2 and 3, which conjures a third track on the two-column
     mobile grid and squeezes the big tile to nothing (measured 0.02px wide at
     375px, 2026-10-05). The resets must carry the same :nth-child specificity
     as the flip rules they override, or they lose. */
  .case-collage--quad-left.is-flipped  .case-collage__wide,
  .case-collage--quad-right.is-flipped .case-collage__wide { grid-column: 1 / 3; grid-row: auto; }
  .case-collage--quad-left  .case-collage__tile:nth-child(3),
  .case-collage--quad-left  .case-collage__tile:nth-child(4),
  .case-collage--quad-left.is-flipped  .case-collage__tile:nth-child(3),
  .case-collage--quad-left.is-flipped  .case-collage__tile:nth-child(4),
  .case-collage--quad-right.is-flipped .case-collage__tile:nth-child(3),
  .case-collage--quad-right.is-flipped .case-collage__tile:nth-child(4) { grid-column: auto; grid-row: auto; }
  /* An "Empty space" cell is deliberate AIR in the desktop collage. Stacked
     on a phone it reads as a missing image instead, so it is dropped here
     and the tile it was paired with takes the full width. */
  /* Every empty collage wrapper, not just the cells. A feature row whose
       stack slot is set to Empty space leaves a .case-collage__stack with no
       children: it measures 0 high, so it looks harmless, but it is still a
       flex/grid child and therefore still eats a full 20px gap. On mobile,
       where everything is one column, that reads as an unexplained hole. */
  /* ALL SIX collage wrappers, enumerated from inc/media-render.php. Only
     cell/stack/tile were listed before, so a feature row left a 0-high
     .case-collage__stack still eating a 20px gap (Carplus) and a quad row
     left a visible 343x172 .case-collage__wide hole (TVG). Add a wrapper to
     the renderer and it has to be added here too. */
  .case-collage__big:empty,
  .case-collage__cell:empty,
  .case-collage__narrow:empty,
  .case-collage__stack:empty,
  .case-collage__tile:empty,
  .case-collage__wide:empty { display: none; }
  .case-collage--quad-left:has(.case-collage__tile:empty)  .case-collage__tile:not(:empty),
  .case-collage--quad-right:has(.case-collage__tile:empty) .case-collage__tile:not(:empty) { grid-column: 1 / 3; }
  .case-collage--quad-left .case-collage__big .case-mediagrid__item,
  .case-collage--quad-right .case-collage__big .case-mediagrid__item { aspect-ratio: 3 / 4; height: auto; }
  .case-collage__big,
  .case-collage--feature-right .case-collage__big,
  .case-collage--feature-right .case-collage__stack:nth-child(2),
  .case-collage--feature-right .case-collage__stack:nth-child(3) { grid-column: auto; grid-row: auto; }
  .case-collage--quad-left .case-collage__big,
  .case-collage--quad-right .case-collage__big { grid-column: 1 / 3; }
  .case-collage__stack .case-mediagrid__item { aspect-ratio: 3 / 2; height: auto; }
  /* …but an explicit shape wins on mobile too (a square quote card must not be
     cropped to 3:2). */
  .case-collage__stack .case-mediagrid__item--square      { position: static; height: auto; aspect-ratio: 1 / 1; width: 100%; }
  .case-collage__stack .case-mediagrid__item--landscape43 { aspect-ratio: 4 / 3; }
  .case-collage__stack .case-mediagrid__item--portrait    { aspect-ratio: 3 / 4; }
  .case-collage__stack .case-mediagrid__item--portrait45  { aspect-ratio: 4 / 5; }
  .case-collage--offset-left  .case-collage__cell:first-child,
  .case-collage--offset-right .case-collage__cell:last-child { margin-top: 0; }
  .case-collage--air-airy { padding-inline: var(--margin-primary); gap: 20px; }
  .case-mediagrid.case-collage--air-airy { padding-inline: var(--margin-primary); gap: 20px; }
  .case-collage--solo .case-collage__cell { width: 100%; }
  .case-about__subheading { font-size: var(--title-sm); line-height: var(--title-sm-lh); }
  /* Figma mobile spec: 24px above the client name, 20px between the name and
     "More about the client", 32px below; sides come from .container (16px). */
  .case-about-section { padding-top: 24px; padding-bottom: 32px; }
.case-about{order: 1;}
  .case-about-row { gap: 20px; flex-direction: column; padding-bottom: 0; }
  .case-about__client { text-align: left; order: 0; }   /* type comes from the responsive tokens */
  /* Tags stack vertically on mobile (Figma mobile spec) */
  .case-hero-x__meta { flex-direction: column; align-items: flex-start; }
      .expertise-text{
        padding-block: 40px;
      }
}

/* ── Related Work — dark section between Contact and Footer (Figma 2982-9463) ── */
.case-related { background-color: #1C1B1F; padding-block: var(--space-100) var(--space-60); }
.case-related__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 20px;
  row-gap: 48px;
  align-items: start;
}
.case-related__heading {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--title-md);
  line-height: var(--title-md-lh);
  font-weight: 400;
  letter-spacing: -0.02em;
  color: #F0F1FA;
}
/* The cards are the shared .case-card component (cards.css). The bespoke
   item, its image zoom and its dot-to-arrow used to live here; they were
   dropped on 2026-10-05 so Related Work matches every other card grid, with
   the liquid edge and the water ripple included. Only the band and its grid
   belong to this page now. */

@media (max-width: 767px) {
  .case-related { padding-block: 60px 40px; }
  .case-related__grid { grid-template-columns: 1fr; row-gap: 32px; }
  .case-related__heading { font-size: var(--title-sm); line-height: var(--title-sm-lh); }
}


/* ==========================================================================
   TILLFALLIGT TEST - bara TVG-caset (body.postid-794), Kajsa 2026-10-05
   ==========================================================================
   "Vad hander om vi tar bort mittfaran?" Mediaraderna slutar lyda
   --container-max (1340px) och gar ut till skarmkanten i stallet, med den
   60px-gutter .container redan har. Full-bleed-rader paverkas inte - de var
   redan kant till kant.

   TEXTEN ar MEDVETET orord. Vid 1920 blir en full bredd-paragraf ~1800px per
   rad, vilket ar langt forbi lasbar radlangd - att halla texten i en kolumn ar
   precis vad mittfaran ar till for. Bred media mot mattfull text ar en vanlig
   redaktionell kombination, sa det ar den halvan som ar vard att prova.

   Ta bort hela blocket for att aterstalla. Inget annat rordes. */
/* Contained-raderna ar MED igen (Kajsa 2026-10-06). De ar rundade rutor, och
   da ska de ligga en gutter fran skarmkanten i stallet for att sitta i
   mittfaran: utan taket blir de 1440 breda med `padding-inline:
   var(--margin-primary)`, alltsa 60px luft pa varje sida och 1320 bild - exakt
   Figmas matt. Med taket blev bilden 1220 bred och lag 110px in.
   Rundningen foljer med och ska gora det; det ar det som skiljer en contained
   rad fran en full-bleed, som har radie 0 och gar kant till kant. */
body.postid-794 .case-media .container,
body.postid-794 .case-media__full--contained {
  max-width: none;
}


/* Scroll glint, per-letter (.rv) and line (.ln) reveals live in components/reveal.css. */


/* -- Contained full row: Figma's box is 1320x810 ---------------------------
   A contained row takes its height from the image itself, so a tall or
   portrait source ran the row far past a screen - the Frolunda coaster shot
   came out absurdly tall (Kajsa 2026-10-08). 810 is Figma's own height for
   this row, and it is a FLAT cap on purpose (Kajsa 2026-10-08): with
   min(810px, 100vh) the row height changed with the window, so the same case
   looked different on every screen. 810 everywhere, rounded corners and all.
   Same specificity as the base .case-media__full rules (0,2,0), so this has
   to stay LAST in the file to win. Embeds are untouched: a contained tile is
   at most ~1340 wide, and 16:9 of that is 754 - always under the cap. */
.case-media__full--contained .case-mediagrid__item,
.case-media__full--contained .case-mediagrid__item img,
.case-media__full--contained .case-mediagrid__item video {
  max-height: 810px;
}

/* Film fit = Contain: hand the box back to the player, which fits the film
   inside it and draws its own bars. Same geometry as an interactive embed,
   and the opposite of the default cover formula. */
.case-embed.is-fit-contain iframe {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
}


/* -- Tile labels and colour blocks (2026-10-12) -------------------------
   Text ON a tile is real HTML (a <figcaption>), never baked into the
   image, so it stays editable, selectable, translatable and crisp at every
   pixel density. Both are per-slot options on the SHARED media builder, so
   the cases, Always On, About, Career and Services all get them.
   The palette is a fixed list in media-render.php -- the editor cannot
   invent a colour, which is how a design system keeps one palette. */
.case-media__item--labelled { position: relative; }
.case-media__label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 24px;   /* Figma 6528:7938, measured on the card */
  color: var(--text-light);
  font-size: var(--title-card);
  line-height: var(--title-card-lh);
  letter-spacing: var(--title-card-ls);
  text-wrap: balance;
  pointer-events: none;   /* never swallows a click on a play-on-click film */
}

/* A painted box in place of an image. No absolute positioning: the figure
   already has an aspect-ratio, so 100%/100% fills it and a colour tile
   WITHOUT a label does not need a positioning context at all. */
.case-media__fill { display: block; width: 100%; height: 100%; }
.case-media__fill--blue  { background: var(--dragster-blue); }
.case-media__fill--dark  { background: var(--bg-dark); }
.case-media__fill--deep  { background: var(--bg-secondary); }
.case-media__fill--white { background: var(--bg-white); }
/* White block: the label has to flip or it is white on white. */
.case-media__fill--white ~ .case-media__label { color: var(--text-title); }

/* ── Contained full row + landscape embed: a DEFINITE width (Kajsa 2026-10-14) ──
   The contained cap (max-height: 810px, further up) assumed a contained tile is at
   most ~1340 wide, so its 16:9 box (754) stays under the cap and "embeds are
   untouched". That stopped being true when --container-max became 100vw: past
   ~1560px the row is wider than 810 * 16/9 = 1440, and with only aspect-ratio +
   max-height the cap shrinks the WIDTH to keep 16:9. Measured on Stallbacka at
   1626: a 1440 box in a 1506 row, with a gap beside it while the image row below
   filled the row. Same trap as the --crop rule (width: 100% is load-bearing).
   A definite width makes aspect-ratio set the HEIGHT, which the cap clamps, so the
   box fills the row like an image does. The player COVERS the box (the formula the
   collage tiles use) instead of stretching into it: the --natural stretch rule has
   no :not(--contained) and would pillarbox the film once the box is off-ratio.
   Square and portrait embeds are left alone - their ratio is the point of their
   box. Under ~1560px nothing changes: the box is already under the cap. */
.case-media__full--contained .case-mediagrid__item:has(.case-embed:not(.case-embed--square):not(.case-embed--portrait)) { width: 100%; }
.case-media__full--contained .case-embed:not(.case-embed--square):not(.case-embed--portrait) iframe { top: 50%; left: 50%; transform: translate(-50%, -50%); width: max(100cqw, calc(100cqh * var(--embed-r, 1.7778))); height: max(100cqh, calc(100cqw / var(--embed-r, 1.7778))); }

/* Rounded (contained) rows: ONE height rule for every rounded row, image, video and embed alike (Kajsa 2026-10-15). 810 up to 1599px wide (laptops), 1080 from 1600px. Breaks on WIDTH, not window height, so the same case looks the same on the same screen width. Same specificity as the 810 rule far above, so it wins on source order and must stay LAST. */
@media (min-width: 1600px) {
  .case-media__full--contained .case-mediagrid__item,
  .case-media__full--contained .case-mediagrid__item img,
  .case-media__full--contained .case-mediagrid__item video {
    max-height: 1080px;
  }
}

/* Landscape embeds in rounded rows: cap 1080 at EVERY width (Kajsa 2026-10-19). The 810 cap above is right for images, but a film in a row wider than ~1440 (810 * 16/9) was held to 810 and could not grow; with the definite width: 100% rule above the box now fills the whole row and gets taller with it, up to 1080. Square/portrait embeds keep their own box. Must stay LAST: it out-specifies the 810 rule, but the 1600px block above also sets 1080 on the item, so order keeps the intent obvious. */
.case-media__full--contained .case-mediagrid__item:has(.case-embed:not(.case-embed--square):not(.case-embed--portrait)) {
  max-height: 1080px;
}
