/* findpostcode.com.au theme remap. Loaded after main-min.css and tokens.css in html_head.ascx.
   This is a "shadow" layer, not an edit to main.css: main.css is compiled Sass output with no
   source tree in this repo, so editing it directly would silently be lost on any future
   recompile-from-source. Rules here reference tokens.css custom properties and win on normal
   cascade order (same selector, later file, no !important needed) - see the rollout plan for the
   full rationale. Grows phase by phase; never intended as a one-shot rewrite of main.css. */

/* --------------------------------------------------------------------------
   Shared chrome - dark mode for the page background and the header's white top
   bar. Shadows main.css:1266 (body) and main.css:2304 (.header). The dark navy
   .header__nav band and .footer (main.css:2324, :4768) are left as-is - they're
   already #2f353e/dark, which reads fine unchanged in both light and dark mode,
   so no shadow is needed there. Deeper per-component dark-mode coverage (island
   cards, admin tables/forms) is Phase 4/5's job, not this shared-chrome phase -
   toggling dark mode now correctly darkens the page shell; content areas that
   haven't been touched yet keep their current light colours until they are.
   -------------------------------------------------------------------------- */

/* The two full-bleed blocks (.site-nav__group--primary and .marquee) use the standard
   `width: 100vw; margin-left: calc(50% - 50vw)` recipe. 100vw counts the classic scrollbar but
   100% does not, so each overflows by half the scrollbar width - measured as a constant 8px of
   horizontal document scroll. `clip` rather than `hidden`: hidden would make this a scroll
   container and break the sticky header; clip just trims. Ignored by browsers that lack it,
   which leaves today's behaviour rather than a worse one. */
html { overflow-x: clip; }

body { background-color: var(--bg); color: var(--ink); }
.header { background-color: var(--surface); }

/* Shadows main.css:9873 (.txt-input, textarea, select) - those are hardcoded
   background:#fff with no explicit text colour, so once body's own colour
   became var(--ink) above, dark mode would otherwise render light text on a
   still-white input: invisible. This is the admin-wide form field style. */
.txt-input,
textarea,
select {
    background: var(--surface);
    color: var(--ink);
    border-color: var(--border);
}

/* Shadows main.css:7426 (.island-body) - the generic admin "card" panel (organisation detail,
   note forms, message centre, etc.). Explicit surface background rather than relying on it to
   inherit the page background, so cards keep reading as distinct panels in dark mode too. */
.island-body {
    background-color: var(--surface);
    border-color: var(--border);
}

/* Shadows main.css:4768/4773 (.footer) - already dark navy in both modes, this only repoints the
   literal hex at the token so it stops drifting from the palette if that colour is ever retuned. */
.footer { background-color: var(--navy); }

/* --------------------------------------------------------------------------
   WCAG 2.2 AA contrast fixes - all three measured 4.19:1 against the 4.5:1
   requirement for body-size text; --accent-strong (#b03509) measures 6.3:1.
   See www/style-guide/theme-preview/README.md for the full measurement.
   -------------------------------------------------------------------------- */

/* Shadows main.css:1323 (a{color:#dd480e}) - the base link colour, so this is
   every body-copy link on the site. */
a { color: var(--accent-strong); }

/* Shadows main.css:3638 (.search-results-box .search-results .search-results-row .h4) */
.search-results-box .search-results .search-results-row .h4 { color: var(--accent-strong); }

/* Shadows main.css:10427 (.btn-main background-color) */
.btn-main { background-color: var(--accent-strong); }

/* --------------------------------------------------------------------------
   New inline-SVG icon sizing (controls/global/icons.ascx). Coexists with the
   older .icon.icon__* CSS-sprite system already in main.css - each call site
   migrates to this when it's next touched, not as a mass find-replace.
   -------------------------------------------------------------------------- */

svg.icon, .btn__icon, .faq-card__chevron, .explain-card__icon svg {
    width: 1.15rem;
    height: 1.15rem;
    flex: 0 0 auto;
}

/* --------------------------------------------------------------------------
   Shared CTA button (controls/global/CtaButton.ascx). Ported from theme-preview.css.
   -------------------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: 2.75rem;
    padding: var(--sp-3) var(--sp-5);
    border: 1px solid transparent;
    border-radius: var(--radius);
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    transition: background-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

/* Each of these carries :visited purely for specificity. main-min.css has
   `a:visited { color: #f2642c }`, which is 0,1,1 and beats a bare component class at 0,1,0 -
   so once somebody had followed the link, the primary button rendered #f2642c on #b03509
   (1.98:1) instead of white on #b03509 (6.25:1). It only appears for visited links, which is
   why it survives a fresh browser profile. Adding :visited takes these to 0,2,0. */
.btn--primary,
.btn--primary:visited { background: var(--accent-strong); color: var(--btn-primary-ink); }
.btn--primary:hover { background: var(--btn-primary-hover-bg); color: var(--btn-primary-ink); }

.btn--ghost,
.btn--ghost:visited { background: transparent; border-color: var(--border); color: var(--ink); }
.btn--ghost:hover { background: var(--surface-sunken); border-color: var(--ink-muted); }

/* Row-level actions inside a table, where a full-size button is too heavy.
   Kept at 2.25rem rather than the 2.75rem elsewhere: these sit inside a table
   row that is already a large target, and the row does not need the height. */
.btn--sm {
    min-height: 2.25rem;
    padding: var(--sp-1) var(--sp-3);
    font-size: var(--step--1);
}

/* --------------------------------------------------------------------------
   Tool cards (Default.aspx's "tools" section - whole-card-clickable, icon +
   heading + body + "more" link). Ported from theme-preview.css. Single-use
   today, so plain markup + these classes rather than a dedicated control.
   -------------------------------------------------------------------------- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
    gap: var(--sp-5);
    margin-top: var(--sp-6);
}

.card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}

.card__link,
.card__link:visited { color: inherit; text-decoration: none; }
.card__link h3,
.card__link h2 { margin: 0; }
.card__link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card__link:hover h3,
.card__link:hover h2 { color: var(--accent-strong); text-decoration: underline; text-decoration-thickness: 2px; }

.card:has(.card__link:hover) { box-shadow: var(--shadow-lift); transform: translateY(-2px); border-color: var(--accent); }

.card:has(.card__link:focus-visible) {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-color: var(--focus);
}

@supports selector(:has(*)) {
    .card__link:focus-visible { outline: none; }
}

.card p { color: var(--ink-muted); margin: 0; }

.card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: var(--radius);
    background: var(--accent-quiet);
    color: var(--accent-strong);
}

.card__icon svg { width: 1.75rem; height: 1.75rem; }

.card__more {
    margin-top: auto;
    font-weight: 700;
    font-size: .9em;
    color: var(--accent-strong);
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
}

.card__more svg { width: .9rem; height: .9rem; }

/* --------------------------------------------------------------------------
   Explain card (controls/global/ExplainCard.ascx). Ported from theme-preview.css.
   -------------------------------------------------------------------------- */

.explain-cards {
    display: grid;
    gap: var(--sp-5);
    margin-top: var(--sp-6);
}

.explain-card {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--sp-2) var(--sp-5);
    align-items: start;
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.explain-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    flex: 0 0 auto;
    border-radius: var(--radius);
    background: var(--accent-quiet);
    color: var(--accent-strong);
}

.explain-card__icon svg { width: 1.75rem; height: 1.75rem; }

.prose { max-width: var(--measure); }
.prose h3 { margin-top: var(--sp-7); font-size: 1.2em; }
.prose dfn { font-style: normal; font-weight: 700; border-bottom: 1px dotted var(--ink-muted); }

.explain-card__body.prose { max-width: none; }
.explain-card__body.prose p { max-width: none; }
.explain-card__body.prose h3:first-child { margin-top: 0; }

@media (max-width: 30rem) {
    .explain-card { grid-template-columns: 1fr; }
}

/* tabindex on the wrapper makes an overflowing table keyboard-scrollable, so it needs its own focus
   ring. No max-width - full stretch to match the explain-cards above it. */
.table-scroll {
    margin-top: var(--sp-7);
    overflow-x: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.table-scroll:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.data-table { width: 100%; border-collapse: collapse; font-size: .9em; }

.data-table caption {
    padding: var(--sp-4) var(--sp-5);
    text-align: left;
    color: var(--ink-muted);
    border-bottom: 1px solid var(--border);
}

.data-table th,
.data-table td {
    padding: var(--sp-3) var(--sp-5);
    text-align: left;
    border-bottom: 1px solid var(--border);
}

.data-table thead th {
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-muted);
}

.data-table tbody th { font-weight: 600; }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table__abbr { color: var(--ink-muted); font-weight: 400; }

.data-table tbody tr:last-child th,
.data-table tbody tr:last-child td { border-bottom: 0; }

/* Admin list tables: a narrow checkbox column and a right-aligned actions column. */
.data-table__check { width: 1%; white-space: nowrap; }

.data-table__actions {
    white-space: nowrap;
    text-align: right;
}

.data-table__actions .btn { margin-left: var(--sp-2); }
.data-table__actions .btn:first-child { margin-left: 0; }

.data-table tfoot th,
.data-table tfoot td {
    border-top: 2px solid var(--border);
    border-bottom: 0;
    font-weight: 700;
    background: var(--surface-sunken);
}

/* --------------------------------------------------------------------------
   FAQ accordion (controls/global/FaqAccordion.ascx). Ported from theme-preview.css.
   `open` in the markup means every card starts expanded in the raw HTML - see the
   control's own header comment for why that matters for AEO/GEO.
   -------------------------------------------------------------------------- */

.faq-cards {
    display: grid;
    gap: var(--sp-5);
    margin-top: var(--sp-6);
}

.faq-card {
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.faq-card summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    list-style: none;
    cursor: pointer;
    font-weight: 700;
    line-height: 1.25;
}

.faq-card summary::-webkit-details-marker { display: none; }

.faq-card summary:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-radius: 4px;
}

.faq-card__chevron { color: var(--ink-muted); transition: transform .18s ease; }
.faq-card[open] .faq-card__chevron { transform: rotate(180deg); }

.faq-card p { margin-top: var(--sp-3); margin-bottom: 0; max-width: none; color: var(--ink-muted); }

/* --------------------------------------------------------------------------
   Hero + search (controls/modules/search_postcode.ascx, used on Default.aspx's
   hero and 11 other pages - see that control's own header comment). Ported
   from theme-preview.css.
   -------------------------------------------------------------------------- */

.hero {
    position: relative;
    padding-block: var(--sp-8);
    background: var(--accent-quiet);
    border-bottom: 1px solid var(--border);
    text-align: center;
}

.hero__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.hero__lead {
    font-size: 1.15em;
    color: var(--ink-muted);
    max-width: 48ch;
    margin-bottom: var(--sp-6);
}

.hero h1 { max-width: 28ch; }
.hero h1 em { font-style: normal; color: var(--accent-strong); }

/* Shadows main.css:3079 (.banner-display) - the search control's wrapper. It carries a decorative
   street-map background image and white text colour, designed for the OLD banner treatment. Once
   nested inside the new .hero (or any other new section), that image renders as an unintended
   layer behind the search card - this neutralises it everywhere search_postcode.ascx is used,
   without removing the wrapper's padding/layout behaviour (harmless to keep). */
.banner-display {
    background: var(--accent-quiet);
    color: var(--ink);
    padding-block: var(--sp-6);
    border-bottom: 1px solid var(--border);
    text-align: left;
}

/* On the home page the .hero already paints that band and pads it, so the control inside it
   contributes neither. */
.hero .banner-display {
    background: none;
    padding-block: 0;
    border-bottom: 0;
}

/* Shadows main.css:1602 (h1,.h1,h2,.h2,h3,.h3...{text-transform:uppercase;color:#dd480e}) for
   headings inside the new component patterns specifically - the brand's uppercase/orange heading
   style is the existing sitewide look and is intentionally left alone everywhere else; these
   sections were designed with normal-case, ink-coloured headings (see theme-preview.css). */
.hero h1,
.section h1,
.section h2,
.explain-card__body h3,
.card__link h3,
.card__link h2,
.teaser__title {
    text-transform: none;
    font-weight: 700;
    color: var(--ink);
}

/* The heading/lead are centred with the section; the search form and chips reset back to
   left-aligned internally - only the box itself is centred, via .hero__inner's flex. */
.search { text-align: left; }
.chips { justify-content: center; }

.search {
    max-width: 44rem;
    width: 100%;
    margin-inline: auto;
    padding: var(--sp-5);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.search .h1 { font-size: 1.4em; margin-bottom: var(--sp-3); }

.search__fieldset { margin: 0; padding: 0; border: 0; }

.search__row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.search__input {
    flex: 1 1 16rem;
    min-width: 0;
    min-height: 2.75rem;
    padding: var(--sp-3) var(--sp-4);
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--ink-muted);
    border-radius: var(--radius);
}

.search__input::placeholder { color: var(--ink-muted); opacity: 1; }

.search__hint {
    margin-top: var(--sp-3);
    margin-bottom: 0;
    font-size: .9em;
    color: var(--ink-muted);
}

.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); align-items: center; margin-top: var(--sp-5); }
.chips__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.chips__label { font-size: .9em; color: var(--ink-muted); margin: 0; }

.chips a,
.chips a:visited {
    display: inline-flex;
    align-items: center;
    min-height: 2.25rem;
    padding: var(--sp-2) var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .9em;
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
}

.chips a:hover { border-color: var(--accent-strong); color: var(--accent-strong); }

/* --------------------------------------------------------------------------
   Client-logo marquee. Ported from theme-preview.css.
   -------------------------------------------------------------------------- */

.marquee {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-top: var(--sp-6);
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
    mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
}

.marquee__track {
    display: flex;
    align-items: center;
    width: max-content;
    /* 48s. Fourteen logos through a loop timed for three read as a blur. */
    animation: marquee-scroll 48s linear infinite reverse;
    /* Promote once, so the browser composites the scroll instead of repainting it each frame. */
    will-change: transform;
}

.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track { animation-play-state: paused; }

/* Spacing lives on the items, not on the list, and that is what makes the loop seamless.
   The track holds two identical lists and slides by exactly half its width, so the spacing
   either side of the join has to equal the spacing between any two logos.

   It did not. `gap` only separates a flex container's own children, so it spaced the logos
   inside each list and did nothing between one list and the next. The `padding-inline` that
   might have bridged the join never applied either: the list-reset further down this file
   sets `padding: 0` on .marquee__list, same specificity and later in the source, so it won.
   The join therefore had no spacing at all, and the two logos across it collided once per
   cycle - the jolt that looked like the carousel restarting.

   A half-gap margin on each item adds to one full gap at the join and everywhere else alike.
   Do not put gap back on the list or on the track: a track gap is counted once in the track
   width but the animation moves by 50% of it, so the loop stops landing on a whole period. */
/* The height is explicit, and that is deliberate. The two lists are identical markup, so they
   should lay out identically - but the visible one and the aria-hidden copy were sitting on
   different lines, which is what made the band appear to drop as it came round. Sizing them
   from their contents leaves that free to happen again the moment one logo measures
   differently. Pinned to the tile plus its vertical padding, neither list can be taller than
   the other and both centre on the same line. */
.marquee__list {
    display: flex;
    align-items: center;
    align-self: center;
    height: 5rem;
    /* The list-reset further down this file zeroes margin-inline but not margin-block, so both
       lists kept the browser's default 1em above and below. In a flex track those margins are
       real space that appears and disappears around the join as the loop comes round, which is
       the padding that looked like it switched on and off. */
    margin-block: 0;
}

/* One tile size for every logo. The source artwork ranges from a very wide wordmark
   (the Athlete's Foot) to a tall narrow crest (SCEGGS), so height alone left the row
   ragged; a fixed box with object-fit: contain scales each one to fit without
   distorting it or cropping it. */
.marquee__list img {
    /* display: block, not the default inline. An inline image sits on the text baseline and
       the line box keeps descender space under it, which lifts the image inside its own tile.
       It shows up on whichever logo is tallest in its box - here the Sydney Grammar crest,
       which is portrait (287x348) and so fills the full height, while the 1.72 wordmarks fit
       by width and sit shorter. */
    display: block;
    box-sizing: border-box;
    width: 9.5rem;
    height: 4.5rem;
    object-fit: contain;
    object-position: center;
    mix-blend-mode: var(--client-blend);
    background: var(--client-plate-bg);
    /* The tile is 4.5rem inside a 5rem row, so the breathing space above and below comes from
       centring rather than from padding here. An earlier version used
       calc(var(--client-plate-pad) + 0.25rem), which is invalid whenever that token is a
       unitless 0 - as it is in light mode - so the padding silently applied in dark mode only. */
    padding: var(--client-plate-pad, 0px);
    border-radius: var(--radius);
    filter: grayscale(1);
    opacity: .75;
    transition: filter .2s ease, opacity .2s ease;
}

.marquee__list li {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: 100%;
    margin-inline: calc(var(--sp-8) / 2);
}

/* Narrower tiles on a phone, so more than two are visible at once. */
@media (max-width: 40rem) {
    .marquee__list { height: 4rem; }
    .marquee__list img { width: 7rem; height: 3.5rem; }
    .marquee__list li { margin-inline: calc(var(--sp-6) / 2); }
}

/* An endlessly moving band is a vestibular trigger and there is no way to stop it other than
   hovering, which a keyboard or touch user cannot rely on. Hold it still when the system asks. */
@media (prefers-reduced-motion: reduce) {
    .marquee__track { animation: none; }
    .marquee { overflow-x: auto; }
}

.marquee__list img:hover { filter: grayscale(0); opacity: 1; }

@keyframes marquee-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* --------------------------------------------------------------------------
   Theme switch (header dark/light/auto toggle - see www/js/theme-toggle.js)
   -------------------------------------------------------------------------- */

.theme-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 2.75rem;
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--ink);
    font-weight: 600;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}

.theme-toggle:hover { background: var(--surface-sunken); border-color: var(--ink-muted); }

#theme-toggle-value { display: inline-block; min-width: 3.1em; text-align: left; }

.theme-toggle__icons { display: inline-flex; flex: 0 0 auto; color: var(--accent-strong); }
.theme-toggle__icon { width: 1.15rem; height: 1.15rem; display: none; }
.theme-toggle__icon--auto { display: block; }

:root[data-theme-choice="light"] .theme-toggle__icon--auto,
:root[data-theme-choice="dark"] .theme-toggle__icon--auto { display: none; }
:root[data-theme-choice="light"] .theme-toggle__icon--light { display: block; }
:root[data-theme-choice="dark"] .theme-toggle__icon--dark { display: block; }

/* Section modifiers and the teaser grid, ported from the theme preview. The preview's
   --step-1 / --step--1 type scale does not exist here, so those become the em values this
   file already uses elsewhere. .section--sunken was referenced by Default.aspx before this
   and had no rule at all. */

.section--sunken { background: var(--surface-sunken); }

.section__lead {
    font-size: var(--step-1);
    color: var(--ink-muted);
    max-width: var(--measure);
    margin-top: var(--sp-3);
}

.teasers {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
    gap: var(--sp-5);
    margin-top: var(--sp-6);
}

/* The whole teaser is the link target, the same as .card. This deliberately had no overlay
   so the longer body copy stayed selectable, but .teaser:hover lifts the whole card, so the
   card promised it was clickable while the cursor stayed an arrow everywhere except the few
   words of the title. Matching .card is the lesser cost. */
.teaser {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
        "tag  tag"
        "icon title"
        "body body";
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
    padding: var(--sp-5);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    transition: box-shadow .18s ease, border-color .18s ease;
}

.teaser:hover { box-shadow: var(--shadow-lift); border-color: var(--accent); }

.teaser__title a,
.teaser__title a:visited { color: inherit; text-decoration: none; }
.teaser__title a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.teaser__title a:hover { color: var(--accent-strong); text-decoration: underline; text-decoration-thickness: 2px; }

/* The outline belongs on the card, not on the few words of the title the link wraps. */
.teaser:has(.teaser__title a:focus-visible) {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-color: var(--focus);
}
.teaser__title a:focus-visible { outline: none; }

.teaser__tag {
    grid-area: tag;
    margin: 0;
    font-size: .9em;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

.teaser__icon {
    grid-area: icon;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius);
    background: var(--accent-quiet);
    color: var(--accent-strong);
}

.teaser__icon svg { width: 1.4rem; height: 1.4rem; }

.teaser__title { grid-area: title; margin: 0; }
.teaser__title a,
.teaser__title a:visited { color: var(--ink); text-decoration: none; }
.teaser__title a:hover { color: var(--accent-strong); text-decoration: underline; text-decoration-thickness: 2px; }
.teaser__title a:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 2px; }

.teaser > p:last-child {
    grid-area: body;
    margin: 0;
    max-width: none;
    font-size: .9em;
    color: var(--ink-muted);
}


/* main-min.css sets `.main { text-align: center }` on the master page's <main> wrapper, so
   everything inherits centring. The theme preview has no such wrapper and is left-aligned
   below the hero. Scoped to .section, so .hero stays centred as designed. */
/* Preview: .section { padding-block: var(--sp-8) }. Never ported, so the new sections sat
   tighter than the approved design. */
.section { text-align: left; padding-block: var(--sp-6); }

/* main-min.css caps .container at 1300px; the preview uses 74rem, which is what makes the
   teaser grid fall to three columns rather than four. Scoped to the new sections. */
.hero > .container,
.section > .container { max-width: var(--container); }

/* main-min.css styles bare `th` dark grey with white text, which reaches both the header row
   and the row headers in the first column. .data-table never reset it. */
.data-table th { background: transparent; color: inherit; }
.data-table thead th { color: var(--ink-muted); }


/* Heading sizes from the preview's type scale. Scoped, not applied to bare h1/h2/h3, so the
   rest of the site keeps main-min.css's sizing until its own template is done. */
.hero h1 { font-size: var(--step-4); line-height: 1.1; }
.section h2 { font-size: var(--step-3); line-height: 1.15; }
.card__link h3,
.card__link h2,
.explain-card__body h3,
.teaser__title { font-size: var(--step-1); }

/* --------------------------------------------------------------------------
   Site header (controls/global/header.ascx), ported from theme-preview.css.
   The admin band above it keeps main-min.css's .header__nav styling untouched.
   -------------------------------------------------------------------------- */

.site-header {
    position: sticky;
    top: 0;
    /* Above Leaflet's control layer, which sits at 1000. The map is also isolated below, so this
       is the second of two defences rather than the only one - third-party embeds (AdSense, the
       Facebook SDK) pick their own z-index and a site header should outrank them. */
    z-index: 1100;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    text-align: left;
}

/* Grid, not flex: the primary band spans the full row including under the logo, which a flex
   child cannot do from inside a narrower sibling's box. .site-nav is display:contents so its two
   children act as direct grid items. */
.site-header__inner {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: minmax(4.5rem, auto) auto;
    grid-template-areas: "logo utility" "primary primary";
    align-items: center;
    column-gap: var(--sp-5);
    row-gap: var(--sp-2);
    padding-block: var(--sp-3) 0;
    max-width: var(--container);
}

.site-logo { grid-area: logo; display: inline-flex; border-radius: 4px; }
.site-logo img { width: auto; height: 2.5rem; max-width: none; filter: var(--logo-filter); }
.site-logo { flex: 0 0 auto; }

.nav-toggle {
    display: none;
    grid-area: utility;
    justify-self: end;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--ink);
    cursor: pointer;
}

.nav-toggle:hover { background: var(--surface-sunken); border-color: var(--ink-muted); }
.nav-toggle__icon { width: 1.4rem; height: 1.4rem; }
.nav-toggle__icon--close { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__icon--open { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__icon--close { display: block; }

.site-nav { display: contents; grid-area: primary; }
/* The welcome line and the account actions share this row. A flex row ending at the right
   edge keeps the greeting hard against the buttons rather than floating in the middle of the
   header, and wrap + row-gap means a long greeting drops above the buttons on a narrow screen
   instead of squeezing them. */
.site-nav__group--utility {
    grid-area: utility;
    justify-self: end;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    column-gap: var(--sp-5);
    row-gap: var(--sp-2);
    max-width: 100%;
}

/* Full-bleed band. The usual `width: 100vw; margin-left: calc(50% - 50vw)` recipe assumes the
   element's containing block is the centred container - here it is a grid area, so the 50% and
   the 50vw do not cancel and the band lands off-centre, leaving a gap at one edge on wide
   screens. Painting the colour outwards with a shadow and clipping it vertically needs no
   arithmetic, works at any width, and cannot cause horizontal overflow. */
.site-nav__group--primary {
    grid-area: primary;
    background: var(--accent-quiet-deep);
    border-top: 1px solid var(--border);
    box-shadow: 0 0 0 100vmax var(--accent-quiet-deep);
    clip-path: inset(0 -100vmax);
}

.site-nav__group--primary .site-nav__list a:not(.btn):hover { background: var(--surface); }

.site-nav__group--primary .site-nav__list {
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--sp-5);
    padding-block: var(--sp-1);
}

.site-nav__list { display: flex; flex-wrap: wrap; gap: var(--sp-1); align-items: center; margin: 0; padding: 0; list-style: none; }
.site-nav__list li { margin: 0; }

/* :not(.btn) is required: `.site-nav__list a` is 0,1,1 and would beat `.btn--primary`'s 0,1,0. */
.site-nav__list a:not(.btn) {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 2.75rem;  /* clears the 24x24 floor in WCAG 2.2 2.5.8 */
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius);
    color: var(--ink);
    font-size: var(--step--1);
    font-weight: 600;
    text-decoration: none;
}

.site-nav__list a:not(.btn):hover { background: var(--surface-sunken); color: var(--accent-strong); }
.site-nav__list a:not(.btn)[aria-current="page"] { color: var(--accent-strong); background: var(--accent-quiet); }
.site-nav__list a:not(.btn):focus-visible,
.nav-toggle:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.site-nav__list .btn { font-size: var(--step--1); }
.site-nav__icon { width: 1.15rem; height: 1.15rem; flex: 0 0 auto; }

/* The margin-right is gone - column-gap on the utility row handles the spacing now, and the
   two together double-counted it. */
.user-notifications {
    margin: 0;
    font-size: var(--step--1);
    color: var(--ink-muted);
    text-align: right;
}

.user-notifications__count {
    display: inline-block;
    min-width: 1.5rem;
    padding: 0 var(--sp-2);
    border-radius: 999px;
    background: var(--accent-quiet-deep);
    color: var(--accent-strong);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 60rem) {
    .nav-toggle { display: inline-flex; }

    .site-nav {
        display: none;
        flex-direction: column;
        align-items: stretch;
        row-gap: 0;
        padding-block: var(--sp-2) var(--sp-4);
        border-top: 1px solid var(--border);
    }

    .nav-toggle[aria-expanded="true"] ~ .site-nav { display: flex; }
    .site-nav__group { flex-basis: auto; }

    /* Full-bleed belongs to the desktop band only. In the panel it overflowed the viewport
       and painted past the panel's own edges. */
    .site-nav__group--primary {
        background: transparent;
        box-shadow: none;
        clip-path: none;
        border-top: 1px solid var(--border);
        margin-top: var(--sp-3);
        padding-top: var(--sp-3);
    }

    .site-nav__list a:not(.btn)[aria-current="page"] { background: var(--accent-quiet); }
    .site-nav__group--primary .site-nav__list { max-width: none; margin-inline: 0; padding-inline: 0; padding-block: 0; }
    .site-nav__list { flex-direction: column; align-items: stretch; gap: 0; width: 100%; }
    .site-nav__list li { width: 100%; }
    .site-nav__list a:not(.btn),
    .site-nav__list .btn,
    .theme-toggle { width: 100%; justify-content: flex-start; }
    .site-nav__list .btn { margin-block: var(--sp-1); }
}

/* Search card additions: the visible label and large button from the preview. .search__heading
   replaces the old div.h1 that SetHeading() rendered - still a heading in look, but no longer a
   second visual h1 on pages that already have one. */
.search__label { display: block; margin-bottom: var(--sp-2); font-weight: 700; font-size: var(--step--1); }
.search__heading { margin: 0 0 var(--sp-3); font-size: var(--step-2); font-weight: 700; color: var(--ink); }
.btn--lg { font-size: var(--step-1); padding: var(--sp-4) var(--sp-6); }

/* main-min.css scopes three rules to .banner-display (the search control's wrapper) that were
   written for the old photographic hero. They outrank the flat .search__* classes on
   specificity, which is why the input rendered with no border and the button stretched to a
   flex row inflated by the input's margin. */
.banner-display .container .search__input { border: 1px solid var(--ink-muted); margin-bottom: 0; }

/* 4.28em of block padding, designed to give the old background image room. Inside the new
   .hero that doubles up with the hero's own padding; standalone on the other pages that use
   this control it is simply larger than the theme. */
.banner-display .container { padding-block: var(--sp-6); }
.hero .banner-display .container { padding-block: 0; }

/* main-min.css indents every ul by 3rem (`ol,ol ol,ol ul,ul,ul ol,ul ul { margin-left: 3rem }`).
   The theme preview resets `ul { margin: 0; padding: 0 }` globally, which was never ported - so
   on the live page every list-based component sat 3rem right of the headings above it while its
   right edge stayed on the container edge. Scoped to the new components rather than resetting
   ul sitewide, which would move content on every template not yet redesigned. */
.cards,
.teasers,
.chips__list,
.faq-cards,
.marquee__list,
.site-footer__nav ul { margin-inline: 0; padding: 0; list-style: none; }

/* One container for the whole site. main-min.css uses `width: 90%; max-width: 1300px`, which
   drifts with the viewport; the theme uses a fixed gutter and a 74rem cap. Admin and console
   were still on the old one, so their content sat at a different width to every public page.
   The full-bleed bands keep their own overrides below. */
.container {
    width: 100%;
    max-width: var(--container);
    padding-inline: var(--sp-5);
    margin-inline: auto;
}

.hero > .container,
.section > .container,
.site-header__inner {
    width: 100%;
    padding-inline: var(--sp-5);
}

/* The preview's global focus ring. :where() carries zero specificity, so this only reaches
   elements nothing else styles - main-min.css's own `.txt-input:focus` still wins where it
   applies. Net effect is a visible ring on the new components, nothing moved elsewhere. */
:where(a, button, input, summary, [tabindex]):focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-radius: 4px;
}

/* The search control's own .container is not a direct child of .hero, so it kept main-min's
   `width: 90%` and sat narrower than the hero's own container. */
.banner-display > .container { width: 100%; max-width: none; padding-inline: var(--sp-5); }
.hero .banner-display > .container { padding-inline: 0; }

/* Wider than the preview's 44rem, at the client's request. */
.search { max-width: 56rem; }

/* Standard paragraph rhythm. :where() is zero-specificity, so this sets a baseline only where
   nothing else already decides - every existing rule in main-min.css still wins. Sitewide
   deliberately: bare <p> was falling back to the UA default and reading as unspaced. */
:where(p) { margin: 0 0 var(--sp-4); }

/* --------------------------------------------------------------------------
   Site footer (controls/global/footer.ascx). Extends the preview's .site-footer
   into the multi-column link hub the preview's four-link row only sketched.
   -------------------------------------------------------------------------- */

.site-footer {
    padding-block: var(--sp-7);
    margin-top: var(--sp-8);
    background: var(--navy-dark);
    color: var(--ink-on-dark);
    text-align: left;
}

.site-footer__inner {
    display: grid;
    grid-template-columns: minmax(min(16rem, 100%), 1.6fr) repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
    gap: var(--sp-6) var(--sp-5);
    width: 100%;
    max-width: var(--container);
    padding-inline: var(--sp-5);
}

.site-footer__logo { height: 2.25rem; width: auto; max-width: none; filter: brightness(0) invert(1); }

.site-footer__blurb {
    margin: var(--sp-3) 0 0;
    font-size: var(--step--1);
    color: var(--ink-on-dark-muted);
    max-width: 38ch;
}

.site-footer__heading {
    margin: 0 0 var(--sp-2);
    font-size: var(--step--1);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--ink-on-dark-muted);
}

.site-footer__col ul { margin: 0; padding: 0; list-style: none; }
.site-footer__col li { margin: 0; }

.site-footer a,
.site-footer a:visited { color: var(--c-white); text-decoration: none; }

.site-footer__col a {
    display: inline-flex;
    align-items: center;
    min-height: 2.5rem;   /* WCAG 2.2 2.5.8 target size */
    font-size: var(--step--1);
    font-weight: 600;
}

.site-footer__col a:hover { text-decoration: underline; text-decoration-thickness: 2px; }
.site-footer a:focus-visible { outline: 3px solid var(--focus-on-dark); outline-offset: 2px; }

.site-footer__bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3) var(--sp-5);
    justify-content: space-between;
    align-items: center;
    width: 100%;
    max-width: var(--container);
    padding-inline: var(--sp-5);
    margin-top: var(--sp-6);
    padding-top: var(--sp-5);
    border-top: 1px solid rgba(255, 255, 255, .15);
}

.site-footer__meta { font-size: var(--step--1); color: var(--ink-on-dark-muted); margin: 0; }
.site-footer__top { font-size: var(--step--1); font-weight: 600; min-height: 2.5rem; display: inline-flex; align-items: center; }
.site-footer__top:hover { text-decoration: underline; text-decoration-thickness: 2px; }

/* --------------------------------------------------------------------------
   Breadcrumbs and the state grid (postcode/postcode-finder.aspx).
   Scoped to the postcode hierarchy - local-listings has its own, deliberately
   not shared, because the two are separate features.
   -------------------------------------------------------------------------- */

.breadcrumbs { margin-bottom: var(--sp-5); text-align: left; }
.breadcrumbs ol { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.breadcrumbs li { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--step--1); color: var(--ink-muted); }
.breadcrumbs li + li::before { content: "\203A"; color: var(--ink-muted); }
.breadcrumbs a,
.breadcrumbs a:visited { color: var(--ink-muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--accent-strong); text-decoration: underline; text-decoration-thickness: 2px; }

.section h1 { font-size: var(--step-3); line-height: 1.15; margin: 0 0 var(--sp-3); }

.cards--state { grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }

/* Tinted rather than the default white .card surface. --accent-quiet is #fdf1ec in light mode
   and already has a dark-mode counterpart, which a fresh literal would not. */
.state-card {
    align-items: flex-start;
    background: var(--accent-quiet);
    border-color: var(--accent-quiet-deep);
}

.card.state-card:has(.card__link:hover) { background: var(--surface); }

/* The state silhouettes are potrace SVGs with a hardcoded fill="#000000", so they need the
   same inversion the logo gets rather than a colour of their own. */
.state-card__map {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 6.5rem;
}

.state-card__map img {
    width: auto;
    height: 100%;
    max-width: 100%;
    object-fit: contain;
    filter: var(--logo-filter);
    opacity: .85;
}

.card:has(.card__link:hover) .state-card__map img { opacity: 1; }

/* --------------------------------------------------------------------------
   Small screens. The redesigned components only; templates not yet done keep
   main-min.css's own breakpoints.
   -------------------------------------------------------------------------- */

@media (max-width: 40rem) {
    /* The button wrapped under a full-width input and sat half-width, which reads as broken
       rather than deliberate. */
    .search__row { flex-direction: column; align-items: stretch; }
    .search__row .btn { width: 100%; }
    .search__input { flex: 0 0 auto; }

    .hero { padding-block: var(--sp-7) var(--sp-6); }

    .chips { justify-content: flex-start; }

    .site-footer__inner { grid-template-columns: 1fr; gap: var(--sp-5); }
    .site-footer__bar { flex-direction: column; align-items: flex-start; }

    /* The state table's min-content width exceeded the viewport, and enough of it escaped
       .table-scroll to give the document a horizontal scrollbar. Tightening it here means it
       fits outright at this width rather than relying on the wrapper to clip. */
    /* Full-bleed is not worth a horizontal scrollbar at this width. */
    .marquee { width: 100%; margin-left: 0; }

    .table-scroll { max-width: 100%; min-width: 0; }
    .data-table { font-size: .8em; }
    .data-table caption { padding: var(--sp-3) var(--sp-4); }
    .data-table th,
    .data-table td { padding: var(--sp-2) var(--sp-3); }
}

/* --------------------------------------------------------------------------
   Letter index (postcode/postcode-state.aspx). Scoped to the postcode
   hierarchy; templates 4 and 5 reuse it, local-listings does not.
   -------------------------------------------------------------------------- */

.letter-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(8.5rem, 100%), 1fr));
    gap: var(--sp-4);
    margin: var(--sp-6) 0 0;
    padding: 0;
    list-style: none;
}

.letter-tile { margin: 0; }

.letter-tile > a,
.letter-tile > a:visited,
.letter-tile--empty,
.locality-tile > a,
.locality-tile > a:visited {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-height: 5rem;
    padding: var(--sp-4);
    background: var(--accent-quiet);
    border: 1px solid var(--accent-quiet-deep);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    color: var(--ink);
    text-decoration: none;
    transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}

.letter-tile > a:hover,
.locality-tile > a:hover {
    background: var(--surface);
    box-shadow: var(--shadow-lift);
    transform: translateY(-2px);
    border-color: var(--accent);
    color: var(--accent-strong);
}

.letter-tile > a:focus-visible,
.locality-tile > a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.letter-tile__char { font-size: var(--step-2); font-weight: 700; line-height: 1; }
.letter-tile__count { font-size: var(--step--1); color: var(--ink-muted); }

/* No postcodes for this letter. Not a link, so it carries no hover or focus treatment - it is
   there so the A-Z stays complete rather than silently skipping letters. */
.letter-tile--empty {
    background: var(--surface-sunken);
    box-shadow: none;
    color: var(--ink-muted);
}

.letter-tile--empty .letter-tile__char { font-weight: 600; }

/* Locality list (postcode/postcode-state-detail.aspx). Shares the tile visuals with the letter
   index above - same hierarchy, same component, so the rules are grouped rather than copied. */

.locality-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
    gap: var(--sp-4);
    margin: var(--sp-6) 0 0;
    padding: 0;
    list-style: none;
}

.locality-tile { margin: 0; }
.locality-tile > a { min-height: 4.25rem; }
.locality-tile__name { font-weight: 700; }
.locality-tile__postcode { font-size: var(--step--1); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.locality-tile > a:hover .locality-tile__postcode { color: inherit; }

/* Sort control above a result list. */
.list-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    margin-top: var(--sp-5);
}

.list-toolbar__label { font-weight: 700; font-size: var(--step--1); }

/* Pushes whatever follows it to the far end of the toolbar. */
.list-toolbar__spacer { flex: 1 1 auto; }

.list-toolbar__select {
    min-height: 2.75rem;
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--ink-muted);
    border-radius: var(--radius);
    font: inherit;
}

.list-toolbar__select:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Links state their own cursor rather than relying on the UA default. :where() is zero
   specificity, so main-min.css's deliberate exceptions still win - a[href^="tel:"] and
   .complex-link both set cursor: text on purpose. Mainly this documents the intent for the
   whole-card overlay, where the clickable area is a ::after rather than the visible text. */
:where(a[href]) { cursor: pointer; }

/* --------------------------------------------------------------------------
   Locality detail (postcode/postcode.aspx). Postcode hierarchy only.
   -------------------------------------------------------------------------- */

.locality-layout {
    display: grid;
    grid-template-columns: minmax(min(20rem, 100%), 2fr) minmax(min(20rem, 100%), 3fr);
    gap: var(--sp-6);
    margin-top: var(--sp-6);
    align-items: start;
}

.locality-layout__map .map {
    width: 100%;
    min-height: 24rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.fact-list {
    margin: 0;
    padding: 0;
    background: var(--accent-quiet);
    border: 1px solid var(--accent-quiet-deep);
    border-radius: var(--radius-lg);
}

.fact-list__row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--accent-quiet-deep);
}

.fact-list__row:last-child { border-bottom: 0; }
.fact-list dt { margin: 0; font-size: var(--step--1); color: var(--ink-muted); }
.fact-list dd { margin: 0; font-weight: 700; text-align: right; }

.locality-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-top: var(--sp-5);
}

.locality-listings__heading { margin: var(--sp-7) 0 var(--sp-4); font-size: var(--step-2); }

.locality-listings {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
    gap: var(--sp-4);
}
.locality-listing { margin: 0; }

.locality-listing > a,
.locality-listing > a:visited {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}

.locality-listing > a:hover { border-color: var(--accent); color: var(--accent-strong); }
.locality-listing > a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.locality-listing__name { font-weight: 700; }
.locality-listing__meta { font-size: var(--step--1); color: var(--ink-muted); }
.locality-listing > a:hover .locality-listing__meta { color: inherit; }

.locality-related { margin-top: var(--sp-6); }

@media (max-width: 52rem) {
    .locality-layout { grid-template-columns: 1fr; }
    .locality-layout__map { order: -1; }
    .locality-layout__map .map { min-height: 16rem; }
}

/* Leaflet lays its panes out from z-index 200 to 700 and its controls at 800-1000, all in the
   root stacking context, so they painted straight over the sticky header when the page scrolled
   under them. Giving the container its own stacking context traps them: a positioned element
   with an explicit z-index starts one, and isolation states the intent for anything added later.
   Applies wherever a map appears, not just the locality page. */
.leaflet-container {
    position: relative;
    z-index: 0;
    isolation: isolate;
}

/* --------------------------------------------------------------------------
   Local listings state index (local-listings/Default.aspx).

   Visually close to the postcode hierarchy's .cards--state, and deliberately
   not shared with it: postcode lookup and business listings are separate
   features that are expected to diverge. Duplication here is the instruction,
   not an oversight.
   -------------------------------------------------------------------------- */

.cards--listing-state { grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }

.listing-state-card {
    align-items: flex-start;
    background: var(--accent-quiet);
    border-color: var(--accent-quiet-deep);
}

.card.listing-state-card:has(.card__link:hover) { background: var(--surface); }

.listing-state-card__map {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 6.5rem;
}

.listing-state-card__map img {
    width: auto;
    height: 100%;
    max-width: 100%;
    object-fit: contain;
    filter: var(--logo-filter);
    opacity: .85;
}

.card:has(.card__link:hover) .listing-state-card__map img { opacity: 1; }

.listing-cta { margin-top: var(--sp-6); }

/* Inline page notice. Replaces main-min.css's .feedback_warning, which renders as an h5. */
.notice {
    margin: var(--sp-5) 0 0;
    padding: var(--sp-4) var(--sp-5);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--ink);
}

.notice--warning { border-color: var(--accent-quiet-deep); background: var(--accent-quiet); }

/* The two search cards submit differently: the postcode one uses a <button>, which stretches to
   the flex line, while the business one uses asp:Button, rendering <input type="submit"> - a
   replaced element that neither stretches nor accepts align-self. So the pair only lined up on
   one of the two pages.

   main-min.css also carries `input[type=search] { box-sizing: content-box }` from its normalize
   reset, at 0,1,1, which outranks a plain .search__input at 0,1,0 - so any height set there was
   added to padding and borders rather than including them. Both are pinned to one explicit
   height, at a specificity that wins. */
/* Keyed on the class rather than input[type="search"]: the distance pages use asp:TextBox
   without TextMode, so they render type="text" and were missed, coming out shorter than the
   button beside them. `input.search__input` is 0,2,1, which still beats main-min's
   `input[type=search] { box-sizing: content-box }` at 0,1,1. */
.search__row input.search__input,
.search__row .search__input,
.search__row .search__select,
.search__row .btn,
.search__row input.btn {
    box-sizing: border-box;
    height: 4rem;
}

/* --------------------------------------------------------------------------
   Category and suburb tiles (local-listings hierarchy). Parallel to the
   postcode hierarchy's .letter-tile / .locality-tile and deliberately not
   shared with them.
   -------------------------------------------------------------------------- */

.category-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
    gap: var(--sp-4);
    margin: var(--sp-6) 0 0;
    padding: 0;
    list-style: none;
}

.category-tile { margin: 0; }

.category-tile > a,
.category-tile > a:visited {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-height: 4.5rem;
    padding: var(--sp-4);
    background: var(--accent-quiet);
    border: 1px solid var(--accent-quiet-deep);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    color: var(--ink);
    text-decoration: none;
    transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}

.category-tile > a:hover {
    background: var(--surface);
    box-shadow: var(--shadow-lift);
    transform: translateY(-2px);
    border-color: var(--accent);
    color: var(--accent-strong);
}

.category-tile > a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.category-tile__name { font-weight: 700; }
.category-tile__count { font-size: var(--step--1); color: var(--ink-muted); }
.category-tile > a:hover .category-tile__count { color: inherit; }

/* An empty category is still a link - it is how a business finds a gap it can fill. Muted so
   the grid reads count-first, but it lifts on hover like any other tile so it does not look
   disabled. */
.category-tile--empty > a,
.category-tile--empty > a:visited {
    background: var(--surface-sunken);
    border-color: var(--border);
    box-shadow: none;
    color: var(--ink-muted);
}

.category-tile--empty > a:hover {
    background: var(--surface);
    border-color: var(--accent);
    color: var(--accent-strong);
}

/* Business result list (local-listings hierarchy). Parallel to .locality-listing on the
   postcode side, deliberately separate. */

.business-list { margin: var(--sp-5) 0 0; padding: 0; list-style: none; display: grid; gap: var(--sp-3); }
.business-item { margin: 0; position: relative; }

.business-item > a,
.business-item > a:visited {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}

.business-item > a:hover { border-color: var(--accent); color: var(--accent-strong); }
.business-item > a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.business-item__name { font-weight: 700; }
.business-item__meta { font-size: var(--step--1); color: var(--ink-muted); }
.business-item > a:hover .business-item__meta { color: inherit; }
.business-item__edit { position: absolute; top: var(--sp-3); right: var(--sp-4); font-size: var(--step--1); font-weight: 700; }

/* Chain-brand logos. object-fit: contain keeps a wordmark's own proportions
   whatever shape the supplied file is, so a square and a wide logo both sit on
   the same baseline without the card jumping. */
.business-item__logo {
    align-self: flex-start;
    width: auto;
    max-width: 96px;
    max-height: 32px;
    height: auto;
    object-fit: contain;
    object-position: left center;
    margin-bottom: var(--sp-1);
}

.listing-brand { margin: 0 0 var(--sp-3); }
.listing-brand__logo {
    width: auto;
    max-width: 160px;
    max-height: 64px;
    height: auto;
    object-fit: contain;
    object-position: left center;
}

.business-detail__heading { margin: var(--sp-6) 0 var(--sp-3); font-size: var(--step-1); }

/* Compact variant: no heading or hint, so it needs less vertical room. */
.search--compact { padding: var(--sp-4) var(--sp-5); }

/* Radius search: two labelled fields plus a submit on one row. */
.search__row--radius { align-items: flex-end; }
.search__field { display: flex; flex-direction: column; gap: var(--sp-2); }
.search__field--grow { flex: 1 1 16rem; min-width: 0; }
.search__field .search__label { margin-bottom: 0; }
/* .search__field stacks label over input, so it is a column flex container - and in a column
   the flex-basis on .search__input (16rem, meant as a width) becomes the main size and wins
   over height. Reset it so the explicit height applies. */
.search__field .search__input { width: 100%; flex: 0 0 auto; }

.search__select {
    box-sizing: border-box;
    height: 4rem;
    max-width: 100%;
    padding: var(--sp-2) var(--sp-3);
    background: var(--bg);
    color: var(--ink);
    border: 1px solid var(--ink-muted);
    border-radius: var(--radius);
    font: inherit;
}

.search__select:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.steps { margin: 0; padding-left: var(--sp-5); display: grid; gap: var(--sp-2); }
.steps li { list-style: decimal; color: var(--ink-muted); }

/* Panels and the measurement toggle (distance hierarchy). */
.section--flush { padding-bottom: 0; }

.panel {
    max-width: 44rem;
    margin: 0 auto var(--sp-5);
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.panel h2 { margin-top: 0; font-size: var(--step-1); }
.panel--center { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; }

.toggle-btn[aria-pressed="true"] {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--btn-primary-ink);
}

.distance-result { font-size: var(--step-2); font-weight: 700; margin-bottom: var(--sp-4); }

/* main-min.css lays .form-main__field out as a two-column row with the label in a narrow
   left gutter. Inside the new panels that reads as a mis-set table, so fields stack. */
.panel .form-main__field { display: block; margin-bottom: var(--sp-4); }
.panel .form-main__inputs { margin-left: 0; width: 100%; }
.panel .search__label,
.panel .form-main__label { display: block; margin-bottom: var(--sp-2); font-weight: 700; font-size: var(--step--1); }
.panel .txt-input { width: 100%; box-sizing: border-box; min-height: 3rem; }
.panel fieldset { border: 0; margin: 0; padding: 0; }

/* ==========================================================================
   Legacy page bridge

   Pages that have not had their markup rebuilt still use main-min.css's
   original class names. Rather than rewrite eighteen more templates - several
   of which carry the audit's authentication work and are not worth disturbing
   for a restyle - these map the old names onto the theme's tokens. The result
   is one visual language across the site while the markup catches up.

   Everything here is a shadow of an existing main-min.css rule, so removing a
   line returns that component to its previous appearance rather than breaking
   it.
   ========================================================================== */

/* Page headings. main-min.css renders these uppercase, weight 300, in the
   brand orange, which measures 4.19:1 on white and fails AA. */
.txt-page-heading {
    text-transform: none;
    font-weight: 700;
    color: var(--ink);
    font-size: var(--step-3);
    line-height: 1.15;
}

/* Section band. Was a flat grey fill edge to edge. */
.grey-fill {
    background: var(--surface-sunken);
    padding-block: var(--sp-6);
}

/* The generic card/panel. */
.island,
.island-body {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.island-body { padding: var(--sp-5); }
.island .island-body { border: 0; box-shadow: none; border-radius: 0; }

/* Forms. The old layout puts the label in a narrow left gutter, which breaks
   down at small widths and reads as a mis-set table. */
.form-main__field { margin-bottom: var(--sp-4); }

/* The second main-min rule floats this into an 18em right-aligned gutter, which only works on
   wide screens and leaves the field hanging. Stacked instead. */
.form-main__label {
    display: block;
    float: none;
    width: auto;
    max-width: none;
    text-align: left;
    padding-right: 0;
    margin: 0 0 var(--sp-2);
    font-weight: 700;
    font-size: var(--step--1);
    color: var(--ink);
}

.form-main__label .req-field { position: static; color: var(--accent-strong); }

.form-main__notes { display: block; font-weight: 400; color: var(--ink-muted); }
/* main-min pushes the inputs 20em right to sit beside the floated label, and .box narrows
   them to 95%. Both are undone so a field is label-above-input, full width. */
.form-main__inputs,
.box .form-main__field .form-main__inputs {
    margin-left: 0;
    width: 100%;
}

.form-main__inputs .txt-input,
.form-main__inputs textarea,
.form-main__inputs select { width: 100%; }

.txt-input,
textarea,
select {
    box-sizing: border-box;
    min-height: 3rem;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius);
    border: 1px solid var(--ink-muted);
    font: inherit;
}

textarea { min-height: 8rem; }

.txt-input:focus-visible,
textarea:focus-visible,
select:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Buttons. */
/* :not(.btn__menu-toggle) matters: the admin band's hamburger is a .btn-main and main-min
   hides it above the mobile breakpoint with `display: none` at the same specificity. Setting
   display here put it back on screen at every width, and it pushed the admin nav onto a second
   row under the search box. */
.btn-main:not(.btn__menu-toggle),
.btn-main:not(.btn__menu-toggle):visited {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: 2.75rem;
    padding: var(--sp-3) var(--sp-5);
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--accent-strong);
    color: var(--btn-primary-ink);
    font-weight: 700;
    text-transform: none;
    line-height: 1.2;
}

.btn-main:hover,
.btn-main:focus { background: var(--btn-primary-hover-bg); color: var(--btn-primary-ink) !important; }

.btn-main__ghost,
.btn-main__ghost:visited {
    background: transparent;
    border-color: var(--border);
    color: var(--ink);
}

.btn-main__ghost:hover { background: var(--surface-sunken); border-color: var(--ink-muted); color: var(--ink) !important; }
.btn-fat { padding: var(--sp-4) var(--sp-5); }

/* Feedback messages. These render as h5 in main-min.css. */
.feedback {
    display: block;
    margin: var(--sp-4) 0;
    padding: var(--sp-4) var(--sp-5);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--ink);
    font-size: var(--step-0);
    font-weight: 400;
    text-transform: none;
}

.feedback_error { border-color: var(--accent-strong); background: var(--accent-quiet); }
.feedback_warning { border-color: var(--accent-quiet-deep); background: var(--accent-quiet); }

/* Result lists used by the search pages. */
.search-results-box {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.search-results-row { border-bottom: 1px solid var(--border); }
.search-results-row:last-child { border-bottom: 0; }

.postcode-list { margin: var(--sp-5) 0 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); gap: var(--sp-4); }
.postcode-list li { margin: 0; }

.postcode-list li a,
.postcode-list li a:visited {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-4);
    background: var(--accent-quiet);
    border: 1px solid var(--accent-quiet-deep);
    border-radius: var(--radius-lg);
    color: var(--ink);
    text-decoration: none;
}

.postcode-list li a:hover { background: var(--surface); border-color: var(--accent); color: var(--accent-strong); }

/* Old inline breadcrumb spans, on pages whose markup has not been rebuilt. */
.breadcrumbs { font-size: var(--step--1); color: var(--ink-muted); }
.breadcrumbs span[itemprop="title"] { color: inherit; }

/* `.box .form-main__field .form-main__label` paints the label as a solid orange chip with a
   10em cap, which reads as a filled tag rather than a label. 0,3,0, so it needs the same depth
   to shadow. */
.box .form-main__field .form-main__label,
.box .form-main__label {
    float: none;
    width: auto;
    max-width: none;
    padding: 0;
    margin: 0 0 var(--sp-2);
    background-color: transparent;
    color: var(--ink);
    text-align: left;
}

/* main-min.css's blanket heading rule - uppercase, weight 300, brand orange - reaches every
   heading on the templates that have not been rebuilt. Inside the theme's panels and cards it
   is the wrong treatment, and the orange measures 4.19:1 on white. */
.island h1, .island h2, .island h3, .island h4,
.island-body h1, .island-body h2, .island-body h3, .island-body h4,
.panel h1, .panel h2, .panel h3, .panel h4,
.grey-fill h2, .grey-fill h3,
.search-results h2, .search-results h3, .search-results h4,
.island .h1, .island .h2, .island .h3,
.island-body .h1, .island-body .h2, .island-body .h3 {
    text-transform: none;
    font-weight: 700;
    color: var(--ink);
}

/* An .island with nothing in it still paints a card. */
.island:empty, .island-body:empty { display: none; }

/* Measurement choice, inside the locations panel. */
.measure-toggle { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
.measure-toggle__legend { padding: 0; margin-bottom: var(--sp-2); font-weight: 700; font-size: var(--step--1); color: var(--ink); }
.measure-toggle__options { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.measure-toggle__hint { margin: var(--sp-2) 0 0; font-size: var(--step--1); color: var(--ink-muted); }

/* Two fields plus a submit on one row, as on the radius search. */
.search__row--pair { align-items: flex-end; }
.search__row--pair .search__field { flex: 1 1 14rem; }
.search .measure-toggle { margin-top: var(--sp-4); margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Console and admin dashboards.
   -------------------------------------------------------------------------- */

.console__section-heading { margin: var(--sp-7) 0 var(--sp-4); font-size: var(--step-2); }

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
    gap: var(--sp-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.stat {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-5);
    margin: 0;
    background: var(--accent-quiet);
    border: 1px solid var(--accent-quiet-deep);
    border-radius: var(--radius-lg);
}

.stat__value { font-size: var(--step-3); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.stat__label { font-weight: 700; font-size: var(--step--1); }
.stat__note { font-size: var(--step--1); color: var(--ink-muted); }
.stat-grid__note { margin-top: var(--sp-3); font-size: var(--step--1); color: var(--ink-muted); }

.cards--console { grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); }

/* Simple bar chart, drawn from a list so it degrades to readable text with no CSS. */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 7rem; margin: var(--sp-4) 0 0; padding: 0; list-style: none; }
.spark__bar { flex: 1 1 0; display: flex; align-items: flex-end; min-width: 2px; }
.spark__fill { display: block; width: 100%; background: var(--accent-strong); border-radius: 2px 2px 0 0; min-height: 2px; }
.spark__bar:hover .spark__fill { background: var(--accent); }
.chart-caption { margin-top: var(--sp-2); font-size: var(--step--1); color: var(--ink-muted); }

.locality-faq { margin-top: var(--sp-7); }
.locality-faq__heading { margin: 0 0 var(--sp-4); font-size: var(--step-2); }

/* A heading inside a card is a card heading, not a section heading - .section h2's --step-3
   is far too large once it is nested. */
.explain-card__body h2,
.card h2:not(.card__link h2),
.panel h2 { font-size: var(--step-1); line-height: 1.25; }

/* --------------------------------------------------------------------------
   Pager (local-listings/search-results.aspx).
   -------------------------------------------------------------------------- */

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-top: var(--sp-5);
}

.pager__link,
.pager__link:visited {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: var(--sp-2) var(--sp-4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--accent-strong);
    font-weight: 700;
    text-decoration: none;
}

.pager__link:hover { border-color: var(--accent); }
.pager__link:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Rendered as a span, not a disabled control, so there is nothing to tab to at either end. */
.pager__link.is-disabled { color: var(--ink-muted); background: transparent; border-style: dashed; }

.pager__status { font-size: var(--step--1); color: var(--ink-muted); }

/* The admin pager has first/previous and next/last as pairs, plus a rows-per-page
   control, rather than the two single links the public pager uses. */
.pager__group {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* --------------------------------------------------------------------------
   Search results layout: map with an information panel beside it, results
   below. Used by all three search results pages so they read the same way -
   the postcode search, the business search and the surrounding-postcodes
   search. Sharing one layout is deliberate here: these are the same kind of
   page, and the inconsistency between them was the thing being fixed.
   -------------------------------------------------------------------------- */

.search-layout {
    display: grid;
    grid-template-columns: minmax(min(22rem, 100%), 3fr) minmax(min(18rem, 100%), 2fr);
    gap: var(--sp-6);
    margin-top: var(--sp-6);
}

.search-layout__map .map {
    width: 100%;
    min-height: 26rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.search-layout__aside { display: flex; flex-direction: column; gap: var(--sp-4); }

/* The results sit below the map band, full width, so a long list is not forced
   into a narrow column beside a map that has stopped being useful. */
.search-results-list { margin-top: var(--sp-7); }
.search-results-list__heading { margin: 0 0 var(--sp-4); font-size: var(--step-1); }

.search-aside__panel {
    padding: var(--sp-5);
    background: var(--accent-quiet);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.search-aside__panel h2 { margin: 0 0 var(--sp-3); font-size: var(--step-0); }
.search-aside__panel p:last-child { margin-bottom: 0; }

.search-aside__facts { margin: 0; display: grid; gap: var(--sp-2); }
.search-aside__facts div { display: flex; justify-content: space-between; gap: var(--sp-3); }
.search-aside__facts dt { color: var(--ink-muted); }
.search-aside__facts dd { margin: 0; font-weight: 700; text-align: right; }

.search-aside__links { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--sp-2); }
.search-aside__links a { font-weight: 700; }

@media (max-width: 60rem) {
    .search-layout { grid-template-columns: 1fr; }
    .search-layout__map .map { min-height: 18rem; }
}

/* Both result lists sit full width below the map band now, so they lay out in columns
   rather than one tall single-file list. The narrower minimum on .business-list reflects
   its longer content - a business name plus suburb, state and postcode. */
.search-results-list .locality-grid { margin-top: 0; }
.search-results-list .business-list {
    margin-top: 0;
    grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
    gap: var(--sp-4);
}

/* --------------------------------------------------------------------------
   Admin / console band (controls/global/header.ascx).
   -------------------------------------------------------------------------- */

/* The hamburger came back on desktop because .btn above sets display:inline-flex and this
   stylesheet loads after main-min.css, where .btn__menu-toggle is display:none at the same
   specificity - so source order decided it. Re-assert it, and keep the button below
   40.0625em, where main-min hides #nav-main and the toggle is the only way to the menu. */
.btn.btn__menu-toggle { display: none; }

/* One flex row: nav, then search. main-min positions .search-mini absolutely against a
   hand-tuned top offset and reserves room for it with a large padding-right on .nav-main.
   Both are replaced here - a flex row centres the two against each other at every width
   without either needing to know the other's height. */
.header__nav .container {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding-block: var(--sp-2);
    padding-inline: var(--sp-5);
}

.header__nav .nav-main { padding-right: 0; }

.header__nav .search-mini {
    position: relative;
    top: auto;
    right: auto;
    flex: 0 1 21.5rem;
    width: auto;
    min-width: 0;
}

/* .btn sets a 2.75rem min-height, which beats main-min's height:100% and pushed the submit
   button out of the 30px search box. */
.header__nav .search-mini .btn {
    min-height: 0;
    padding: 0;
    width: 2em;
}

@media all and (min-width: 40.0625em) {
    .header__nav .container { min-height: 3.25rem; }
    .header__nav #nav-main { flex: 1 1 auto; min-width: 0; }
}

/* Below the nav breakpoint #nav-main is hidden and the toggle stands in for it. The toggle
   and the search share the row; the nav drops onto its own full-width line when expanded. */
@media all and (max-width: 40em) {
    .header__nav .container {
        flex-wrap: wrap;
        gap: var(--sp-3);
        padding-inline: var(--sp-4);
        min-height: 3rem;
    }

    .js .btn.btn__menu-toggle {
        display: inline-flex;
        flex: 0 0 auto;
        min-height: 0;
        height: 2.25rem;
        padding: 0 var(--sp-4);
    }

    .header__nav .search-mini { flex: 1 1 auto; }
    .header__nav #nav-main { flex: 0 0 100%; order: 3; }
}

/* At 320px the logo (max-width:none, so it never shrinks) plus the menu button was wider than
   the row and pushed the button off the right edge. Dropping the logo height scales its width
   with it and keeps both on the line; 23rem is below every current phone but covers the old
   320px class of device. */
@media (max-width: 23rem) {
    .site-logo img { height: 2rem; }
    .site-header__inner { column-gap: var(--sp-3); }
}

/* --------------------------------------------------------------------------
   Forms. The admin, console and account pages all share this shape: a card,
   fieldsets with a visible legend, stacked fields, and an actions row.
   -------------------------------------------------------------------------- */

/* A form is easier to read narrow than full width - a 60rem input is a long way
   for the eye to travel back from. */
.container--narrow { max-width: 52rem; }

.form-card {
    margin-top: var(--sp-6);
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.form-fieldset { margin: 0 0 var(--sp-6); padding: 0; border: 0; }
.form-fieldset:last-of-type { margin-bottom: 0; }

/* A real legend, not a hidden one. These forms hid every legend, which left the
   password boxes sitting under the contact fields with nothing saying they were a
   separate thing. */
.form-legend {
    padding: 0;
    margin-bottom: var(--sp-4);
    font-size: var(--step-1);
    font-weight: 700;
    line-height: 1.25;
}

.form-hint { margin: calc(var(--sp-4) * -1) 0 var(--sp-4); font-size: var(--step--1); color: var(--ink-muted); }

.form-field { margin-bottom: var(--sp-4); }

/* A checkbox that gates something, such as letting a rename move the listing URL.

   asp:CheckBox with a Text does NOT put CssClass on the input. It renders
   <span class="..."><input type="checkbox"><label></span> and the class lands on the SPAN, so
   sizing that class to 1.1rem squashed the input and its label into a 1.1rem column and the
   label text ran straight through the hint underneath. Size the input itself; let the span
   just lay the pair out. */
.form-check { display: flex; align-items: flex-start; flex-wrap: wrap; margin: var(--sp-2) 0 var(--sp-4); }
.form-check__input { display: inline-flex; align-items: flex-start; gap: var(--sp-2); flex: 1 1 100%; }
.form-check input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1.1rem;
    height: 1.1rem;
    margin: 0.15rem 0 0;
    accent-color: var(--accent);
}
.form-check label { margin: 0; cursor: pointer; }
/* .form-hint carries a negative top margin to sit tight under an input; there is no input
   above it here, so it would ride up over the label. */
.form-check .form-hint { flex: 1 1 100%; margin: var(--sp-1) 0 0; }

.form-label {
    display: block;
    margin-bottom: var(--sp-2);
    font-weight: 700;
    font-size: var(--step--1);
}

.form-field .txt-input,
.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="tel"],
.form-field input[type="password"],
.form-field textarea,
.form-field select {
    box-sizing: border-box;
    width: 100%;
    min-height: 2.75rem;      /* WCAG 2.2 2.5.8 target size */
    padding: var(--sp-2) var(--sp-3);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--ink);
    font: inherit;
}

.form-field textarea { min-height: 9rem; resize: vertical; }

.form-field .txt-input:focus-visible,
.form-field textarea:focus-visible,
.form-field select:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }

/* Two fields on a line above phone width, one below. */
.form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: 0 var(--sp-5); }

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    margin-top: var(--sp-6);
    padding-top: var(--sp-5);
    border-top: 1px solid var(--border);
}

/* The notice is a block, so it takes the whole row above the buttons rather than
   shoving them sideways when it appears. */
.form-actions .feedback { flex: 1 1 100%; margin: 0 0 var(--sp-2); }

/* A card that is a notice rather than a form - the pending-activation panel on login. */
.form-card--notice { border-left: 4px solid var(--accent); }
.form-card--notice h2 { margin: 0 0 var(--sp-3); font-size: var(--step-1); }

/* A secondary action that is a link, not a button, sitting in the actions row. */
.form-actions__link { margin-left: auto; font-weight: 700; }

.form-footnote { margin-top: var(--sp-5); font-size: var(--step--1); color: var(--ink-muted); }

@media (max-width: 30rem) {
    .form-actions__link { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   Listing editor (console and admin): the form, with the map beside it.
   -------------------------------------------------------------------------- */

.listing-editor {
    display: grid;
    grid-template-columns: minmax(min(28rem, 100%), 3fr) minmax(min(18rem, 100%), 2fr);
    gap: var(--sp-6);
    align-items: start;
    margin-top: var(--sp-6);
}

.listing-editor__form .form-card { margin-top: 0; }
.listing-editor__aside { display: flex; flex-direction: column; gap: var(--sp-4); }

.listing-editor__aside .map {
    width: 100%;
    min-height: 20rem;
    margin-top: var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.listing-editor__aside .search-aside__links { list-style: disc; padding-left: var(--sp-5); }
.listing-editor__aside .search-aside__links li { font-size: var(--step--1); color: var(--ink-muted); }

/* A hint that follows its input rather than preceding it. */
.form-hint--after { display: block; margin: var(--sp-2) 0 0; font-size: var(--step--1); color: var(--ink-muted); }

/* Destructive action. Outlined rather than solid so it does not compete with the save
   button for the eye, but unmistakably not neutral. */
.btn--danger,
.btn--danger:visited {
    background: transparent;
    border-color: var(--danger, #a02020);
    color: var(--danger, #a02020);
}

.btn--danger:hover,
.btn--danger:focus { background: var(--danger, #a02020); color: #fff; }

/* The editor is a wide form; on a laptop the map belongs above it, not squeezed beside. */
@media (max-width: 64rem) {
    .listing-editor { grid-template-columns: 1fr; }
    .listing-editor__aside { order: -1; }
}

/* --------------------------------------------------------------------------
   Contact page: form with a helper column beside it.
   -------------------------------------------------------------------------- */

.contact-layout {
    display: grid;
    grid-template-columns: minmax(min(26rem, 100%), 3fr) minmax(min(17rem, 100%), 2fr);
    gap: var(--sp-6);
    align-items: start;
    margin-top: var(--sp-6);
}

.contact-layout__form .form-card { margin-top: 0; }
.contact-layout__aside { display: flex; flex-direction: column; gap: var(--sp-4); }
.contact-layout__aside .search-aside__panel p { font-size: var(--step--1); }

@media (max-width: 60rem) {
    .contact-layout { grid-template-columns: 1fr; }
}

/* "(optional)" alongside a label, in the label's own weight rather than shouting. */
.form-label__optional { font-weight: 400; color: var(--ink-muted); }

/* The honeypot. Taken out of layout entirely rather than moved off-screen with the
   hide-visually pattern: hide-visually keeps a field in the accessibility tree and in the
   tab order, which is exactly wrong here - a real person should never reach it. */
.form-field.is-hidden { display: none; }

/* --------------------------------------------------------------------------
   CMS page body (pages.aspx). Arbitrary HTML from the content editor, so this
   styles elements rather than classes - the editor cannot be relied on to add
   any.
   -------------------------------------------------------------------------- */

/* Full width of the container, not a 68ch measure. 68ch is the better line length for
   reading prose and that is why it was here - but these pages are reference documents
   people scan for a heading, not articles read start to finish, and the narrow column
   made them look like a column of text abandoned in a wide page. The container is
   already capped at 74rem with a gutter, so "full width" is still bounded. */
.cms-content { margin-top: var(--sp-5); }

.cms-content > :first-child { margin-top: 0; }
.cms-content h2 { margin: var(--sp-7) 0 var(--sp-3); font-size: var(--step-2); line-height: 1.2; }
.cms-content h3 { margin: var(--sp-6) 0 var(--sp-3); font-size: var(--step-1); line-height: 1.25; }
.cms-content h4 { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--step-0); }
.cms-content p { margin: 0 0 var(--sp-4); }
.cms-content ul,
.cms-content ol { margin: 0 0 var(--sp-4); padding-left: var(--sp-6); }
.cms-content li { margin-bottom: var(--sp-2); }
.cms-content a { font-weight: 700; }

.cms-content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius);
}

.cms-content blockquote {
    margin: 0 0 var(--sp-4);
    padding: var(--sp-4) var(--sp-5);
    border-left: 4px solid var(--accent);
    background: var(--accent-quiet);
}

.cms-content blockquote > :last-child { margin-bottom: 0; }

/* A table pasted into the editor gets the same treatment as the site's own, and can
   scroll rather than forcing the page wide on a phone. */
.cms-content table {
    display: block;
    overflow-x: auto;
    width: 100%;
    margin: 0 0 var(--sp-5);
    border-collapse: collapse;
}

.cms-content th,
.cms-content td {
    padding: var(--sp-3);
    border-bottom: 1px solid var(--border);
    text-align: left;
}

.cms-content th { font-weight: 700; }

/* --------------------------------------------------------------------------
   AdSense slot (controls/modules/google_ad.ascx). The control renders nothing
   at all when no slot is configured, so this never applies to an empty page.
   -------------------------------------------------------------------------- */

/* Space is reserved before the ad arrives. A responsive unit is display:block with no
   height until AdSense fills it, so without this the whole page jumps down when it does -
   that is a Cumulative Layout Shift hit on every page view, and CLS is a ranking signal,
   so it costs traffic as well as being unpleasant. 100px is the shortest unit AdSense
   commonly serves at this width; taller fills grow the box rather than shifting content
   that has already been read. */
.google-ads {
    min-height: 100px;
    margin: var(--sp-5) auto 0;
    padding-inline: var(--sp-5);
    width: 100%;
    max-width: var(--container);
    box-sizing: border-box;
    text-align: center;
}

.google-ads .adsbygoogle { display: block; }

/* Never let a fixed-size unit push the page wider than the viewport. */
.google-ads ins { max-width: 100%; }

@media (max-width: 40rem) {
    .google-ads { padding-inline: var(--sp-4); min-height: 100px; }
}

/* Privacy / terms / contact in the footer bar. The bar is a space-between flex row, so
   this sits between the copyright and "back to top" and keeps the same muted treatment
   as the copyright rather than looking like primary navigation. */
.site-footer__legal ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.site-footer__legal a,
.site-footer__legal a:visited {
    font-size: var(--step--1);
    font-weight: 700;
    color: var(--ink-on-dark-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.site-footer__legal a:hover { color: var(--ink-on-dark); }
.site-footer__legal a:focus-visible { outline: 3px solid var(--focus-on-dark); outline-offset: 2px; }

/* A dashboard figure that links to the page which acts on it. The whole card is the target
   via a stretched anchor, the same pattern the public cards use - the label alone is a few
   characters and a poor click target. */
.stat--link { position: relative; transition: border-color .15s ease, box-shadow .15s ease; }
.stat--link:hover { border-color: var(--accent); box-shadow: var(--shadow-lift); }

.stat__link,
.stat__link:visited { color: inherit; text-decoration: none; }
.stat__link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.stat--link:hover .stat__label { color: var(--accent-strong); text-decoration: underline; }
.stat--link:has(.stat__link:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.stat__link:focus-visible { outline: none; }
