/*
Theme Name: Trustbridge
Theme URI: https://trustbridge.com/
Author: Purple, Rock, Scissors (PRPL)
Author URI: https://purplerockscissors.com/
Description: Brand-agnostic, tokenized block theme (a design-system foundation) for the Empath Health web program. Trustbridge is its first consumer. All theming is token-driven via theme.json; re-skin per brand with the style variations in /styles.
Requires at least: 6.6
Tested up to: 6.9
Requires PHP: 8.1
Version: 0.29.21
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: trustbridge
*/

/*
 * Intentionally minimal. This is a block theme — styling lives in theme.json
 * (design tokens -> CSS custom properties) and in block markup, NOT here.
 * Add CSS to this file only for things theme.json genuinely cannot express.
 *
 * Browser defaults are reset separately, in assets/reset.css, which is loaded
 * in a cascade layer so it can never outrank a token. This file is unlayered
 * and prints after the global styles, so anything here beats everything —
 * which is why it stays short.
 */

/*
 * Design-system base rules theme.json cannot express.
 * These mirror _ds/…/tokens/base.css — see DESIGN.md.
 */

/*
 * The stepped page gutter is in assets/tokens.css, which the editor loads too.
 */

/*
 * Motion honours the OS preference at the token, not at each call site. The
 * design system zeroes its duration tokens under `prefers-reduced-motion`, so
 * every transition built on them stops at once; components that also move
 * something (a card lift) still cancel the transform themselves.
 */
@media (prefers-reduced-motion: reduce) {

	:root {
		--wp--custom--motion--duration-fast: 0ms;
		--wp--custom--motion--duration-base: 0ms;
		--wp--custom--motion--duration-slow: 0ms;
		--wp--custom--motion--duration-panel: 0ms;
	}
}

/*
 * Links carry the design system's underline treatment; theme.json sets only the
 * colour. Not wrapped in :where() — theme.json's own link rule is `:root
 * :where(a…)`, whose `text-decoration` shorthand resets thickness to `auto`, so
 * this needs real specificity to survive.
 */
a:not(.wp-element-button) {
	text-underline-offset: 3px;
	text-decoration-thickness: 1px;
	transition: color var(--wp--custom--motion--duration-fast) var(--wp--custom--motion--ease-standard);
}

a:not(.wp-element-button):hover {
	text-decoration-thickness: 2px;
}

/*
 * Where page content's sections land — bands butting together, the prose
 * page's vertical rhythm, the hero-aligned edge — is in
 * assets/blocks/post-content.css, which the editor loads too.
 */

/*
 * Running text keeps a readable measure. The design system applies this to
 * every `p`; here it is scoped to post content, because the header, footer and
 * pattern paragraphs set their own widths and a global 66ch cap fights them.
 *
 * Scoped again to paragraphs **nested inside something**, never to
 * post-content's own children. A direct child is already capped by the
 * constrained layout at `layout.contentSize`, which is a pixel value every
 * element resolves identically; capping it a second time in `ch` would make
 * the width depend on each element's own font size, so a paragraph and the
 * heading above it would land on different left edges. Inside a pattern the
 * section column is far wider than any reading measure, which is where this
 * cap earns its place.
 */
.wp-block-post-content :not(.wp-block-post-content) > p {
	max-width: var(--wp--custom--measure--body);
}

.wp-block-post-content p {
	text-wrap: pretty;
}

/*
 * ...but a paragraph narrower than its column gets centred, not left-aligned:
 * core's constrained layout gives every child `margin-left: auto` and
 * `margin-right: auto`, both `!important`, and those same margins are what
 * centre the content column inside the section's padding — so the margins
 * cannot simply be zeroed without pulling the paragraph out of the column
 * altogether.
 *
 * Two consequences, handled differently.
 *
 * On a page whose content is plain prose, every one of post-content's own
 * children — heading, paragraph, list, image — is capped by the constrained
 * layout at `layout.contentSize` and centred by those auto margins, so they
 * share one width and one left edge and nothing here has to exempt anything.
 *
 * An exemption used to live here, as `.wp-block-post-content > p {
 * max-width: none }`, on the premise that post-content's own box was already
 * 760px so uncapping a paragraph was harmless. The premise was false:
 * post-content is `alignfull`, so its box is the viewport and only its
 * *children* are capped. Removing the cap let paragraphs fill 1360px while the
 * constrained layout still held headings at 760px, and every imported page
 * rendered its headings 300px right of their own body copy. Do not
 * reintroduce it — cap the nested case instead, above.
 *
 * Inside a pattern the section column is far wider than the measure, so the
 * cap is wanted and the centring is not. That one is solved in the markup:
 * running text goes in a group with the **flow** layout, which emits no auto
 * margins, so the paragraph is capped and still starts at the column edge. See
 * the theme README.
 */

/*
 * Nothing in post content is wider than the column it sits in.
 *
 * Core's constrained layout caps a child at the content size as a **pixel**
 * value, so below that width the child keeps its pixels and the document grows
 * past the viewport. Measured across the site, 57 of 186 views overflowed at
 * 390px and 47 of those by exactly 390px — a document twice the viewport, which
 * a phone answers by zooming the whole page out rather than showing a
 * scrollbar. `assets/reset.css` already says `max-width: 100%` for media, but
 * it is inside `@layer trustbridge-reset` and a layered declaration loses to
 * core's unlayered one, so the cap has to be restated here, unlayered.
 *
 * Form controls are in the list because the legacy import left raw `input` and
 * `textarea` elements in post content, and a `textarea` carrying `cols` is
 * 739px wide whatever the viewport.
 */
.wp-block-post-content :is(img, svg, video, canvas, iframe, embed, object,
	input, textarea, select, table, pre) {
	max-width: 100%;
}

/*
 * That cap is 100% of whatever box the thing sits in, which is what a pattern
 * wants. Post-content's *own* children have a narrower ceiling — the content
 * column the copy is set to — so cap them to the lesser of the two, and make
 * the standalone case a block.
 *
 * The legacy import left plenty of bare `img` and `iframe` straight in post
 * content. Inline elements take no auto margins, so the constrained layout
 * centres the text column and leaves the image at the content-box edge, 300px
 * to its left. `display: block` is what lets the layout's own
 * `margin-inline: auto` reach them, which puts the image in the same column as
 * the copy it belongs to. Direct children only: an image inside a paragraph or
 * a pattern is inline for a reason.
 */
.wp-block-post-content > :is(img, picture, iframe, video, table, pre) {
	display: block;
	max-width: min(100%, var(--wp--style--global--content-size));
	margin-inline: auto;
}

/*
 * The stand-in panel a card gets when its post has no featured image. The
 * markup comes from the `render_block_core/post-featured-image` filter in
 * `functions.php`, which is where the reason it is generated rather than
 * styled-in-place is written down.
 *
 * `aspect-ratio` rather than a height, so the panel occupies exactly what the
 * photograph would and a row of cards still lines up. The mark is a
 * `background-image` at a fixed size, not a stretched `img`: a brand mark
 * cropped to fill 16:9 reads as a mistake, and one scaled to the panel would
 * grow with the column. Centred on a wash from the palette so the panel is
 * legibly a placeholder and not a failed image.
 */
.tb-featured-fallback {
	aspect-ratio: 16 / 9;
	background-color: var(--wp--custom--brand--orchid-wash);
	background-image: url("assets/img/empath-fins.svg");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 72px auto;
}

/*
 * An unbreakable string — a bare email address, a long URL — is not something a
 * column cap can shrink, and one is enough to push the document past the
 * viewport on its own. Two views did exactly that after everything above was
 * capped. Inherited, so it reaches the text nodes as well as the elements.
 */
.wp-block-post-content {
	overflow-wrap: break-word;
}

/*
 * A legacy video embed carries both `width` and `height`, so capping the width
 * alone leaves the height fixed and letterboxes the frame. Every one of them is
 * a 560×315 YouTube embed, which is 16:9 exactly.
 */
.wp-block-post-content iframe[width][height] {
	aspect-ratio: 16 / 9;
	height: auto;
}

/*
 * Buttons. theme.json owns the ground, radius, padding and type; the flex box
 * and the 48px tap target are the design system's Button base, which theme.json
 * has no field for.
 */
.wp-element-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--wp--custom--space--2);
	min-height: var(--wp--custom--tap--min);
	text-align: center;
}

/* Eyebrow: uppercase, tracked-out, coral ink. Used above section headings. */
.tb-eyebrow {
	font-size: var(--wp--preset--font-size--caption);
	font-weight: var(--wp--custom--weight--semibold);
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	text-transform: uppercase;
	color: var(--wp--custom--brand--link);
}

/* Lede: the intro paragraph under a hero or section heading. */
.tb-lede {
	font-size: var(--wp--preset--font-size--lede);
	line-height: var(--wp--custom--leading--snug);
	color: var(--wp--custom--color--text);
}

/*
 * Core block defaults we now own.
 *
 * `add_theme_support( 'wp-block-styles' )` is deliberately NOT declared — see
 * functions.php. It loads core's opinionated per-block `theme.css`, which
 * hardcodes values the design system has no say over. Of the blocks this theme
 * uses, four shipped one:
 *
 *   group          padding: 1.25em 2.375em on any group with a background
 *   template-part  the same, plus margin-block: 0
 *   separator      border-bottom: 2px solid; width: 100px
 *   search         border: 1px solid #ccc on the submit button
 *
 * What those files were genuinely doing for us is restated below, in tokens.
 * The rest is gone on purpose; don't restore the theme support to get it back.
 */

/* A hairline rule in the current text colour, not core's 2px 100px stub. */
.wp-block-separator {
	border: 0;
	border-top: 1px solid currentColor;
	margin-inline: auto;
}

/* Marker classes core sets on separators; honour both so old and new agree. */
.wp-block-separator.has-css-opacity {
	opacity: 0.4;
}

.wp-block-separator.has-alpha-channel-opacity {
	opacity: 1;
}

.wp-block-separator.has-background:not(.is-style-dots) {
	border-top: 0;
	height: 1px;
}

/*
 * Keep the affordance core's padding provided — an editor who sets a background
 * shouldn't get type jammed against the edge — but take it from the spacing
 * scale. Group and template part had it from the theme.css we dropped;
 * paragraph and heading hardcode the same 1.25em/2.375em in their *base*
 * stylesheet, which loads either way, so they need overriding at core's own
 * specificity rather than removing.
 */
:where(.wp-block-group.has-background),
:where(.wp-block-template-part.has-background),
:root :where(p.has-background),
:where(h1, h2, h3, h4, h5, h6).wp-block-heading.has-background {
	padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--50);
}

:where(.wp-block-template-part.has-background) {
	margin-block: 0;
}

/*
 * core/navigation paints its submenu panels #fff on pure black whenever the
 * *nav* carries no background or text colour of its own. The overlay escapes
 * this (its preset classes are !important), but the desktop mega panels do not
 * — and DESIGN.md has no pure black anywhere in it. Same selectors as core, so
 * the specificity matches and source order decides; style.css prints last.
 */
.wp-block-navigation:not(.has-background) .wp-block-navigation__submenu-container {
	background-color: var(--wp--custom--brand--surface);
	border-color: var(--wp--custom--brand--border);
}

.wp-block-navigation:not(.has-text-color) .wp-block-navigation__submenu-container {
	color: var(--wp--custom--brand--ink);
}

/*
 * The skip link is the first thing a keyboard user sees, and core renders it
 * #444 on #eee. It is a structure ground: primary carrying primary-contrast,
 * 14.2:1, with the on-structure focus colour for the ring — the light-ground
 * one does not clear 3:1 against purple.
 */
.screen-reader-text:focus,
.skip-link.screen-reader-text:focus {
	background-color: var(--wp--custom--brand--primary);
	color: var(--wp--custom--brand--primary-contrast);
	outline-color: var(--wp--custom--color--focus-on-structure);
}

/*
 * The search label keeps its weight. The submit button does not get core's
 * #ccc border back: it is a `.wp-element-button`, so theme.json already gives
 * it a ground, a radius and a contrast partner.
 */
.wp-block-search__label {
	font-weight: 700;
}

/*
 * Focus visibility (WCAG 2.2 SC 2.4.11, SC 1.4.11).
 *
 * theme.json can express `:focus` on the link and button *elements*, but not a
 * site-wide `:focus-visible` ring covering buttons, disclosure toggles and the
 * navigation overlay controls — so it lives here, referencing the focus token.
 * On the purple structure grounds (utility bar, footer) the ring switches to
 * `focus-on-structure`, because the light-ground focus colour does not clear
 * 3:1 against them; that swap is in the section styles in functions.php.
 */
:focus-visible {
	outline: 2px solid var(--wp--custom--color--focus);
	outline-offset: 2px;
}
