/* ==========================================================================
   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%;
	}
}
