/* ==========================================================================
   HERO BANNER

   This one stylesheet is loaded in two places on purpose:

   1. In the page head, because the image, the text, the flag and the bar columns 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 "hero-banner" in
      styleResourceCodes.

   The LIGHT DOM section styles the slotted image, text, flag and columns. The SHADOW DOM section
   styles the banner, the overlay, the positioning grid and the bar.

   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. Custom properties are set on the host by
   the JS and inherit into both trees.

   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 elements carry light DOM classes and are declared once, below.
   ========================================================================== */


/* ==========================================================================
   LIGHT DOM - slotted image, text, flag and columns
   ========================================================================== */

/* Full Browser Width. Same technique as the storefront hero rule in theme.css - negative margins of
   half the difference between the element's own width and the viewport - so the two behave alike.

   Matched on both "true" and "1". instance.mvt paints the raw checkbox value server-side, so before
   the component upgrades the attribute is "1"; after upgrade the JS rewrites it to "true". Without
   both, the banner would render constrained and then jump to full width, and this is the one setting
   where that jump is the whole layout rather than a detail. */

hero-banner[data-full-width="true"][data-full-width],
hero-banner[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
   ========================================================================== */

.hero-banner__picture {
	display: block;
	height: 100%;
	width: 100%;
}

.hero-banner__image {
	display: block;
	height: 100%;
	object-fit: var(--hero-banner__image-fit, cover);
	width: 100%;
}

/* At Banner Height "Defined by Image Size" there is no box to fill, so the image sets the height
   and object-fit has nothing to do. */

hero-banner[data-height="auto"] .hero-banner__picture,
hero-banner[data-height="auto"] .hero-banner__image {
	height: auto;
	object-fit: fill;
}


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

.hero-banner__subheading,
.hero-banner__heading,
.hero-banner__body {
	display: block;

	/* text-shadow INHERITS, which is what makes this reach the text at all. These three are mmx-text
	   elements: the words are inside their shadow roots, where no rule of ours can select them. An
	   inherited property crosses that boundary on its own, so declaring it on the host is enough and
	   no ::part() hook is needed - the same reason text-align works on these elements today.

	   The whole value arrives as one custom property from #textShadow(), so switching the group off
	   removes the property and this falls back to none. */
	text-shadow: var(--hero-banner__text-shadow, none);
}

.hero-banner__subheading {
	margin: 0 0 0.5rem;
}

.hero-banner__heading {
	margin: 0 0 1rem;
}

.hero-banner__body {
	margin: 0;
}


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

/* The flag's own colours, padding, point width and radius 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. */

.hero-banner__callout {
	--hero-banner__flag-background: #26213f;
	--hero-banner__flag-color: #ffffff;
	--hero-banner__flag-point: 25px;
	--hero-banner__flag-radius: 5px;
	--hero-banner__flag-padding-top: 1px;
	--hero-banner__flag-padding-right: 24px;
	--hero-banner__flag-padding-bottom: 1px;
	--hero-banner__flag-padding-left: 24px;

	align-items: center;
	background-color: var(--hero-banner__flag-background);
	border-radius: var(--hero-banner__flag-radius);
	box-sizing: border-box;
	color: var(--hero-banner__flag-color);
	display: inline-flex;
	justify-content: center;
	max-width: 100%;
	padding:
		var(--hero-banner__flag-padding-top)
		var(--hero-banner__flag-padding-right)
		var(--hero-banner__flag-padding-bottom)
		var(--hero-banner__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 the shape.

   Each of these is the straight-edged polygon with --flag-clip as an override: a polygon cannot
   have rounded vertices, so a Flag Corner Radius makes the JS replace the whole thing with a
   measured path() carrying arcs at every vertex.

   The point's width is added to the padding on whichever side it is on, rather than being folded
   into Flag Padding, so the editor's four numbers stay symmetrical and the label stays centred in
   the square part of the flag whatever the point width is. */

.hero-banner__callout[data-point="right"] {
	clip-path: var(--hero-banner__flag-clip, polygon(0 0, calc( 100% - var(--hero-banner__flag-point) ) 0, 100% 100%, 0 100%));
	padding-right: calc( var(--hero-banner__flag-padding-right) + var(--hero-banner__flag-point) );
}

.hero-banner__callout[data-point="right"][data-flip="1"] {
	clip-path: var(--hero-banner__flag-clip, polygon(0 0, 100% 0, calc( 100% - var(--hero-banner__flag-point) ) 100%, 0 100%));
}

.hero-banner__callout[data-point="left"] {
	clip-path: var(--hero-banner__flag-clip, polygon(var(--hero-banner__flag-point) 0, 100% 0, 100% 100%, 0 100%));
	padding-left: calc( var(--hero-banner__flag-padding-left) + var(--hero-banner__flag-point) );
}

.hero-banner__callout[data-point="left"][data-flip="1"] {
	clip-path: var(--hero-banner__flag-clip, polygon(0 0, 100% 0, 100% 100%, var(--hero-banner__flag-point) 100%));
}

.hero-banner__callout-label {
	display: inline-block;
}

/* The flag always has a background of its own, so unlike the banner text this is unconditional -
   the label would otherwise take the typography theme's colour and disappear into the navy.
   ::part() rules written in this outer tree take precedence over rules inside mmx-text's shadow
   root, which is the only hook that can reach in and reassert it. */

.hero-banner__callout-label:not([data-own-color])::part(text__inner) {
	color: var(--hero-banner__flag-color);
}

.hero-banner__callout:focus-visible {
	outline: 2px solid var(--hero-banner__flag-background);
	outline-offset: 3px;
}


/* Bar columns
   ========================================================================== */

.hero-banner__column {
	box-sizing: border-box;
	min-width: 0;
	text-align: var(--hero-banner__bar-align, center);
}

/* The bottom bar sits on a solid colour, not the photograph, so it does not inherit the shadow the
   banner copy needs. Reset rather than restructure: the three selectors above are the ones the
   editor's control is described as affecting, and the bar has its own. */

.hero-banner__column .hero-banner__subheading,
.hero-banner__column .hero-banner__heading,
.hero-banner__column .hero-banner__body {
	text-shadow: none;
}

.hero-banner__column .hero-banner__heading {
	margin: 0 0 0.25rem;
}

.hero-banner__column .hero-banner__body {
	margin: 0;
}

.hero-banner__button {
	display: inline-block;
	margin-top: 1rem;
	vertical-align: top;
}


/* ==========================================================================
   SHADOW DOM - banner, overlay, positioning grid and bar
   ========================================================================== */

:host {
	display: block;
}

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

/* The bleed above widens the host; this releases the wrapper inside it. Without this the host would
   span the viewport while the content stayed capped at Maximum Width, and the checkbox would look
   like it did nothing. */

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

/* Three layers in one grid cell: image, overlay, content. All three are placed in the same cell
   rather than stacked with position: absolute, so the banner's height can still be driven by the
   image when Banner Height is "Defined by Image Size" - an absolutely positioned image would
   collapse the banner to nothing. */

[part~="banner"] {
	border-radius: var(--hero-banner__radius, 0);
	box-sizing: border-box;
	display: grid;
	overflow: hidden;
	position: relative;
}

[part~="media"],
[part~="overlay"],
[part~="content-area"] {
	grid-area: 1 / 1;
}

[part~="media"] {
	min-height: 0;
	min-width: 0;
}

/* Always in the tree, at zero opacity unless asked for, so a preview_property_selector can always
   resolve to it. */

[part~="overlay"] {
	background-color: var(--hero-banner__overlay-color, #26213f);
	opacity: var(--hero-banner__overlay-opacity, 0);
	pointer-events: none;
}

/* Banner heights. On the banner rather than the image, so the image fills whatever box it is
   given and the overlay and content match it. */

:host([data-height="s"]) [part~="banner"] {
	height: 320px;
}

:host([data-height="m"]) [part~="banner"] {
	height: 480px;
}

:host([data-height="l"]) [part~="banner"] {
	height: 640px;
}

:host([data-height="screen"]) [part~="banner"] {
	height: 100svh;
}


/* The nine-point content grid
   ========================================================================== */

/* Two custom properties rather than nine rules. The JS splits the setting into a block and an
   inline value and this reads both, so adding a position would be a data change rather than a
   stylesheet change. */

[part~="content-area"] {
	box-sizing: border-box;
	display: grid;
	padding: var(--hero-banner__padding, 56px);
	position: relative;
	z-index: 1;
}

[part~="stack"] {
	align-self: var(--hero-banner__stack-block, center);
	display: flex;
	flex-direction: column;
	justify-self: var(--hero-banner__stack-inline, center);
	max-width: var(--hero-banner__text-max-width, 640px);
	row-gap: var(--hero-banner__flag-gap, 24px);
	text-align: var(--hero-banner__text-align, center);
}

/* The flag shrinks to its label, so the stack has to say which edge it hugs - and it has to be the
   edge the text is aligned to, or the two read as unrelated. */

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

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

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

/* An empty slot still generates a flex item and would take the row gap with it, leaving a hole
   where the flag would have been. */

:host([data-callout="false"]) [part~="stack"] slot[name="callout"] {
	display: none;
}


/* The bottom bar
   ========================================================================== */

/* The bar has its own radius, separate from the banner's. With a bar in place the banner's bottom
   corners are covered by it, so rounding the whole hero means the banner's top corners and the
   bar's bottom corners - which is why these are two settings rather than one.

   overflow: hidden so a rounded corner actually clips the bar's own background rather than leaving
   the colour squared off underneath the curve. */

[part~="bar"] {
	background-color: var(--hero-banner__bar-color, transparent);
	border-radius: var(--hero-banner__bar-radius, 0);
	box-sizing: border-box;
	overflow: hidden;
	padding: var(--hero-banner__bar-padding, 28px 56px);
	width: 100%;
}

:host([data-bar="false"]) [part~="bar"] {
	display: none;
}

/* minmax(0, 1fr) rather than 1fr, because a bare 1fr floors at min-content: one long unbroken word
   in a column would push the whole row wider than the bar instead of wrapping. */

[part~="columns"] {
	align-items: start;
	display: grid;
	gap: var(--hero-banner__bar-gap, 32px);
	grid-template-columns: repeat(var(--hero-banner__columns, 1), minmax(0, 1fr));
}


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

@media ( max-width: 47.9375em ) {

	/* Full screen is the only height worth keeping on a phone. The fixed ones are all taller than
	   they are wide there, which turns a banner into a poster. */

	:host([data-height="s"]) [part~="banner"],
	:host([data-height="m"]) [part~="banner"],
	:host([data-height="l"]) [part~="banner"] {
		height: 380px;
	}

	[part~="content-area"] {
		padding: 1.5rem 1.25rem;
	}

	/* A 640px text block is wider than the screen, so it has to give up its cap rather than
	   overflow. */

	[part~="stack"] {
		max-width: none;
	}

	/* Four columns on a 390px screen is four unreadable slivers, so they stack. */

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

	[part~="bar"] {
		padding: 1.5rem 1.25rem;
	}
}


/* Content Buttons
   ==========================================================================
   The row is a slotted light DOM div, styled directly - this stylesheet is in the real page head,
   which is why nothing here needs ::slotted().

   Alignment arrives already resolved to left, center or right: align-items has no left or right
   keyword, and "Match Text" has to fall back to the content's own alignment, so both decisions are
   made in the template rather than here.

   Stacked uses align-items and side by side uses justify-content, because those are the two axes the
   same three choices land on. flex-wrap so a narrow banner drops the second button rather than
   overflowing the content block. */

.hero-banner__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hero-banner__action-gap, 16px);
}

/* The row is a child of the stack now, not of the content block, so that Button Placement can move
   it above or below the text the way the callout's placement does. The consequence is that the gap
   has to know which side the text is on - above the text, the space belongs underneath. */

hero-banner[data-button-placement="below"] .hero-banner__actions {
	margin-top: var(--hero-banner__action-top-gap, 32px);
}

hero-banner[data-button-placement="above"] .hero-banner__actions {
	margin-bottom: var(--hero-banner__action-top-gap, 32px);
}

hero-banner[data-button-direction="row"] .hero-banner__actions {
	flex-direction: row;
}

hero-banner[data-button-direction="stacked"] .hero-banner__actions {
	flex-direction: column;
}

hero-banner[data-button-direction="row"][data-button-align="left"] .hero-banner__actions {
	justify-content: flex-start;
}

hero-banner[data-button-direction="row"][data-button-align="center"] .hero-banner__actions {
	justify-content: center;
}

hero-banner[data-button-direction="row"][data-button-align="right"] .hero-banner__actions {
	justify-content: flex-end;
}

hero-banner[data-button-direction="stacked"][data-button-align="left"] .hero-banner__actions {
	align-items: flex-start;
}

hero-banner[data-button-direction="stacked"][data-button-align="center"] .hero-banner__actions {
	align-items: center;
}

hero-banner[data-button-direction="stacked"][data-button-align="right"] .hero-banner__actions {
	align-items: flex-end;
}

/* Fill the Container means the whole row when stacked and an equal share of it when side by side.
   flex-basis 0 rather than auto for the second case, or a long label would claim more than its share
   and the pair would come out lopsided. */

.hero-banner__action[data-own-width="full"] {
	align-self: stretch;
}

hero-banner[data-button-direction="row"] .hero-banner__action[data-own-width="full"] {
	flex: 1 1 0;
}

.hero-banner__action-arrow {
	margin-left: 0.35em;
}

/* Phones: side by side stops being side by side. Two themed buttons at their natural label width do
   not fit across a phone, and the wrap produces a ragged one-and-one rather than a row. */

@media ( max-width: 47.9375em ) {

	hero-banner[data-button-direction="row"] .hero-banner__actions {
		flex-direction: column;
	}

	hero-banner[data-button-direction="row"][data-button-align="left"] .hero-banner__actions {
		align-items: flex-start;
	}

	hero-banner[data-button-direction="row"][data-button-align="center"] .hero-banner__actions {
		align-items: center;
	}

	hero-banner[data-button-direction="row"][data-button-align="right"] .hero-banner__actions {
		align-items: flex-end;
	}

	hero-banner[data-button-direction="row"] .hero-banner__action[data-own-width="full"] {
		flex: 0 0 auto;
	}

}


/* Columns
   ==========================================================================
   A third layout rather than a variant of the other two: side by side is a single row that wraps
   where it runs out of space, columns is a fixed number of tracks that every button divides evenly
   however long its label is.

   Grid rather than flex with a percentage basis, because 1fr already means "an equal share of what
   is left after the gaps" - the same result in flex needs a calc() against the gap count, which has
   to be rewritten every time the gap changes.

   Alignment is justify-items here, not justify-content: justify-content would move the whole grid
   inside its container while leaving each button hard against the left of its own track. */

hero-banner[data-button-direction="columns"] .hero-banner__actions {
	display: grid;
	grid-template-columns: repeat(var(--hero-banner__action-columns, 2), 1fr);
}

hero-banner[data-button-direction="columns"][data-button-align="left"] .hero-banner__actions {
	justify-items: start;
}

hero-banner[data-button-direction="columns"][data-button-align="center"] .hero-banner__actions {
	justify-items: center;
}

hero-banner[data-button-direction="columns"][data-button-align="right"] .hero-banner__actions {
	justify-items: end;
}

/* Fill the Container inside a track means the track, and justify-items has to step aside for it -
   a stretched item cannot also be start-aligned. */

hero-banner[data-button-direction="columns"] .hero-banner__action[data-own-width="full"] {
	justify-self: stretch;
	width: 100%;
}

/* Phones: one column, for the same reason side by side collapses. Declared after the alignment rules
   above so the single track wins on source order at equal specificity. */

@media ( max-width: 47.9375em ) {

	hero-banner[data-button-direction="columns"] .hero-banner__actions {
		grid-template-columns: 1fr;
	}

}
