/*
Theme Name:   Brotchie's Barbershop
Description:  GeneratePress child theme for a barbershop. Palette and
              typography set in theme.json; all venue values render
              through Block Bindings.
              Registers no post types - those belong to nlv-core, so that a
              theme switch cannot take the content with it.
Author:       NovelLogic
Author URI:   https://novellogic.ca
Template:     generatepress
Version:      0.1.0
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 8.1
License:      GPL-2.0-or-later
License URI:  https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  nlv-brotchie
*/

/* ---------------------------------------------------------------------------
 * NO Theme URI HEADER, DELIBERATELY.
 *
 * A Theme URI here would spell out the repository host, owner account and repo
 * name. The owner account is a tier-identity needle in the checker: it is the
 * short hop from a shipped file to the client. Demo 1's style.css omits this
 * header for the same reason. Do not add one. (This comment does not quote the
 * account name either - writing the needle into the file to explain why the
 * needle is absent would defeat the point and trip the checker.)
 *
 * THE NAME IS LOCKED. `brotchie` IS NO LONGER A PLACEHOLDER.
 *
 * It was one until the name gate ran. It passed - BC's own corporate registry
 * returns no registered organisation by that name, and nothing trades on
 * Brotchie Ledge, the reef off the Inner Harbour the shop is named after.
 * Identifiers here are `brotchie_*` / `BROTCHIE_*` / `.brotchie-*`, text
 * domain and theme directory `nlv-brotchie`, and none of them are expected to
 * change again.
 *
 * Theme Name renders the demo brand, the way demo 1 renders its own
 * demo brand. Note what that means: this string is a BUSINESS NAME, not a code
 * identifier, and it is attached to a business that does not exist. The
 * disclosure is not optional - see inc/layout.php.
 * ------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
 * WHAT LIVES HERE AND WHAT LIVES IN theme.json
 *
 * theme.json is the single source of design truth: palette, type scale,
 * spacing, the content/wide measures and the @font-face rules. Everything it
 * can express belongs there, because those values reach the block editor as
 * well as the front end, so an editor sees what a visitor will see.
 *
 * This file holds only what theme.json cannot express: the parent-theme
 * container reset, the full-bleed fix, the service cards, the queue chip, the
 * disclosure band and the accessibility floor.
 *
 * ASSET VERSIONING: do not add a hand-written version constant for this file.
 * functions.php versions it with filemtime(). Demo 1 shipped a hand-bumped
 * constant alongside Cache-Control: max-age=31536000 and a layout fix appeared
 * not to deploy for a year.
 *
 * WHAT IS INHERITED FROM DEMO 2, AND WHY
 *
 * Sections 1, 1b, 4, 5, 5b, 9 and 11 are demo 2's, carried deliberately.
 * They are not style - they are measured fixes for GeneratePress behaviour
 * that this site has identically. Rediscovering them would cost days and they
 * would be rediscovered wrong. Where this theme diverges it is marked and
 * argued, so the difference reads as a decision rather than as drift.
 * ------------------------------------------------------------------------ */

:root {
	--brotchie-band: clamp( 48px, 6vw, 80px );
	--brotchie-hairline: 1px;
	--brotchie-radius: 2px;
}


/* ===========================================================================
 * 1. PARENT CONTAINER RESET
 *
 * GeneratePress wraps the content in a white, padded, max-width container.
 * Full-bleed colour bands break out of it horizontally, but the container
 * still paints a white frame across the top of every page, so the page reads
 * as two design systems fighting: a white document with coloured panels
 * punched through it.
 *
 * This site has no document frame. The page ground is `bone` and the bands sit
 * on it edge to edge.
 *
 * Selectors are matched at GeneratePress's own specificity. `.one-container
 * .site-content` is (0,2,0) and beats a bare `.site-content`; demo 1 lost that
 * tie once already and the fix appeared not to work.
 * ======================================================================== */

body,
.site,
.site-content,
.one-container .site-content,
.separate-containers .site-main,
.separate-containers .inside-article,
.one-container .inside-article,
.inside-article,
.entry-content {
	background-color: transparent;
	box-shadow: none;
	border-radius: 0;
}

body {
	background-color: var(--wp--preset--color--bone);
}

.one-container .site-content,
.separate-containers .site-main,
.separate-containers .inside-article,
.one-container .inside-article {
	padding: 0;
	margin: 0;
}

/* A full-bleed band that opens a page must meet the header, not float below a
 * residual strip of page background. */
.entry-content > .alignfull:first-child,
.entry-content > .wp-block-group.alignfull:first-child {
	margin-top: 0;
}

/* Belt-and-braces only. The real removal of the parent theme's page title is
 * the `generate_show_title` filter in inc/layout.php - killing it there keeps
 * it out of the DOM, which matters because the hero display line is already
 * the page's h1 and a second one is an accessibility and SEO fault, not just a
 * visual one. This covers the case where the filter stops taking after a
 * parent-theme update. */
.page .entry-header .entry-title,
.page .page-header-content .page-title {
	display: none;
}


/* ===========================================================================
 * 1b. FULL-BLEED WITHOUT vw  -  the horizontal-scroll fix
 *
 * CARRIED FROM DEMO 2 UNCHANGED. Do not re-derive this; it was measured in a
 * headless render, not inferred, and this site has both causes identically.
 *
 *   (a) .no-sidebar .entry-content .alignfull uses
 *       `margin-left: calc(-100vw / 2 + 100% / 2); max-width: 100vw`.
 *       100vw INCLUDES the classic scrollbar; the content box does not. On
 *       demo 2 at 1920 the band computed 1920px inside a 1905px client width -
 *       15px of overflow on every page that has a band, which is all of them.
 *
 *   (b) .entry-content .alignwide uses `width: calc(100% + 80px)` - GP's
 *       legacy assumption of 40px container padding. It CLIPPED TEXT on demo
 *       2: a contact column ran past the viewport edge mid-sentence.
 *
 * THE FIX REMOVES vw FROM THE MATH ENTIRELY by removing the reason it was
 * needed. The 1200px cap lives on the outer wrapper `.site.grid-container`.
 * Free that and .entry-content is already viewport-width, so a full-bleed band
 * needs no negative margin at all - just `width: 100%`.
 *
 * Header, nav and footer are unaffected: each carries its OWN .grid-container
 * (.inside-header, .inside-navigation, .inside-site-info) with its own cap.
 *
 * NOT DONE, deliberately: `body { overflow-x: clip }`. That hides (a) and
 * leaves (b)'s clipped sentence clipped - the text would still be unreachable,
 * just without a scrollbar to prove it.
 *
 * Doubled class selectors are for specificity: GP's rules are (0,3,0) and
 * (0,2,0), and matching them exactly would leave the result to source order.
 * ======================================================================== */

.site.grid-container {
	max-width: none;
}

.entry-content .alignfull.alignfull {
	margin-left: 0;
	margin-right: 0;
	width: 100%;
	max-width: none;
}

.entry-content .alignwide.alignwide {
	width: auto;
	max-width: var(--wp--style--global--wide-size, 1360px);
	margin-inline: auto;
}


/* ===========================================================================
 * 2. TYPOGRAPHY
 *
 * The scale lives in theme.json. What is here is the handful of relationships
 * it cannot express.
 *
 * LIBRE CASLON DISPLAY IS DISPLAY ONLY. It is a high-contrast display cut: the
 * hairline strokes that make it read as confident at 100px disappear at 16px,
 * so it carries display lines, section heads and service names, and nothing
 * else. Navigation, body, buttons, labels, prices, durations and every piece
 * of UI text are Archivo. That split is the reason the site ships two families
 * rather than one, and it is the same rule demo 2 applies to Syne.
 * ======================================================================== */

body,
.main-navigation a,
.menu-toggle,
button,
input,
select,
textarea,
.wp-element-button {
	font-family: var(--wp--preset--font-family--body);
}

.brotchie-display,
.entry-content h1,
.entry-content h2,
.brotchie-service__name {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 400;
}

/* Caslon's sidebearings are generous; at display sizes the default tracking
 * reads loose. Tightened here rather than in theme.json because it is
 * size-dependent, not a global property of the face. */
.brotchie-display {
	letter-spacing: -0.015em;
	line-height: 1.02;
}

/* Eyebrows are Archivo, NOT Caslon. A letterspaced high-contrast serif at 12px
 * is the exact thing that reads as costume-vintage, which the brief rules out. */
.brotchie-eyebrow {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--slate);
}


/* ===========================================================================
 * 3. THE HEADER
 *
 * WARNING - THE WORDMARK IS NOT SOLVED YET. There is no tracking solve in this
 * file and there must not be a guessed one: demo 1's values only reproduce at
 * one specific base tracking, and eyeballing them produced a different mark.
 * When the wordmark lands it goes in inc/branding.php with its solve recorded.
 *
 * WARNING - THE NAV BREAKPOINT IS ALSO UNSOLVED. No constant exists yet. Demo
 * 1's 914 is non-transferable - it was measured as that site's nav width plus
 * its container padding - and demo 2's 980 is flagged by demo 2's own comment
 * as an unverified placeholder. Measure this header.
 * ======================================================================== */

.site-header {
	background-color: var(--wp--preset--color--bone);
	border-bottom: var(--brotchie-hairline) solid var(--wp--preset--color--hairline-l);
}

.main-navigation a,
.menu-toggle {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--wp--preset--color--ink);
}


/* ===========================================================================
 * 4. ONE VERTICAL RHYTHM FOR THE FULL-BLEED BANDS
 *
 * CARRIED FROM DEMO 2. On that site every band's padding was authored per
 * block in post_content, so it drifted - and worse, the scale was FIXED, so a
 * phone got the same 160px between bands as a 1440 desktop. The clamp is the
 * fix for that, not just for the drift.
 *
 * WARNING - THIS RULE IS INERT UNTIL THE BUILDER STOPS AUTHORING PADDING. An
 * inline style attribute beats any stylesheet rule. Shipping this first
 * changes nothing; the builder run then hands control over. The two halves can
 * deploy in either order with no broken state in between.
 *
 * THE FOOTER GAP IS THE SAME RULE, NOT A SEPARATE PATCH. Putting the space on
 * the footer means it no longer depends on what the last block happens to be.
 * ======================================================================== */

.entry-content > .alignfull {
	padding-block: var(--brotchie-band);
}

.site-info {
	padding-block-start: var(--brotchie-band);
}


/* ===========================================================================
 * 5. THE PHOTO CARD
 *
 * Structure is demo 2's and the reasoning below is why. The RADIUS is not, and
 * that is the one deliberate divergence in this file.
 *
 * Demo 2's card is heavily rounded - 32px, 48px at desktop - and its own
 * comment says the radius is the identity decision: round everything and it
 * becomes cute. That argument is sound and it is exactly why it does not
 * transfer. Chandlery's register is square and hairline: a chart, a shipping
 * notice, a brass rule. A 48px radius here would read as a different brand
 * wearing this palette. So the card takes --brotchie-radius, the same 2px as
 * the buttons, and the contrast demo 2 gets from round-against-square this
 * site gets from brass-against-ink.
 *
 * WARNING - THE RATIO LIVES ON .brotchie-card, NOT ON .brotchie-slot. That is
 * demo 2's fix and it is load-bearing. The ratio used to sit on the slot,
 * which only the reserved placeholder carries - so the moment a real
 * photograph existed the builder emitted a plain figure, the reserved ratio
 * was silently dropped, and every vertical-rhythm judgement made against the
 * placeholder was wrong.
 *
 * object-fit: cover is explicit because the default is `fill`. With a
 * non-native ratio `fill` silently STRETCHES a photograph instead of cropping
 * it, which is the kind of fault nobody reports because it looks like a bad
 * photograph rather than a bug.
 * ======================================================================== */

.brotchie-card {
	aspect-ratio: var(--brotchie-card-ratio, var(--brotchie-slot-ratio, auto));
}

.brotchie-card img {
	border-radius: var(--brotchie-radius);
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}


/* ===========================================================================
 * 5b. THE RESERVED PHOTO SLOT
 *
 * CARRIED FROM DEMO 2, and it matters more here than it did there, because
 * this site has no photography at all yet - stills are generated and none
 * exist.
 *
 * WARNING - IT IS SCAFFOLDING AND IT SHIPS. Deliberate. Rendering nothing
 * would mean every vertical-rhythm decision on this site is made against a
 * composition missing all of its photographs, and would have to be made again.
 * A visible frame is also self-reporting: it says WHICH shot is missing to
 * anyone looking at the page, without opening the builder.
 *
 * It disappears by itself the moment the attachment exists.
 *
 * Square corners. The frame is a rule, and rules here are square and hairline.
 * ======================================================================== */

.brotchie-slot {
	display: flex;
	align-items: center;
	justify-content: center;
	border: var(--brotchie-hairline) solid var(--wp--preset--color--hairline-l);
	margin-inline: auto;
}

.brotchie-slot__slug {
	font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", Menlo, Consolas, monospace;
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--slate);
}


/* ===========================================================================
 * 6. COLOUR BANDS
 *
 * Text colour is set PER BAND, not left to inherit. Demo 2 learned this the
 * expensive way: its builder emitted a background colour and no text colour,
 * so dark fields inherited near-black body text and whole lines were
 * effectively invisible.
 *
 * Every pairing below is >= 4.5:1, and every ratio is recorded with its
 * provenance in _local/audit/brand.md, generated from theme.json so it cannot
 * drift from what ships.
 *
 *     band      text     ratio
 *     bone      ink      14.48
 *     ink       bone     14.48
 *     slate     bone      8.09
 *
 * WARNING - brass and verdigris ARE NOT BAND GROUNDS. Neither carries body
 * text at any size; brass on bone is 2.23, below even the 3:1 for non-text.
 * They are accents: rules, chips, marks. Their text-safe variants are
 * brass-deep and verdigris-deep, and those only work on bone. See brand.md.
 * ======================================================================== */

.brotchie-band--bone {
	background-color: var(--wp--preset--color--bone);
	color: var(--wp--preset--color--ink);
}

.brotchie-band--ink {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--bone);
}

.brotchie-band--slate {
	background-color: var(--wp--preset--color--slate);
	color: var(--wp--preset--color--bone);
}

/* Secondary text inside a dark band. `slate` is a band ground, not a text
 * colour on one - slate on ink is 1.79. `mist` is the solved answer at 8.81. */
.brotchie-band--ink .brotchie-muted,
.brotchie-band--slate .brotchie-muted {
	color: var(--wp--preset--color--mist);
}

.brotchie-band--bone .brotchie-muted {
	color: var(--wp--preset--color--slate);
}

/* The brass rule. Decoration, never a divider that carries meaning: a
 * separator that has to be perceivable to be understood needs 3:1, and brass
 * on bone is 2.23. For a meaningful boundary use the hairline plus a heading. */
.brotchie-rule {
	border: 0;
	border-top: 2px solid var(--wp--preset--color--brass);
	width: 64px;
	margin-inline: 0;
}


/* ===========================================================================
 * 7. THE SERVICES PAGE
 *
 * THE SPLIT IS BY SERVICE LENGTH, NEVER BY BARBER. Under 45 minutes you walk
 * in; 45 minutes or more you book. One threshold, stated once per group in
 * words, not inferred from the cards and not repeated as a badge on each one.
 *
 * Booking behaviour belongs to the GROUP, which is why the cards are identical
 * in shape and why a duration can sit on every card without implying anything.
 * Only booked cards carry a CTA - a walk-in card with a book button
 * contradicts the whole split.
 *
 * NO JUMP RAIL. Demo 2's rail exists for a long single-page menu; seven
 * services in two groups do not warrant it. Declining it drops the JS file and
 * the sticky-offset custom property with it. That is a decision, not an
 * omission - do not add one back without re-reading this.
 * ======================================================================== */

.brotchie-services__group + .brotchie-services__group {
	margin-block-start: var(--brotchie-band);
}

.brotchie-services__rule {
	font-size: var(--wp--preset--font-size--large);
	max-width: 34rem;
	margin-block: 0.5rem 2rem;
}

/* The 1px grid gap IS the rule between cards - one hairline, not two abutting
 * borders doubling to 2px on interior edges. The cards paint over the gap. */
.brotchie-services__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
	gap: var(--brotchie-hairline);
	background-color: var(--wp--preset--color--hairline-l);
	border: var(--brotchie-hairline) solid var(--wp--preset--color--hairline-l);
}

.brotchie-service {
	background-color: var(--wp--preset--color--bone);
	padding: 1.75rem;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.brotchie-service__name {
	font-size: var(--wp--preset--font-size--xx-large);
	line-height: 1.1;
	margin: 0;
}

.brotchie-service__meta {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	margin-block-start: auto;
	padding-block-start: 1rem;
}

.brotchie-service__duration {
	color: var(--wp--preset--color--slate);
}

/* The price chip. ink on brass measures 6.49:1 - solved as a FILL, not as a
 * text colour, because that is how it is used. */
.brotchie-service__price {
	background-color: var(--wp--preset--color--brass);
	color: var(--wp--preset--color--ink);
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small);
	letter-spacing: 0.02em;
	padding: 0.3rem 0.6rem;
	border-radius: var(--brotchie-radius);
	white-space: nowrap;
}

/* Two services in the walk-in group are deliberately the same cut at the same
 * price under different names. Nothing here should try to collapse, dedupe or
 * visually link them - the repetition is the idea, not a data error. */


/* ===========================================================================
 * 8. THE LIVE QUEUE CHIP
 *
 * This is demo 2's batch chip doing a different job, and reusing it is what
 * pulls in inc/query.php's Query Loop fix rather than rediscovering it.
 *
 * It appears ONCE, in the walk-in group header, because that is the only group
 * the wait is relevant to. On a booked service it would be noise at best and
 * misleading at worst.
 *
 * bone on verdigris-deep measures 6.69:1. verdigris itself is NOT used here -
 * it is 3.98 on ink and 3.63 on bone, decorative either way.
 *
 * WARNING - THE VALUE IS PLUGIN STATE, NOT PRESENTATION. nlv-core decides what
 * the wait is and whether it is stale; this only decides what it looks like.
 * On a demo site the record is pinned, which disables the staleness guard.
 * ======================================================================== */

.brotchie-queue {
	display: inline-flex;
	align-items: baseline;
	gap: 0.5rem;
	background-color: var(--wp--preset--color--verdigris-deep);
	color: var(--wp--preset--color--bone);
	padding: 0.4rem 0.8rem;
	border-radius: var(--brotchie-radius);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
}

/* When the shop is closed there is no queue to report. The chip says so rather
 * than disappearing - an absent chip reads as a page that failed to load. */
.brotchie-queue--closed {
	background-color: var(--wp--preset--color--slate);
}


/* ===========================================================================
 * 9. THE ENQUIRY FORM
 *
 * WARNING - THE HONEYPOT MUST NEVER BE VISIBLE, AND MUST NEVER BE display:none.
 *
 * nlv-core emits it as a real, submittable text input inside
 * <p class="nlv-form__hp" aria-hidden="true"> with tabindex="-1". Its whole
 * mechanism is that a bot fills it in and a person cannot, so it has to stay
 * in the DOM and stay submittable. display:none is the obvious fix, and some
 * bots skip hidden fields, which blunts the trap.
 *
 * So it is clipped instead: present, submittable, zero visible area, label
 * included because the whole <p> is clipped. Left unstyled it renders a
 * labelled "Leave this field empty" box on the page a prospect reads most
 * carefully.
 *
 * CARRIED FROM DEMO 2 VERBATIM. Do not simplify.
 * ======================================================================== */

.nlv-form__hp {
	position: absolute !important;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip: rect( 0 0 0 0 );
	clip-path: inset( 50% );
	white-space: nowrap;
}

.nlv-form__row {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	margin-block: 0 1rem;
}

.nlv-form__row label {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: var(--wp--preset--color--slate);
}

.nlv-form__row input,
.nlv-form__row select,
.nlv-form__row textarea {
	font-size: var(--wp--preset--font-size--medium);
	padding: 0.7rem 0.8rem;
	border: var(--brotchie-hairline) solid var(--wp--preset--color--slate);
	border-radius: var(--brotchie-radius);
	background-color: var(--wp--preset--color--bone);
	color: var(--wp--preset--color--ink);
}

.nlv-form__notice {
	padding: 0.9rem 1rem;
	border-radius: var(--brotchie-radius);
	font-size: var(--wp--preset--font-size--small);
}

.nlv-form__notice--ok {
	background-color: var(--wp--preset--color--verdigris-deep);
	color: var(--wp--preset--color--bone);
}

.nlv-form__notice--err {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--bone);
}


/* ===========================================================================
 * 10. THE DEMO DISCLOSURE BAND
 *
 * THE DISCLOSURE IS NOT OPTIONAL AND NOT DECORATIVE.
 *
 * This site depicts a business that does not exist, at an address that does
 * not exist, with a phone number reserved for fiction. Somebody will find it
 * without the context of a sales conversation - a search result, a shared
 * link, a screenshot. Saying so plainly, on every page, is the difference
 * between a demonstration and a fabricated business listing.
 *
 * For the same reason this site emits NO LocalBusiness schema. Structured data
 * saying "here is a real barbershop at this address with these hours" is a
 * machine-readable assertion that the fiction is a fact, and it is the one
 * thing most likely to be consumed without the human-readable disclosure
 * attached. No aggregate ratings, no reviews, no map embed.
 *
 * WARNING - THIS IS THE VISUAL HALF ONLY. The band's text comes from
 * inc/layout.php on generate_credits, and the machine-readable half is a
 * mu-plugin that does not exist yet. Both live demos had to be retrofitted
 * with one; this site builds it in. Styling the band is not shipping the
 * disclosure.
 * ======================================================================== */

.site-info {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--bone);
}

.brotchie-fineprint {
	color: var(--wp--preset--color--mist);
	max-width: 46rem;
	margin-block-start: 1rem;
}


/* ===========================================================================
 * 10b. THE 404
 *
 * The disclosure has to reach here too. A 404 is a page a stranger lands on
 * from a stale link with no context at all, which is precisely the case the
 * disclosure exists for - and it is the page most likely to be forgotten,
 * because it is not in the page set and nobody reviews it.
 * ======================================================================== */

.error404 .site-content {
	min-height: 40vh;
	display: flex;
	align-items: center;
}

.error404 .brotchie-display {
	font-size: var(--wp--preset--font-size--display-s);
}


/* ===========================================================================
 * 11. ACCESSIBILITY FLOOR
 *
 * 16px minimum anywhere on this site, disclosure band included. Demo 2's
 * benchmark weakness was small low-contrast type, and half of that fix is size
 * rather than colour. Fineprint that cannot be read is not a disclosure.
 * ======================================================================== */

.site-info,
.site-info a,
.brotchie-fineprint,
.brotchie-service__duration {
	font-size: var(--wp--preset--font-size--medium);
}

/* NOT :where(). :where() contributes zero specificity, so the focus ring could
 * be silently beaten by any later single-class rule that happens to set an
 * outline - and a focus indicator that disappears without anyone noticing is
 * the worst kind of accessibility regression. Plain selectors, real weight. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

/* The skip link. GeneratePress ships its own blue on its own light panel; the
 * panel is what makes the chip visible and is deliberately left alone.
 * brass-deep is the branded replacement and it is DARKER than the blue it
 * replaces, so this does not trade contrast for brand.
 * (0,3,1) against GeneratePress's (0,2,0), so it wins on weight. */
a.skip-link.skip-link:focus {
	color: var(--wp--preset--color--brass-deep);
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}


/* ===========================================================================
 * 3b. THE WORDMARK
 *
 * The mark is live text. inc/branding.php emits one element per line, each
 * carrying its own tracking as an inline custom property; this decides
 * everything about how those lines look. Same split as the photo card - the
 * PHP writes which, the stylesheet decides how.
 *
 * WARNING - --brotchie-track IS ZERO ON BOTH LINES UNTIL THE SOLVE IS RUN.
 * Until then the two lines will not optically align. That is expected and it
 * is not a CSS bug; see the docblock in inc/branding.php.
 *
 * --brotchie-track-fix is load-bearing, not tidying. letter-spacing adds space
 * after EVERY glyph including the last, so without the negative compensator
 * each line carries one extra trailing gap and every solved value is wrong by
 * exactly that amount.
 * ======================================================================== */

.brotchie-wordmark {
	display: inline-flex;
	flex-direction: column;
	line-height: 0.94;
}

.brotchie-wordmark__line {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 400;
	font-size: var(--wp--preset--font-size--x-large);
	text-transform: uppercase;
	color: var(--wp--preset--color--ink);
	letter-spacing: var(--brotchie-track, 0);
	margin-right: var(--brotchie-track-fix, 0);
}

/* The site title link wraps the mark. GeneratePress underlines it on hover by
 * default, which on a two-line lockup draws a rule under only the second line
 * and reads as a mistake rather than as a hover state. */
.site-title a:hover .brotchie-wordmark,
.site-title a:focus .brotchie-wordmark {
	text-decoration: none;
}


/* ===========================================================================
 * 12. THE HERO
 *
 * DEMO 2'S COMPOSITION, UNVARIED: eyebrow, one oversized display line, one
 * photo card. Type is the subject and the photograph supports it.
 *
 * There is deliberately very little here. The display line's size comes from
 * theme.json's scale, its face and tracking from section 2, the card from
 * section 5 and the band rhythm from section 4. A hero that needed a large
 * block of its own CSS would mean it had stopped being demo 2's hero, which is
 * the thing this build is explicitly not doing.
 *
 * The eyebrow-to-display ratio lands around 9x, the same relationship both
 * earlier demos use. Scale contrast is what separates a composition from an
 * arrangement, and it is the house move even though the faces differ.
 * ======================================================================== */

.brotchie-hero {
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1.25rem;
}

.brotchie-hero__display {
	font-size: var(--wp--preset--font-size--display-m);
	max-width: 18ch;
	margin: 0;
}

/* The card is alignwide and carries its ratio from the builder. Nothing about
 * its shape is decided here - see section 5. */
.brotchie-hero .brotchie-card {
	width: 100%;
	margin-block-start: 1rem;
}

/* The CTA is the only button above the fold, so it takes the ink fill from
 * theme.json's button element styles unchanged. No hero-specific variant:
 * a second button treatment that exists only in the hero is how a design
 * system starts having exceptions. */
