/*
 * ICONS builder layout.
 *
 * The wrapper carries `alignfull` so core's constrained layout lets it out of
 * contentSize. These rules restate that independently, because a page template
 * that is not constrained — or a theme.json that changes — should not be able
 * to reintroduce the cap.
 *
 * A 12-column grid with per-breakpoint spans. Twelve because it divides by
 * 2, 3, 4 and 6, which covers every layout anyone actually asks for.
 *
 * Spans resolve through a cascading fallback, so a column set to 4 on desktop
 * and left alone everywhere else is full width on a phone, full width on a
 * tablet, and a third on desktop — without anyone configuring the phone. That
 * default is the single most common way a hand-built page breaks.
 */

.ib-layout {
	max-inline-size: none;
	inline-size: 100%;
	margin-inline: 0;
}

/* Cancel the root padding a constrained parent applies, so a full-width row
 * really does reach the edge.
 *
 * The child selector is deliberate but was not enough on its own. The layout's
 * parent is `.entry-content`, not the element carrying `has-global-padding`, so
 * this never matched on a real page — and `.entry-content` was itself capped at
 * the content measure by the constrained `<main>` above it. A builder page came
 * out 704px wide with every heading wrapping every three words.
 *
 * The fix that mattered is in the templates: `wp:post-content` is now
 * `align:full`, so `.entry-content` spans the page and ordinary posts keep
 * their measure from post-content's own constrained layout. This rule stays for
 * the case where the layout *is* a direct child of a padded container. */
.has-global-padding > .ib-layout.alignfull {
	margin-inline: calc(var(--wp--style--root--padding-right, 0px) * -1) calc(var(--wp--style--root--padding-left, 0px) * -1);

	/*
	 * `inline-size: 100%` above resolves against the padded parent's *content*
	 * box, and a negative margin does not grow a box that has been given an
	 * explicit width. So the compensation moved the layout left by the root
	 * padding and never widened it: every full-bleed row ran off the left edge
	 * and left a strip of page background down the right, the same width on
	 * every builder page on every site.
	 *
	 * Measured on the live page at 1425px: the row was 1387.4px and started at
	 * x=0 — 37.6px short, which is exactly the two root paddings.
	 */
	inline-size: auto;
}

.ib-row {
	position: relative;
	z-index: 1;
	--ib-row-pad: var(--ib-row-pad-mobile, var(--icons-space-xl));

	/*
	 * Rides up over the row above. Zero unless asked for, and asked for per
	 * breakpoint — the pattern it exists for is a card sitting on the corner of
	 * a photograph, which only works where there is room for it. Left at zero on
	 * mobile the same row simply follows the picture, which is what the design
	 * it was copied from does at that width too.
	 */
	--ib-row-pull: var(--ib-row-pull-mobile, 0px);

	margin-block-start: var(--ib-row-pull);

	padding-block: var(--ib-row-pad);
	padding-inline: var(--icons-space-sm);
}

.ib-row__inner {
	display: grid;
	grid-template-columns: repeat(12, minmax(0, 1fr));
	gap: var(--ib-row-gap, var(--icons-space-md));
	margin-inline: auto;
	inline-size: 100%;
	max-inline-size: var(--ib-row-width, var(--icons-container, var(--wp--style--global--wide-size, 1216px)));
}

/*
 * Twelve tracks have eleven gaps between them, and nothing stops those eleven
 * from being wider than the container.
 *
 * On a 375px phone the content box is 343px, and a row set to `xl` has a 57px
 * gap: 11 x 57 = 631px of gap alone. The tracks are `minmax(0, 1fr)` so they
 * dutifully collapse to zero, and a full-width column -- which spans all twelve
 * tracks *and* the eleven gaps -- comes out 631px wide inside a 343px row. The
 * page then scrolls sideways and every section sits at a different left edge,
 * which reads as the layout shifting rather than as one row being too wide.
 * Six of thirteen rows on the first site to hit it were doing this.
 *
 * Below the desktop breakpoint columns are stacked or two-up, so the horizontal
 * gap is mostly invisible anyway: capping it at a share of the container costs
 * nothing there and guarantees the tracks keep most of the room. A percentage
 * resolves against the grid's own inline size, so it is the container that
 * decides, not the viewport -- a row nested in something narrow stays correct.
 * The vertical gap is untouched: that one is the visible rhythm on a phone.
 */
@media (max-width: 63.999rem) {
	.ib-row__inner {
		column-gap: min(var(--ib-row-gap, var(--icons-space-md)), 3%);
	}
}

/* Width is the *content* measure, not the row.
 *
 * `.ib-row` has no maximum and carries the background, so a row's colour, image
 * or video reaches both edges of the screen whatever this is set to. These
 * values only decide how wide the text inside is allowed to get. A hero with a
 * full-bleed video and a readable line length is `normal`, not `full` — a
 * mistake worth naming, because the first real layout made it.
 *
 * The container token comes from base.css, which the admin builder screen does
 * not load, so the wide-size fallback stays spelled out here. */
.ib-row.is-width-narrow { --ib-row-width: var(--wp--style--global--content-size, 704px); }
.ib-row.is-width-normal { --ib-row-width: var(--icons-container, var(--wp--style--global--wide-size, 1216px)); }
.ib-row.is-width-wide   { --ib-row-width: 1472px; }

/* Full removes the measure and keeps the gutter. It used to zero the padding
 * too, which put text hard against the edge of the screen — and against the
 * edge of a phone, where there is no margin to spare. Nothing wanted that: a
 * genuinely edge-to-edge picture is the element's job, not the row's. */
.ib-row.is-width-full   { --ib-row-width: none; }

/* ------------------------------------------------------- Row media
 *
 * A row with a picture or a video behind it. The media is laid under the
 * content and an optional scrim sits between them, because white text over an
 * unmodified photograph is legible right up until the moment the photograph
 * changes.
 *
 * Only a row that actually resolved media becomes a stacking context. Applying
 * this to every row would mean every row paid for a containing block it does
 * not use.
 */

.ib-row.has-media {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background-image: var(--ib-row-image);
	background-size: cover;
	background-position: center;
}

.ib-row__video {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;

	/* A video has an intrinsic aspect ratio and a row does not, so one of the
	 * two axes always has to be cropped. */
	object-fit: cover;
	z-index: -2;
	pointer-events: none;
}

.ib-row__overlay {
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
}

/* The content has to sit above media that was pushed behind the row's own
 * background, which a negative z-index alone does not guarantee. */
.ib-row.has-media > .ib-row__inner {
	position: relative;
	z-index: 1;
}

/* Autoplaying video is exactly the motion this setting is asking us to stop.
 * Dropping the element leaves the poster showing through as the row's
 * background image, so the row keeps its picture rather than going flat. */
@media (prefers-reduced-motion: reduce) {
	.ib-row__video {
		display: none;
	}
}

/* ------------------------------------------------------------------ Tile
 *
 * A picture with a title on it. The image is a real <img> rather than a
 * background so it gets srcset, lazy loading and an alt attribute — a
 * background-image has none of those, and a grid of them is the heaviest thing
 * on a page like this.
 *
 * Height cascades by tier the same way spans and padding do: a tier left unset
 * inherits the next smallest.
 */

.ib-tile {
	--ib-tile-h: var(--ib-tile-h-mobile, 220px);

	position: relative;
	isolation: isolate;
	overflow: hidden;
	display: flex;
	min-block-size: var(--ib-tile-h);
	padding: var(--icons-space-sm);
	text-decoration: none;
	color: inherit;
}

@media (min-width: 48rem) {
	.ib-tile {
		--ib-tile-h: var(--ib-tile-h-tablet, var(--ib-tile-h-mobile, 220px));
	}
}

@media (min-width: 64rem) {
	.ib-tile {
		--ib-tile-h: var(--ib-tile-h-desktop, var(--ib-tile-h-tablet, var(--ib-tile-h-mobile, 220px)));
	}
}

.ib-tile.is-align-bottom { align-items: flex-end; }
.ib-tile.is-align-center { align-items: center; }
.ib-tile.is-align-top    { align-items: flex-start; }

.ib-tile__img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	z-index: -2;
	transition: transform var(--icons-transition-base);
}

.ib-tile__overlay {
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	opacity: var(--ib-tile-overlay, 0.35);
	transition: opacity var(--icons-transition-base);
}

.ib-tile__body {
	display: flex;
	flex-direction: column;
	gap: var(--icons-space-3xs);
}

.ib-tile__eyebrow {
	font-size: var(--icons-text-sm);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	opacity: 0.85;
}

.ib-tile__text {
	margin-block-start: 0.5em;
	max-width: 42ch;
	font-size: var(--icons-text-sm);
	line-height: 1.5;
	opacity: 0.88;
}

.ib-tile__title {
	font-family: var(--icons-font-heading);
	font-size: var(--icons-text-xl);
	font-weight: 650;
	line-height: 1.15;
	letter-spacing: -0.014em;
}

/* Only a linked tile reacts: movement on something you cannot click is a
 * promise the page does not keep. */
.ib-tile.is-linked:hover .ib-tile__img,
.ib-tile.is-linked:focus-visible .ib-tile__img {
	transform: scale(1.04);
}

/* Lifting the scrim rather than replacing it, so a tile set dark stays dark
 * relative to itself instead of every tile hovering to the same value. */
.ib-tile.is-linked:hover .ib-tile__overlay,
.ib-tile.is-linked:focus-visible .ib-tile__overlay {
	opacity: calc(var(--ib-tile-overlay, 0.35) * 0.55);
}

.ib-row.is-valign-center  .ib-col { align-self: center; }
.ib-row.is-valign-bottom  .ib-col { align-self: end; }
.ib-row.is-valign-stretch .ib-col { align-self: stretch; }

/* A row given a minimum height has spare space, and vertical alignment has to
 * place the content inside it — not only line the columns up against each
 * other. Applied only where there is a minimum height, so an ordinary row's
 * box model does not change at all.
 *
 * Row media and the scrim are both absolutely positioned, so neither becomes a
 * flex item here. */
.ib-row.has-min-height {
	display: flex;
	flex-direction: column;

	/* A row asked for 100vh should be 100vh, not 100vh plus its padding. Rows
	 * are content-box, so a full-screen hero came out 96px taller than the
	 * screen and pushed whatever sat at its foot below the fold. Scoped to
	 * rows that set a minimum height, so no other row's box model moves. */
	box-sizing: border-box;
}

.ib-row.has-min-height.is-valign-center { justify-content: center; }
.ib-row.has-min-height.is-valign-bottom { justify-content: flex-end; }

.ib-col.is-valign-center { align-self: center; }
.ib-col.is-valign-bottom { align-self: end; }

/* ------------------------------------------------------------- Columns */

.ib-col {
	--ib-span: var(--ib-span-mobile, 12);

	grid-column: span var(--ib-span);
	min-inline-size: 0;
	padding: var(--ib-col-pad-mobile, 0);
}

@media (min-width: 48rem) {
	.ib-col {
		--ib-span: var(--ib-span-tablet, var(--ib-span-mobile, 12));

		padding: var(--ib-col-pad-tablet, var(--ib-col-pad-mobile, 0));
	}

	.ib-row {
		--ib-row-pad: var(--ib-row-pad-tablet, var(--ib-row-pad-mobile, var(--icons-space-xl)));
		--ib-row-pull: var(--ib-row-pull-tablet, var(--ib-row-pull-mobile, 0px));
	}
}

@media (min-width: 64rem) {
	.ib-col {
		--ib-span: var(--ib-span-desktop, var(--ib-span-tablet, var(--ib-span-mobile, 12)));

		padding: var(--ib-col-pad-desktop, var(--ib-col-pad-tablet, var(--ib-col-pad-mobile, 0)));
	}

	.ib-row {
		--ib-row-pad: var(--ib-row-pad-desktop, var(--ib-row-pad-tablet, var(--ib-row-pad-mobile, var(--icons-space-xl))));
		--ib-row-pull: var(--ib-row-pull-desktop, var(--ib-row-pull-tablet, var(--ib-row-pull-mobile, 0px)));
	}
}

/* ------------------------------------------------------------ Elements */

.ib-el + .ib-el {
	margin-block-start: var(--icons-space-sm);
}

.ib-heading,
.ib-text > :first-child {
	margin-block-start: 0;
}

.ib-heading {
	line-height: var(--icons-heading-line-height, 1.15);
}

.ib-text > :last-child {
	margin-block-end: 0;
}

.has-text-align-center { text-align: center; }
.has-text-align-right  { text-align: right; }

.ib-image {
	margin: 0;
}

.ib-image__img {
	inline-size: 100%;
	block-size: auto;
}

.ib-spacer {
	block-size: var(--ib-spacer-mobile, 32px);
}

@media (min-width: 48rem) {
	.ib-spacer { block-size: var(--ib-spacer-tablet, var(--ib-spacer-mobile, 32px)); }
}

@media (min-width: 64rem) {
	.ib-spacer { block-size: var(--ib-spacer-desktop, var(--ib-spacer-tablet, var(--ib-spacer-mobile, 32px))); }
}

.ib-divider {
	border: 0;
	border-block-start: 1px solid var(--icons-color-border);
	margin-inline: 0;
}

.ib-button-wrap.has-text-align-center { text-align: center; }
.ib-button-wrap.has-text-align-right  { text-align: right; }

.ib-button {
	display: inline-flex;

	/* Only matters once something gives the button a width -- a phone stacking
	 * a pair to `width: 100%`, say. Content-box then adds the padding on top of
	 * the 100% and the button hangs off the screen by its own side padding. */
	box-sizing: border-box;
	align-items: center;
	justify-content: center;
	gap: 0.5em;
	text-decoration: none;
	border: 1px solid transparent;
	transition: filter var(--icons-transition-base), background-color var(--icons-transition-base);
	cursor: pointer;
}

/*
 * Zero specificity on purpose: a theme.json that sets a button radius — a pill,
 * a square — has to win, and it is loaded before this file. Written as a plain
 * class it would not, and the theme's own setting would silently do nothing.
 */
:where(.ib-button) {
	border-radius: var(--icons-radius-md);
}

.ib-button.is-size-sm { padding: 0.45em 1em;   font-size: var(--icons-text-sm); }
.ib-button.is-size-md { padding: 0.6em 1.4em;  font-size: var(--icons-text-md); }
.ib-button.is-size-lg { padding: 0.75em 1.8em; font-size: var(--icons-text-lg); }

.ib-button.is-solid {
	background: var(--icons-color-primary);
	color: var(--icons-color-base);
}

.ib-button.is-solid:hover {
	filter: brightness(1.1);
	text-decoration: none;
}

.ib-button.is-outline {
	border-color: currentColor;
	color: var(--icons-color-primary);

	/* theme.json styles wp-element-button, and core emits that at a
	 * specificity this rule beats -- but only for properties it actually
	 * declares. Without this line the button keeps the solid fill from
	 * theme.json while taking its text colour from here, so an outline
	 * button on a primary-filled theme is primary text on a primary
	 * background: a coloured rectangle with the label invisible inside it.
	 * Found on a dark palette, where it is total; it was always wrong. */
	background: transparent;
}

.ib-button.is-outline:hover {
	background: var(--icons-color-primary);
	color: var(--icons-color-base);
	text-decoration: none;
}

/*
 * For a button sitting on a coloured band, or beside a solid one where two
 * oranges would compete.
 *
 * `contrast` on `base` is the palette's own definition of opposite, so this is
 * correct in both directions rather than only on a dark one: on Blok it is a
 * bone button with near-black type, on Klei a dark button with paper type. The
 * first version hardcoded `base` as the ground, which happened to be right on
 * the palette it was written for and would have been backwards on a light one.
 */
.ib-button.is-inverse {
	background: var(--icons-color-contrast);
	color: var(--icons-color-base);
	border-color: transparent;
}

.ib-button.is-inverse:hover {
	filter: brightness(0.94);
	text-decoration: none;
}

.ib-button.is-text {
	color: var(--icons-color-primary);
	padding-inline: 0;

	/* Same reason as is-outline: theme.json fills wp-element-button, and a
	 * style that only sets a colour keeps that fill. A "text" button rendered
	 * as a solid orange bar with orange type inside it. Every style that is not
	 * solid has to say what its background is -- the suite asserts that now,
	 * because this was the third one found by looking at a page. */
	background: transparent;
	border-color: transparent;
}

.ib-video :where(iframe, video) {
	inline-size: 100%;
	aspect-ratio: 16 / 9;
	block-size: auto;
	border-radius: var(--icons-radius-md);
}

/* ------------------------------------------------------------------ Icon
 *
 * The svg carries its own width and height, so alignment is all that is left
 * to arrange. currentColor means the icon takes the colour of whatever it sits
 * in unless the element sets one.
 */

.ib-icon { line-height: 0; }

.ib-icon.has-text-align-center { text-align: center; }
.ib-icon.has-text-align-right  { text-align: right; }

.ib-icon__link {
	display: inline-flex;
	color: inherit;
	text-decoration: none;
}

.icons-icon {
	display: inline-block;
	vertical-align: middle;
	flex: none;
}

/* ------------------------------------------------------------- Accordion
 *
 * <details> supplies the behaviour; this only makes it look deliberate. The
 * marker is replaced with a chevron drawn in CSS so it can rotate, since the
 * native triangle cannot be animated and looks like a debug control.
 */

.ib-acc__item {
	border-block-end: 1px solid var(--icons-color-border);
}

.ib-acc__item:first-child {
	border-block-start: 1px solid var(--icons-color-border);
}

.ib-acc__title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--icons-space-sm);
	padding-block: var(--icons-space-sm);
	cursor: pointer;
	font-family: var(--icons-font-heading);
	font-weight: 600;
	list-style: none;
}

/* Safari draws its own marker unless this is said in its own dialect. */
.ib-acc__title::-webkit-details-marker { display: none; }

.ib-acc__title::after {
	content: "";
	flex: none;
	inline-size: 0.55em;
	block-size: 0.55em;
	border-inline-end: 2px solid currentColor;
	border-block-end: 2px solid currentColor;
	transform: rotate(45deg) translate(-0.1em, -0.1em);
	transition: transform var(--icons-transition-base);
}

.ib-acc__item[open] > .ib-acc__title::after {
	transform: rotate(-135deg) translate(-0.15em, -0.15em);
}

.ib-acc__body {
	padding-block-end: var(--icons-space-sm);
	max-inline-size: var(--icons-measure-prose);
}

.ib-acc__body > :first-child { margin-block-start: 0; }
.ib-acc__body > :last-child  { margin-block-end: 0; }

/* ------------------------------------------------------------- Icon list */

.ib-iconlist {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--icons-space-2xs);
}

.ib-iconlist__item {
	display: flex;
	align-items: flex-start;
	gap: var(--icons-space-2xs);
}

.ib-iconlist__item > .icons-icon {
	color: var(--ib-iconlist-color, var(--icons-color-primary));

	/* Nudged onto the first line's optical centre rather than its box top. */
	margin-block-start: 0.15em;
}

/* -------------------------------------------------------- Hiding by tier
 *
 * Each rule hides only inside its own band, so nothing has to be turned back
 * on afterwards. The obvious alternative — hide by default, `display: revert`
 * above a breakpoint — throws away whatever display value the element actually
 * had, which quietly breaks any element that is a flex or grid container.
 *
 * The bands match the builder's tiers, which are not the same as the utility
 * classes in base.css: those cut at four widths, these at the three a design
 * is described in.
 */

@media (max-width: 47.999rem) {
	.ib-hide-mobile { display: none; }
}

@media (min-width: 48rem) and (max-width: 63.999rem) {
	.ib-hide-tablet { display: none; }
}

@media (min-width: 64rem) {
	.ib-hide-desktop { display: none; }
}

/* ------------------------------------------------------------------- Map
 *
 * The placeholder is a real button at the map's own size, so the page does not
 * jump when the map replaces it.
 */

.ib-map {
	--ib-map-h: var(--ib-map-h-mobile, 260px);

	position: relative;
	block-size: var(--ib-map-h);
	border-radius: var(--icons-radius-sm, 6px);
	overflow: hidden;
	background: var(--icons-color-surface, #f2f3f5);
}

@media (min-width: 48rem) {
	.ib-map { --ib-map-h: var(--ib-map-h-tablet, var(--ib-map-h-mobile, 260px)); }
}

@media (min-width: 64rem) {
	.ib-map { --ib-map-h: var(--ib-map-h-desktop, var(--ib-map-h-tablet, var(--ib-map-h-mobile, 420px))); }
}

.ib-map__frame {
	inline-size: 100%;
	block-size: 100%;
	border: 0;
	display: block;
}

.ib-map__ask {
	inline-size: 100%;
	block-size: 100%;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 6px;
	border: 1px dashed var(--icons-color-border, #d9dce1);
	background: transparent;
	color: inherit;
	cursor: pointer;
	padding: var(--icons-space-sm, 16px);
	text-align: center;
}

.ib-map__ask:hover { border-color: var(--icons-color-primary, #3858e9); }
.ib-map__ask-label { font-weight: 600; }
.ib-map__ask-note { font-size: 0.8125em; opacity: 0.72; max-inline-size: 34ch; }

/* A form placed from a plugin: give its controls the site's own shapes rather
 * than the plugin's defaults. */
.ib-form input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.ib-form select,
.ib-form textarea {
	inline-size: 100%;

	/* 100% plus padding plus border is wider than 100%. Inputs and textareas
	 * are content-box by default, so every field overhung its column by the
	 * padding -- invisible on a wide screen, and on a phone it widened the
	 * grid and shifted the whole page. `select` is border-box in every UA,
	 * which is why one field in the set looked correct and the rest did not. */
	box-sizing: border-box;
	padding: 0.6em 0.8em;
	border: 1px solid var(--icons-color-border, #d9dce1);
	border-radius: var(--icons-radius-sm, 6px);
	background: var(--icons-color-base, #fff);
	color: inherit;
	font: inherit;
}

.ib-form input:focus, .ib-form select:focus, .ib-form textarea:focus {
	outline: 2px solid var(--icons-color-primary, #3858e9);
	outline-offset: 1px;
}

/* ---------------------------------------------------------------- Numbers */

/*
 * A band of figures — 1974, 40 staff, 24/7. Laid out as a row that wraps
 * rather than a grid, so three numbers in a narrow column stack and the same
 * three across a full-width row sit side by side without being told to.
 */
.ib-stat {
	display: flex;
	flex-wrap: wrap;
	gap: var(--icons-space-lg) var(--icons-space-2xl);
}

.ib-stat__item {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.ib-stat__value {
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.02em;
}

.ib-stat__label {
	font-size: var(--icons-text-sm);
	opacity: 0.66;
}

/* ------------------------------------------------------------------ Quote */

.ib-quote {
	margin: 0;
	padding-inline-start: var(--icons-space-md);
	border-inline-start: 3px solid currentColor;
}

.ib-quote blockquote {
	margin: 0;
	font-style: italic;
	line-height: 1.35;
	text-wrap: balance;
}

.ib-quote__cite {
	margin-block-start: var(--icons-space-sm);
	font-size: var(--icons-text-sm);
	font-style: normal;
	opacity: 0.7;
}

.ib-quote__cite span::before {
	content: " — ";
}

/* ------------------------------------------------------------------ Table */

/*
 * Scrolls inside its own column. A wide table that widens the page instead
 * puts a horizontal scrollbar under the whole site, and the row that caused
 * it is three sections up where nobody looks.
 */
.ib-table {
	overflow-x: auto;
}

.ib-table table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--icons-text-sm);
}

.ib-table th,
.ib-table td {
	padding: var(--icons-space-xs) var(--icons-space-sm);
	text-align: start;
	border-block-end: 1px solid var(--icons-color-border, rgba(0, 0, 0, 0.12));
}

.ib-table th {
	font-size: var(--icons-text-sm);
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	opacity: 0.6;
}

.ib-table tbody tr:last-child td {
	border-block-end: 0;
}

/* ---------------------------------------------------------------- Cards */

/*
 * A picture with the words underneath, on a surface — the other card every
 * design reference has, beside the tile's words-on-the-photograph.
 *
 * The surface is drawn from `currentColor` rather than a palette slot, so the
 * same element looks deliberate on a cream row and on a near-black one without
 * anybody setting a colour per row. A card that needs the brand's own tint
 * gets it from the child theme.
 */
.ib-cards {
	--ib-cards-cols: var(--ib-cards-cols-mobile, 1);
	--ib-cards-radius: 0px;
	--ib-cards-gap: var(--icons-space-sm);
}

.ib-cards__track {
	display: grid;
	grid-template-columns: repeat(var(--ib-cards-cols), minmax(0, 1fr));
	gap: var(--ib-cards-gap);
}

.ib-cards__item {
	display: flex;
	flex-direction: column;
	margin: 0;
	min-inline-size: 0;
	border-radius: var(--ib-cards-radius);
	overflow: hidden;
}

.ib-cards.has-surface .ib-cards__item {
	background: color-mix(in srgb, currentColor 5%, transparent);
	border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
	transition: background var(--icons-transition-base, 220ms ease),
		border-color var(--icons-transition-base, 220ms ease);
}

.ib-cards.has-surface .ib-cards__item:hover {
	background: color-mix(in srgb, currentColor 9%, transparent);
	border-color: color-mix(in srgb, currentColor 24%, transparent);
}

.ib-cards__link {
	display: flex;
	flex-direction: column;
	color: inherit;
	text-decoration: none;
	block-size: 100%;
}

.ib-cards__media {
	inline-size: 100%;
	block-size: auto;
	object-fit: cover;
	display: block;
}

.ib-cards__body {
	display: flex;
	flex-direction: column;
	padding: var(--icons-space-sm);
}

.ib-cards__title {
	font-weight: 600;
	letter-spacing: -0.02em;
	line-height: 1.25;
}

.ib-cards__eyebrow {
	margin-block-start: 0.35em;
	font-size: var(--icons-text-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	opacity: 0.75;
}

.ib-cards__text {
	margin-block: 0.8em 0;
	font-size: var(--icons-text-sm);
	line-height: 1.5;
	opacity: 0.72;
}

/*
 * The carousel is the same grid turned into one scrolling row. `grid-auto-flow:
 * column` rather than flex, so the column count keeps meaning "how many are in
 * view" and the card width stays a division of the track.
 */
.ib-cards.is-carousel .ib-cards__track {
	grid-template-columns: none;
	grid-auto-flow: column;
	grid-auto-columns: calc(
		(100% - (var(--ib-cards-cols) - 1) * var(--ib-cards-gap)) / var(--ib-cards-cols)
	);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
	-ms-overflow-style: none;
	padding-block-end: 2px;
}

.ib-cards.is-carousel .ib-cards__track::-webkit-scrollbar {
	display: none;
}

.ib-cards.is-carousel .ib-cards__item {
	scroll-snap-align: start;
}

.ib-cards__nav {
	display: flex;
	gap: var(--icons-space-3xs);
	margin-block-start: var(--icons-space-sm);
}

.ib-cards__btn {
	inline-size: 44px;
	block-size: 44px;
	display: grid;
	place-items: center;
	padding: 0;
	border-radius: var(--icons-radius-pill, 999px);
	border: 1px solid color-mix(in srgb, currentColor 26%, transparent);
	background: none;
	color: inherit;
	cursor: pointer;
	transition: background var(--icons-transition-base, 220ms ease),
		color var(--icons-transition-base, 220ms ease),
		border-color var(--icons-transition-base, 220ms ease);
}

.ib-cards__btn svg {
	inline-size: 19px;
	block-size: 19px;
}

.ib-cards__btn:hover {
	background: currentColor;
	border-color: currentColor;
}

/*
 * The arrow has to invert with the button, and there is no token for "whatever
 * is behind this row". Filtering the mark is the one way to flip it that works
 * on a cream row and a near-black one alike.
 */
.ib-cards__btn:hover svg {
	filter: invert(1) grayscale(1) contrast(3);
}

@media (min-width: 48rem) {
	.ib-cards {
		--ib-cards-cols: var(--ib-cards-cols-tablet, var(--ib-cards-cols-mobile, 1));
	}
}

@media (min-width: 64rem) {
	.ib-cards {
		--ib-cards-cols: var(
			--ib-cards-cols-desktop,
			var(--ib-cards-cols-tablet, var(--ib-cards-cols-mobile, 1))
		);
	}
}

@media (prefers-reduced-motion: reduce) {
	.ib-cards.is-carousel .ib-cards__track {
		scroll-behavior: auto;
	}
}
