/* ==========================================================================
   IMAGE AND TEXT CALLOUT

   This one stylesheet is loaded in two places on purpose:

   1. In the page head, because the image, the text and the flag are all
      server-rendered into light DOM slots so the copy is in the page source for
      crawlers and stays readable if the custom element never upgrades.
   2. Inside the shadow root, because MMX_Element.renderStylesheetLinks() collects
      head <link data-resource-code> elements and the component lists
      "image-gallery-callout" in styleResourceCodes.

   The LIGHT DOM section styles the slotted image, text, button and flag. The
   SHADOW DOM section styles the layout grid, the background band and the panel.

   Every state attribute lives on the HOST element, so the shadow rules read it
   through :host() and the light DOM rules read it as a plain ancestor selector.
   That is what lets a single setting reach both trees. Custom properties are set
   on the host by the component JS and inherit into both.

   Nothing here uses ::slotted(). For a slotted element the outer document's rules
   beat the shadow tree's ::slotted rules at equal specificity, so any geometry
   declared in both places would silently lose. The slotted elements carry light
   DOM classes and are declared once, below.
   ========================================================================== */


/* ==========================================================================
   LIGHT DOM - the host element, slotted image, text, button and flag
   ========================================================================== */

/* Full browser width
   ========================================================================== */

/* Opt in only, from the Full Browser Width checkbox. Same technique the store already uses
   on mmx-hero and the global header: pull each side out by the difference between half the
   containing block and half the viewport, so the element spans the window however narrow
   the column it was dropped into is.

   This has to be a LIGHT DOM rule rather than :host(), because the host sits in the outer
   tree - and for the host, the outer document's rules beat anything the shadow sheet says
   whatever the specificity, so a :host() version would lose to the theme's own width rules
   and silently do nothing.

   The attribute is repeated to raise specificity to 0,2,1. The theme clamps this element's
   ancestors with two-class selectors like .o-wrapper.t-global-sequence at 0,2,0, and if the
   host itself ever picks up such a pair a plain element+attribute rule at 0,1,1 would lose
   to it. This is the same reason the store's own rule chains .o-wrapper.t-fragment__global-header
   rather than using the single class.

   Both "true" and "1" are matched: the JS normalises the attribute to true/false, but
   instance.mvt paints the raw checkbox value server-side, and this is the one setting whose
   pre-upgrade state is a visible layout jump rather than a detail. */

image-gallery-callout[data-full-width="true"][data-full-width],
image-gallery-callout[data-full-width="1"][data-full-width] {
	display: block;
	margin-left: calc( 50% - 50vw );
	margin-right: calc( 50% - 50vw );
	max-width: 100vw;
	width: 100vw;
}

/* Image
   ========================================================================== */

/* mmx does not give <picture> a display, so it is inline by default and picks up
   a text-node baseline gap under the image. Making it a block removes it. */

/* The shadow goes on the IMAGE, not the <picture> around it. The radius is on the image, and a
   shadow follows the border box of the element it is declared on - put it on the picture and a
   rounded image would sit inside a square shadow.

   The whole value arrives as one custom property from #imageShadow(), so switching the group off
   removes the property and this falls back to none. Nothing needs to know the parts. */

.image-gallery-callout__picture {
	display: block;
	height: auto;
	width: 100%;
}

.image-gallery-callout__image {
	border-radius: var(--image-gallery-callout__image-radius, 0);
	box-shadow: var(--image-gallery-callout__image-shadow, none);
	display: block;
	height: auto;
	width: 100%;
}

/* With a fixed image height the image has to crop or letterbox to fill it, which
   is what Image Fit chooses between. At Image Height "Defined by Image Size"
   there is no fixed box, so object-fit has nothing to do and is left off. */

/* Every Image Height other than "Defined by Image Size" hands the image a box whose
   height it did not choose, which is exactly when Image Fit means something.
   "Match the Text Height" is in this list for the same reason - the box is the
   text's height rather than a fixed number, but it is still a box to fill.

   Without object-fit the image would stretch out of proportion rather than
   cropping or letterboxing, so the two settings are declared together and Image
   Fit is hidden in Page Builder for the one value that has no box. */

image-gallery-callout[data-image-size="s"] .image-gallery-callout__image,
image-gallery-callout[data-image-size="m"] .image-gallery-callout__image,
image-gallery-callout[data-image-size="l"] .image-gallery-callout__image,
image-gallery-callout[data-image-size="match"] .image-gallery-callout__image {
	height: 100%;
	object-fit: var(--image-gallery-callout__image-fit, cover);
}

/* object-fit needs a box with a definite height to fit inside. The picture is that
   box, and percentage heights on it resolve against the media cell, so it has to be
   told to fill the cell or "Fit Inside the Area" has nothing to letterbox against
   and silently behaves like no setting at all. */

image-gallery-callout[data-image-size="s"] .image-gallery-callout__picture,
image-gallery-callout[data-image-size="m"] .image-gallery-callout__picture,
image-gallery-callout[data-image-size="l"] .image-gallery-callout__picture,
image-gallery-callout[data-image-size="match"] .image-gallery-callout__picture {
	display: block;
	height: 100%;
}

/* The rule above resolves its 100% against the media cell, which the in-flow placements do
   not render - so it comes out as auto and the image falls back to its natural size. A
   single one then ends up as tall as the container is wide, and Image Fit has no box to fit
   inside, so neither Fill nor Fit does anything at all.

   The height therefore lands on each picture instead, as a real length from the JS.
   Declared after the rule above so it wins at equal specificity, and the three placements
   are enumerated rather than using :not() so this cannot touch the default even before the
   element upgrades and the attribute exists. */

image-gallery-callout[data-image-placement="top"] .image-gallery-callout__picture,
image-gallery-callout[data-image-placement="between"] .image-gallery-callout__picture,
image-gallery-callout[data-image-placement="bottom"] .image-gallery-callout__picture {
	height: var(--image-gallery-callout__image-height, auto);
}

/* "Match the Text Height" is the one Image Height whose media cell has no height of its own: it
   is a stretched grid item, so its height IS the row's. That makes the picture rule above
   circular - a percentage height resolved against an auto-height parent computes to auto, so
   height: 100% collapses all the way down to the image's INTRINSIC height. That height then
   feeds the row, the panel stretches to match it, and the copy floats in the middle of a band
   far taller than itself. Which is the exact opposite of matching the text, and no amount of
   reducing Text Padding touches it, because the space is not padding at all.

   Taking the picture out of flow breaks the circularity. An out-of-flow child contributes
   nothing to its parent's intrinsic size, so the row is sized by the panel alone; the media
   cell's stretched height is then definite, and inset: 0 fills it exactly.

   Scoped to a single image beside the text. In a gallery several pictures share the media
   column, where absolute positioning would stack them on top of one another, so the gallery
   stays on the in-flow path. data-gallery="false" rather than :not([data-gallery="true"])
   because both the template and the JS always write the attribute explicitly. */

image-gallery-callout[data-image-placement="beside"][data-image-size="match"][data-gallery="false"] .image-gallery-callout__picture {
	height: 100%;
	inset: 0;
	position: absolute;
	width: 100%;
}


/* Text
   ========================================================================== */

/* mmx-text has no host display of its own, so each of these would run inline and
   the three would collapse onto one line. */

.image-gallery-callout__subheading,
.image-gallery-callout__heading,
.image-gallery-callout__body {
	display: block;
}

/* Each element's own alignment, independent of Text Alignment. The fallback is `inherit`, not a
   value: at "Match Text Alignment" the JS publishes nothing, the declaration resolves to inherit,
   and the element takes the panel's text-align exactly as it did before these controls existed.
   Every saved instance therefore renders unchanged.

   text-align inherits across the shadow boundary, so setting it on the mmx-text host reaches the
   text inside its shadow root without needing a ::part() hook.

   These are the ragged edge only. Which side the copy's BLOCK sits on is the band's business and
   is handled by the clamp further down - so a right-aligned heading inside a left-anchored band
   stays inside the colour rather than sliding out of it. */

.image-gallery-callout__subheading {
	margin: 0 0 0.5rem;
	text-align: var(--image-gallery-callout__subheading-align, inherit);
}

.image-gallery-callout__heading {
	margin: 0 0 1rem;
	text-align: var(--image-gallery-callout__heading-align, inherit);
}

.image-gallery-callout__body {
	margin: 0;
	text-align: var(--image-gallery-callout__body-align, inherit);
}


/* Links
   ========================================================================== */

/* A real list, so the set is announced as N items and the bullet is the list's own marker. The
   padding-inline-start is the room the marker needs to sit in - list-style-position stays outside,
   which is what keeps a wrapped second line indented under the first rather than under the bullet.

   A top margin rather than a gap on the content column, so a links group left switched off costs
   nothing - a gap would reserve space between the copy and a list that never arrived. */

.image-gallery-callout__links {
	display: flex;
	flex-direction: column;
	list-style-position: outside;
	margin: 1rem 0 0;
	padding-inline-start: 1.25em;
	row-gap: var(--image-gallery-callout__link-gap, 16px);
	text-align: var(--image-gallery-callout__link-align, inherit);
}

/* Side by side wraps onto as many lines as it needs, with the same one gap value doing duty in both
   axes so "Space Between Links" means the same thing whichever layout is chosen. */

image-gallery-callout[data-link-flow="inline"] .image-gallery-callout__links {
	column-gap: var(--image-gallery-callout__link-gap, 16px);
	flex-direction: row;
	flex-wrap: wrap;
	justify-content: var(--image-gallery-callout__link-justify, flex-start);
}

/* No marker also gives up the indent it was reserving, otherwise the list would sit a stop to the
   right of the copy above it for no visible reason. Side by side likewise: bullets strung along a
   row read as separators rather than list markers. */

image-gallery-callout[data-link-marker="none"] .image-gallery-callout__links,
image-gallery-callout[data-link-flow="inline"] .image-gallery-callout__links {
	list-style: none;
	padding-inline-start: 0;
}

.image-gallery-callout__link-item {
	margin: 0;
}

/* A plain inline box, NOT inline-flex.

   inline-flex blockifies its children, and text-decoration propagated from an ancestor is then
   drawn separately across each flex item's own box rather than once across the line. The label and
   the arrow each got their own underline with the margin between them left undecorated, so the rule
   came through visibly broken - and because align-items centres two items whose baselines differ,
   the two segments could sit at different heights as well.

   Inline puts them on one baseline and lets a single underline run across the label, the margin and
   the arrow as one continuous rule. It also matches how the button's arrow already works.

   The decoration is declared on the ANCHOR rather than on the label for the same reason: one box
   drawing one line. It is also stated explicitly in both directions rather than left to the theme,
   because a store-level `a:hover` at 0,1,1 outranks a single class here and would otherwise decide
   this on its own. */

.image-gallery-callout__link {
	display: inline;
	text-decoration: none;
}

.image-gallery-callout__link:hover,
.image-gallery-callout__link:focus-visible {
	text-decoration: underline;
}

/* The arrow is a light DOM child of the mmx-text label, NOT a sibling of it, which is what keeps
   the two on one line.

   mmx-text renders its content inside an element carrying a .type-* class, and mmx-text's own
   stylesheet gives every one of those - .type-paragraph-l included - display: block. So the label
   is a block-level box in the anchor's flow, and a sibling arrow after it had nowhere to go but the
   next line. Nothing about the anchor could fix that: the break was between two boxes, one of which
   was a block.

   Inside the label the arrow is just another inline in the same text flow, so there is no box
   boundary to break at, and the underline runs through it as one rule. This is exactly how the
   button's arrow already works - it is emitted inside mmx-button rather than after it - and the
   link was the odd one out.

   The colour is left to inherit rather than being given one of its own, so it follows the label's
   typography instead of drifting the moment that changes. */

.image-gallery-callout__link-arrow {
	margin-left: 0.35em;
}

/* No top margin: the button lives in the callout stack now, and the stack's row-gap is
   what separates it from the flag. A margin here would be added on top of that gap and
   would still be there when the flag is absent. */

.image-gallery-callout__button {
	display: inline-block;
	vertical-align: top;
}

/* The arrow is a light DOM child of <mmx-button>, so it is slotted into the button's own
   shadow root and lands inside the anchor - which is what makes it inherit the button
   theme's colour, weight and font without being told any of them.

   It cannot be a ::part(button)::after instead: mmx-button only emits part="button" on
   its legacy render path, and the themed path - which is the one a Button Theme uses -
   has no part at all, so there would be nothing to hang a pseudo-element on.

   instance.mvt emits it immediately after the label with no whitespace between them, so
   this margin is the whole of the gap. */

.image-gallery-callout__button-arrow {
	display: inline-block;
	margin-left: 0.4em;
}

/* Every typography theme in the Theme Editor carries its own color, and mmx-text
   injects that theme stylesheet INSIDE its own shadow root, so the color wins over
   anything inherited from the panel. On a dark background that leaves dark text on
   a dark panel, which reads as no text at all.

   ::part() rules written in this outer tree take precedence over rules inside the
   shadow tree, so this is the one hook that can reach in and reassert a chosen
   color. It applies only when a Text Color was actually set - hence the
   data-has-text-color gate on the host - and steps aside for any field that has an
   explicit per-field Font Color, which instance.mvt marks with data-own-color. */

image-gallery-callout[data-has-text-color] .image-gallery-callout__subheading:not([data-own-color])::part(text__inner),
image-gallery-callout[data-has-text-color] .image-gallery-callout__heading:not([data-own-color])::part(text__inner),
image-gallery-callout[data-has-text-color] .image-gallery-callout__body:not([data-own-color])::part(text__inner) {
	color: var(--image-gallery-callout__text-color);
}


/* Callout flag
   ========================================================================== */

/* The flag's own colors, padding and point width are declared here as real custom
   properties rather than as var() fallbacks, so an unconfigured flag still paints
   navy with white text. instance.mvt emits each one inline only when the editor
   set it, and an inline declaration outranks this rule.

   These are safe to declare on the element because they are the flag's own
   properties. The host-set layout properties must never be declared here, because
   a declaration on a descendant shadows the value the JS puts on the host. */

.image-gallery-callout__callout {
	--image-gallery-callout__flag-background: #26213f;
	--image-gallery-callout__flag-color: #ffffff;
	--image-gallery-callout__flag-point: 25px;
	--image-gallery-callout__flag-padding-top: 1px;
	--image-gallery-callout__flag-padding-right: 24px;
	--image-gallery-callout__flag-padding-bottom: 1px;
	--image-gallery-callout__flag-padding-left: 24px;

	align-items: center;
	background-color: var(--image-gallery-callout__flag-background);
	border-radius: var(--image-gallery-callout__flag-radius, 0);
	box-sizing: border-box;
	color: var(--image-gallery-callout__flag-color);
	display: inline-flex;
	justify-content: center;
	max-width: 100%;
	padding:
		var(--image-gallery-callout__flag-padding-top)
		var(--image-gallery-callout__flag-padding-right)
		var(--image-gallery-callout__flag-padding-bottom)
		var(--image-gallery-callout__flag-padding-left);
	text-align: center;
	text-decoration: none;
}

/* The point is cut out of the flag rather than added to it, so the flag's box
   stays a plain rectangle and the surrounding layout is unaffected by it.

   The apex sits at the bottom corner, matching the mockups: the top edge stops
   short by the point width and the right edge runs down to the full width at the
   baseline.

   The point's width is added to the padding on whichever side it is on, rather than
   being folded into Flag Padding itself. That keeps the editor's four padding
   numbers symmetrical and meaning what they say, and it is what centres the label in
   the part of the flag that is actually square - previously the padding had to
   compensate for the cut by hand, which left the label sitting off to one side and
   moved every time the point width changed. */

/* Each of these is the straight-edged polygon, with --flag-clip as an override. A
   polygon cannot have rounded vertices and border-radius rounds the box's corners
   rather than the shape's, so the tip of the point stays sharp however much radius is
   applied - which is why a Flag Corner Radius makes the JS replace the whole thing
   with a measured path() carrying arcs at every vertex. With no radius set the
   property is never written and these are what run, so the common case stays pure
   CSS.

   Flipping moves the inset from the bottom edge to the top, turning the point upside
   down for a flag sitting in a top corner. */

.image-gallery-callout__callout[data-point="right"] {
	clip-path: var(
		--image-gallery-callout__flag-clip,
		polygon(0 0, calc( 100% - var(--image-gallery-callout__flag-point) ) 0, 100% 100%, 0 100%)
	);
	padding-right: calc( var(--image-gallery-callout__flag-padding-right) + var(--image-gallery-callout__flag-point) );
}

.image-gallery-callout__callout[data-point="right"][data-flip="1"] {
	clip-path: var(
		--image-gallery-callout__flag-clip,
		polygon(0 0, 100% 0, calc( 100% - var(--image-gallery-callout__flag-point) ) 100%, 0 100%)
	);
}

.image-gallery-callout__callout[data-point="left"] {
	clip-path: var(
		--image-gallery-callout__flag-clip,
		polygon(var(--image-gallery-callout__flag-point) 0, 100% 0, 100% 100%, 0 100%)
	);
	padding-left: calc( var(--image-gallery-callout__flag-padding-left) + var(--image-gallery-callout__flag-point) );
}

.image-gallery-callout__callout[data-point="left"][data-flip="1"] {
	clip-path: var(
		--image-gallery-callout__flag-clip,
		polygon(0 0, 100% 0, 100% 100%, var(--image-gallery-callout__flag-point) 100%)
	);
}

.image-gallery-callout__callout-label {
	display: inline-block;
}

/* The flag always has a background of its own, so unlike the panel text this is
   unconditional - the label would otherwise take the typography theme's color and
   disappear into the navy. */

.image-gallery-callout__callout-label:not([data-own-color])::part(text__inner) {
	color: var(--image-gallery-callout__flag-color);
}

.image-gallery-callout__callout:focus-visible {
	outline: 2px solid var(--image-gallery-callout__flag-background);
	outline-offset: 3px;
}


/* ==========================================================================
   SHADOW DOM - layout grid, background band, panel
   ========================================================================== */

:host {
	display: block;
}

[part~="wrapper"] {
	box-sizing: border-box;
	margin-inline: auto;
	max-width: var(--image-gallery-callout__max-width, 1440px);
	position: relative;
	width: 100%;
}

/* Widening the host is only half of it: the wrapper carries the background band and the grid,
   so leaving its cap in place would let the host span the window while everything inside it
   stayed centred at 1440px - the checkbox would appear to do nothing at all. Maximum Width is
   hidden in Page Builder while this is on for the same reason. */

:host([data-full-width="true"]) [part~="wrapper"],
:host([data-full-width="1"]) [part~="wrapper"] {
	max-width: none;
}

/* Background Vertical Extension is block padding on the wrapper rather than a
   negative inset on the band itself.

   The band is pinned to the wrapper's top and bottom, so the wrapper growing is what
   makes the band taller - and it makes it taller than the image by exactly this
   amount on each side. Doing it with negative insets on the band would have grown
   the band out of the wrapper and underneath whatever component sits above or below
   it on the page.

   Only applied when the Text Background group is on, otherwise every instance would
   carry vertical padding for a band that is not there. */

:host([data-background="true"]) [part~="wrapper"] {
	padding-block: var(--image-gallery-callout__background-vertical, 40px);
}

/* With a gap the band is clamped to the text column, so it is not behind the image any
   more and there is nothing for it to stand taller than - the image should match it
   exactly. Padding on the wrapper cannot do that, because the band includes the padding
   and the grid sits inside it, leaving a stretched image permanently shorter than the
   band by the extension on each edge. So with a gap the extension comes off the wrapper
   and is folded into the panel's block padding by the JS instead, which insets only the
   copy. */

:host([data-background="true"][data-separate="true"]) [part~="wrapper"] {
	padding-block: 0;
}


/* The layout grid
   ========================================================================== */

/* The two columns come from the host as a ready-made pair, because the fr values
   have to be swapped along with the grid-column assignments - otherwise flipping
   the image to the right side would also hand the image the text's share of the
   width.

   fr rather than percentages because the gap is a length: fr units divide what is
   left after the gap comes out, so the columns still add up whatever it is set
   to. */

[part~="layout"] {
	column-gap: var(--image-gallery-callout__gap, 0px);
	display: grid;
	grid-template-columns: var(--image-gallery-callout__columns, 50fr 50fr);
	position: relative;
}

/* The media cell is placed in its row by align-self rather than stretched into it,
   because an image with a fixed height in a row made tall by the text would
   otherwise sit at the top of that row with no way to move it. "Match the Text
   Height" is the stretch case and is the only value that hands the image a height
   it did not choose. */

[part~="media"] {
	align-self: center;
	grid-column: 1;
	grid-row: 1;
	min-height: 0;
	min-width: 0;
}

:host([data-image-vertical-align="flex-start"]) [part~="media"] {
	align-self: start;
}

:host([data-image-vertical-align="center"]) [part~="media"] {
	align-self: center;
}

:host([data-image-vertical-align="flex-end"]) [part~="media"] {
	align-self: end;
}

[part~="panel"] {
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	grid-column: 2;
	grid-row: 1;
	justify-content: var(--image-gallery-callout__vertical-align, center);
	min-width: 0;

	/* One side at a time rather than a shorthand. The block values are the single
	   source of truth for the panel's vertical padding: the flag clearance subtracts
	   them, and the background's vertical extension is folded into them by the JS when
	   there is a gap. A shorthand plus overrides would mean two places computing the
	   same number. */
	padding-block: var(--image-gallery-callout__panel-padding-top, 56px) var(--image-gallery-callout__panel-padding-bottom, 56px);
	padding-inline: var(--image-gallery-callout__panel-padding-left, 56px) var(--image-gallery-callout__panel-padding-right, 56px);
	position: relative;
	text-align: var(--image-gallery-callout__text-align, left);
	z-index: 1;
}

:host([data-image-position="right"]) [part~="media"] {
	grid-column: 2;
}

:host([data-image-position="right"]) [part~="panel"] {
	grid-column: 1;
}

/* Text Padding's two side values MIRROR with Image Position, so they follow the text rather than
   the screen. The first one is always the outer edge of the component, the second is always the
   edge facing the image.

   Without this the padding is literal, and the shipped default of 0 on the left and 56 on the
   right - which is right for the default layout, text on the left hugging the container edge with
   breathing room next to the image - lands exactly backwards the moment the image moves to the
   left: the 0 ends up against the image and the 56 against the outer edge. Everything else about
   Image Position already mirrors, the grid columns above and the band's anchor further down, so
   the padding staying put made the flip look half-applied.

   Which side the band anchors to is the reference: image right anchors the band left, image left
   anchors it right, so "outer edge" means left in the first case and right in the second. */

:host([data-image-position="left"]) [part~="panel"] {
	padding-inline: var(--image-gallery-callout__panel-padding-right, 56px) var(--image-gallery-callout__panel-padding-left, 56px);
}

/* Overlaying puts both cells in the same column span and stacks them, with the
   panel on top. The panel keeps its side, so the text still sits on whichever half
   the Image Position setting implies - it just has image behind it now. */

:host([data-overlay="true"]) [part~="media"] {
	grid-column: 1 / -1;
	grid-row: 1;
}

:host([data-overlay="true"]) [part~="panel"] {
	grid-row: 1;
}

/* Image Pop-Out Above & Below.

   The panel is inset, the image is not. Nothing has a negative margin and nothing overflows: the
   row's height is set by whichever column is taller, so pulling the copy in by this much on each
   side leaves the image spanning the full row and standing proud of the text block by exactly that
   amount top and bottom. In Match the Text Height the media stretches to the row, so it grows with
   the row rather than being left behind.

   Doing it the other way - negative block margins on the media - would have made the image overflow
   the component and collide with whatever sits above and below, and would have needed the clipping
   on the media cell released, which is what keeps Image Corner Radius from cropping square.

   margin rather than padding, because the panel's block padding is already the single source of
   truth for the flag clearance calculation and adding to it would have moved that goalpost.

   One shorthand with two values rather than margin-block-start and -end separately: margin-block
   takes block-start then block-end, so the two custom properties land on the right sides and there
   is still only one declaration to cancel on phones. */

:host([data-image-placement="beside"]) [part~="panel"] {
	margin-block: var(--image-gallery-callout__image-popout-top, 0px) var(--image-gallery-callout__image-popout-bottom, 0px);
}

/* Image heights. The media cell is what gets the height; the image fills it and
   crops or letterboxes according to Image Fit. */

:host([data-image-size="s"]) [part~="media"] {
	height: 240px;
}

:host([data-image-size="m"]) [part~="media"] {
	height: 360px;
}

:host([data-image-size="l"]) [part~="media"] {
	height: 480px;
}

/* "Match the Text Height" is a value of Image Height, so there is no fixed height to
   contradict. The cell stretches into the row the text made and Image Vertical
   Position has nothing left to do, which is why it is declared here rather than
   left to that setting. */

:host([data-image-size="match"]) [part~="media"] {
	align-self: stretch;
	height: auto;
}

/* The containing block for the out-of-flow picture, so inset: 0 fills the stretched cell rather
   than the nearest positioned ancestor further up. overflow: hidden so the image is clipped to
   the cell instead of painting past it - with the picture out of flow there is nothing else to
   stop it, and Image Corner Radius on the image would otherwise leave the crop square.

   Gated identically to the light DOM rule above. If these two ever disagree the picture goes
   absolute against some ancestor of the component and lands somewhere arbitrary on the page,
   which is why both carry the same three conditions rather than one being loosened. */

:host([data-image-placement="beside"][data-image-size="match"][data-gallery="false"]) [part~="media"] {
	overflow: hidden;
	position: relative;
}


/* The gallery
   ========================================================================== */

/* display: contents until there is more than one image, so a single image is a direct
   child of the media cell and its geometry is untouched by anything below. Every rule in
   this section is gated on data-gallery="true", which the JS only sets above one image -
   so the single-image case cannot reach any of them whatever else is configured.

   The grid follows mmx-image-across, which is the shipped component that solves this same
   problem, rather than anything invented here. */

[part~="gallery"] {
	display: contents;
}

:host([data-gallery="true"]) [part~="gallery"] {
	display: grid;
	gap: var(--image-gallery-callout__image-gap, 16px);
	grid-template-columns: repeat(var(--image-gallery-callout__gallery-columns, 1), 1fr);
}

/* In the text flow rather than beside it.

   No media column is rendered for these, so the panel takes the whole width - an empty
   column would still claim its share of the grid.

   The gallery has to be a real grid here even for a single image, because display: contents
   leaves nothing to hang the margin on. That is safe: none of these rules can match the
   default, which is beside the text. */

:host([data-image-placement="top"]) [part~="layout"],
:host([data-image-placement="between"]) [part~="layout"],
:host([data-image-placement="bottom"]) [part~="layout"] {
	grid-template-columns: minmax(0, 1fr);
}


/* Text Only
   ========================================================================== */

/* One column and one cell, so the panel spans the whole component and the copy behaves like a
   plain text area at 100%. The JS publishes the same single track through --columns, and this
   states it again rather than relying on it alone: the property is host-set, so an instance that
   has not upgraded yet would otherwise fall back to the two column pair and paint a narrow column
   for an image that is never coming.

   The panel is put back in column 1 explicitly. Its base rule places it in column 2 and Image
   Position moves it to 1, and in a one column grid a column 2 assignment would create an implicit
   second track and push the whole panel out of view.

   No rule here hides the media, because in this mode it is never rendered at all - all four of
   render()'s media branches are gated on the mode. Hiding it with CSS would have left the element
   in the tree, still claiming a grid track before the stylesheet loaded. */

:host([data-mode="text"]) [part~="layout"] {
	grid-template-columns: minmax(0, 1fr);
}

/* Text Padding also stops mirroring here: with no image there is no "outer edge" and no "image
   side" for the two values to be defined against, so they go back to meaning literally left and
   right. Declared in the same block as the column so there is only ever one
   :host([data-mode="text"]) [part~="panel"] rule - two blocks on one selector is what made an
   earlier CSS baseline diff go blind, because it keys on the selector.

   This has to come after the Image Position mirror rule to win, since the two are the same
   specificity. It does: the mirror sits with the panel rules and this is further down the file. */

:host([data-mode="text"]) [part~="panel"] {
	grid-column: 1;
	padding-inline: var(--image-gallery-callout__panel-padding-left, 56px) var(--image-gallery-callout__panel-padding-right, 56px);
}

/* The band covers everything, so it has no side to be anchored to and Background Reach has nothing
   to reach across. Both insets rather than one plus a width, so it cannot pick up a left or right
   anchor from the Image Position rules. */

:host([data-mode="text"]) [part~="background"] {
	left: 0;
	right: 0;
	width: auto;
}

/* With a background on, the percentage clamps the TEXT - and only the text.

   It used to clamp the layout column, which holds the whole panel, so it took the images along
   with it. The images should stay the full width of the container; it is the copy that has to
   sit inside the band.

   So the clamp is a max-width on the three text elements themselves rather than on anything
   they sit in, which leaves the gallery untouched by it. They are light DOM, so this reads the
   host attribute as an ancestor selector.

   Text Padding still provides the breathing room off the band's edges, and it is outside this
   max-width - the padding belongs to the panel, the max-width to the copy inside it. */

image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__subheading,
image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__heading,
image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__body,
image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__links {
	max-width: var(--image-gallery-callout__text-container-width, 100%);
}

/* A block narrowed by max-width sits at the START of its containing block, which is the left. The
   panel is the full width of the component for these placements, so with the image on the left -
   band anchored right - the clamped copy stayed pinned to the left while the band sat on the
   right, and the two came apart.

   Worse, it made Text Alignment do two jobs at once. text-align was the only thing that appeared
   to move the copy, so "text on the right" and "text justified left" could not both be had: the
   block itself never moved, only the ragged edge inside it.

   An auto inline-start margin pushes the clamped block to the outer edge, matching the band. Text
   Alignment is then purely justification and all three of its values work on either side.

   Only for the in-flow placements, which is exactly when data-text-bounded is set - beside the
   text there is no clamp, the copy fills its own column and the block has nowhere to go. */

image-gallery-callout[data-text-bounded="true"][data-image-position="left"] .image-gallery-callout__subheading,
image-gallery-callout[data-text-bounded="true"][data-image-position="left"] .image-gallery-callout__heading,
image-gallery-callout[data-text-bounded="true"][data-image-position="left"] .image-gallery-callout__body,
image-gallery-callout[data-text-bounded="true"][data-image-position="left"] .image-gallery-callout__links {
	margin-inline-start: auto;
}

:host([data-image-placement="top"]) [part~="gallery"],
:host([data-image-placement="between"]) [part~="gallery"],
:host([data-image-placement="bottom"]) [part~="gallery"] {
	display: grid;
	gap: var(--image-gallery-callout__image-gap, 16px);
	grid-template-columns: repeat(var(--image-gallery-callout__gallery-columns, 1), 1fr);
}

/* Matching the heading's own bottom margin, so the images sit in the rhythm of the copy
   rather than being crammed against it. Nothing below the last one, because whatever
   follows brings its own top margin - or nothing follows at all. */

:host([data-image-placement="top"]) [part~="gallery"],
:host([data-image-placement="between"]) [part~="gallery"] {
	margin-bottom: 1rem;
}

:host([data-image-placement="bottom"]) [part~="gallery"] {
	margin-top: 1rem;
}

/* Match the Text Height means nothing once the images are IN the text - there is no
   separate column to match. Left to size themselves instead of stretching to a height
   that no longer exists. */

:host([data-image-placement="top"][data-image-size="match"]) [part~="gallery"],
:host([data-image-placement="between"][data-image-size="match"]) [part~="gallery"],
:host([data-image-placement="bottom"][data-image-size="match"]) [part~="gallery"] {
	height: auto;
}


/* Horizontal scroll on desktop and tablet.

   A track per image at a computed width, rather than letting fr divide space that by definition
   is not enough. The "peek" leaves part of the next image showing so it is visible there is more
   to scroll to, and it collapses to zero when everything already fits - which is what
   peek-is-active does: image count minus columns, clamped to 0 or 1.

   The per-column gap share comes out of each track too, or the accumulated gaps push the last
   visible image out of view.

   This only ever engages when there are more images than columns. With Column Count on Auto that
   used to be impossible - Auto meant one column per image, so everything always fit and Scroll
   did nothing here. Auto under Scroll now resolves to three, so a fourth image starts scrolling.
   Phones are handled separately at the end of this sheet: one at a time, whatever this says. */

:host([data-gallery="true"][data-image-overflow="scroll"]) [part~="gallery"] {
	--image-gallery-callout__peek-base-amount: 15vw;
	--image-gallery-callout__peek-is-active: min(max(var(--image-gallery-callout__image-count, 1) - var(--image-gallery-callout__gallery-columns, 1), 0), 1);
	--image-gallery-callout__peek: calc( var(--image-gallery-callout__peek-is-active) * var(--image-gallery-callout__peek-base-amount) );
	--image-gallery-callout__gap-per-column: calc( ( var(--image-gallery-callout__image-gap, 16px) * ( var(--image-gallery-callout__gallery-columns, 1) - 1 ) ) / var(--image-gallery-callout__gallery-columns, 1) );
	--image-gallery-callout__image-width: calc( ( 100% - var(--image-gallery-callout__peek) ) / var(--image-gallery-callout__gallery-columns, 1) - var(--image-gallery-callout__gap-per-column) );

	grid-template-columns: repeat(var(--image-gallery-callout__image-count, 1), var(--image-gallery-callout__image-width));
	overflow-x: auto;
	scroll-snap-type: x mandatory;
}

/* Scroll controls - desktop and tablet only.

   The frame exists only when the gallery actually scrolls, and it holds the gallery and the
   arrows. The dots are a sibling of it rather than a child, so the arrows can centre on the
   gallery without the dots' height dragging the midpoint down.

   Both are hidden below the phone breakpoint at the end of this sheet: on a phone the images are
   one at a time and swiping is the control. */

[part~="gallery-frame"] {
	position: relative;
}

[part~="arrow"] {
	align-items: center;
	appearance: none;
	background-color: rgba(255, 255, 255, 0.9);
	border: 0;
	border-radius: 50%;
	color: var(--image-gallery-callout__control-color, #26213f);
	cursor: pointer;
	display: flex;
	height: 2.75rem;
	justify-content: center;
	padding: 0;
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	transition: opacity 0.15s ease-in-out;
	width: 2.75rem;
	z-index: 2;
}

[part~="arrow"] svg {
	height: 1.5rem;
	width: 1.5rem;
}

[part~="arrow"]:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/* Kept in place rather than removed at the ends, so the row does not reflow as you scroll. */

[part~="arrow"][disabled] {
	cursor: default;
	opacity: 0.25;
}

[part~="arrow-prev"] {
	left: 0.5rem;
}

[part~="arrow-next"] {
	right: 0.5rem;
}

[part~="dots"] {
	align-items: center;
	display: flex;
	gap: 0.5rem;
	justify-content: center;
	margin-top: 1rem;
}

[part~="dot"] {
	appearance: none;
	background-color: var(--image-gallery-callout__control-color, #26213f);
	border: 0;
	border-radius: 50%;
	cursor: pointer;
	height: 0.625rem;
	opacity: 0.3;
	padding: 0;
	transition: opacity 0.15s ease-in-out, transform 0.15s ease-in-out;
	width: 0.625rem;
}

[part~="dot"]:hover {
	opacity: 0.6;
}

[part~="dot"]:focus-visible {
	outline: 2px solid var(--image-gallery-callout__control-color, #26213f);
	outline-offset: 3px;
}

.image-gallery-callout__dot--current {
	opacity: 1;
	transform: scale(1.3);
}

@media ( prefers-reduced-motion: reduce ) {

	[part~="arrow"],
	[part~="dot"] {
		transition: none;
	}
}

/* scroll-mobile is included with no media query around it, which is safe: scroll-snap-align on a
   child of something that is not a scroll container does nothing at all. Declaring it once beats
   repeating the selector inside the phone query, and it cannot leak a behaviour onto the desktop
   because there is no scrolling there to snap. */

image-gallery-callout[data-gallery="true"][data-image-overflow="scroll"] .image-gallery-callout__picture,
image-gallery-callout[data-gallery="true"][data-image-overflow="scroll-mobile"] .image-gallery-callout__picture {
	scroll-snap-align: start;
}

/* With a grid the fixed Image Heights cannot stay on the media cell - it holds the whole
   grid, so a height there would be divided between the rows. They move onto each picture
   instead, which is the height of one image, and the cell gives its height back.

   Only in gallery mode. The single-image rules further up are left exactly as they are. */

:host([data-gallery="true"][data-image-size="s"]) [part~="media"],
:host([data-gallery="true"][data-image-size="m"]) [part~="media"],
:host([data-gallery="true"][data-image-size="l"]) [part~="media"] {
	height: auto;
}

image-gallery-callout[data-gallery="true"][data-image-size="s"] .image-gallery-callout__picture {
	height: 240px;
}

image-gallery-callout[data-gallery="true"][data-image-size="m"] .image-gallery-callout__picture {
	height: 360px;
}

image-gallery-callout[data-gallery="true"][data-image-size="l"] .image-gallery-callout__picture {
	height: 480px;
}

/* Match the Text Height stretches the cell, and a grid does not inherit its parent's
   height, so the gallery has to be told to fill it. */

:host([data-gallery="true"][data-image-size="match"]) [part~="gallery"] {
	height: 100%;
}


/* The background band
   ========================================================================== */

/* The band is a real element rather than a pseudo-element on the wrapper, so it
   can be targeted by a preview_property_selector and so it can sit behind the grid
   without the grid having to know about it.

   It is anchored to the TEXT side and grows inward. Its width is computed by the JS
   from the text column upward, never from the raw setting, so the band always
   covers the text exactly and Background Reach only decides how far past it the
   band carries on. The fallback is the 50/50 text column plus a quarter of the
   image, matching the shipped defaults. */

[part~="background"] {
	background-color: var(--image-gallery-callout__background-color, #f2f2f2);
	border-radius: var(--image-gallery-callout__background-radius, 0);
	bottom: 0;
	position: absolute;
	top: 0;
	width: var(--image-gallery-callout__background-width, 62.5%);
	z-index: 0;
}

:host([data-image-position="left"]) [part~="background"] {
	right: 0;
}

:host([data-image-position="right"]) [part~="background"] {
	left: 0;
}

/* Overlaying makes a band behind the text pointless - and worse, it would sit
   between the image and the text and hide the image. */

:host([data-overlay="true"]) [part~="background"],
:host([data-background="false"]) [part~="background"] {
	display: none;
}


/* The outline rule
   ========================================================================== */

/* Drawn on the panel with border rather than outline, so it follows the panel's
   own box and the chosen sides can differ. The width comes from the host and is
   only set when the rule is enabled, so an absent value leaves 0 and no rule. */

:host([data-outline="true"]) [part~="panel"] {
	border-color: var(--image-gallery-callout__outline-color, #26213f);
	border-style: solid;
	border-width: 0;
}

:host([data-outline="true"][data-outline-sides="top"]) [part~="panel"] {
	border-top-width: var(--image-gallery-callout__outline-width, 3px);
}

:host([data-outline="true"][data-outline-sides="bottom"]) [part~="panel"] {
	border-bottom-width: var(--image-gallery-callout__outline-width, 3px);
}

:host([data-outline="true"][data-outline-sides="left"]) [part~="panel"] {
	border-left-width: var(--image-gallery-callout__outline-width, 3px);
}

:host([data-outline="true"][data-outline-sides="right"]) [part~="panel"] {
	border-right-width: var(--image-gallery-callout__outline-width, 3px);
}

:host([data-outline="true"][data-outline-sides="top-bottom"]) [part~="panel"] {
	border-block-width: var(--image-gallery-callout__outline-width, 3px);
}

:host([data-outline="true"][data-outline-sides="all"]) [part~="panel"] {
	border-width: var(--image-gallery-callout__outline-width, 3px);
}


/* Keeping the text clear of a corner flag
   ========================================================================== */

/* A corner stack is absolutely positioned and therefore out of flow, so without this the
   last line of body copy runs underneath it - which is exactly what happens the moment
   Text Padding is set to 0.

   The JS measures each edge and publishes --stack-clearance-top / -bottom only when
   something is actually there, so these rules need no gating on a position: an absent
   property falls back to 0px and reserves nothing. Two stacks at the same edge reserve
   the taller of the two; a stack in the text flow reserves nothing, because it is
   already pushing the copy along itself.

   What is reserved is only the part the panel's own padding is not already providing, so
   a generous Text Padding costs nothing and a Text Padding of 0 gets the whole height.
   max() keeps it from ever going negative, which would pull the copy into the stack
   instead. */

[part~="content"] {
	padding-bottom: max(
		0px,
		calc( var(--image-gallery-callout__stack-clearance-bottom, 0px) - var(--image-gallery-callout__panel-padding-bottom, 0px) )
	);
	padding-top: max(
		0px,
		calc( var(--image-gallery-callout__stack-clearance-top, 0px) - var(--image-gallery-callout__panel-padding-top, 0px) )
	);
}


/* Where the flag sits
   ========================================================================== */

/* Each STACK carries its own data-position, so one set of rules positions both the
   flag's stack and the button's - no parallel set keyed off a different host attribute,
   and no way for the two to drift apart.

   Button Position decides whether there are two stacks or one. "With the Callout Flag"
   renders a single stack holding both, in the order the value names; anything else
   renders a stack each, which is what lets the link stay in the text flow while the flag
   hangs off a corner.

   The corner values are flush to the panel's corners rather than inset by the panel
   padding, which is how the mockups draw them - the stack hangs off the text block
   rather than sitting inside its measure. */

[part~="stack"] {
	display: flex;
	flex-direction: column;
	row-gap: var(--image-gallery-callout__flag-gap, 24px);
}

/* A slot is display: contents by default, and an empty one still generates a flex item
   that takes a row gap with it - which would leave a hole where a missing button or
   flag would have been. Each half is hidden outright unless something is actually
   slotted into it, which the JS reports as data-has-button and data-has-flag on the
   host. */

[part~="actions"],
[part~="stack"] slot[name="callout"] {
	display: none;
}

:host([data-has-button]) [part~="actions"] {
	display: block;
}

:host([data-has-flag][data-callout="true"]) [part~="stack"] slot[name="callout"] {
	display: block;
}

/* The stack shrinks to its widest child, so it has to be told which edge to hug. In
   the flow that follows the text alignment; in a corner the corner decides, which is
   why those are declared second. */

:host([data-align="center"]) [part~="stack"] {
	align-items: center;
}

:host([data-align="right"]) [part~="stack"] {
	align-items: flex-end;
}

/* The same setting that spaces a corner stack off the copy. In the flow it is a plain
   top margin; in a corner it is reserved on the copy via --stack-clearance-* instead.
   One control either way, so the space reads the same whichever position is chosen. */

[part~="stack"][data-position="inline"] {
	margin-top: var(--image-gallery-callout__flag-gap, 24px);
}

/* An inline stack is "within the text flow", so it has to be held to the same bounds as the text
   is - the same clamp, anchored to the same side. The stack is a sibling of the content column and
   a flex item of the panel, so it stretches to the panel's full width by default; with the copy
   clamped to the band and the stack not, the flag and the button sat out on bare page while the
   text stayed inside the colour. Which is the whole promise of "within the text flow" broken.

   The clamp cannot go on the content column instead, because for these placements the images live
   in it too and would be dragged in with the copy. That is the same mistake the text clamp already
   had to be moved off, so this is deliberately a third place reading one computed width rather
   than a wider container that would sweep the images up.

   The width is DEFINITE rather than a max-width, and that is the whole subtlety. An auto cross-axis
   margin on a flex item absorbs the free space and neutralises align-self, so the item stops
   stretching and falls back to its content size. With only a max-width that meant the stack shrank
   to the width of the flag itself - and then align-items had no free space left to position
   anything in, so Text Alignment could no longer move the flag or the button at all. It was pinned
   to the outer edge whatever was chosen.

   Giving the stack a real width means it does not need to stretch: the auto margin still absorbs
   what is left over and pushes it to the outer edge, but the box stays as wide as the band, which
   is the space align-items needs to place the flag inside. max-width: 100% keeps it from
   overflowing the panel if the computed band is ever wider than the column.

   Inline only. The corner positions are absolutely positioned against the panel and are meant to
   sit at its corners, not the band's, so they keep the full width they anchor to. */

:host([data-text-bounded="true"]) [part~="stack"][data-position="inline"] {
	max-width: 100%;
	width: var(--image-gallery-callout__text-container-width, 100%);
}

:host([data-text-bounded="true"][data-image-position="left"]) [part~="stack"][data-position="inline"] {
	margin-inline-start: auto;
}

[part~="stack"][data-position="top-left"],
[part~="stack"][data-position="top-right"],
[part~="stack"][data-position="bottom-left"],
[part~="stack"][data-position="bottom-right"] {
	position: absolute;
	z-index: 2;
}

[part~="stack"][data-position="top-left"],
[part~="stack"][data-position="top-right"] {
	top: 0;
}

[part~="stack"][data-position="bottom-left"],
[part~="stack"][data-position="bottom-right"] {
	bottom: 0;
}

[part~="stack"][data-position="top-left"],
[part~="stack"][data-position="bottom-left"] {
	align-items: flex-start;
	left: 0;
}

[part~="stack"][data-position="top-right"],
[part~="stack"][data-position="bottom-right"] {
	align-items: flex-end;
	right: 0;
}


/* Small screens
   ========================================================================== */

@media ( max-width: 47.9375em ) {

	/* Everything stacks with the image on top, whichever side it takes on desktop
	   and whether or not it was overlaid. A 25% text column is unreadable at 390px
	   and text over an image is unreadable at any width once the image is only as
	   wide as the phone. */

	[part~="layout"] {
		column-gap: 0;
		grid-template-columns: minmax(0, 1fr);
		row-gap: 1.5rem;
	}

	[part~="media"],
	:host([data-image-position="right"]) [part~="media"],
	:host([data-overlay="true"]) [part~="media"] {
		grid-column: 1;
		grid-row: 1;
	}

	/* A media query adds NO specificity, so the two padding-inline rules outside it -
	   :host([data-image-position="left"]) and :host([data-mode="text"]), both at 0,2,0 - outranked
	   the bare [part~="panel"] at 0,1,0 in this list and kept their DESKTOP horizontal padding on
	   phones. An image-right instance got 1.25rem and an image-left one got 56px, from one setting,
	   which is why two callouts differing only by Image Position had visibly different padding here.
	   Both are named explicitly so every case lands on the same phone padding.

	   Which is also the right answer on its own terms: the mirror exists to put the padding on the
	   "outer edge" versus the "image side", and once everything is stacked there is no image beside
	   the text for either to mean anything. */

	[part~="panel"],
	:host([data-image-position="right"]) [part~="panel"],
	:host([data-image-position="left"]) [part~="panel"],
	:host([data-mode="text"]) [part~="panel"],
	:host([data-overlay="true"]) [part~="panel"] {
		grid-column: 1;
		grid-row: 2;
		padding: 1.5rem 1.25rem;
	}

	/* The picture goes back IN FLOW here, and it has to.

	   Beside the text on a desktop it is taken out of flow so it cannot inflate the row - that is
	   what makes "Match the Text Height" match the text. But on a phone the media and the panel are
	   in separate ROWS, so there is no sibling to take the height from: an out-of-flow child
	   contributes nothing, the media row collapses to zero, and the image disappears entirely.

	   The gating attributes are all still true at this width, which is exactly why this needs an
	   explicit release rather than relying on the desktop rule not matching. */

	/* Round All Corners on Mobile. BOTH states are overridden here, which is the point: the desktop
	   per-corner pattern never carries over to a phone. Rounding two corners of an image beside the
	   copy reads as a deliberate shape; the same two corners on an image stacked at the full width
	   of a phone looks like a mistake. So the phone answer is all four or none.

	   Ticked takes one radius for all four corners - the largest corner set for the desktop, worked
	   out by the JS. Unticked is square. The desktop and tablet settings are untouched either way;
	   these rules only exist inside this query.

	   Both "true"/"1" and "false"/"0" are matched: the JS normalises the attribute to true/false,
	   but instance.mvt paints the raw checkbox value server-side.

	   Specificity 0,2,1 against the base rule's 0,1,0, and inside the query which is last in the
	   file - a media query adds nothing of its own, so the higher specificity is what wins. */

	image-gallery-callout[data-image-mobile-radius="true"] .image-gallery-callout__image,
	image-gallery-callout[data-image-mobile-radius="1"] .image-gallery-callout__image {
		border-radius: var(--image-gallery-callout__image-mobile-radius, 0);
	}

	image-gallery-callout[data-image-mobile-radius="false"] .image-gallery-callout__image,
	image-gallery-callout[data-image-mobile-radius="0"] .image-gallery-callout__image {
		border-radius: 0;
	}

	image-gallery-callout[data-image-placement="beside"][data-image-size="match"][data-gallery="false"] .image-gallery-callout__picture {
		height: auto;
		inset: auto;
		position: static;
		width: 100%;
	}

	:host([data-image-placement="beside"][data-image-size="match"][data-gallery="false"]) [part~="media"] {
		align-self: start;
		height: auto;
	}

	/* An absolutely positioned band cannot follow a row it no longer shares, so the
	   color moves onto the panel itself and covers exactly the text. */

	[part~="background"] {
		display: none;
	}

	/* And with the band gone, the padding that existed only to make it stand taller than the image
	   has nothing left to do. It was still being applied though - the rule that sets it lives
	   outside this query and a media query adds no specificity of its own, so nothing here was
	   beating it. On a phone the media is the first row, so it showed up as a band of dead space
	   above the image with no colour in it to explain why.

	   Same selector repeated at the same 0,2,0 specificity; this query is last in the file, so it
	   wins on order. The gap variant already sets 0 at 0,3,0 and is unaffected either way. */

	:host([data-background="true"]) [part~="wrapper"] {
		padding-block: 0;
	}

	/* The percentage clamp comes off on phones. Below this breakpoint the band is not a band any
	   more - it is hidden and the colour moves onto the panel, which is full width - so a copy
	   column at 62.5% of a 390px screen would just be a narrow strip inside a full-width block
	   of colour with nothing to line up against.

	   max-width: none rather than 100%, so it beats the clamp outright instead of relying on the
	   percentage resolving against the same box. */

	image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__subheading,
	image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__heading,
	image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__body,
	image-gallery-callout[data-text-bounded="true"] .image-gallery-callout__links {
		max-width: none;
	}

	/* The inline flag and button are held to the same clamp as the copy on a desktop, so they have
	   to be let go here for the same reason it is - the band is hidden at this width and the colour
	   is on the full-width panel, so a 62.5% box would leave the flag stranded partway across it
	   while everything above it ran the full width.

	   The auto start margin comes off with it. It only existed to push the box to the outer edge of
	   a two column layout, and there is one column here.

	   Both outer selectors are repeated at their own specificity - 0,3,0 and 0,4,0 - because a media
	   query adds none of its own and a shorter selector in here would simply lose to them. */

	:host([data-text-bounded="true"]) [part~="stack"][data-position="inline"],
	:host([data-text-bounded="true"][data-image-position="left"]) [part~="stack"][data-position="inline"] {
		margin-inline-start: 0;
		max-width: none;
		width: auto;
	}

	/* Horizontal scroll, phones only.

	   One image at a time, with a slice of the next one showing so it is obvious there is more to
	   reach - which is what "scroll after the first image" means. grid-auto-flow: column keeps
	   every image on one row and grid-auto-columns sizes each to the viewport less that peek, so
	   the track total exceeds the gallery and the gallery is what scrolls.

	   Column Count is deliberately ignored here. On a 390px screen the answer is always one at a
	   time, whatever it is set to for the desktop.

	   overflow-x lives on the gallery and nowhere else, so the images scroll inside it and the
	   gallery box - and everything around it - stays exactly where it is. */

	/* No arrows or dots on a phone. The images are one at a time there and swiping is the
	   control - a 44px arrow over a 390px image is in the way, not helpful. */

	[part~="arrow"],
	[part~="dots"] {
		display: none;
	}

	/* "Scroll on Mobile Only" is this rule and nothing else.

	   It exists because the two original choices each lost something on one screen. Wrap gave the
	   rows-and-columns layout the desktop wants - eight images as two rows of four - and then
	   collapsed to eight full-width images stacked down a phone. Scroll gave the phone the right
	   thing and took the second row away from the desktop, because a scroller is one row by
	   definition.

	   So the new value simply declines to match any of the desktop scroll rules further up the
	   sheet, leaving the plain wrap grid in place there, and joins the phone rule here. No new
	   machinery: the rows-of-four case already worked under Wrap, and one-at-a-time already worked
	   under Scroll. What was missing was a way to ask for both.

	   grid-template-columns: none is the load-bearing line for the new value in particular. On the
	   desktop it is repeat(4, 1fr) from the wrap grid, and an explicit template would keep those
	   four tracks and lay the images out in the block direction instead of scrolling. */

	:host([data-gallery="true"][data-image-overflow="scroll"]) [part~="gallery"],
	:host([data-gallery="true"][data-image-overflow="scroll-mobile"]) [part~="gallery"] {
		--image-gallery-callout__peek: 15vw;

		grid-auto-columns: calc( 100% - var(--image-gallery-callout__peek) );
		grid-auto-flow: column;
		grid-template-columns: none;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
	}

	:host([data-background="true"]) [part~="panel"] {
		background-color: var(--image-gallery-callout__background-color, #f2f2f2);
	}

	/* Corner-positioned flags come back into the flow. Hanging a flag off a corner
	   of a full-width panel puts it half off the screen or on top of the text. */

	[part~="stack"][data-position="top-left"],
	[part~="stack"][data-position="top-right"],
	[part~="stack"][data-position="bottom-left"],
	[part~="stack"][data-position="bottom-right"] {
		bottom: auto;
		left: auto;
		margin-top: 1.25rem;
		position: static;
		right: auto;
		top: auto;
	}

	:host([data-image-size="l"]) [part~="media"] {
		height: 320px;
	}

	/* A row of four on a desktop is a row of thumbnails on a phone, so wrapping collapses
	   to a single column. Scrolling keeps its columns, because a horizontal scroller of
	   one-at-a-time is exactly what it should be on a phone.

	   Wrap only, deliberately: "scroll-mobile" arrives here as a wrap grid and must NOT be
	   flattened to a single stacked column, since scrolling is the whole point of it on this
	   screen. Its own rule above sets grid-template-columns: none. */

	:host([data-gallery="true"][data-image-overflow="wrap"]) [part~="gallery"] {
		grid-template-columns: 1fr;
	}

	/* No pop-out once the columns stack. Overhang between two blocks that sit one above the other
	   reads as a mistake rather than a flourish, and there is no second column to stand proud of.

	   Same selector as the rule it cancels, at 0,2,0 - the bare [part~="panel"] reset further up
	   this query is only 0,1,0 and would lose. */

	:host([data-image-placement="beside"]) [part~="panel"] {
		margin-block: 0;
	}

}


/* ==========================================================================
   TABLET AND PHONE - image shape
   ========================================================================== */

/* Image Shape on Tablet and Mobile. Stacked at the full width of a narrow screen, a portrait source
   becomes enormous - a 3:4 photo at 800px is over a thousand pixels tall, most of a screen of image
   before the copy starts. Holding the picture to a ratio caps that at any width, which a pixel
   height could not: whatever height suited a phone would be wrong on a tablet.

   Covers tablet as well as phone, so this is its own query at 64em rather than an addition to the
   48em one below it.

   aspect-ratio on the PICTURE, alongside the height: auto it already has - the two compose rather
   than compete, since a ratio plus a width is what produces the height. That is also why this does
   not have to out-specify the 0,4,1 phone rule for a matched image: that rule sets height: auto
   too, and agreeing with it is enough.

   The height: auto here is for the in-flow placements, whose picture is given a real length by the
   JS at 0,2,1. This rule is the same specificity and sits at the very end of the file, so it wins
   on order and the ratio has something to resolve against.

   The four shapes are enumerated rather than keyed on the attribute merely being present. The
   attribute now always carries the real setting, including the empty string for Original
   Proportions - it has to, because this attribute is one the props observe, and removing it would
   read back as the prop DEFAULT rather than as "no shape". Presence therefore no longer means "a
   ratio was chosen", and the values have to be named. Same 0,2,1 specificity per member. */

@media ( max-width: 63.9375em ) {

	image-gallery-callout[data-image-mobile-shape="square"] .image-gallery-callout__picture,
	image-gallery-callout[data-image-mobile-shape="4-3"] .image-gallery-callout__picture,
	image-gallery-callout[data-image-mobile-shape="16-9"] .image-gallery-callout__picture,
	image-gallery-callout[data-image-mobile-shape="3-4"] .image-gallery-callout__picture {
		aspect-ratio: var(--image-gallery-callout__mobile-aspect, 1 / 1);
		height: auto;
	}

	/* Filling the ratio and cropping into it rather than stretching. Image Fit is respected, so an
	   editor who chose Contain still gets letterboxing inside the shape instead of a crop. */

	image-gallery-callout[data-image-mobile-shape="square"] .image-gallery-callout__image,
	image-gallery-callout[data-image-mobile-shape="4-3"] .image-gallery-callout__image,
	image-gallery-callout[data-image-mobile-shape="16-9"] .image-gallery-callout__image,
	image-gallery-callout[data-image-mobile-shape="3-4"] .image-gallery-callout__image {
		height: 100%;
		object-fit: var(--image-gallery-callout__image-fit, cover);
	}
}

/* Anchor ID
   ==========================================================================
   A zero-height marker emitted just before the component when Anchor ID is set, so /page.html#name
   lands here. It takes no space and is invisible to assistive tech.

   scroll-margin-top is the point of it: without it the browser scrolls the anchor to the very top
   of the viewport and the sticky header covers the first part of the section. The offset is a
   custom property so a store with a taller header can raise it in its own stylesheet.
   ========================================================================== */

.image-gallery-callout__anchor {
	display: block;
	height: 0;
	scroll-margin-top: var(--image-gallery-callout__anchor-offset, 6rem);
	visibility: hidden;
}


/* Scroll galleries clip the image shadow
   ==========================================================================
   In scroll overflow the gallery carries overflow-x: auto, and a box with overflow other than
   visible on one axis computes the other axis to auto too - so it becomes a scroll container and
   clips its children's shadows on all four sides. In wrap overflow it is a plain grid and clips
   nothing. That, and not the number of images, is why the shadow appeared on one gallery and not
   another.

   Padding rather than a negative margin. The gallery already carries margin-top and margin-bottom
   for the top and between placements, and a negative block margin here would have to be reconciled
   with both of them - two rules that would then have to agree forever. Padding inside a scroll
   container is part of its scrollable area, so the shadow simply has somewhere to be.

   The trade is stated plainly: a scroll gallery is a little taller than a wrapping one by exactly
   the reach of the shadow. Switch the shadow off and the property is removed, the padding falls back
   to zero, and the geometry is what it always was.

   Both selectors are duplicated from the two places that declare overflow-x: auto - the base rule
   and the phone one - because a shadow needs the room at every width.

   padding-INLINE as well as block, added after the first attempt only padded the block axis and left
   the last image's shadow shaved off at the scroll container's right edge. The inline padding is
   cancelled by an equal negative margin, because unlike the block axis this one would otherwise be
   visible: padding alone would indent the first image from the panel's text, and the images are meant
   to line up with the copy above them. Padding then negative margin nets to zero, and the shadow gets
   its room from a box that is wider than its cell rather than from content that has moved.

   The bleed is into the panel's own padding, which is where a shadow of this size fits. If the panel
   padding is ever set smaller than the shadow's reach the glow will graze the panel edge - the
   alternative was clipping it, which is the bug being fixed. */

:host([data-gallery="true"][data-image-overflow="scroll"]) [part~="gallery"] {
	margin-inline: calc( -1 * var(--image-gallery-callout__image-shadow-room, 0px) );
	padding-block: var(--image-gallery-callout__image-shadow-room, 0px);
	padding-inline: var(--image-gallery-callout__image-shadow-room, 0px);
}

/* The phone repeat, and scroll-mobile needs it here rather than above: this is the breakpoint where
   it becomes a scroll container, and a scroll container is the only thing that clips the shadows.
   On the desktop it is a plain wrap grid, which clips nothing, so it wants no room reserved. */

@media ( max-width: 47.9375em ) {

	:host([data-gallery="true"][data-image-overflow="scroll"]) [part~="gallery"],
	:host([data-gallery="true"][data-image-overflow="scroll-mobile"]) [part~="gallery"] {
		margin-inline: calc( -1 * var(--image-gallery-callout__image-shadow-room, 0px) );
		padding-block: var(--image-gallery-callout__image-shadow-room, 0px);
		padding-inline: var(--image-gallery-callout__image-shadow-room, 0px);
	}

}
