/* Use a real palette slug here. This previously read `almost-black`, which is not in the
   palette, so the declaration was invalid and body text silently fell back to the
   inherited default. */
body,
.editor-styles-wrapper {
  color: var(--wp--preset--color--black);
}
.editor-styles-wrapper h1.wp-block-post-title {
  color: black !important;
}

/* `strong` and `b` carry no weight of their own. The HTML rendering spec gives them
   `font-weight: bolder`, which resolves against the PARENT's computed weight through a
   lookup table rather than landing on a fixed 700. Body copy here is Inter 300, and
   `bolder` from 300 is 400, not 700. So every bolded run in body text was rendering one
   step above light and read as barely emphasised at all.

   Measured 2026-08-11 before fixing: 48 instances across 11 pages, and every one of the
   six pages sampled reported 300 to 400 with no exceptions. The comps bold inline copy at
   700 and Inter ships a real 700 file, so state it rather than inheriting a relative value.

   This has to be CSS. `WP_Theme_JSON::ELEMENTS` covers link, heading, h1 to h6, button,
   caption, cite, textInput and select, with no `strong` or `b`, so theme.json genuinely
   cannot express it. That is non-negotiable 5's escape hatch, not a shortcut.

   Deliberately not scoped to body copy. A `strong` inside a 700 heading resolves to 900
   today, and 700 there simply matches the heading, which is the better outcome. No page
   currently has that case. */
strong,
b {
  font-weight: 700;
}


/* Core overrides two of this theme's font size tokens, and core wins.

   Core's block library stylesheet ships a backwards compatibility rule for the two legacy
   Gutenberg size names:

     :root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}

   `normal` and `huge` are the only two of this theme's ten slugs that collide with it,
   because they are the only two that reuse a legacy name. Core's rule and theme.json's own
   output are both bare `:root`, so they tie on specificity and source order decides it.
   Measured on /about-us: `global-styles-inline-css` starts at byte 37104 and
   `wp-block-library-inline-css` at 57677, so core is later and core wins.

   The failure is silent and total: `huge` rendered a flat 42px at EVERY width from 320 to
   1920 rather than its declared clamp. That is why the page banner h1 read oversized on a
   phone, and why it never reached the comp's 67px on desktop despite CLAUDE.md claiming it
   did. Nothing errors, and the token looks correct in theme.json and in the editor's own
   size control, which is what let it sit here unnoticed.

   `:root:root` is (0,2,0) against core's (0,1,0), so this wins on specificity rather than
   on source order. That is deliberate: Perfmatters inlines and reorders stylesheets on this
   site, so an order-based fix would be riding its behaviour instead of the cascade.

   THE VALUE IS RESTATED FROM theme.json AND WILL DRIFT IF THAT IS EDITED. It is exactly
   what WordPress emits for `fontSizes.huge` at min 32px, max 67px with fluid on. Same trap
   as `single-project.html` restating `contentSize`: if you own a restated value, you own
   keeping it in step. Read the emitted value back rather than trusting this comment:

     curl -s <page> | grep -o "font-size--huge:[^;}]*"

   Both colliding slugs are corrected here. `huge` was done first, on 2026-08-21, and `normal`
   in the same session once the wrap risk had been measured rather than assumed: it was the
   larger of the two, at 166 instances plus the header nav, and the inline check-list
   groupings on the eight service subpages had been fitted by measuring the rendered page and
   so were fitted against the wrong 16px. Before and after groupings were captured at 390,
   1280, 1600 and 1920 on all eight; see CLAUDE.md for what moved. */
:root:root {
  --wp--preset--font-size--huge: clamp(32px, 2rem + ((1vw - 3.2px) * 2.188), 67px);
  --wp--preset--font-size--normal: clamp(17px, 1.063rem + ((1vw - 3.2px) * 0.063), 18px);
}


/* customize */


/* PLUGIN CANDIDATE, flagged 2026-08-03. Jeff may move this into Intergetik Theme,
   see the note at the end of this comment before doing so.

   Remove the gap WordPress puts between the top level parts of the page. It shows
   up as unwanted white space above the footer, 32px here, which is not in the
   design. Every block theme has this. Core emits it at
   wp-includes/class-wp-theme-json.php:3290 from styles.spacing.blockGap:

     :where(.wp-site-blocks) > *            { margin-block-start: <blockGap>; margin-block-end: 0 }
     :where(.wp-site-blocks) > :first-child { margin-block-start: 0 }

   Core's own default is 24px; this theme's theme.json says 2rem, hence 32px. So it
   is our value but not our idea, and it is not something an editor ever asked for.

   Measured 2026-08-03: header is :first-child so it was already 0, main computes 0,
   and only the footer carried the 32px. The rule is written against all children
   rather than the footer alone because the intent is general, every section in this
   design carries its own padding and the space between parts should come from that,
   not from a layout default. A part added later should inherit that intent.

   No !important and no specificity games needed: core wraps the selector in :where(),
   so it has zero specificity and any real selector beats it.

   Do NOT change styles.spacing.blockGap in theme.json to achieve this. That value is
   also the fallback gap for every block that does not set its own, so zeroing it
   would collapse the vertical rhythm inside groups site wide. The root layout gap
   cannot be expressed separately in theme.json, which is why this is CSS.

   If this moves to Intergetik Theme: make it opt in per site rather than
   unconditional. It is right for a full bleed section design like this one, and
   wrong for any client theme that relies on the default gap to separate its
   header, content and footer. */
.wp-site-blocks > * {
	margin-block-start: 0;
}


/*--- Gravity Forms customization ---*/
.gform-theme--foundation .gform_fields {
	row-gap: 20px;
}
/* Never use `#gform_wrapper_N` or `#gform_N` selectors. Those ids are whatever the
   admin assigned on a given install, so they are wrong per environment, and they
   outrank the per-form wrapper classes (.anzac-hero-form, .anzac-footer-form,
   .anzac-contact-form) that alignment is set on. Scope to the wrapper class on the
   containing block instead. */
input[type=text]::placeholder,
textarea::placeholder {
  color: #000;
  opacity: 0.55;
}
@media (max-width: 640px) {
	#input_2_1 {
		border: 1px solid #fff;
	}
}

/* Submit buttons, from the comp (Figma node 383:1857): solid black, white Inter
   Bold 16px, uppercase with 1.2px tracking, square corners, 23/18 padding.

   !important is required and sanctioned here. Gravity Forms styles its submit
   through an id plus attribute selector, `#gform_wrapper_2[data-form-index="0"]
   .gform-theme-button`, which outranks any class based selector we would
   reasonably write. See the Gravity Forms note in CLAUDE.md.

   Deliberately not scoped to a form id: those are whatever the admin assigned on
   a given install, so an id here would silently stop applying in another
   environment. Every submit on this site should look like this. */
.gform-theme .gform_footer button[type="submit"],
.gform-theme .gform_footer input[type="submit"],
.gform-theme .gform_page_footer button[type="submit"],
.gform-theme .gform_page_footer input[type="submit"] {
	background: var(--wp--preset--color--black) !important;
	border: 0 !important;
	border-radius: 0 !important;
	color: var(--wp--preset--color--white) !important;
	font-family: var(--wp--preset--font-family--inter) !important;
	font-size: 16px !important;
	font-weight: 700 !important;
	letter-spacing: 1.2px !important;
	line-height: 1 !important;
	padding: 18px 23px !important;
	text-transform: uppercase !important;
}

.gform-theme .gform_footer button[type="submit"]:hover,
.gform-theme .gform_footer input[type="submit"]:hover,
.gform-theme .gform_page_footer button[type="submit"]:hover,
.gform-theme .gform_page_footer input[type="submit"]:hover {
	background: var(--wp--preset--color--dark-gray) !important;
}

.gform_confirmation_message {
	color: #000;
}

/* Home banner enquiry form.
   Scoped to the .anzac-hero-form wrapper on the pattern's group rather than a
   #gform_wrapper_N id, because the id is whatever the admin assigned on that
   install. UNVERIFIED: written from the Figma frame, not tested against a live
   form. Gravity Forms Foundation may need higher specificity or !important on
   some of these once a real form is in place.
   The field text in the design is placeholder text with the label visually
   hidden. That is field configuration in the Gravity Forms admin, not CSS. */
.anzac-hero-form .gform-theme--foundation .gform_fields {
	row-gap: 12px;
}
.anzac-hero-form .gform-theme--foundation .gfield input[type=text],
.anzac-hero-form .gform-theme--foundation .gfield input[type=email],
.anzac-hero-form .gform-theme--foundation .gfield input[type=tel],
.anzac-hero-form .gform-theme--foundation .gfield select,
.anzac-hero-form .gform-theme--foundation .gfield textarea {
	background: var(--wp--preset--color--white);
	border: 0;
	border-radius: 0;
	padding: 14px 19px;
	font-size: 16px;
}
.anzac-hero-form .gform-theme--foundation .gfield textarea {
	min-height: 109px;
}
.anzac-hero-form .gform-theme--foundation .gform_footer {
	justify-content: flex-start;
	padding-bottom: 0;
}
.anzac-hero-form .gform-theme--foundation .gform_footer .gform_button {
	background: var(--wp--preset--color--black);
	color: var(--wp--preset--color--white);
	border: 0;
	border-radius: 0;
	padding: 18px 23px;
	font-size: 16px;
	font-weight: 700;
	letter-spacing: 1.2px;
	text-transform: uppercase;
}

/* Footer newsletter signup. One email field and a submit sitting side by side
   inside a single bordered pill, so the wrapper border comes from the block and
   the field and button are stripped back to nothing.
   The Gravity Forms "orbital" theme sets its own colours through --gf-* custom
   properties, so the submit is re-pointed at the palette rather than fought with
   !important. */
.anzac-footer-form .gform_wrapper form {
	display: flex;
	align-items: stretch;
	gap: 0;
}
.anzac-footer-form .gform_wrapper .gform_body {
	flex: 1 1 auto;
	min-width: 0;
}
.anzac-footer-form .gform-theme--foundation .gform_fields {
	row-gap: 0;
}
.anzac-footer-form .gform-theme--foundation .gfield input[type=email] {
	background: transparent;
	border: 0;
	border-radius: 0;
	padding: 14px 18px;
	font-size: 16px;
	box-shadow: none;
}
.anzac-footer-form .gform_wrapper .gform_footer {
	flex: 0 0 auto;
	align-items: center;
	margin: 0;
	padding: 6px;
	width: auto;
}
/* !important is required here, not lazy. Gravity Forms styles its submit with an
   ID plus attribute selector (#gform_wrapper_2[data-form-index="0"].gform-theme ...)
   which outranks any reasonable class-based selector. Overriding a plugin is the
   sanctioned case for !important. */
/* The newsletter submit is a deliberate exception to the black uppercase submit
   above: the comp (Figma node 0:41 inside the Footer instance) shows a white
   rounded pill reading "Subscribe" in sentence case, 103x33 inset about 8px inside
   the 291x48 white field.

   Every property the general rule sets is restated here, including
   text-transform, letter-spacing and line-height. That is not redundancy: leaving
   any of them out lets the general rule show through, which is exactly what
   happened on 2026-07-30 when this button started rendering "SUBSCRIBE". */
.anzac-footer-form .gform-theme--foundation .gform_footer .gform_button {
	background: var(--wp--preset--color--white) !important;
	color: var(--wp--preset--color--black) !important;
	border: 0 !important;
	border-radius: 6px !important;
	/* Flat, on Jeff's instruction 2026-08-11. Declared as none rather than deleted,
	   because Gravity Forms' own Orbital button shadow would otherwise show through:
	   the whole reason this block carries !important is that the plugin's selector
	   outranks ours. */
	box-shadow: none !important;
	padding: 8px 16px !important;
	margin: 0 !important;
	width: auto !important;
	font-size: 15px !important;
	font-weight: 400 !important;
	letter-spacing: normal !important;
	line-height: 1.2 !important;
	text-transform: none !important;
}
/* Teal on hover, on Jeff's instruction 2026-08-11. The comp shows no hover state, so
   the colour is the site's established button teal rather than a guess.

   No `transition` is declared here on purpose. One was tried and measured as dead:
   Gravity Forms already sets `transition: all 0.15s` on this button, and its
   id-plus-attribute selector outranks ours, which is the same reason every property
   above carries `!important`. That existing transition covers `background-color`, so the
   fade works without us restating it. Adding one would need `!important` to win and
   would buy nothing.

   `brand-teal` specifically, and the label deliberately stays black at 7.34:1. Worth
   recording why the darker token is wrong here: `brand-teal-dark` `#00727a` under black
   text measures 3.69:1 and fails, so choosing it would have forced the label to white
   as a second change. Do not swap the tokens without re-measuring both.

   `:focus-visible` is included so a keyboard user gets the same affordance as a mouse
   user. It only adds a background, so the focus ring is untouched. */
.anzac-footer-form .gform-theme--foundation .gform_footer .gform_button:hover,
.anzac-footer-form .gform-theme--foundation .gform_footer .gform_button:focus-visible {
	background: var(--wp--preset--color--brand-teal) !important;
	color: var(--wp--preset--color--black) !important;
}


/* Contact page enquiry form, Gravity Forms "Contact Us Inquiry" (id 3 locally).
   From Figma node 303:2608. Scoped to the .anzac-contact-form wrapper on the
   containing group, never a #gform_wrapper_N id, for the reason given above.

   The two up rows come from the form's own layoutGridColumnSpan values, set when
   the form was created, so no CSS lays the grid out. What CSS is needed is the
   field chrome, which the comp specifies precisely and which the Orbital theme
   does differently. */
.anzac-contact-form .gform-theme--foundation .gform_fields {
	row-gap: 24px;
}

.anzac-contact-form .gform-theme--framework .gfield--type-choice .gfield_label,
.anzac-contact-form .gform-theme--foundation .gfield_label {
	color: #000101;
	font-family: var(--wp--preset--font-family--inter);
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.6px;
	line-height: 16px;
	margin-bottom: 8.5px;
}

.anzac-contact-form .gform-theme--foundation .gfield input[type="text"],
.anzac-contact-form .gform-theme--foundation .gfield input[type="email"],
.anzac-contact-form .gform-theme--foundation .gfield input[type="tel"],
.anzac-contact-form .gform-theme--foundation .gfield textarea {
	background-color: var(--wp--preset--color--white);
	border: 1px solid #c5c6ca;
	border-radius: 0;
	color: var(--wp--preset--color--black);
	font-size: 14px;
	padding: 17px 17px 16px;
}

/* The comp's placeholder grey. The site wide placeholder rule near the top of
   this file sets black at 55% opacity, which reads darker than the comp here. */
.anzac-contact-form .gform-theme--foundation .gfield input::placeholder,
.anzac-contact-form .gform-theme--foundation .gfield textarea::placeholder {
	color: #6b7280;
	opacity: 1;
}

.anzac-contact-form .gform-theme--foundation .gfield textarea {
	height: 146px;
	line-height: 24px;
	padding-top: 13px;
}

/* The drop zone. Gravity Forms' multi file uploader supplies the dashed box and
   its own copy ("Drop files here or"), so this only restyles the container the
   comp draws: 2px dashed, 8px radius, 128px tall, white. */
.anzac-contact-form .gform-theme--foundation .gform_drop_area {
	background-color: var(--wp--preset--color--white);
	border: 2px dashed #c5c6ca;
	border-radius: 8px;
	color: #44474a;
	font-size: 14px;
	line-height: 20px;
	min-height: 128px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	padding: 20px;
}

/* Full width black submit, 12px and NOT uppercase, against the site wide submit
   rule above which is 16px and uppercase.

   Every property that rule sets is restated, including text-transform,
   letter-spacing and line-height. That is not redundancy: leaving any of them out
   lets the general rule show through. Exactly that happened to the newsletter
   button on 2026-07-30, see the note above it. */
.anzac-contact-form .gform-theme .gform_footer button[type="submit"],
.anzac-contact-form .gform-theme .gform_footer input[type="submit"] {
	background: #050505 !important;
	border: 0 !important;
	border-radius: 0 !important;
	box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1) !important;
	color: var(--wp--preset--color--white) !important;
	font-family: var(--wp--preset--font-family--inter) !important;
	font-size: 12px !important;
	font-weight: 700 !important;
	letter-spacing: 1.2px !important;
	line-height: 16px !important;
	padding: 16px !important;
	text-transform: none !important;
	width: 100% !important;
}

.anzac-contact-form .gform-theme .gform_footer {
	margin-top: 8px;
	padding-bottom: 0;
}

/* Gravity Forms' own uploader chrome, brought closer to the comp.

   Two things cannot be matched exactly and are deliberate deviations: the drop
   zone's copy is Gravity Forms' ("Drop files here or" plus a Select files
   button) where the comp writes "Click to upload or drag and drop", and the
   accepted-types line is generated from the field's own settings. Both are
   functional strings the uploader owns, so they are restyled rather than replaced.
   The alternative would be scripting over a third party uploader, which is worse
   than a copy difference. */
.anzac-contact-form .gform-theme--foundation .gform_button_select_files {
	background: var(--wp--preset--color--brand-teal);
	border: 0;
	border-radius: 4px;
	color: var(--wp--preset--color--white);
	font-family: var(--wp--preset--font-family--inter);
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 0.6px;
	padding: 9px 16px;
}

.anzac-contact-form .gform-theme--foundation .gform_fileupload_rules {
	color: #44474a;
	font-size: 10px;
	line-height: 15px;
	margin-top: 6px;
}

/* The comp shows no required indicators, so they are hidden. Worth knowing what
   that costs: Gravity Forms still sets aria-required on the inputs, so assistive
   technology is told, but a sighted user is not. If that trade is not wanted, drop
   this rule and the fields will read "(Required)" as Gravity Forms intends. */
.anzac-contact-form .gform-theme--foundation .gfield_required {
	display: none;
}


/* Contact page details column: the three rows are core blocks, and only the rule
   between them needs CSS. The comp draws it as a 2px line whose colour fades from
   the logo teal into the section background (Figma node 303:2671).

   The gradient is here rather than a block attribute because core/separator's
   save() handles style.color.background but NOT style.color.gradient, despite
   `gradients: true` in its supports. A gradient set on the block serializes into
   the comment but nothing ever emits it: measured background-image was `none`.
   Both stops are palette tokens, so nothing is hardcoded by putting it here.

   height plus border: 0 because core renders a separator as a 1px border, and
   width: 100% because core's default (non-wide) separator style is a 100px centred
   line. Deliberately NOT max-width: none, which would override the parent
   constrained layout's 311px and run the rule the full width of the column. */
.anzac-contact-rule {
	background-image: linear-gradient(
		90deg,
		var(--wp--preset--color--brand-teal) 0%,
		var(--wp--preset--color--pale-sand) 100%
	);
	border: 0;
	height: 2px;
	width: 100%;
}


/* No `.wp-block-details` styling here on purpose. `core/accordion` is the block to reach
   for, and it is what the enquiry bar uses on nine pages. If a details block is ever
   genuinely wanted, style it fresh. */


/*--- Home banner review card ---*/
/* The translucent background and hairline border are block attributes on the
   group in patterns/home-banner.php. Only the backdrop blur is here, because no
   block support or theme.json key can express it. */
.anzac-glass-card {
	backdrop-filter: blur(2px);
	-webkit-backdrop-filter: blur(2px);
}


/*--- Recent Projects carousel ---*/
/* The section is a Query Loop over the project post type, carrying the Carousel
   block style from the Intergetik Carousel plugin. That plugin owns the scrolling
   and injects the two nav buttons immediately before the post template;
   everything here is the design layer on top, which is the agreed split.

   The comp puts the arrows on the heading's line, to its right, so the Query Loop
   becomes a two column grid: heading left, arrows right, cards spanning both
   below. Grid rather than absolute positioning, so nothing depends on the
   heading's height or the button size. */
.anzac-projects-carousel {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: center;
}

.anzac-projects-carousel .anzac-projects-carousel__head {
	grid-column: 1;
}

.anzac-projects-carousel .intergetik-carousel__nav {
	grid-column: 2;
	gap: 16px;
}

.anzac-projects-carousel .wp-block-post-template {
	grid-column: 1 / -1;
	margin-top: 37px;
}

/* How many cards show is now a block setting in the inspector, so it is not set
   here. The plugin computes card width from that count and needs the gap term to
   match the block's own blockGap, which is 20px on this Query Loop. Getting this
   wrong is the one way the arithmetic drifts, so they are stated together. */
.anzac-projects-carousel {
	--igc-gap: 20px;
}

/* The whole card is the link, not just the title. Same technique as the Portfolio
   cards: the post title is already a link, so it is stretched over the card with an
   absolutely positioned pseudo element rather than the featured image being linked as
   well, which would put two links to the same project in the tab order and read the
   destination twice.

   Two things have to be true for it to reach the card's edges.

   1. The pseudo element resolves against its nearest POSITIONED ancestor, and core gives
      .wp-block-cover__inner-container `position: relative`, so that is always what it
      finds. With `contentPosition: bottom left` core also shrink wraps that container
      (`width: auto`) and lets it sit at its content's height, so it is stretched to fill
      the card here and its own content held at the bottom by the flex column. Core's
      selector for the shrink wrap is four classes, hence the doubled class below.
      The category label is not a link: anzac_unlink_project_taxonomy_terms() strips that
      anchor, so the card really does contain exactly one.
   2. The cover's padding sits between that container and the card edge, so a plain
      `inset: 0` would leave a dead 27px frame down both sides and 34px along the bottom.
      The negative insets ARE that padding, restated because a pseudo element cannot see
      it. Keep them in step with the cover's padding attribute, and note the card can
      never overhang its neighbour if they drift, because core gives .wp-block-cover
      `overflow: clip`.

   The underline is the only hover affordance the card has, since an enlarged hit area is
   invisible otherwise. :focus-within matches :hover so a keyboard user gets the same. */
.anzac-projects-carousel .wp-block-post > .wp-block-cover.has-custom-content-position.has-custom-content-position .wp-block-cover__inner-container {
	align-self: stretch;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	width: 100%;
}

.anzac-projects-carousel .wp-block-post .wp-block-post-title a::after {
	content: "";
	inset: 0 -27px -34px;
	position: absolute;
}

.anzac-projects-carousel .wp-block-post > .wp-block-cover:hover .wp-block-post-title a,
.anzac-projects-carousel .wp-block-post > .wp-block-cover:focus-within .wp-block-post-title a {
	text-decoration: underline;
}


/* The arrows from the comp: 48px tall, hairline gray border, 12px radius, and an
   8px chevron. The plugin ships an unsized chevron drawn with borders and no
   colour opinion, so only the dimensions and the frame are set here. */
.anzac-projects-carousel .intergetik-carousel__button {
	min-height: 48px;
	padding-inline: 20px;
	border: 1px solid var(--wp--preset--color--gray);
	border-radius: 12px;
}

.anzac-projects-carousel .intergetik-carousel__button::before {
	inline-size: 8px;
	block-size: 8px;
}


/*--- Service boxes go two up on mobile, not one ---*/
/* Four boxes (Renovations, Additions, New Construction, Design-Build) in a
   core/columns. Core stacks every column to full width below 782px, which leaves
   four tall boxes and a lot of scrolling. Two up reads far better at that size.

   No block attribute can do this. `isStackedOnMobile: false` is the only related
   control and it keeps all four side by side, which is far too narrow on a phone.

   !important and the doubled class are both required, not lazy. Core's rule is

     @media (max-width:781px){
       .wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column{flex-basis:100%!important}}

   which is three classes with !important, so an override needs at least that
   specificity plus !important of its own. Matching core's selector shape and adding
   the hook class gets to four.

   The 7.5px is half the block's 15px blockGap: two columns plus one gap then come
   to exactly 100%. If that blockGap changes, change this with it. Revisit when
   WordPress 7.1 lands responsive controls. */
@media (max-width: 781px) {
	.anzac-service-grid.wp-block-columns:not(.is-not-stacked-on-mobile) > .wp-block-column {
		flex-basis: calc(50% - 7.5px) !important;
	}
}


/*--- Homepage hero and intro rows: no CSS needed ---*/
/* Both rows used to be `core/group` flex layouts whose children carried
   `selfStretch: fixed` with a `flexSize` percentage, and each needed a hand written media
   query to stack on a phone, because flex items shrink before they wrap and keep that basis
   once they do. On 2026-08-18 all four children were converted to `core/columns` and
   `core/column`, which stack natively at core's own 781px boundary, so both media queries
   were deleted rather than kept. `.anzac-intro-columns` went with them and its class was
   removed from the page. If a future flex row squishes on mobile, convert it rather than
   writing the media query again. See CLAUDE.md. */

/*--- Navigation collapses to the hamburger below 1100px ---*/
/* Core collapses the Navigation block to its overlay below 600px and there is no
   attribute for the breakpoint, it is hardcoded in two media queries in
   wp-includes/blocks/navigation/style.css. Between 600px and roughly 1000px the
   full six item menu is still rendered, and logo plus menu plus CTA do not fit on
   one row, so the header silently wrapped to a second row and grew from 212px to
   about 285px. The hero reserves a fixed amount of space for the header, so a
   taller header put the navigation on top of the hero heading. Measured 2026-08-03:
   the collision ran from 8px at 480px wide up to 125px at 600px wide.

   1100px per Jeff. The header is a single row at every width again, which is what
   the comp shows and what makes the reserved space below predictable.

   Only `display` is overridden, deliberately. Core's other desktop declarations
   (position, width, z-index) sit on the same `:not(.is-menu-open)` selector, so
   they only apply while the menu is closed, and while it is closed it is
   `display: none` and they cannot be observed. When it opens, core's base rules
   supply `position: fixed` and the full screen inset on their own.

   Prefixed with .wp-block-navigation purely to outrank core by one class, since
   stylesheet order between core block styles and the theme is not guaranteed. */
@media (max-width: 1099.98px) {
	.wp-block-navigation .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
		display: none;
	}

	.wp-block-navigation .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}
}


/*--- Mobile overlay: a submenu must not move its own parent ---*/
/* Reported by Jeff 2026-08-18: opening a submenu in the mobile overlay shifted the
   parent item sideways. Measured at 390px, the "Services" label jumped from x=135.9
   to x=0, a 135.9px lurch to the edge of the screen, while all five siblings stayed
   exactly where they were. So the item you just pressed was the only one that moved,
   which is the worst possible choice of one.

   There are TWO separate mechanisms here and fixing only the first leaves the same
   defect one level down, so all three declarations are needed.

   The overlay's list is a flex column, and because the navigation block is
   justifyContent: center core sets `--navigation-layout-justification-setting: center`
   and applies it as `align-items`, so every `li` shrink wraps to its own content and
   is centred.

   1. The parent's own row. The submenu `ul` carries `flex-basis: 100%`, which forces
      its own flex line and stretches the parent `li` from 118px to the full container
      width. The `li` is `display: flex; flex-wrap: wrap` with no justification of its
      own, so its first line, the label plus the toggle button, lands hard against the
      left edge of a box that is suddenly 390px wide. The `li` genuinely has to
      stretch, since the submenu needs the width, so the fix is to justify that first
      line the way every sibling is justified rather than to stop the stretch.

   2. Everything BELOW the parent. A shrink wrapped `li` is as wide as its widest
      line, so its width depends on how deep the open submenus go: measured at 600px,
      the Services `li` was 437.7 wide with one level open and 600 with two. It is
      centred, so growing re-anchored its left edge from 81.2 to 0 and dragged the
      whole subtree with it. Child labels sit against a padding edge rather than being
      centred, so they moved by the full difference. This is why the level two item
      "Residential Services" still shifted, from 130.3 to 49.2 at 600px, after
      mechanism 1 was fixed, and why it only showed at 600px and above, where the
      submenu box has slack.

      Pinning the list to the full content width and stretching the parent to it
      removes the variable entirely: every box inside now has a fixed left edge, so
      nothing below can re-anchor however many levels are open.

   Read the justification from core's own custom property rather than hardcoding
   `center`, so this follows the block's setting instead of fighting it: switch the nav
   to left or right in the inspector and the parent row follows. Core sets the property
   on the navigation block, so it inherits down to the `li`.

   Verified byte identical to the old collapsed rendering: with everything closed, all
   six top level labels sit exactly where they did before, because a stretched list
   still centres shrink wrapped children about the same axis. Measured stable at 360,
   390, 430, 600, 767 and 1099 with one and with two levels open, no horizontal
   overflow at any of them.

   It cannot reach the desktop menu: `.is-menu-open` exists only while the overlay is
   open, and above 1100px the submenu is positioned and never affects its parent's
   width.

   Prefixed with .wp-block-navigation to outrank core by one class, since stylesheet
   order between core block styles and the theme is not guaranteed. */
.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	width: 100%;
}

.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-submenu {
	align-self: stretch;
	justify-content: var(--navigation-layout-justification-setting, initial);
}


/*--- Split panel: text one side, photo bleeding to the wide edge ---*/
/* A band of solid colour running edge to edge with a photo panel flush to one side
   of it, full height. Built for the Renovations page's "Navigating the Complexities
   of Older Homes" (Figma node 275:2797) and intended for the sibling service pages.

   Two nested blocks, and the nesting is the point: the solid colour is an alignfull
   group so it bleeds to the viewport on any monitor, while the core/media-text
   inside it is alignwide so the photo stops at wideSize and is never stretched
   across an ultrawide display. That is the 1920px ceiling rule applied inside a
   single section rather than to the section as a whole.

   The photo itself needs no CSS. core/media-text with imageFill gives the media
   cell height: 100% and the img object-fit: cover, which is exactly the panel.

   Only the text side needs a rule. Core pads the content cell by 8% of the block,
   which does not land on the 1280px content column that the sections above and
   below use, so the text would sit visibly inboard of theirs. This restates it, and
   falls back to the standard section padding once the viewport is narrower than the
   content column.

   Three classes to outrank core's two deterministically. Stylesheet order between
   core block styles and the theme is not guaranteed, so equal specificity would be
   a coin toss. Same reason the navigation overrides carry an extra class.

   100vw rather than a percentage: percentage padding on a grid item resolves
   against its own grid area, not the block, so a percentage cannot see the block's
   width. The cost is that 100vw counts the scrollbar, so the text can sit up to
   about 8px right of the sections above when a classic scrollbar is showing. */
.wp-block-media-text.anzac-split-panel > .wp-block-media-text__content {
	padding-left: max(min(calc(1.5vw + 15px), 44px), calc((min(100vw, 1920px) - 1280px) / 2));
	padding-right: min(calc(1.5vw + 15px), 44px);
	padding-block: min(calc(3.5vw + 20px), 87px);
}

/* Core stacks media-text at 600px, which is its own boundary and not this theme's.
   Between 601 and 781 the text cell is barely 54% of a phone-width viewport and the
   copy reads as a column of two or three words. Moved to the 782px boundary core
   uses everywhere else, which is the only load bearing breakpoint here.

   Core's own three rules restated rather than extended, because the boundary is the
   whole change. grid-template-columns keeps !important because core's does. There
   is no mobile comp, so this is the same holding pattern as the rest of the theme. */
@media (max-width: 781px) {
	.wp-block-media-text.anzac-split-panel.is-stacked-on-mobile {
		grid-template-columns: 100% !important;
	}

	.wp-block-media-text.anzac-split-panel.is-stacked-on-mobile > .wp-block-media-text__media {
		grid-column: 1;
		grid-row: 1;
		/* Core's 250px floor reads as a letterbox strip once it is full width. */
		min-height: 320px;
	}

	.wp-block-media-text.anzac-split-panel.is-stacked-on-mobile > .wp-block-media-text__content {
		grid-column: 1;
		grid-row: 2;
	}
}


/*--- Page banner: the inner hero used by subpages ---*/
/* Built for About (Figma node 383:1947) and intended for every subpage banner.
   The comp puts the heading block near the top of the banner and runs a breadcrumb
   and the enquiry bar along the bottom edge, so the two need to be pushed apart.

   Core gives .wp-block-cover `align-items: center`, which centres the inner
   container as a single flex item and leaves it only as tall as its content, so
   there is no free space for space-between to distribute. No block attribute
   stretches it: cover exposes contentPosition, which sets align-items to a corner
   or centre but never to stretch. Hence this rule, and hence only this rule, the
   distribution itself is a normal flex layout the block could have expressed. */
/*--- Financing: the Acorn Finance card ---*/
/* A facsimile of Acorn's own widget, standing in until they supply a real embed. Only
   the slider and the Check Your Rate button are a picture (attachment 216); the header
   bar, the heading, the copy and the reassurance line are blocks.

   **Its colours and type are Acorn's, and deliberately outside the design system.** The
   navy #001430, the pale blue #d3e4fe, the greys #43474f and #c4c6d0 and the 32px
   heading are all raw values in the block attributes rather than palette tokens or
   scale steps, because they describe someone else's brand. Minting Anzac tokens for
   them would be wrong. #02aab4 is the one exception: it is one to two per channel from
   `brand-teal`, which the palette rule already maps, so the token is used.

   Only two things here cannot be block attributes. */

/* The navy header bar runs to the card's edges, so the card has to clip it to its own
   16px radius. There is no overflow block support. */
.anzac-acorn-card {
	overflow: hidden;
}

/* The 32x32 teal badge around the Acorn glyph. A flex group sizes to its content, and
   no block attribute sets a fixed square, so the comp's 32px box is set here. */
.anzac-acorn-card__badge {
	flex: 0 0 auto;
	height: 32px;
	width: 32px;
}

/* Below the card's 896px the header bar's two ends have nowhere to go, so the row wraps
   and the right hand label drops under the left. Centring both reads as intended rather
   than as a broken row. Nothing above 600px changes. */
@media (max-width: 600px) {
	.anzac-acorn-card__header {
		justify-content: center;
		text-align: center;
	}
}


/*--- About: careers listings embed ---*/
/* A cross-origin iframe holding the client's Loxo careers page, so nothing inside it can
   be styled, measured or read from here. Loxo's own snippet is
   width='100%' height='100%' frameborder=0 and they ship no postMessage auto-resize, so
   the height is ours to state. Per the third party iframe standard: the embed URL is
   content, the sizing is design.

   The URL carries ?disable_addthis=true, Loxo's documented parameter for suppressing the
   floating AddToAny share bar. Without it their page adds a position:fixed 288px column
   which, inside an iframe, pins itself to the iframe's own viewport and never scrolls
   away.

   cqw, NOT vw, and that is the point. The iframe sits in a constrained section, so its
   width is min(contentSize, viewport - section padding). A vw expression would have to
   restate both of those numbers and would silently drift the next time contentSize moves,
   which it already did once (1300 to 1280). A container query unit reads the box the
   iframe actually occupies, so it absorbs both and needs no cap and no media query.

   31.65 and 1095 are a measured upper envelope over the rendered Loxo page, taken at
   fourteen widths from 280 to 1280. The height has to grow with the width at all only
   because Loxo's page carries a client supplied banner image at width:100%, ratio
   866/400: 161px tall at 348, 591px at 1280. Nothing is ever short across that range and
   the overshoot peaks at 66px, which is invisible, since Loxo's own section is a flat
   #f6f6f6 with 95px of bottom padding already.

   Tuned to the FOUR jobs posted on 2026-08-18. Each further posting adds 154px at desktop
   and up to 185px at a width where its title wraps to two lines, so add 185 to the px
   term per new job, or leave it and accept the iframe's own scrollbar. */
.anzac-careers-embed {
	container-type: inline-size;
}

.anzac-careers-embed iframe {
	border: 0;
	display: block;
	height: calc(31.65cqw + 1095px);
	width: 100%;
}


/*--- Portfolio: project type filter ---*/
/* The filter row is a flex group of core paragraphs, each holding a link, sitting
   INSIDE the core/query block so core's Interactivity router handles the clicks. See
   anzac_project_filter_links() in functions.php for the directive and the active class.

   Only the tab geometry is here, because a link cannot be given padding through block
   attributes: the paragraph is the block, the anchor is what needs the hit area. The
   comp draws the active indicator flush with the bar's bottom edge (Figma 284:4806,
   5px tall), which is why the anchor is full height and the indicator is its border
   rather than a separate element. */
.anzac-project-filter {
	min-height: 63px;
}

.anzac-project-filter p {
	display: flex;
}

/* line-height must be set HERE, not on the group. theme.json gives core/paragraph
   line-height 1.78, and a rule targeting the paragraph beats a value inherited from
   its parent, so a line-height on the filter group is ignored and the bar came out
   73.7px against the comp's 63. 21 + 18 + 19 + 5 is the comp's 63. */
.anzac-project-filter a {
	align-items: center;
	border-bottom: 5px solid transparent;
	display: flex;
	/* The comp's tabs are Inter Regular. theme.json gives paragraphs 300, which reads
	   too light at this size, and the same inheritance point as line-height applies. */
	font-weight: 400;
	line-height: 1;
	padding: 21px 19px 19px;
}

.anzac-project-filter a.is-active {
	border-bottom-color: var(--wp--preset--color--brand-teal);
	font-weight: 700;
}

/* Hover only needs to hint, since the active state carries the indicator. The teal
   here is the accessible one, per the body link standard. */
.anzac-project-filter a:not(.is-active):hover {
	color: var(--wp--preset--color--brand-teal-dark);
}

/* Below 782px the ten tabs need 1108.8px of content against a 348px bar, so the row
   wrapped onto five lines and the filter alone was 305px tall, putting the first card
   at y=877 on a 390px phone. It becomes a single line swipeable strip instead: 63px at
   every width from 320 up, with every filter still one tap away rather than behind a
   disclosure, which would cost the same 63px collapsed AND a tap to reach anything.

   Deliberately NOT scroll snapped. Snapping aligns a tab flush with the container edge,
   which is exactly what makes a strip look complete when it is not. Left free, a tab is
   clipped mid-label at rest, and that clipped label IS the affordance that there is more
   to the right. Verified where the clip lands rather than assumed.

   Above 782px nothing changes: the row still wraps, so it is still two lines from 782
   through 1099 exactly as it is today. That range was measured and deliberately left
   alone, since 782 is the boundary the rest of the theme uses. */
@media (max-width: 781px) {
	.anzac-project-filter {
		flex-wrap: nowrap;
		overflow-x: auto;
		/* Stops a horizontal swipe chaining into the page's own scroll once the strip
		   reaches either end. */
		overscroll-behavior-x: contain;
		/* A classic scrollbar would add its own height to a bar whose whole purpose here
		   is to be 63px. Below 782px the pointer is a finger, where scrollbars are overlay
		   and invisible anyway, so nothing is lost. */
		scrollbar-width: none;
	}

	.anzac-project-filter::-webkit-scrollbar {
		display: none;
	}

	/* Load bearing. A flex item shrinks before it overflows, so without this the ten
	   tabs squeeze themselves into the 348px bar and wrap their labels instead of
	   scrolling, which looks like the bug it replaced. */
	.anzac-project-filter p {
		flex: 0 0 auto;
	}
}


/*--- Single project: banner and gallery ---*/
/* The detail banner is 532px in the comp against 661 for a page banner, so it gets its
   own floor rather than the shared token. Same clamp shape: solved for the comp's 532
   at 1600 and a 360 floor at 390, which is the same proportion the page banner uses.

   BOTH classes in the selector, deliberately. It has to beat
   `.anzac-page-banner { min-height: var(--wp--custom--page-banner-min-height) }`, which
   is a single class and therefore an exact specificity tie, decided by source order.
   `.anzac-page-banner` is declared further down this file, so a single-class selector
   here silently lost and the banner rendered at 661. */
.anzac-page-banner.anzac-project-banner {
	min-height: clamp(360px, calc(14.21vw + 305px), 532px);
}

/* Every cell is the same shape in the comp (506 x 334) whatever the source photo is,
   so the ratio is forced here rather than left to whoever uploads the images. This is
   also why the gallery is authored as ordinary content per project: the shape is the
   theme's business, the pictures are not.

   core/gallery's own crop option only equalises heights within a row, which is not the
   same thing as a fixed ratio across the grid. */
.anzac-project-gallery .wp-block-gallery .wp-block-image img {
	aspect-ratio: 506 / 334;
	height: auto;
	object-fit: cover;
	width: 100%;
}

/* The category and the types read as one run of text under the project title, joined by
   a middot: "Residential · Addition, Kitchen".

   `display: inline` on the two lists is the load bearing part, and it is why this is not
   a flex row. `core/post-terms` renders a div, so as flex items the two lists wrap as
   whole units, which on a phone put the category on one line and left the separator
   ORPHANED at the start of the next: "Residential" / "· Addition, Bath, Whole Home
   Remodel". Inline, the whole thing wraps as ordinary text mid-list instead, which is
   what "reads like a paragraph" actually means. Measured at 390px before and after.

   The dot is a pseudo element on the second list rather than a character in the content,
   because either list can be empty: `core/post-terms` renders nothing at all when a post
   has no terms in that taxonomy, so a literal separator would dangle. As a
   `:not(:first-child)` rule it simply never appears. That matters today, because only 4
   of the 16 projects carry any `project_type` terms.

   `content: "\00b7" / ""` gives it an empty alternative text so it is not announced
   between the two lists. Browsers without the alt syntax read the character, which is a
   graceful enough fallback.

   The two blocks are deliberately adjacent in the template with NO newline between them,
   because inline elements would otherwise be separated by a whitespace text node and the
   dot would sit 4px off centre. If the template is ever reserialized by the Site Editor
   that newline comes back; the result is slightly uneven spacing, not a break. */
.anzac-project-taxonomies > .wp-block-post-terms {
	display: inline;
}

.anzac-project-taxonomies > .wp-block-post-terms:not(:first-child)::before {
	content: "\00b7" / "";
	margin: 0 8px;
}

/* The type terms are real links, to the Portfolio page filtered by that term, so they
   need an affordance. The category is not a link and gets none.

   An underline on hover rather than the site's usual teal, because these sit on a
   photograph: `brand-teal-dark` would be the site standard but it is a dark colour on an
   unpredictable background and would read worse than the white it replaces. Underline is
   legible whatever the photo does. `:focus-visible` matches so a keyboard user gets it.

   The colour itself comes from the block's own elements.link setting rather than from
   here, which is what stops the anchors rendering black over the photo: a text colour on
   a block whose content is a link does nothing, and core/post-terms ALWAYS renders each
   term as an anchor. */
.anzac-project-taxonomies a:hover,
.anzac-project-taxonomies a:focus-visible {
	text-decoration: underline;
}


/* A short last row keeps the cell size instead of stretching to fill. Jeff's call,
   2026-08-13.

   core gives every gallery figure `flex-grow: 1`
   (`blocks/gallery/style.css`: `.wp-block-gallery.has-nested-images figure.wp-block-image`),
   so a row with fewer than three items shares the leftover width between them. With a
   photo count that is not a multiple of three the effect is dramatic rather than subtle,
   because the forced aspect ratio above scales the height with it: a lone trailing image
   measured 1280 x 845 against the 414 x 273 every other cell gets, so the last picture
   rendered nine times the area of its neighbours.

   `flex-grow: 0` is the whole fix. It changes nothing on full rows, which is worth
   knowing before assuming this is risky: at three across the cells already total the
   container exactly (414 x 3 + 19 x 2 = 1280), so there is no free space for grow to
   distribute and the measured widths are identical either way. It only bites on the
   short row, which is exactly the case being corrected. The row then aligns left, since
   the gallery's justify-content is the default.

   Specificity: core's rule is three classes and an element, this is four and an element,
   so it wins on its own and needs no !important.

   Scoped to `.anzac-project-gallery` rather than every gallery, because filling the row
   is reasonable default behaviour for a gallery whose cells are not a fixed shape. It is
   only wrong here because the ratio is forced. */
.anzac-project-gallery .wp-block-gallery.has-nested-images figure.wp-block-image {
	flex-grow: 0;
}


/*--- Portfolio: project cards ---*/
/* The card is a core/cover on the featured image with the title bar as its only child,
   bottom aligned. Two things block attributes cannot express:

   1. The cover's inner container is `width: 100%` but core also gives it a max-width
      from the constrained layout, which would pull the title bar in from the card's
      8px padding. Restating width and dropping the max-width lets the bar span the
      card, which is what the comp draws (393 inside 410).
   2. Nothing to do with colour. The bar's fill and its hover are both further down this
      file, under "The title bar's tint". They used to be a block attribute and had to
      move, because an inline style cannot be overridden by a :hover rule. */
/* The inner container is stretched to fill the card, with the title bar held at its
   bottom edge. That is not cosmetic: it is what makes the whole card clickable.

   The stretched link below is an absolutely positioned pseudo element, so its geometry
   resolves against its nearest POSITIONED ancestor. Core gives
   .wp-block-cover__inner-container `position: relative`, so that is always the ancestor
   it will find, whatever the card does. Making the inner container fill the cover
   therefore makes the pseudo element cover the card.

   Two dead ends, recorded so they are not retried: putting `position: relative` on the
   anchor makes the anchor itself the containing block, so the link only covers its own
   text, which is the state this replaced. And setting the inner container to
   `position: static` does let the pseudo element reach the cover, but the container then
   paints below the absolutely positioned featured image and the title disappears. */
.anzac-project-card .wp-block-cover__inner-container {
	align-items: flex-end;
	align-self: stretch;
	display: flex;
	max-width: none;
	width: 100%;
}

.anzac-project-card {
	position: relative;
}

/* `position: static` is the load bearing line here. Core gives every constrained
   layout group `position: relative`
   (`blocks/group/style.css`: `:where(.wp-block-group.wp-block-group-is-layout-constrained)`),
   which made the title bar the containing block for the stretched link below, so the
   link covered the bar and nothing else. Measured: the pseudo element came out
   394 x 115 instead of 394 x 332. Core's selector is wrapped in `:where()` so it has
   zero specificity and a single class beats it. */
.anzac-project-card__label {
	position: static;
	width: 100%;
}

/* The title bar's tint, and its solid hover. Jeff's call, 2026-08-13.

   The colour lives here rather than as a block attribute because it HAS to. It used to be
   `style.color.background` on the group, which core writes as an inline style, and an
   inline style beats every stylesheet rule short of `!important`, so no `:hover` could
   reach it. Same shape as the hero's reserved padding, the carousel's `--igc-slides` and
   the cover `minHeight`: a value that has to respond to state cannot live inline. Moving
   it made the hover expressible without a single `!important`.

   The cost, worth knowing: the card tint is no longer a swatch in the block inspector.
   That is the trade for having a hover at all, and the bar is a design system component
   rather than something an editor rewords.

   color-mix keeps the 70% rest tint tied to the palette token instead of freezing
   `#00a9b5b3`, so a change to brand-teal carries through to both states. The flat hex
   above it is the fallback for anything without color-mix, and is the exact value the
   block used to carry.

   :focus-within matches :hover because the whole card is one stretched link, so a
   keyboard user tabbing to it gets the same feedback a mouse user gets. */
.anzac-project-card__label {
	background-color: #00a9b5b3;
	background-color: color-mix(in srgb, var(--wp--preset--color--brand-teal) 70%, transparent);
	transition: background-color 150ms ease;
}

/* brand-teal-DARK on hover, not the bright brand-teal the bar uses at rest. Jeff's call,
   2026-08-13, taken on the numbers: the titles are bold and 22.6px at 1600 rising to 24px
   at 1920, so they are WCAG large text and need 3:1. White on solid `brand-teal` is
   2.86:1 and misses; white on `brand-teal-dark` is 5.69:1 and clears 4.5:1 as well.

   So the hover both darkens and resolves the one state on this card whose contrast can
   actually be measured. At rest the bar is still 70% over a photograph and has no fixed
   ratio, which is open item 5 in ACCESSIBILITY.md and is unchanged by this. */
.anzac-project-card:hover .anzac-project-card__label,
.anzac-project-card:focus-within .anzac-project-card__label {
	background-color: var(--wp--preset--color--brand-teal-dark);
}

@media (prefers-reduced-motion: reduce) {
	.anzac-project-card__label {
		transition: none;
	}
}

/* The whole card is one link, not two. The post title is already a link, so it is
   stretched over the card rather than also linking the featured image, which would put
   two links to the same place in the tab order and read the destination twice to a
   screen reader. The anchor is deliberately left unpositioned; see above. */
.anzac-project-card__label a::after {
	content: "";
	inset: 0;
	position: absolute;
}

/* Every title bar is the height of a two line title, so a one line card and a two line
   card are the same size. Jeff's call.

   The floor is on the title rather than the bar because `em` and `lh` then resolve
   against the title's own fluid font size, so it shrinks with the type instead of
   holding a desktop height on a phone. `2lh` is exactly two lines and tracks any
   line-height change; the `em` above it is the fallback for browsers without `lh`, and
   is 2 x the 1.2 line-height set on the block.

   Selected by the block's own class rather than by tag. This was `h3` and silently
   stopped applying the moment the block's `level` attribute changed to 2, which is a
   semantic decision that should never have been able to break the layout. */
.anzac-project-card__label .wp-block-post-title {
	display: grid;
	min-height: 2.4em;
	min-height: 2lh;
	place-items: center;
}

/* The grid drops to two up and then one as it narrows. core/post-template's grid layout
   emits a minimumColumnWidth or a fixed columnCount; with columnCount set it does not
   respond, so the breakpoints are here. */
@media (max-width: 1099.98px) {
	.anzac-project-grid.is-layout-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 781px) {
	.anzac-project-grid.is-layout-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/*--- Portfolio: the Load More button ---*/
/* Makes core's pagination link look like the site's solid teal CTA button.

   The values are MEASURED off "Connect with our Team", the solid teal button on nine
   pages, not read off theme.json's `styles.blocks.core/button`. That distinction
   matters and cost a first pass: theme.json's base button is 18px/600 with 10px block
   padding, but every solid teal button an editor has actually placed overrides it with
   the comp's 16px/700, 16/24 padding and two layer shadow. The base is not what anyone
   sees, so matching it would have produced a button that resembles nothing on the site.

   The font size is the CLAMP core generated from that 16px, not a flat 16px. Fluid
   typography applies to custom block font sizes too, so the real buttons render 15.58px
   at 1600 and 16px at 1920; a flat 16px here would sit a third of a pixel proud of every
   other button below 1920 and would not shrink on a phone.

   MIRRORED, so it is not free of its source: if the CTA spec changes, change this with
   it. There is no mechanism for "look like a core/button" without being one, and three
   routes were rejected. `.wp-element-button` buys nothing, because this theme declares
   no `styles.elements.button` (the real buttons carry that class and get nothing from
   it). Adding one would put the same spec in theme.json twice. And
   `styles.blocks.core/query-pagination-next` would also restyle the blog's Next link in
   wp_template 503, where Previous sits beside it and would be left a bare text link.

   So it is CSS scoped to `.anzac-project-pagination`, which is non-negotiable 5's escape
   hatch: page-specific scoping is the one thing theme.json cannot express. Colours stay
   as preset custom properties so the palette remains the single source.

   Core's own query-pagination stylesheet sets only margins and arrow spacing, so nothing
   here is fighting it and no !important is needed. */
.anzac-project-pagination .wp-block-query-pagination-next {
	background-color: var(--wp--preset--color--brand-teal);
	border-radius: 4px;
	box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.1), 0px 4px 6px -4px rgba(0, 0, 0, 0.1);
	color: var(--wp--preset--color--white);
	font-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.125), 16px);
	font-weight: 700;
	line-height: 1.5;
	padding: 16px 24px;
	text-decoration: none;
}

/* Inverts exactly as the solid button does: white fill, brand-teal-dark label at
   5.69:1. Not the brighter brand-teal, which is 2.86:1 on white and fails. */
.anzac-project-pagination .wp-block-query-pagination-next:hover,
.anzac-project-pagination .wp-block-query-pagination-next:focus-visible {
	background-color: var(--wp--preset--color--white);
	color: var(--wp--preset--color--brand-teal-dark);
}

/* While the fetch is in flight. The script sets aria-busy, so the state is announced
   as well as shown, and it clears the attribute in a finally-style callback so this
   cannot stick. */
.anzac-project-pagination .wp-block-query-pagination-next[aria-busy="true"] {
	opacity: 0.6;
}


/*--- Footer link hover ---*/
/* Every footer link goes teal on hover and on active, and is never teal at rest.
   Jeff's rule. Covers the Related Links column, the contact details, the copyright bar
   and the social icons.

   Scoped to `footer.wp-block-template-part` rather than a class on the footer pattern,
   because the tag is what distinguishes it from the header, which is the same template
   part markup, and because it reaches the copyright bar too, which is a sibling group
   outside the footer pattern's main wrapper.

   brand-teal-dark rather than brand-teal: on the footer's off-white (#f3efe9) the bright
   brand teal measures 2.50:1, which fails even the 3:1 threshold for icons, while
   brand-teal-dark is 4.97:1. Hover is transient but still has to be readable.

   No !important needed. The per-block elements.link rules these override are
   `.wp-elements-<hash> a:where(:not(.wp-element-button))`, which is one class plus one
   element, and this selector is higher. */
footer.wp-block-template-part a:hover,
footer.wp-block-template-part a:active {
	color: var(--wp--preset--color--brand-teal-dark);
}

/* The social icons need their own rule at core's specificity plus one, and the reason
   is subtle. Core sets `color: currentColor` on `.wp-block-social-link-anchor:visited`
   through four chained classes, so on a link that is BOTH visited and hovered that rule
   would beat the one above and snap the icon back to the inherited colour. Since the
   two placeholder icons point at `#`, which counts as visited, that is not theoretical.

   Setting `color` on the anchor is enough to recolour the glyph: core gives the svg
   `fill: currentColor`, so the fill follows. Note the `li` carries an inline
   `color: #000000` from the block's iconColor, which is why the rule targets the anchor
   inside it and not the list item, where no selector could win. */
footer.wp-block-template-part .wp-block-social-links .wp-block-social-link.wp-social-link .wp-block-social-link-anchor:hover,
footer.wp-block-template-part .wp-block-social-links .wp-block-social-link.wp-social-link .wp-block-social-link-anchor:active {
	color: var(--wp--preset--color--brand-teal-dark);
}


/*--- Footer: the Related Links list runs across on phones ---*/
/* Jeff asked for `flex-direction: row` on `.footer-related-links` as a mobile style, and for
   **450px** as the breakpoint, 2026-08-18. His call, made with the numbers below in hand.

   `.footer-related-links` is the six item nav group, authored `layout: {type: flex,
   orientation: vertical}`, which core emits as `flex-direction: column`. Six stacked links is
   a lot of height for very little content; as a row they land on two lines.

   **What the direction change does to the width, because it has a knock-on.** As a column the
   group shrink wraps to about 105px. As a row its max-content width becomes roughly 390px, so
   the Related Links section grows out to its own per-block `max-width: 300px`. That section
   sits beside Contact, capped at 230px, inside a wrapping pair: at 105px the two fit side by
   side, at 300px they cannot, so the pair stacks.

   Measured footer heights both ways:

   | viewport | column | row |
   | --- | --- | --- |
   | 390 | 1306, pair already stacked | 1158 |
   | 430 | 996 | 1164 |
   | 450 | 998 | 1167 |
   | 600 | 1014 | 1189 |

   The pair pairs up between 410 and 415px on its own. So below that the row is free and saves
   about 148px, and from 415 to 450 it trades the paired layout for the row and the footer is
   roughly 170px taller. That band is the deliberate cost of the 450 breakpoint.

   **The 415 flip is a consequence of Contact's 230px cap and the section gap**, both of which
   live in the footer's own blocks, so re-measure if either changes.

   `flex-wrap` is deliberately not restated: core's flex layout already emits
   `flex-wrap: wrap` here, verified in the computed style, so the links wrap on their own once
   the direction changes. Same reasoning as the service grid rule.

   Doubled with `.wp-block-group` because core sets the column direction through a single
   generated class, and stylesheet order between core block styles and the theme is not
   guaranteed. */
@media (max-width: 450px) {
	.wp-block-group.footer-related-links {
		flex-direction: row;
	}
}


/* The banner's height floor, fluid rather than the comp's flat 661px.

   At 390px the banner's content needs only 365px, so a flat 661px floor left roughly
   300px of empty photo on a phone. The token solves for the comp's 661 at 1600 and
   Jeff's 420 at 390: clamp(420px, calc(19.92vw + 342px), 661px). It freezes at 661
   from about 1601px up, so desktop is unchanged, and 420 holds below 390 too.

   This is CSS rather than a block attribute because `core/cover`'s `minHeight` is a
   plain number with a separate unit, so it cannot hold a clamp, and it writes
   `min-height` inline where no stylesheet rule can reach it. **A banner adopts this
   by having its inline minHeight removed**; the five other page banners still carry
   theirs, so they are untouched until someone decides to convert them. Same shape as
   the header overlay reserved space, and for the same reason. */
.anzac-page-banner {
	min-height: var(--wp--custom--page-banner-min-height);
}

.anzac-page-banner .wp-block-cover__inner-container {
	align-self: stretch;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
}

/* Turning the inner container into a flex column costs the constrained layout its
   width, which has to be given back. Core centres each child with
   `margin-inline: auto`, and an auto margin on a FLEX item shrink wraps it instead
   of centring a full width box, so both sections collapsed to their contentSize and
   sat mid-banner. Restating width: 100% alongside the max-width restores it.
   Worth remembering generally: auto margins mean something different once the
   parent becomes a flex container. */
.anzac-page-banner .wp-block-cover__inner-container > * {
	width: 100%;
	max-width: var(--wp--style--global--content-size);
	margin-left: auto;
	margin-right: auto;
}

/* A minimum 25px between the banner heading block and the breadcrumb row, phones only.

   The rule above makes the inner container `justify-content: space-between`, so the space
   between the two is FREE SPACE rather than a declared gap. On desktop there is plenty. On
   a phone the banner floors at 420px while the heading block grows (the h1 wraps to four,
   five, even six lines), so the free space runs out and the two meet: measured **0px** on
   About and Renovations from 360 through 600, with the breadcrumb sitting against the last
   line of body copy.

   Written as the blockGap margin rather than as `padding-bottom` on the heading group,
   which is what this looks like it should be, or as `gap` on the container, which is what
   it looks like it should be second. Both were tried:

   - The heading group carries `padding: 0` as a block attribute, so WordPress writes it
     inline, and an inline style beats every stylesheet rule short of `!important`. Same
     shape as the cover `minHeight` and the hero's reserved padding.
   - `gap` works but STACKS with the blockGap margin the container already emits, and only
     some banners carry that margin: theme.json gives `core/group` `margin-top: 0`, so a
     banner whose second child is the `.anzac-banner-footer` group had 0 while one whose
     second child is `core/breadcrumbs` directly had 32px. `gap: 25px` therefore produced
     25px on About and the subpages and **57px** on Portfolio, Contact and Financing.

   Overriding the margin instead replaces that mechanism rather than adding to it, so every
   banner lands on the same 25px. It beats theme.json's `core/group` rule on specificity
   (0,2,0 against 0,1,0) with no `!important`. `space-between` still distributes any
   remaining free space on top, so this is a floor and not a fixed gap. Scoped below 782px
   because desktop has never had the problem. */
@media (max-width: 781px) {
	.anzac-page-banner .wp-block-cover__inner-container > * + * {
		margin-block-start: 25px;
	}
}

/* The teal bar runs flush into the banner's bottom edge with only its top corners
   rounded, so the cover's bottom padding is zeroed on the block and the breadcrumb
   carries its own 26px instead. Both of those are block attributes, not CSS. */


/*--- Enquiry accordion in a page banner ---*/
/* The teal "What are you looking for help with?" bar is a disclosure on subpages:
   clicking it reveals Gravity Form 1. The homepage hero has the same bar but is a
   separate scope, .anzac-hero-enquiry further down this file, so nothing here touches
   it. The two are opposites on a phone: this one is forced open, the hero one shut.

   core/accordion, new in 6.9, does the hard part. It renders a real <button
   type="button"> with aria-expanded, aria-controls, a matching aria-labelledby on
   the panel, `inert` on the panel while collapsed, and keyboard handling, all
   server rendered. That is why this is core's block and not a hand rolled toggle:
   every one of those is a thing a bespoke version gets wrong. */
.anzac-enquiry-accordion {
	width: 420px;
	max-width: 100%;
}

.anzac-enquiry-accordion .wp-block-accordion-heading {
	margin: 0;
}

.anzac-enquiry-accordion .wp-block-accordion-heading__toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	width: 100%;
	border: 0;
	border-radius: 4px 4px 0 0;
	background: transparent;
	color: inherit;
	font: inherit;
	text-align: left;
	cursor: pointer;
}

/* The comp's up-right arrow in place of core's literal "+" character. Mask so it
   takes the heading's colour; font-size: 0 hides the plus itself. */
.anzac-enquiry-accordion .wp-block-accordion-heading__toggle-icon {
	flex: 0 0 auto;
	width: 22px;
	height: 22px;
	font-size: 0;
	background-color: currentColor;
	-webkit-mask: url("../images/icon-arrow-up-right.svg") no-repeat center / contain;
	mask: url("../images/icon-arrow-up-right.svg") no-repeat center / contain;
}

/* Core turns its plus into an x with a 45deg rotation. An arrow wants a half turn. */
.anzac-enquiry-accordion .wp-block-accordion-item.is-open > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transform: rotate(180deg);
}

/* Slide open rather than snap.

   Core collapses the panel with `display: none` on [inert], and display cannot be
   transitioned, so out of the box it blinks. Core clearly intended otherwise, it
   declares `transition: grid-template-rows` on the item, but its own display rule
   defeats that. Putting the panel back in flow and collapsing the grid row to 0fr
   makes the intended transition work.

   `inert` stays on the panel and is untouched. It is what holds the collapsed form
   out of the accessibility tree and out of the tab order, so this is purely visual.
   Never drop inert to achieve the hiding, or a keyboard user can tab into a form
   they cannot see.

   min-height: 0 is required, not decorative. A grid item defaults to
   `min-height: auto`, which refuses to shrink below its content, so without it the
   0fr row stays content height and nothing collapses.

   The grid goes on the PANEL as a single row, not on the item as its second row.
   Both look equivalent and only one works: on the item, the open state's `1fr` has no
   free space to claim in an auto height container and, with the child's automatic
   minimum removed by min-height: 0, it resolves to 0 and nothing ever opens. A one
   row grid on the panel resolves `1fr` against its own content instead. Measured both.

   The [inert] selector is repeated so this outranks core's `display: none`, which
   sits at class-plus-attribute specificity.

   margin-block-start: 0 is what closes the gap between the bar and the form. It came
   from the root block gap, 2rem, landing between the item's two children. Core hints
   at the same problem by zeroing it on the collapsed panel, which is exactly why the
   gap was invisible until the panel opened.

   The panel itself must carry no padding, or the collapsed row cannot go below it and
   a strip of empty form background shows under the closed bar. The padding lives on a
   group inside the panel for that reason. */
/* margin-block-start: 0 removes the 32px the root block gap drops between the bar
   and the form. Invisible while closed, which is why it only showed on open.

   The rest is the slide. Core hides the panel with `display: none` on [inert], which
   cannot be transitioned, so the panel is put back in flow and its height animated
   instead. The [inert] selector is repeated so this outranks core's rule, which sits
   at class-plus-attribute specificity.

   `inert` itself is untouched and still does all the accessibility work: it keeps the
   collapsed form out of the accessibility tree and out of the tab order. Never drop
   it to achieve the hiding.

   max-height rather than a grid 0fr/1fr slide. The grid version is more elegant and
   needs no magic number, but it resolved to zero height in the open state here and
   the cause was never pinned down. 1200px is a ceiling, not a height: it only has to
   exceed the real content, which measures 456px. If the form grows past 1200 the
   panel will clip, so raise it rather than wonder why the submit button vanished. */
.anzac-enquiry-accordion .wp-block-accordion-panel,
.anzac-enquiry-accordion .wp-block-accordion-panel[inert] {
	display: grid;
	grid-template-rows: 0fr;
	margin-block-start: 0;
	transition: grid-template-rows 320ms ease-out;
}

.anzac-enquiry-accordion .wp-block-accordion-item.is-open .wp-block-accordion-panel {
	grid-template-rows: 1fr;
}

.anzac-enquiry-accordion .wp-block-accordion-panel > * {
	overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
	.anzac-enquiry-accordion .wp-block-accordion-panel {
		transition: none;
	}
}

/* Above 782px the panel SLIDES UP FROM BEHIND THE BAR, and the banner's own clip hides
   it. Technique borrowed from the retirestrongfa.com hero, which does this better than
   the height animation that used to be here.

   Rather than growing the panel's height, the panel sits at full height, parked behind
   the bar and below the banner's bottom edge, and `transform` slides it into view. Three
   things that buys, all of them real:

   1. **It animates `transform`, not `grid-template-rows`.** A height animation relayouts
      on every frame; a transform is composited, so the motion is smoother for free.
   2. **It uses `.wp-block-cover`'s `overflow: clip` as the mask rather than fighting it.**
      The previous version set `overflow: visible` on the banner so the panel would not be
      clipped. That is gone. The clip is now load bearing in our favour, so do not
      "helpfully" restore `overflow: visible`: it would reveal the parked form hanging
      below the banner.
   3. **`max-height` plus `overflow-y: auto` caps the form** rather than hoping it fits.
      That is the honest answer to a failed submit, where Gravity Forms adds an error
      summary and per field messages and the form grows past the space above the bar: it
      scrolls instead of having its top silently clipped. 470px clears today's 456px form
      and still fits inside the 494px available at 782px, the tightest width.

   Two things remain load bearing from the previous version:

   - **`z-index` on the panel, and deliberately none on the banner.** The form reaches the
     overlay header, which sits at 10, so it needs to beat it or its top is painted over
     and the first fields become unreachable. A z-index on the BANNER breaks this, because
     that makes the banner a stacking context and confines the panel to its layer.
   - **The item is the containing block**, so it needs `position: relative`, declared
     outside this query because it is harmless at every width.

   The bar needs to paint over the parked panel, hence the z-index pair: bar 21, panel 20.

   Deviation from theirs, deliberate: they translate the WHOLE card, so their bar rides up
   with the form. Ours translates only the panel, so the teal bar stays pinned to the
   banner's bottom edge and stays aligned with the breadcrumb beside it, and the form
   slides out from behind it. */
.anzac-enquiry-accordion .wp-block-accordion-item {
	position: relative;
}

@media (min-width: 782px) {
	/*
	 * The WHOLE card moves, bar included, so the teal bar rides up with the form
	 * exactly as it does on retirestrongfa.com.
	 *
	 * The root stays in flow at the bar's height and the ITEM is what gets lifted out.
	 * That split is the point: `.anzac-banner-footer` is a flex row holding the
	 * breadcrumb and this accordion with `verticalAlignment: bottom`, so if the root
	 * collapsed to nothing the row would lose the bar's 74px and the breadcrumb would
	 * drop to the banner's bottom edge. Keeping the root at the bar's height means the
	 * row measures exactly what it did before and the breadcrumb does not move.
	 *
	 * `bottom: 0` anchors the card's bottom to the root's bottom, which is the OPEN
	 * state: form flush with the banner's bottom edge, bar sitting a form's height
	 * above it. The closed state then translates down by everything except the bar,
	 * so the bar lands back in the root's box and the form hangs below the banner
	 * where the cover's clip hides it.
	 */
	.anzac-enquiry-accordion {
		/* The one magic number, and the reason it is a custom property rather than a
		   literal: it appears in three places below and they must never drift apart.
		   Deliberately NOT a theme.json token, because it is a measurement of this
		   bar (26px padding, one 18px line, 26px padding) rather than a design
		   decision anyone would want to reuse or restyle. */
		--anzac-enquiry-bar-h: 74px;

		position: relative;
		height: var(--anzac-enquiry-bar-h);
	}

	/* Pin the bar to that height so the arithmetic cannot silently go wrong if the
	   heading's padding or font size is edited in the inspector. */
	.anzac-enquiry-accordion .wp-block-accordion-heading__toggle {
		min-height: var(--anzac-enquiry-bar-h);
	}

	.anzac-enquiry-accordion .wp-block-accordion-item {
		position: absolute;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 20;
		transform: translateY(calc(100% - var(--anzac-enquiry-bar-h)));
		transition: transform 320ms ease-out;
	}

	.anzac-enquiry-accordion .wp-block-accordion-item.is-open {
		transform: translateY(0);
	}

	/* The panel is ordinary flow content inside the card now, so the mobile height
	   slide is neutralised and the form is capped instead. */
	.anzac-enquiry-accordion .wp-block-accordion-panel,
	.anzac-enquiry-accordion .wp-block-accordion-panel[inert] {
		display: block;
		grid-template-rows: none;
		max-height: 470px;
		overflow-y: auto;
	}

	.anzac-enquiry-accordion .wp-block-accordion-panel > * {
		overflow: visible;
	}
}

/* Both of these are restated AFTER the query above, and that is not tidiness. The
   originals sit further up the file where they only knew about the height slide on the
   PANEL, and `transition: transform` above would otherwise win on source order and
   reinstate the animation for someone who asked for no motion. They name the item as
   well as the panel because above 782px the transition lives on the item. */
@media (prefers-reduced-motion: reduce) {
	.anzac-enquiry-accordion .wp-block-accordion-item,
	.anzac-enquiry-accordion .wp-block-accordion-panel {
		transition: none;
	}
}

/* The banner accordions are `openByDefault: true`, so core server-renders the panel OPEN
   with no `hidden` attribute. That is deliberate and it is the only reason a visitor with
   no JavaScript can use the form at all: `hidden`/`inert` cannot be undone from CSS, so a
   server-closed panel plus a toggle that needs the Interactivity API is a form nobody can
   open. The cost is that the collapsed look now has to be produced by CSS instead of
   coming free from the server.

   `:not(.is-synced)` holds it collapsed from the FIRST PAINT, so the open state is never
   painted and there is no jump in page height. The script adds `.is-synced` only once the
   item's own class agrees that it is shut, never on a timer: releasing early is what made
   the homepage hero spring back open before that bug was fixed. Same mechanism, same
   reason, see the hero block below.

   Both widths need holding, unlike the hero, because a banner bar is closed by default at
   every width: mobile hides the panel with the grid row, desktop with the item's
   transform, so each hold rule is scoped to the width whose mechanism it targets. */
.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-item,
.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-panel {
	transition: none;
}

@media (max-width: 781px) {
	.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-item.is-open .wp-block-accordion-panel {
		grid-template-rows: 0fr;
	}
}

@media (min-width: 782px) {
	.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-item.is-open {
		transform: translateY(calc(100% - var(--anzac-enquiry-bar-h)));
	}
}

/* With scripting off `.is-synced` never arrives, so the hold above would be permanent and
   the panel would be collapsed while still carrying no `hidden` attribute: invisible and
   focusable at once, which is worse than either. Undo it, leaving the form open and
   reachable as the server rendered it. Ties with the rules above on specificity and wins
   on source order, so it MUST stay below them. */
@media (scripting: none) {
	.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-item.is-open .wp-block-accordion-panel {
		grid-template-rows: 1fr;
	}

	.anzac-enquiry-accordion:not(.is-synced) .wp-block-accordion-item.is-open {
		transform: translateY(0);
	}
}

/* Below 782px the panel is in flow and collapsed by default (the client asked for this
   on 2026-08-24, matching the homepage hero), so once OPENED its bottom edge would sit
   flush against the next section: the cover's own bottom padding is zeroed on the block
   so the teal bar can run into the banner's edge. 30px of breathing room, on the open
   state only so a closed bar keeps sitting flush as it does at every other width. */
@media (max-width: 781px) {
	/* `.is-synced` is required, and that is not decoration. `is-open` is server-rendered
	   (openByDefault), so without it this margin lands during the pre-sync hold, while the
	   panel is still collapsed and the 30px has nothing to separate. Measured: the banner
	   went 572 then 542 on a phone, a visible 30px shift on load. */
	.anzac-enquiry-accordion.is-synced .wp-block-accordion-item.is-open .wp-block-accordion-panel {
		margin-bottom: 30px;
	}
}


/*--- Homepage hero enquiry disclosure ---*/
/* The same teal "What are you looking for help with?" bar as the page banners, but in the
   homepage hero, where the client wants the form collapsible on a phone instead of taking
   up most of the first screen.

   It is deliberately its own scope rather than a second class on .anzac-enquiry-accordion:
   that block carries the banner's floating panel and its hover-open, neither of which
   belongs here, and the thirteen subpages using it are not worth the regression risk.
   The duplication below is the bar, the icon and the slide only.

   Why core/accordion rather than hiding a group with a media query: CSS cannot reach
   `inert` or `aria-expanded`, so a CSS-only collapse gives a form that looks shut while
   still being in the tab order and announced as expanded. Core's block server renders a
   real button with both, and the Interactivity API keeps them in step. */
.anzac-hero-enquiry .wp-block-accordion-heading {
	margin: 0;
}

.anzac-hero-enquiry .wp-block-accordion-heading__toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	width: 100%;
	border: 0;
	border-radius: 4px 4px 0 0;
	background: transparent;
	color: inherit;
	font: inherit;
	text-align: left;
	cursor: pointer;
}

/* Same masked arrow as the banner bar, so it takes the heading's colour. */
.anzac-hero-enquiry .wp-block-accordion-heading__toggle-icon {
	flex: 0 0 auto;
	width: 22px;
	height: 22px;
	font-size: 0;
	background-color: currentColor;
	-webkit-mask: url("../images/icon-arrow-up-right.svg") no-repeat center / contain;
	mask: url("../images/icon-arrow-up-right.svg") no-repeat center / contain;
}

.anzac-hero-enquiry .wp-block-accordion-item.is-open > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transform: rotate(180deg);
}

/* Desktop draws no toggle affordance, because the comp shows a plain bar with the form
   already under it and that is still how it behaves. The button keeps working and keeps
   announcing its state, so this is a visual choice rather than a functional one. */
@media (min-width: 782px) {
	.anzac-hero-enquiry .wp-block-accordion-heading__toggle-icon {
		display: none;
	}
}

/* Slide rather than snap, the same grid 0fr/1fr technique as the banner accordion:
   core hides the panel with `display: none` on [inert], which cannot be transitioned.
   The [inert] selector is repeated so it outranks core's own rule. The direct child
   carries the overflow and no padding, or the collapsed row cannot shrink below that
   padding and a strip of form background shows under the closed bar. */
.anzac-hero-enquiry .wp-block-accordion-panel,
.anzac-hero-enquiry .wp-block-accordion-panel[inert] {
	display: grid;
	grid-template-rows: 0fr;
	margin-block-start: 0;
	transition: grid-template-rows 320ms ease;
}

.anzac-hero-enquiry .wp-block-accordion-item.is-open .wp-block-accordion-panel {
	grid-template-rows: 1fr;
}

.anzac-hero-enquiry .wp-block-accordion-panel > * {
	overflow: hidden;
}

.anzac-hero-enquiry.is-instant .wp-block-accordion-panel {
	transition: none;
}

/* The form card keeps the comp's bottom radius. It sits on the inner group rather than
   the panel because the panel is the thing being collapsed. */
.anzac-hero-enquiry .anzac-hero-form .has-off-white-background-color {
	border-radius: 0 0 4px 4px;
}

/* Collapsed from the first paint on a phone, not from the moment the script runs.

   The item is `openByDefault`, so the server sends it open, which is what keeps desktop
   correct and what makes the no-JS case below possible. The cost is that a phone would
   otherwise paint the full 456px form and then animate it shut once the script caught
   up, which reads as the page jumping. This rule collapses it before any of that, and
   the script hands control back by adding .is-synced only once the item's own state
   agrees. Four classes, to outrank the .is-open rule above.

   Releasing this on a timer instead of on the real state was the original bug: when the
   Interactivity API had not hydrated yet the panel sprang open again. */
@media (max-width: 781px) {
	.anzac-hero-enquiry:not(.is-synced) .wp-block-accordion-item .wp-block-accordion-panel {
		grid-template-rows: 0fr;
	}
}

/* With scripting off core's toggle cannot work at all, so a collapsed panel would be a
   form nobody on a phone could ever open. Leave it open, which is what the page did
   before this component existed. Same mechanism as the brush underline's
   `@media (scripting: enabled)`. Ties with the rule above on specificity and wins on
   source order, so it must stay below it. */
@media (max-width: 781px) and (scripting: none) {
	.anzac-hero-enquiry .wp-block-accordion-item.is-open .wp-block-accordion-panel {
		grid-template-rows: 1fr;
	}
}

@media (prefers-reduced-motion: reduce) {
	.anzac-hero-enquiry .wp-block-accordion-panel {
		transition: none;
	}
}


/*--- Article headings, inside a blog post's content ---*/
/* theme.json's element defaults were tuned for landing page section headings, where an
   `h2` is a big statement: `h1` is `xx-large` (45px) and `h2` is `xx-larger` (52px). In a
   page built of sections that is right, because each `h2` opens a section and the page's
   `h1` sits in a banner with its own treatment.

   Inside an article it is upside down: a subheading rendered larger than the post title
   reads as though the page has two competing titles. Measured on a post before this rule,
   the title was 40.8px and the first `h2` in the body 52px.

   So the scale is restated for post content only, stepping down 45 / 36 / 24 / 20 using
   existing tokens rather than minting new sizes. CSS rather than theme.json because it
   needs a descendant combinator to reach only content inside a post, which theme.json
   cannot express: that is one of the escape hatches in non-negotiable 5.

   **Why this does not wreck the landing pages, and it is not the reason you would guess.**
   Those section headings ARE inside `.wp-block-post-content` too, since `page.html` renders
   the page through `core/post-content`, so this selector matches them. They keep their size
   because each one sets an explicit `fontSize` attribute, and core emits preset font size
   classes as `.has-huge-font-size{font-size:...!important}`. The `!important` outranks this
   rule.

   That makes the rule self-limiting in a useful way: it only reaches headings that rely on
   the element default, which is exactly what an editor writing an article produces. Verified
   after adding it that About's headings are still 59.6px while a post's body `h2` is 33.8px.
   The corollary is that it cannot be used to restyle a heading that names its own size. */
.wp-block-post-content h2 {
	font-size: var(--wp--preset--font-size--x-large);
}

.wp-block-post-content h3 {
	font-size: var(--wp--preset--font-size--large);
}

.wp-block-post-content h4 {
	font-size: var(--wp--preset--font-size--medium);
}


/*--- A small icon next to text sits on the baseline, not the optical centre ---*/
/* `core/image` renders a block <figure> wrapping an INLINE <img>, so the figure is as tall
   as a line box while the picture is only as tall as itself, and the picture sits on the
   text baseline inside it. Put that figure in a flex row with `verticalAlignment: center`
   and `align-items: center` dutifully centres the FIGURE, leaving the picture low.

   Measured on the homepage before this rule: the rating row's stars were 7.45px below the
   centre of "5.0" and "based on 50+ reviews", and the Learn More arrow 5.17px below its
   label, while both figures were centred to within 0.01px. So the block attribute was
   right all along and looked wrong, which is why it reads as an alignment bug rather than
   a line-box one.

   `display: block` on the image removes the line box, so the figure collapses to the
   picture's own height and centring the figure now centres the picture. Scoped to
   `.is-resized`, which WordPress adds only when an explicit width is set, so it catches
   the small icons and leaves body images alone. */
.wp-block-image.is-resized img {
	display: block;
}


/*--- Our Approach vertical tabs ---*/
/* Figma node 156:168. The comp puts the section heading ABOVE a vertical tab list and
   a photo BELOW it, all in the left column, with the panel as a sibling on the right.

   That layout is why this is not `core/tabs`, and the reason is structural rather than
   cosmetic: core requires `core/tab-list` and `core/tab-panel` to be direct children of
   `core/tabs`, and `core/tab-list` accepts only tab buttons, so there is nowhere to put
   the heading or the photo. Core's tab list also hard-codes `allowOrientation: false`
   and its view script binds only ArrowLeft/ArrowRight with no `aria-orientation`, so it
   could not do a vertical set correctly even if the layout fitted. Re-check when core
   adds orientation; at that point this becomes a content change, not a code migration.

   So the section is ordinary core blocks and `initApproachTabs` in functions.js upgrades
   it in place. Everything here is keyed off `aria-selected` and the `hidden` attribute,
   both of which that function owns, so the styling can never disagree with the state.
   With no JavaScript the tabs stay links to the Services page and both panels render
   stacked, which is a coherent page rather than a broken one. */
/* Nothing is needed on the list itself. The comp's rule under the pair is already a
   `teal` bottom border block attribute on the second tab, so adding one here would draw
   a second line. */

.anzac-approach-tab {
	cursor: pointer;
	/* 62px in the comp. A min-height rather than a height so a wrapped label grows the
	   row instead of spilling out of it. */
	min-height: 62px;
	transition: background-color 200ms ease-out, color 200ms ease-out;
}

/* The active row: deep-teal fill with a white label, per the comp. This is CSS rather
   than a `backgroundColor` block attribute on the first tab, which is how it was built
   originally: an attribute bakes the active state into whichever tab the editor happens
   to have styled, so it cannot follow the selection. */
.anzac-approach-tab[aria-selected="true"] {
	background-color: var(--wp--preset--color--deep-teal);
}

.anzac-approach-tab[aria-selected="true"],
.anzac-approach-tab[aria-selected="true"] a,
.anzac-approach-tab[aria-selected="true"] p {
	color: var(--wp--preset--color--white);
}

/* Once `initApproachTabs` strips the href the label is no longer a link, but it is still
   an <a>, so theme.json's `p a:hover` rule goes on underlining it. Caught by looking at
   it rather than by measuring: the row is a tab, and a tab does not get link decoration.
   Wins on specificity because theme.json wraps its element styles in `:where()`. */
.anzac-approach-tab a {
	text-decoration: none;
	cursor: inherit;
}

/* The chevron has to follow the selected state, and it cannot do that as authored: the
   two rows point at two different uploads, `icon-chevron-right-white.svg` (attachment
   38) on the first and `icon-chevron-right-black.svg` (37) on the second, because the
   active row used to be a fixed choice. A `core/image` cannot recolour per state.

   So the served <img> is hidden and the glyph repainted on its figure as a mask taking
   `currentColor`, which is the house pattern already used for the check lists, the
   breadcrumb glyphs and the brush underline. `visibility` rather than `display` so the
   figure keeps the 8x16 box the inline width gives it.

   Doing it here rather than in content is deliberate: it means the section needs no
   markup change for the colour to start following the state, and it is correct whichever
   of the two assets each row happens to reference. The tidier end state is to point both
   rows at the black asset and delete attachment 38, which is a content change and can
   wait. */
.anzac-approach-tab .wp-block-image img {
	visibility: hidden;
}

.anzac-approach-tab .wp-block-image {
	background-color: currentColor;
	-webkit-mask: url("../images/icon-chevron-right.svg") no-repeat center / contain;
	mask: url("../images/icon-chevron-right.svg") no-repeat center / contain;
}

/* Keyboard users must be able to see which row they are on, and this has to work on BOTH
   row states, which is what the first attempt got wrong: a `brand-teal-dark` ring around
   a `deep-teal` selected row is teal on teal, about 1.2:1 against the fill it outlines.
   Measured at 2px solid #00727a on a #09868D row, and it reads as no ring at all, which
   is most of the reason the tab set looked unreachable by keyboard.

   A two-tone ring instead, so it does not depend on what is behind it: a white inner band
   filling the offset gap, then a near-black outer ring. Against the selected row that is
   white-on-teal then black-on-white; against an idle row on the off-white section it is
   near-black at roughly 14:1. The ring goes on the ROW because the row is the tab stop,
   and both follow the row's own border radius. */
.anzac-approach-tab:focus-visible {
	outline: 2px solid var(--wp--preset--color--black);
	outline-offset: 2px;
	box-shadow: 0 0 0 2px var(--wp--preset--color--white);
}

/* `hidden` alone loses to any `display` a block sets on itself, so it is restated.

   This is also what makes the service card links below safe: a hidden panel is
   `display: none`, so its four links are out of the tab order and out of the
   accessibility tree rather than being invisible but still reachable. */
.anzac-approach-panel[hidden] {
	display: none;
}

/* Each service card links to its service subpage. Jeff's call, 2026-08-13.

   The anchor is in the card's heading, and it is stretched over the whole card, so the
   hit area is the card rather than just the words. Same technique as the Portfolio
   cards, and easy here because the card group is an ordinary flow container: nothing in
   it creates a competing containing block, which is what made the Portfolio version take
   three attempts.

   ONE link per card, deliberately: the icon is not separately linked, because two
   anchors to the same page would put the destination in the tab order twice and read it
   out twice.

   `color: inherit` because the label must not pick up a link colour; the card is a
   design element that happens to be clickable, and the comp draws plain black text.

   Adding these links also removes a tab stop rather than adding eight. initApproachTabs
   gives a panel `tabindex="0"` ONLY when it contains nothing focusable, which was true
   when the cards were inert. That condition already tests for `a[href]`, so it now
   correctly skips it and the links themselves are the reachable content. */
.anzac-approach-panel .anzac-service-grid .wp-block-column > .wp-block-group {
	position: relative;
}

.anzac-approach-panel .anzac-service-grid h4 a {
	color: inherit;
	text-decoration: none;
}

.anzac-approach-panel .anzac-service-grid h4 a::after {
	content: "";
	inset: 0;
	position: absolute;
}

/* Hovering anywhere on the card underlines its label, so the whole card reads as the
   control it is. Keyboard focus gets the same, and the focus ring is untouched because
   only the decoration is set. */
.anzac-approach-panel .anzac-service-grid .wp-block-column > .wp-block-group:hover h4 a,
.anzac-approach-panel .anzac-service-grid h4 a:focus-visible {
	text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
	.anzac-approach-tab {
		transition: none;
	}
}

/* EDITOR ONLY. Marks the two tab panels so they read as special in the block editor.
   Jeff's request, 2026-08-14.

   Why it is needed: `initApproachTabs` is a front end script, so in the editor nothing
   gives these panels `role="tabpanel"`, no `aria-selected` exists, and the
   `.anzac-approach-panel[hidden]` rule never fires. Both panels therefore render stacked
   and look like two ordinary groups, which is exactly the wrong mental model: on the
   front end only one is ever visible.

   `.editor-styles-wrapper` is the scope that keeps it out of the front end. That class
   only exists in the editor, and it is the same mechanism the editor-parity rules at the
   top of this file use. It works inside the iframed post editor too, since the wrapper is
   still present in there.

   All five parts of the set are marked the same way: one colour, one outline, one label
   style. They were briefly two colours, clay for the panels and purple for the controls, on
   the theory that separating the controls from the things they control helps. Jeff's call on
   2026-08-14 is that it does not, and the labels already say which is which, so do not
   reintroduce a second colour.

   Choices worth keeping:

   - `outline`, never `border`. An outline paints outside the box without joining the box
     model, so it cannot change the element's own width and it never shifts a sibling.
   - `padding: 10px` with `!important`, and `outline-offset: 0`. This started as
     `outline-offset: -4px` with no padding, on the reasoning that the panels carry
     `padding: 0` inside a column at `blockGap: 0` and so sit flush, meaning a positive
     offset would overlap the two outlines. True, but it solved the wrong problem: an inset
     offset puts the dotted band ON the content, where the cards partly cover it and the
     marker reads as broken rather than as a marker. Padding is the answer, because it moves
     the CONTENT inward instead of moving the outline over it, and the boxes stay flush so
     their outlines only ever touch.
   - `!important` on the padding is required and is the sanctioned kind. Every one of these
     elements carries padding as a BLOCK ATTRIBUTE, the panels and the tablist `0` and each
     tab `11px 26px 11px 24px`, and WordPress writes those inline, which no plain selector
     can outrank. Same reason the header's button and badge paddings carry it. Without it the
     declaration is silently inert, which is the failure mode to expect whenever a rule
     targets something an editor can set in the inspector.
   - `#9C27B0` is a hardcoded hex and stays one. Editor chrome is not a design token, and
     adding a purple to the palette would put it in the client's colour picker. 4px dotted is
     also unmistakable against core's own 1px solid block outline, which is the thing it
     would otherwise be confused with, and against the intergetik-cover-slideshow plugin's
     1px dashed marker, which appears on this same page.
   - `:not(:focus-visible)` protects the two-tone keyboard focus ring above. That rule is
     `.anzac-approach-tab:focus-visible` at the same specificity as this one, and this one is
     later in the file, so without the negation it would win and flatten the ring. Moot today
     because the rows are not focusable in the editor, but the ring is load bearing on the
     front end and CLAUDE.md says not to weaken it, so do not remove this.

   The cost is that the editor is now 10px wider inside each marked box than the front end.
   Accepted deliberately: a marker you cannot fully see is worse than a small divergence,
   and every marked element here is a wrapper whose children reflow rather than a fixed
   layout that would break. */
.editor-styles-wrapper .anzac-approach-panel,
.editor-styles-wrapper .anzac-approach-tablist,
.editor-styles-wrapper .anzac-approach-tab:not(:focus-visible) {
	outline: 4px dotted #9C27B0;
	outline-offset: 0;
	padding: 10px !important;
}

/* Shared label styling. Copied from the intergetik-cover-slideshow plugin's `igcs-slides`
   marker rather than invented, so the editor speaks one language for "this element is not
   what it looks like". */
.editor-styles-wrapper .anzac-approach-panel::before,
.editor-styles-wrapper .anzac-approach-tablist::before,
.editor-styles-wrapper .anzac-approach-tab::before {
	color: #9C27B0;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.4px;
	margin-bottom: 8px;
	text-transform: uppercase;
}

.editor-styles-wrapper .anzac-approach-panel::before {
	content: "Tab panel (only the selected one shows on the front end)";
	display: block;
}

.editor-styles-wrapper .anzac-approach-tablist::before {
	content: "Tab list (these rows switch the panels)";
	display: block;
}

/* The row label needs different mechanics from the other two, and this is the whole reason
   it was left out at first. Each row is `layout: flex` with `justifyContent: space-between`,
   so its `::before` is a FLEX ITEM, not a block: as a third item it would join the label and
   the chevron and space-between would redistribute all three.

   `flex: 0 0 100%` plus wrapping puts it on a line of its own, which leaves the original two
   children alone on the second line with their spacing intact. In flow, so nothing overlaps,
   which an absolutely positioned label would have risked inside a 62px row.

   `flex-wrap` is `nowrap` on the block, emitted by core's layout support as a generated
   container class, so `!important` is the reliable way to beat it rather than counting
   selector weight against markup we do not control. */
.editor-styles-wrapper .anzac-approach-tab {
	flex-wrap: wrap !important;
}

.editor-styles-wrapper .anzac-approach-tab::before {
	content: "Tab";
	flex: 0 0 100%;
}


/*--- Affiliation logo row ---*/
/* Each mark carries the comp's own width for its slot as a block attribute, and the
   comp's height then falls out of the natural aspect ratio, so nothing here sizes
   them. All this does is stop a mark overflowing its column once the row wraps.

   This previously forced `height: 56px` on every logo, on the reasoning that the
   five ratios were too different to size by width. That reasoning was wrong, and it
   was hiding a real defect: the logos were in the wrong order, so each had been
   given a neighbour's width. Fixed 2026-08-06 by reordering; the comp's widths then
   reproduce the comp's heights to within half a pixel. Do not reintroduce a fixed
   height here. If a logo looks wrong, check it is in the right slot. */
.anzac-logo-row img {
	height: auto;
	max-width: 100%;
}

/* Two corrections that keep the supplied assets untouched, which matters because
   these are other organisations' marks and several dictate how they may be used.

   `grayscale`: the comp draws all five black, but the HBA logo is supplied in
   colour. Desaturating here gets the comp's monochrome row without editing the
   file. Written against the row rather than one image, so a future logo is
   normalised too.

   `multiply`: four of the five files carry an opaque white box, which the comp
   hides with mix-blend-multiply (visible in the Figma node's own styles). Without
   it, white rectangles sit on the tinted band behind BBB and Energy Star. Multiply
   leaves black artwork untouched and maps the white to the backdrop, whatever the
   section's colour is, so it keeps working if that colour changes. */
.anzac-logo-row img {
	filter: grayscale(1);
	mix-blend-mode: multiply;
}


/*--- Breadcrumbs ---*/
/* core/breadcrumbs, new in 7.0, replacing whatever plugin the old
   .breadcrumb-item--home rules further down were written against. Those are dead
   and should go when the old design's CSS is next swept.

   Core renders <nav><ol><li><a>, with the separator as
   `li:not(:last-child)::after { content: var(--separator, "/") }` and the string
   coming from the block's own `separator` attribute. The comp uses a chevron glyph
   and a house icon instead of text, neither of which `content` can express, so both
   are masks. Mask rather than background-image for the same reason as the brush
   underline: the colour then comes from currentColor, so the breadcrumb inherits
   whatever the banner sets and there is no second copy of the colour to maintain.

   Both selectors are doubled with .wp-block-breadcrumbs purely to outrank core,
   which lands on the identical specificity, and stylesheet order is not guaranteed. */
.anzac-breadcrumbs.wp-block-breadcrumbs li:not(:last-child)::after {
	content: "";
	width: 6px;
	height: 10px;
	margin: 0 14px;
	opacity: 1;
	background-color: currentColor;
	-webkit-mask: url("../images/icon-breadcrumb-separator.svg") no-repeat center / contain;
	mask: url("../images/icon-breadcrumb-separator.svg") no-repeat center / contain;
}

/* The home crumb is the house glyph. font-size: 0 hides the label visually while
   leaving it in the accessibility tree, so the link still reads as "Home". */
.anzac-breadcrumbs.wp-block-breadcrumbs li:first-child a {
	display: inline-flex;
	align-items: center;
	font-size: 0;
}

.anzac-breadcrumbs.wp-block-breadcrumbs li:first-child a::before {
	content: "";
	width: 20px;
	height: 18px;
	background-color: currentColor;
	-webkit-mask: url("../images/icon-breadcrumb-home.svg") no-repeat center / contain;
	mask: url("../images/icon-breadcrumb-home.svg") no-repeat center / contain;
}

.anzac-breadcrumbs.wp-block-breadcrumbs a {
	text-decoration: none;
}


/*--- Header overlay on pages that open with a hero ---*/
/* The comp places the header inside the hero: white logo card and white menu
   sitting over the banner photo, header at y=0 overlapping a banner that also
   starts at y=0. Homepage is Figma node 383:1859, About is 383:1947.

   This is in the stylesheet rather than theme.json for two reasons that
   theme.json genuinely cannot express: it needs a context selector, and it needs
   a media query.

   The header is lifted out of flow rather than the hero being pulled up with a
   negative margin. The first section reserves the space with its own padding-top,
   see the rule below, so the result does not depend on the header's height, which
   changes with the viewport.

   `has-overlay-header` comes from anzac_body_classes() in functions.php, which
   adds it to any singular page whose content opens with a core/cover. It replaced
   `.home` on 2026-08-03 when About became the second such page. A page that does
   not open with a cover keeps the header in normal flow, where the white menu
   would be invisible, and still needs its own treatment. */
.has-overlay-header header.wp-block-template-part {
	position: absolute;
	top: var(--wp-admin--admin-bar--height, 0px);
	left: 0;
	right: 0;
	z-index: 10;
}

/* The other half of that condition, added when the 404 became the first page without a
   dark hero. The navigation block carries `textColor: white` because it normally sits on
   a photo; in normal flow, over a light section, that is white on off-white and the menu
   is simply invisible. It was, on /404test.

   So off the overlay, the menu is the body colour and picks up the site's hover teal.
   The header's white logo badge still reads on a light section, and the Request A Quote
   button is teal with white text either way, so neither needs a counterpart here.

   `!important` on the colour is unavoidable and is the sanctioned kind: the white comes
   from the block's own `has-white-color` class, which core emits with `!important`. The
   selector is scoped to the header so it cannot leak. */
body:not(.has-overlay-header) header.wp-block-template-part .wp-block-navigation a,
body:not(.has-overlay-header) header.wp-block-template-part .wp-block-navigation__responsive-container-open {
	color: var(--wp--preset--color--black) !important;
}

body:not(.has-overlay-header) header.wp-block-template-part .wp-block-navigation a:hover,
body:not(.has-overlay-header) header.wp-block-template-part .wp-block-navigation a:focus-visible {
	color: var(--wp--preset--color--brand-teal-dark) !important;
}

/* The hamburger has to be included, and it is easy to miss because it only shows below
   1100px: it is a button rather than a link, and its glyph is an svg with
   `fill: currentColor`, so it was a white icon on an off-white page with nothing to
   click. The close button inside the overlay is deliberately NOT touched, since the
   overlay has its own light background and its own overlayTextColor. */
body:not(.has-overlay-header) header.wp-block-template-part .wp-block-navigation__responsive-container-open svg {
	fill: currentColor;
}

/* Because the header above is out of flow, the page's first section has to
   reserve the space it would have occupied, or the hero content sits under the
   menu. That reserved space used to be typed into the hero cover as an inline
   padding-top, which was wrong three ways: an editor building a new page had to
   remember to retype it, changing the header meant editing every page, and an
   inline style beats every stylesheet rule, so no media query could ever adjust
   it. That last one is why the mobile fix below had to shrink the logo to fit
   the padding instead of shrinking the padding to fit the logo.

   So the space is reserved here instead, keyed on the same has-overlay-header
   context that lifts the header out of flow. The two rules are a matched pair:
   there is no page where one applies and the other does not, so an editor has
   nothing to remember and page content carries no header-related value at all.

   The amount is --wp--custom--header-overlay-height, declared once in
   theme.json under settings.custom. Change the header's size and every page
   follows from that single value.

   :first-child rather than a hero class, deliberately. A class is something an
   editor can forget, and forgetting it puts the menu on top of the headline,
   which is the failure this rule exists to prevent. Whatever is first on the
   page is what the header overlaps, whether it is a cover, a group or an image.

   Done 2026-08-03: this pair is keyed on has-overlay-header, not on a page. To
   bring another page in, give it a cover as its first block. Do not add a second
   context selector to one rule without adding it to the other.

   `.anzac-project-banner` is named explicitly because a single project's hero comes
   from templates/single-project.html rather than from post content, so it is not
   inside .wp-block-post-content and :first-child cannot reach it. Without it the
   banner kept the cover's own 60px padding and the header landed on the title. Any
   future template that opens with its own cover needs adding here too, to BOTH
   rules.

   Note `main > .wp-block-post-content:first-child`, not a bare
   .wp-block-post-content. On single-project.html the post content sits BELOW the
   banner, holding the gallery, and a bare selector reserved the full header height
   above the first gallery row: 257px of white in the middle of the page. Requiring
   post content to be main's own first child says what the rule actually means, which
   is "reserve space above whatever is at the top of the page".

   The padding must land on the first CHILD rather than on post content itself,
   because that child usually carries a full bleed background which has to fill the
   reserved area. Moving it up to the wrapper leaves a white band above the hero. */
.has-overlay-header main > .wp-block-post-content:first-child > :first-child,
.has-overlay-header .anzac-project-banner {
	padding-top: var(--wp--custom--header-overlay-height);
}

/* Below the 782px boundary the logo drops to 70px and the header is about 60px
   shorter, so it reserves the smaller of the two tokens. Both numbers live in
   theme.json so this rule only chooses between them, it does not restate a value.

   Both tokens are clamps rather than the bare fluid expression they used to be.
   The floor is what stops the collision: measured 2026-08-03 the header is 202 to
   217px tall above 782px and 155 to 160px below it, while the old expression fell
   to 135px at 360px wide and 188px at 900px. The floors are 237 and 180, which
   clear the tallest header in each band by roughly 20 to 35px. The upper end is
   unchanged, so the comp's proportions at 1600px and above are exactly as before.

   These two numbers are the tuning knob for how far the hero content sits below
   the header. Change them in theme.json, nowhere else. */
@media (max-width: 781px) {
	.has-overlay-header main > .wp-block-post-content:first-child > :first-child,
	.has-overlay-header .anzac-project-banner {
		padding-top: var(--wp--custom--header-overlay-height-mobile);
	}
}

/* Keeping the header on one row below 600px. With the hamburger showing, the three
   children measure 128 + 24 + 163 = 315px, and two spacing|50 gaps add about 42px,
   so it needs 357px but a 390px viewport only offers 348px inside the gutter. It
   wrapped, the header grew from 155px to 228px, and it landed on the hero heading.

   Jeff asked to keep Request A Quote at every width, so the space comes from the
   three paddings instead: a tighter row gap, a slimmer button, and less air around
   the logo. The children then need 297px, and the row offers 0.97vw - 30, so a
   single row holds down to about 340px. Verified clear at 360, 375, 390 and up.
   At 320px it still wraps and the header lands on the heading by 36px. Left alone:
   320px is a first generation iPhone SE and nothing current is that narrow.

   All three are cosmetic values to tune, not structural. Widen them back once a
   mobile comp exists and the header is designed rather than fitted.

   The button and badge paddings need !important because both are block attributes
   and WordPress writes them as inline styles, which no stylesheet selector can
   outrank. Same reason the .custom-logo width rule below carries it. The row gap
   does not, because block gap is emitted as a .wp-container-* class rule.

   The alternative was to strip those paddings out of the header template part and
   express them here, the way the hero's reserved space was handled above. Not done
   deliberately: those two are per block design values from the comp that an editor
   should still be able to change in the inspector, whereas the hero's padding was a
   header measurement that never belonged in content. */
@media (max-width: 600px) {
	header.wp-block-template-part .wp-block-group.is-layout-flex {
		gap: 10px;
	}

	header.wp-block-template-part .wp-block-button__link {
		padding-left: 14px !important;
		padding-right: 14px !important;
	}

	header.wp-block-template-part .anzac-logo-badge {
		padding-left: 20px !important;
		padding-right: 20px !important;
	}
}

/* The logo sits on a white pentagon that hangs below the header and comes to a
   point at the bottom centre, like a pennant over the hero. Figma node 0:6
   "Rectangle 76", whose exact path is:

     M0 0 H169 V154 L84.5 172.5 L0 154 V0 Z

   Converted to percentages of the 169 x 172.5 shape, so it scales with the block
   rather than being pinned to the comp's pixel size: the flat bottom edge is at
   154 / 172.5 = 89.28%, and the point is at 50% across.

   A clip-path rather than the exported SVG because it needs no asset, no media
   library entry and no environment-specific URL, and it stays crisp at any size.
   A class is unavoidable here: no block support expresses clip-path. Same
   reasoning as .anzac-glass-card above. */
.anzac-logo-badge {
	clip-path: polygon(0 0, 100% 0, 100% 89.28%, 50% 100%, 0 89.28%);
}

/* No mobile comp exists yet. Below the WordPress mobile boundary the nav
   collapses to a single toggle, so the header's height is set by the logo card.
   Shrink the logo so the header still fits inside the hero's reserved padding
   (137px at 375px wide) instead of overlapping the hero heading. Replace this
   whole block when a mobile comp lands. */
@media (max-width: 781px) {
	.has-overlay-header header.wp-block-template-part .custom-logo {
		width: 70px !important;
		height: auto !important;
	}
}

/* Small phones, below 400px: Jeff's numbers, to stop Request A Quote wrapping.

   The 600px block above bought about 60px and got a single row down to roughly 340px.
   Below that it still wrapped, and the header grew from 153px to 216px and landed on the
   hero heading. Measured at 320px: the three children need 276.8px plus two 10px gaps,
   296.8px against the 280.4px the row actually offered. Short by 16px.

   Two changes, both Jeff's call, and together they free about 42px:

   - The root group's horizontal padding goes to 10px, from the 20.85px that
     `spacing|50` resolves to at this width. Worth 21.7px.
   - The logo card goes to 90px wide, from 110px. Worth 20px. The logo INSIDE it gets
     bigger rather than smaller, 78px against 70px, by taking the card's inline padding
     down from 20px to 6px. So the mark reads larger on the smallest screens even though
     its card is narrower, which is the opposite of what shrinking a header usually does.

   Both need `!important` for the same reason: they are block attributes, so WordPress
   writes them as inline styles and no plain selector can outrank one. An `!important`
   author declaration does beat a non-important inline style, which is what makes this
   work at all.

   `.custom-logo` is listed twice, and this block sits AFTER the 781px logo rule above
   rather than with the other header rules, both for the same reason. A media query adds
   no specificity, so the 781px rule's `.has-overlay-header` scope gives it (0,3,1) and it
   beat this block outright when this sat higher up the file: measured, the logo stayed
   70px and the card came out 82px instead of 90px. The first selector covers pages with
   no overlay header (404, blog), the second ties the overlay rule and now wins on source
   order. Do not move this block back above that one.

   Numbers to re-check if these are tuned again: the card is 6 + 78 + 6, and the header's
   height is 25 + 78 + 38 from the card's own inline vertical padding, so a taller logo
   pushes the header down and it must stay under the 180px mobile floor in
   `--wp--custom--header-overlay-height`. */
@media (max-width: 399.98px) {
	header.wp-block-template-part > .wp-block-group {
		padding-left: 10px !important;
		padding-right: 10px !important;
	}

	header.wp-block-template-part .anzac-logo-badge {
		padding-left: 6px !important;
		padding-right: 6px !important;
	}

	header.wp-block-template-part .custom-logo,
	.has-overlay-header header.wp-block-template-part .custom-logo {
		width: 78px !important;
		height: auto !important;
	}
}




/* BlockStyle Variations to use (see functions.php) */

.is-style-bullets-arrow-red li {
	list-style: none !important;
	padding-left: 30px;
	margin-bottom: 25px !important;
}
.is-style-bullets-arrow-red li::before {
	content: "";
	border-top: 10px solid transparent;
	border-left: 10px solid var(--wp--preset--color--teal);
	width: 0;
	height: 0;
	display: block;
	border-bottom: 10px solid transparent;
	position: absolute;
	margin-left: -30px;
	margin-top: 4px;
}

.is-style-heading-underline::after,
.is-style-heading-alt-underline::after {
	content: "";
	display: block;
	height: 5px;
	margin-top: 16px;
}
.is-style-heading-underline::after {
	width: 45px;
	background: #fff;

}
/* Dark variant, for the footer column headings on the off-white background.
   The two rules above are white, so they only work on dark sections. */
.is-style-heading-underline-dark::after {
	content: "";
	display: block;
	height: 3px;
	width: 32px;
	margin-top: 16px;
	background: var(--wp--preset--color--black);
}
.is-style-heading-alt-underline::after {
	width: 65px;
	background: rgba(255,255,255,0.7);
}

/* The teal brush stroke under a section heading. Replaced four hand placed
   core/image blocks on page 10, so the mark is now a property of the heading
   rather than content an editor has to remember to insert.

   mask-image rather than background-image, deliberately. A mask only reads the
   SVG's alpha, so the colour comes from the palette token instead of from the
   file. That means recolouring is a theme.json change, and it sidesteps the trap
   noted under the header section, where recolouring an uploaded SVG keeps the
   same URL and browsers serve the old colour from cache.

   The asset stays in assets/images/ rather than the media library. It is the one
   category the theme rule carves out: an SVG consumed by a block style variation
   as CSS is design system plumbing, not something anyone inserts into a page. A
   relative path also keeps it environment independent, unlike an attachment URL.

   Intrinsic size is 162.606 x 16.1803, hence the aspect-ratio. Width is the comp's
   163px. */
.is-style-brush-underline::after {
	content: "";
	display: block;
	width: 163px;
	max-width: 100%;
	aspect-ratio: 162.606 / 16.1803;
	/* The four hand placed copies used 7, 5, 13 and 16px. Jeff called that designer
	   oversight rather than intent and picked the tightest, so 5px is the standard
	   now and the other three were the drift. */
	margin-top: 5px;
	background-color: var(--wp--preset--color--brand-teal);
	-webkit-mask: url("../images/heading-brush-underline.svg") no-repeat left center / contain;
	mask: url("../images/heading-brush-underline.svg") no-repeat left center / contain;
}

/* Draw it left to right when it scrolls into view.

   `scripting: enabled` rather than a `no-js` class on <html>. It means the hidden
   start state only exists when JavaScript can actually run, so there is no inline
   head script and no flash of a fully drawn stroke that then collapses. Without
   scripting the stroke simply renders complete, which is the correct fallback for
   a decorative mark.

   clip-path rather than transform: scaleX(). A scale stretches the brush stroke,
   so it reads as the mark being squashed and released. A clip wipe reveals it at
   its true size, which reads as the stroke being drawn. Neither triggers layout.

   The `is-inview` class is added by assets/js/functions.js. */
@media (scripting: enabled) {
	.is-style-brush-underline::after {
		clip-path: inset(0 100% 0 0);
		transition: clip-path 900ms cubic-bezier(0.22, 0.61, 0.36, 1);
	}

	.is-style-brush-underline.is-inview::after {
		clip-path: inset(0 0 0 0);
	}
}

/* Respect the OS setting. The stroke still appears, it just does not animate. */
@media (prefers-reduced-motion: reduce) {
	.is-style-brush-underline::after {
		clip-path: none;
		transition: none;
	}
}

/* The stroke follows the heading's alignment. The pseudo element is a fixed width
   block, so without this it stays hard left under a centred heading. Added for the
   About page's closing "Ready to Bring Your Vision to Life?", Figma node 383:1880,
   where the comp centres it under the text. */
.is-style-brush-underline.has-text-align-center::after {
	margin-left: auto;
	margin-right: auto;
}

.is-style-brush-underline.has-text-align-right::after {
	margin-left: auto;
}

/* The two check marked list forms from the service page comps, Figma nodes
   275:2740 (cards) and 275:2715 (inline). Both are registered in functions.php.

   One asset for both: the comp exports the glyph at 22px and 27px and the path
   data is identical, so it is masked at two sizes rather than shipped twice.

   mask-image rather than background-image, same reasoning as brush-underline
   above: a mask only reads the SVG's alpha, so the colour comes from the palette
   token instead of the file, recolouring becomes a theme.json change, and it
   sidesteps the cached-SVG-colour trap noted under the header section. The tick
   is a knockout in the path rather than a painted shape, so the mask leaves it
   transparent and whatever is behind the item shows through it. That is white in
   both variations, the pill in one and the section in the other.

   The asset stays in assets/images/. An SVG consumed by a block style variation
   as CSS is the one category the no-content-in-the-theme rule carves out. */
.is-style-check-list-card,
.is-style-check-list-inline {
	list-style: none;
	padding-left: 0;
}

.is-style-check-list-card li::before,
.is-style-check-list-inline li::before {
	content: "";
	/* Never let the glyph shrink or stretch when the label wraps. */
	flex: 0 0 auto;
	background-color: var(--wp--preset--color--brand-teal);
	-webkit-mask: url("../images/icon-check-teal.svg") no-repeat center / contain;
	mask: url("../images/icon-check-teal.svg") no-repeat center / contain;
}

/* Cards. Pill widths in the comp vary with the label (407 to 488), so the item
   shrink wraps rather than filling the column. 53px tall, 45px radius, and the
   18px gap is the comp's 71px pitch less the 53px height.

   The list itself is a wrapping row, not a stack. In a narrow column no two pills
   fit side by side so they stack, which is how the Renovations and Residential
   Additions lists render; given a wide column they flow two or three to a row,
   which is what the Design-Build comp shows. Same variation, and the arrangement
   comes from the available width rather than from a second variation. Set a measure
   on the list when a comp's grouping needs pinning, as with the inline variant.

   align-items: flex-start so a pill whose label wraps does not stretch its row
   mates, which keeps every pill's height content driven as it was when the list
   was a stack.

   flex on the item rather than an absolutely positioned ::before, so a label that
   wraps on a narrow screen grows the pill instead of overlapping the glyph. The
   comp's 16px inset and 49px text start come out as 16 + 22 + 11. */
.is-style-check-list-card {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 18px;
}

.is-style-check-list-card li {
	display: flex;
	align-items: center;
	gap: 11px;
	width: fit-content;
	max-width: 100%;
	/* border-box, or min-height applies to the content box and the 30px of vertical
	   padding lands on top of it: the pill measured 83px instead of 53px. */
	box-sizing: border-box;
	min-height: 53px;
	padding: 15px 24px 15px 16px;
	/* The comp's leading on these labels is normal, about 1.22, and that is what
	   makes the geometry land: 15 + 22 + 15 is 52, so the 53px floor decides the
	   height and the pitch comes out at the comp's 71px. Inheriting the theme's 1.4
	   body leading instead pushed every pill 2px over the floor and the rhythm with
	   it. A ratio rather than a fixed px because the font size is fluid. */
	line-height: 1.22;
	border-radius: 45px;
	background-color: var(--wp--preset--color--white);
}

.is-style-check-list-card li::before {
	width: 22px;
	height: 22px;
}

/* Inline. A wrapping row: three items sit on the first line and the fourth wraps,
   which is a width behaviour rather than a fixed grouping, so it stays correct as
   labels are edited. Comp column gap 34, row gap 18 (45px pitch less 27px). */
.is-style-check-list-inline {
	display: flex;
	flex-wrap: wrap;
	column-gap: 34px;
	row-gap: 18px;
}

.is-style-check-list-inline li {
	display: flex;
	align-items: center;
	gap: 12px;
	margin: 0;
}

.is-style-check-list-inline li::before {
	width: 27px;
	height: 27px;
}

.is-style-opening-min-height {
	min-height: 630px;
}


@media (min-width: 960px) {
	.is-style-faux-col .wp-block-cover__image-background {
		left: 23% !important;
		object-fit: contain !important;
	}
}



/* Custom Block Overrides */

/* Breadcrumbs are `core/breadcrumbs`, styled further up this file with
   `icon-breadcrumb-home.svg` and `icon-breadcrumb-separator.svg` as masks. Nothing here. */




/* Custom Shortcodes Overrides */
/* -none- */
