/* ==========================================================================
   SECTION GRID

   This one stylesheet is loaded in two places on purpose:

   1. In the page head, because every section, its image, its copy and its button are
      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 "section-grid" in
      styleResourceCodes.

   The LIGHT DOM section styles the host element and the slotted section blocks. The SHADOW DOM
   section styles the wrapper and the grid.

   Every state attribute lives on the HOST, so the shadow rules read it through :host() and the
   light DOM rules read it as a plain ancestor selector. That is what lets one setting reach both
   trees. Custom properties are set on the host by the 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 geometry declared in both places silently
   loses. The slotted blocks carry light DOM classes and are declared once, below.
   ========================================================================== */


/* ==========================================================================
   LIGHT DOM - the host element and the slotted sections
   ========================================================================== */

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

/* Opt in only, from the Full Browser Width checkbox. 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. Themes clamp this element's ancestors
   with two-class selectors 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.

   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. */

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


/* The section title
   ========================================================================== */

/* mmx-text has no host display of its own, so this would run inline. The space below it is a
   margin here rather than a row gap on the wrapper, so a title left empty costs nothing - a gap
   would reserve room between the title and the grid whether or not any title arrived. */

.section-grid__title {
	display: block;
	margin: 0 0 var(--section-grid__title-gap, 32px);
	text-align: var(--section-grid__title-align, center);
}


/* The section block
   ========================================================================== */

/* The card's own appearance is declared here as real values reading host-set properties with
   fallbacks, so an unconfigured section still paints as a plain white card with a hairline rule.

   A flex column rather than a plain block, so the content half can be told to take the leftover
   height - which is what lets the buttons line up across a row whose copy is different lengths.
   Grid stretches its items to the tallest in the row by default, so every card in a row is
   already the same height for that to work with. */

.section-grid__section {
	background-color: var(--section-grid__card-background, transparent);
	border-radius: var(--section-grid__card-radius, 0);
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	min-width: 0;
	padding: var(--section-grid__card-padding, 24px);
	text-align: var(--section-grid__align, left);
}

/* The border is a separate rule keyed on the attribute rather than a zero-width border in the rule
   above, because a border-width of 0 still changes nothing visually but does leave the property
   set - and Outline Width is only published by the JS while the outline is on. Keying the whole
   border off the attribute means the two can never disagree. */

section-grid[data-outline="true"] .section-grid__section,
section-grid[data-outline="1"] .section-grid__section {
	border: var(--section-grid__card-border-width, 1px) solid var(--section-grid__card-border-color, #e1e1e1);
}


/* The two halves
   ========================================================================== */

/* flex: none on the media so it is sized by its content, and flex: 1 on the content so it takes
   whatever height is left in a stretched card. */

.section-grid__media {
	flex: none;
	height: var(--section-grid__image-height, auto);
	margin-bottom: var(--section-grid__image-gap, 16px);
	min-width: 0;
}

.section-grid__content {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-width: 0;
}

/* Beside places the image and the copy in one row instead of stacking them. A grid rather than
   flex-direction: row, because the media track has to be sized by the image's own width while the
   copy takes the rest - minmax(0, 1fr) rather than 1fr, because a bare 1fr floors at min-content
   and one long unbroken word in the copy would push the row wider than the card.

   The margin that separated them when stacked becomes the column gap, so Space Around Image means
   the same thing in both placements. */

section-grid[data-image-placement="beside"] .section-grid__section {
	align-items: start;
	column-gap: var(--section-grid__image-gap, 16px);
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
}

section-grid[data-image-placement="beside"] .section-grid__media {
	margin-bottom: 0;
}

/* Image Side. Explicit grid-column assignments rather than order, because the media element is
   only present when a section actually has an image - with order alone, a section with no image
   would leave the copy sitting in the media track. */

section-grid[data-image-placement="beside"][data-image-side="left"] .section-grid__media {
	grid-column: 1;
}

section-grid[data-image-placement="beside"][data-image-side="left"] .section-grid__content {
	grid-column: 2;
}

section-grid[data-image-placement="beside"][data-image-side="right"] .section-grid__media {
	grid-column: 2;
}

section-grid[data-image-placement="beside"][data-image-side="right"] .section-grid__content {
	grid-column: 1;
}


/* The 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. */

.section-grid__picture {
	display: block;
	height: 100%;
	width: 100%;
}

.section-grid__image-link {
	display: block;
	height: 100%;
	width: 100%;
}

.section-grid__image {
	border-radius: var(--section-grid__image-radius, 0);
	display: block;
	height: 100%;
	object-fit: var(--section-grid__image-fit, cover);
	width: 100%;
}

/* Full Width is the default state of the rules above - the image fills its section. Every smaller
   size is a width on the MEDIA rather than on the image, so the alignment margins below have a box
   to push around; the image itself stays at 100% of whatever it is given. */

section-grid[data-image-size="s"] .section-grid__media,
section-grid[data-image-size="m"] .section-grid__media,
section-grid[data-image-size="l"] .section-grid__media {
	width: var(--section-grid__image-width, 64px);
}

/* Image Alignment, for a smaller image above the copy. Margins rather than align-self, because
   the media is a flex item in a column - align-self would work, but margin: auto is the one
   mechanism that reads the same for all three values. Only applied for Above placement: beside
   the copy, Image Side decides which track it sits in and a stray auto margin would push it away
   from the copy. */

/* One ready-made value from the JS rather than three attribute rules, because Image Alignment can
   be left on "Match Text Alignment" - and resolving that needs to see the section's Text Alignment
   too, which a stylesheet cannot do: an inherited text-align is not something CSS can turn into a
   margin pair. text-align alone was never going to move this anyway, since the media is a flex item
   with a definite width at every size except Full Width, so it neither follows text-align nor
   stretches. Margins are the only thing that can place it. */

section-grid[data-image-placement="above"] .section-grid__media {
	margin-inline: var(--section-grid__media-margin, 0 auto);
}

/* Full Width has to mean full width.

   The card is a flex column, so a media box would normally stretch across it. An auto margin in the
   cross axis beats align-items: stretch, though - the box drops to shrink-to-fit and the margin
   absorbs what is left. Image Align writes exactly that: its default is "0 auto", left margin zero
   and right margin auto.

   For Small, Medium and Large that is the point - the picture has a width of its own and the margin
   places it. At Full Width there is no width to place, so the same margin left the image at its
   natural size with the remainder as a gap on the right. It only showed between about 580px and
   767px, where the layout is one column and the card is finally wider than the picture; above that
   the columns are narrower than the image and below it the image outgrows the screen.

   Alignment is meaningless for something spanning the whole card, so at Full Width the margin goes
   and the width is stated outright. */

section-grid[data-image-size="full"] .section-grid__media {
	margin-inline: 0;
	width: 100%;
}

/* A fixed height only exists for Full Width, which is the only case where the image is sized by
   its section rather than by a width of its own - and the only case where a row of images shot at
   different aspect ratios needs a common box to line up in.

   The height and object-fit are declared together because neither means anything without the
   other: object-fit needs a box whose height the image did not choose, and a box the image did not
   choose needs object-fit to say what to do about it. That is why Image Fit is hidden in Page
   Builder unless Image Size is Full Width AND Image Height is one of the fixed values - at
   "Defined by Image Size" the JS publishes no height, this rule's fallback leaves it auto, and all
   five fit values would be indistinguishable.

   All five of Miva's values work off this one declaration: cover crops, contain letterboxes, fill
   stretches to the box, none paints at intrinsic size clipped to the box, and scale-down is the
   smaller of none and contain. Nothing here needs to know which was chosen. */

/* Both halves of this live in the base rules above rather than here, so there is only ever one
   .section-grid__media and one .section-grid__image block - two blocks on one selector is what made
   a CSS baseline diff go blind once before, since it keys on the selector.

   The height is on the media and object-fit on the image, both unconditional, and they cost nothing
   until a fixed height is actually chosen: the image is already height: 100%, so with the media at
   auto that resolves to auto and the image is simply itself. Give the media a real height and the
   100% resolves to it, and all five fit values become distinguishable. Nothing has to test the size
   or the height to decide.

   Image Fit is never hidden. I had it appearing only once a fixed height was chosen, on the grounds
   that all five values look identical without a box - true, but not one of the twelve shipped Miva
   components that expose Image Fit gates it, and a control that vanishes is far worse than one that
   is temporarily inert. Set a fixed Image Height and it starts doing something. */


/* The copy
   ========================================================================== */

/* mmx-text has no host display of its own, so each of these would run inline and the whole card
   would collapse onto one line. Margins rather than a gap on the content column, so a field left
   empty costs nothing - a gap would reserve space between elements whose content never arrived. */

.section-grid__subheading,
.section-grid__heading,
.section-grid__body {
	display: block;
}

.section-grid__subheading {
	margin: 0 0 0.25rem;
}

.section-grid__heading {
	margin: 0 0 0.5rem;
}

.section-grid__body {
	margin: 0;
}

/* The wrapper, not the button, carries both the push and the gap.

   margin-top: auto pushes it to the bottom of the content column, which is what lines the buttons up
   across a row of cards whose copy runs to different lengths. The gap has to be padding rather than
   a margin because the auto margin is already spoken for - and it has to be on THIS element rather
   than on the button, because padding on the mmx-button host sits inside the button's own box. On a
   label-width button that is invisible; on a full width one the box is stretched, so the gap became
   part of the element - part of what Page Builder highlights and part of what the browser treats as
   the control. */

.section-grid__actions {
	margin-top: auto;
	min-width: 0;
	padding-top: var(--section-grid__button-gap, 16px);
}

/* An inline-block inside a box that inherits the section's text-align needs no alignment rules of
   its own - it simply follows the text, which is what the three align-self rules here used to do by
   hand. One less mechanism, and it cannot disagree with the copy above it. */

/* The equal-height pass: the JS measures every button with this property cleared, takes the tallest,
   and publishes it here.

   MIN-HEIGHT, not height - reverted in 1.0.18.

   A floor can only ever fail to equalise. A forced height can be actively WRONG: if the measure pass
   ever publishes a bad number - measured before the webfont settles, say - height enforces it and the
   buttons render at a size nothing asked for. min-height caps the damage at "not quite equal", which
   is a quiet failure instead of a loud one.

   The trade is real and worth stating: min-height cannot bring a button that is taller than its
   neighbours DOWN, so if that is the situation the pass will look like it did nothing. That is the
   1.0.13 problem coming back, deliberately, until the intermittent fault is understood. */

.section-grid__button {
	display: inline-block;
	min-height: var(--section-grid__button-height, auto);
	vertical-align: top;

	/* The themed tree carries no part="button" - see #renderTheme() in mmx-button.js - so the
	   ::part rules below reach nothing on any instance using a Button Theme, which is the default.
	   The height for that path is injected into mmx-button's shadow root from the
	   <template data-theme-stylesheet> in instance.mvt instead.

	   It used to be handed over through --mmx-theme-button__height. That is mmx-button's documented
	   route, but it is read once with getComputedStyle during render, racing the page-head link that
	   defines the property - so the height landed or did not depending on load order. Removed rather
	   than kept alongside the template rule: two mechanisms for one job, one of them unreliable, is
	   how you get a bug that only appears sometimes. */
}

/* The host is only a wrapper - the coloured box is the anchor inside mmx-button's shadow root, so a
   height on the host alone would leave a correctly sized wrapper around a short button. ::part()
   rules from this outer tree take precedence over rules inside that shadow root, which is the only
   way to reach it. align-items: center for the themed full width variant, which is a flex container:
   without it a one-line label in a two-line-tall button would sit against the top edge. */

.section-grid__button::part(button) {
	align-items: center;
	min-height: var(--section-grid__button-height, auto);
}

/* Button Width = Fill the Section. The width itself comes from mmx-button's own data-width="full",
   which the template sets; this is what gives it something to fill.

   align-self: stretch is doing the work. mmx-button's host is display: inline-block, and an
   inline-block shrink-to-fits - so a width: 100% child inside it resolves against a box that is
   already only as wide as its content, and the button would stay label-sized however "full" it was
   told to be. As a flex item it is blockified and stretch hands it the column's full width, which
   the 100% then has something real to resolve against.

   Declared AFTER the three alignment rules, because it is the same specificity as they are and
   stretch has to beat whichever one of them is also matching. */

section-grid[data-button-width="full"] .section-grid__button {
	display: block;
}

.section-grid__button-arrow {
	margin-left: 0.35em;
}


/* Buttons Only
   ========================================================================== */

/* In this mode the button IS the cell, so everything the card was wrapped in has to come off -
   otherwise each button sits inside a bordered, padded box and the grid reads as cards that
   happen to contain buttons rather than as a grid of buttons.

   The image and the copy are hidden rather than left to be empty, so switching an instance that
   already has copy in it produces a clean button grid instead of a half-converted one. The fields
   keep their values and come straight back when the mode is switched back.

   The outline rule above is keyed on data-outline, which is independent of the mode, so the border
   is unset here explicitly rather than relying on the editor also turning the outline off. */

section-grid[data-mode="buttons"] .section-grid__section {
	background-color: transparent;
	border: 0;
	padding: 0;
}

section-grid[data-mode="buttons"] .section-grid__media,
section-grid[data-mode="buttons"] .section-grid__subheading,
section-grid[data-mode="buttons"] .section-grid__heading,
section-grid[data-mode="buttons"] .section-grid__body {
	display: none;
}

/* The push and the gap both come off here. With nothing above the button there is nothing to push
   away from, and either one would show up as dead space at the top of the cell. The full width
   itself is already handled: this mode forces Button Width to full, so the rule above has given the
   button display: block and mmx-button's own data-width="full" has done the rest. */

section-grid[data-mode="buttons"] .section-grid__actions {
	margin-top: 0;
	padding-top: 0;
}

/* mmx-button's full width theme variant is a flex container, and a flex item is not centred by the
   host's text-align. ::part(button) rules written in this outer tree take precedence over rules
   inside mmx-button's shadow root, which is the only hook that can reach in and centre the label. */

section-grid[data-mode="buttons"] .section-grid__button::part(button) {
	justify-content: center;
	text-align: center;
}


/* ==========================================================================
   SHADOW DOM - the wrapper and the grid
   ========================================================================== */

:host {
	display: block;
}

[part~="wrapper"] {
	box-sizing: border-box;
	margin-inline: auto;
	max-width: var(--section-grid__max-width, 1440px);
	padding: var(--section-grid__padding, 0);
	width: 100%;
}

/* Widening the host is only half of it: the wrapper carries 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;
}

/* One track per column, from the count the JS publishes. minmax(0, 1fr) rather than 1fr, because a
   bare 1fr floors at min-content: a long unbroken word in one section would then push the whole
   row wider than the wrapper instead of wrapping.

   Rows come from the wrap and are unlimited: ten sections at three columns is four rows, the last
   one short, and nothing caps how many rows there can be. */

[part~="grid"] {
	display: grid;
	gap: var(--section-grid__gap, 24px);
	grid-template-columns: repeat(var(--section-grid__columns, 3), minmax(0, 1fr));
}


/* Tablet
   ========================================================================== */

/* Two columns rather than a computed reduction, because repeat() takes an integer and math
   functions are not allowed in that position - min(var(--columns), 2) is invalid there and would
   compute the whole property to none, collapsing the grid to auto-sized implicit tracks. So the
   count is stepped down explicitly, and a one column grid is excluded from the step so it is not
   widened back out to two. */

@media ( max-width: 63.9375em ) {

	[part~="grid"] {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

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


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

@media ( max-width: 47.9375em ) {

	/* Five columns on a 390px screen is five unreadable slivers, so they stack. Both templates are
	   overridden rather than just the default one, because the tablet step above has already
	   rewritten the property by the time this applies. */

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

	/* A 64px image beside the copy is fine on a phone; a 200px one leaves the copy about a third
	   of a narrow screen. Beside placement keeps its two tracks, but the image gives up its
	   configured width and takes a third of the card at most. */

	section-grid[data-image-placement="beside"] .section-grid__media {
		max-width: 33%;
	}
}


/* Multiple buttons per section
   ==========================================================================
   Everything below is gated on data-multiple, which the template sets only when a section holds
   more than one button. A section with a single button is left on the block layout it has always
   had - an inline-block following the card's text-align - so no existing instance moves.

   The row becomes a flex container only in the multiple case, because flex is what makes stacking
   and side-by-side one setting instead of two layouts. Alignment is resolved server-side to a
   literal left/center/right: align-items has no left or right keyword, so the mapping has to
   happen somewhere, and doing it once in the template beats doing it in three selectors here. */

.section-grid__actions[data-multiple] {
	display: flex;
	flex-wrap: wrap;
	gap: var(--section-grid__button-row-gap, 12px);
}

section-grid[data-button-direction="stacked"] .section-grid__actions[data-multiple] {
	flex-direction: column;
}

section-grid[data-button-direction="row"] .section-grid__actions[data-multiple] {
	flex-direction: row;
}

/* Stacked: the buttons sit on the cross axis, so alignment is align-items. Without this the
   default stretch would make every button fill the card, quietly ignoring Fit the Label. */

section-grid[data-button-direction="stacked"][data-button-align="left"] .section-grid__actions[data-multiple] {
	align-items: flex-start;
}

section-grid[data-button-direction="stacked"][data-button-align="center"] .section-grid__actions[data-multiple] {
	align-items: center;
}

section-grid[data-button-direction="stacked"][data-button-align="right"] .section-grid__actions[data-multiple] {
	align-items: flex-end;
}

/* Side by side: the buttons sit on the main axis, so the same three choices are justify-content. */

section-grid[data-button-direction="row"][data-button-align="left"] .section-grid__actions[data-multiple] {
	justify-content: flex-start;
}

section-grid[data-button-direction="row"][data-button-align="center"] .section-grid__actions[data-multiple] {
	justify-content: center;
}

section-grid[data-button-direction="row"][data-button-align="right"] .section-grid__actions[data-multiple] {
	justify-content: flex-end;
}

/* Button Width inside a flex row.

   data-own-width is on every button, the shared one included, carrying the width that actually
   applies to it - the shared setting, or the per-button override where one was made. It is the
   only signal here, so the shared and per-button paths cannot disagree.

   Fill the Section means something different in each direction: stacked, one button fills the
   card, so it stretches across the cross axis. Side by side, several buttons share the row, so
   they divide it - flex-basis 0 rather than auto, or a long label would claim more than its share
   and the pair would come out lopsided.

   The display rules undo section-grid[data-button-width="full"]'s blanket display: block for any
   button that opted out of it, and restore it for any button that opted in while the shared
   setting was Fit the Label. */

.section-grid__button[data-own-width="auto"] {
	display: inline-block;
}

.section-grid__button[data-own-width="full"] {
	display: block;
}

.section-grid__actions[data-multiple] .section-grid__button[data-own-width="full"] {
	align-self: stretch;
}

section-grid[data-button-direction="row"] .section-grid__actions[data-multiple] .section-grid__button[data-own-width="full"] {
	flex: 1 1 0;
}

/* Phones: side by side stops being side by side.

   Two themed buttons at their natural label width do not fit across a phone-width card, so the row
   wraps and produces a ragged one-and-one. Forcing the column back gives the same result the
   Stacked setting would, which is the only sensible reading of the row at this size. */

@media ( max-width: 47.9375em ) {

	section-grid[data-button-direction="row"] .section-grid__actions[data-multiple] {
		flex-direction: column;
	}

	section-grid[data-button-direction="row"][data-button-align="left"] .section-grid__actions[data-multiple] {
		align-items: flex-start;
	}

	section-grid[data-button-direction="row"][data-button-align="center"] .section-grid__actions[data-multiple] {
		align-items: center;
	}

	section-grid[data-button-direction="row"][data-button-align="right"] .section-grid__actions[data-multiple] {
		align-items: flex-end;
	}

	section-grid[data-button-direction="row"] .section-grid__actions[data-multiple] .section-grid__button[data-own-width="full"] {
		flex: 0 0 auto;
	}

}


/* Button Vertical Position
   ==========================================================================
   .section-grid__actions has carried margin-top: auto since the row existed, which pushes it to the
   bottom of the card and lines the buttons up across cards whose copy runs to different lengths.
   That was the only behaviour available - there was nothing for a setting to change, which is why
   adjusting alignment appeared to do nothing vertically.

   The auto margin is still how it is done, because the card is a flex column and an auto margin in
   the free space is what moves a single item within it. Bottom is the default and repeats the
   existing rule rather than relying on it, so the three states are declared in one place and read
   together.

   Note this moves the ROW, not the buttons within it. Button Alignment is the horizontal control and
   is independent of this one. */

section-grid[data-button-valign="top"]:not([data-mode="buttons"]) .section-grid__actions {
	margin-bottom: auto;
	margin-top: 0;
}

section-grid[data-button-valign="center"]:not([data-mode="buttons"]) .section-grid__actions {
	margin-bottom: auto;
	margin-top: auto;
}

section-grid[data-button-valign="bottom"]:not([data-mode="buttons"]) .section-grid__actions {
	margin-bottom: 0;
	margin-top: auto;
}

/* Buttons Only mode is excluded by hand above, and the :not() is load-bearing.

   section-grid[data-mode="buttons"] .section-grid__actions zeroes both margins, and it has exactly
   the same specificity as these three rules - one element, one attribute, one class. It is declared
   EARLIER in this file, so without the :not() the bottom rule below would win on source order and
   put the auto margin back, breaking a mode that has no copy for the row to sit under. */
