/*
 * Public Directory stylesheet — the whole public visual design.
 *
 * Cascade for CSS, shadow for templates (ADR 0019). This file is
 * emitted by the Directory layout, and an Instance Theme's own
 * stylesheet is emitted AFTER it, so an instance restyles the entire
 * public directory by adding one file and forking nothing. Overriding
 * a template forks it and freezes it at today's version; a cascaded
 * stylesheet keeps receiving master's improvements.
 *
 * TWO LAYERS (plan-07 phase-1). Master ships structure, spacing, the
 * type scale and a NEUTRAL indigo palette; a brand is expressed
 * entirely as VALUES of the tokens declared on :root below. Nothing
 * brand-specific ever enters this repo (ADR 0008). The rule that keeps
 * the token list honest: a theme that sets nothing but --bl-brand and
 * --bl-accent must already look branded. Everything else — spacing,
 * the type scale, the container width, the prose measure — is derived
 * here and is deliberately NOT a token.
 *
 * Every colour and font declaration below reads a token: no literal
 * colour survives outside the :root block, or a theme could not reach
 * it. The class names are the theme's API — the Theme Contract table
 * in the developer manual (Instance Theming) lists what a theme may
 * target, and renaming one is a breaking change that needs a
 * one-release alias here.
 *
 * NO JAVASCRIPT anywhere on the public directory: the nav wraps, the
 * columns are CSS. A directory's value is crawlable pages.
 *
 * Committed COPIES live at webroot/egapps_bizlist/css/ — this is the
 * plugin-asset rule CLAUDE.md documents:
 *
 *     bin/cake plugin assets copy EgappsBizlist
 *
 * Forgetting the copy presents as mysteriously unstyled markup. (The
 * theme's stylesheet is the deliberate opposite: gitignored, edited per
 * instance, served by AssetMiddleware with NO copy step, because a copy
 * of a per-instance file is a copy that can go stale.)
 */

/*
 * Figtree — the Portal's typeface, self-hosted in the app's own
 * webroot/fonts/ (variable woff2, latin + latin-ext, roman + italic).
 * The URLs are relative to this file's SERVED copy under
 * webroot/egapps_bizlist/css/, not to the plugin's path.
 */
@font-face {
    font-family: 'FigtreeVariable';
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
    src: url('../../fonts/FigtreeVariable-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'FigtreeVariable';
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
    src: url('../../fonts/FigtreeVariable-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'FigtreeVariable';
    font-style: italic;
    font-weight: 300 900;
    font-display: swap;
    src: url('../../fonts/FigtreeVariable-Italic-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'FigtreeVariable';
    font-style: italic;
    font-weight: 300 900;
    font-display: swap;
    src: url('../../fonts/FigtreeVariable-Italic-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/*
 * THE TOKENS — named by ROLE, never by colour, so a theme that swaps
 * indigo for pink does not end up with a property called "indigo".
 * These master defaults are today's hues; the developer manual carries
 * the same table.
 */
:root {
    --bl-brand: #3730a3;          /* links, actions, the phone number */
    --bl-brand-strong: #1e1b4b;   /* link titles, the home hero band (a button's hover is derived from --bl-action) */
    --bl-accent: #4338ca;         /* headlines, badges, highlights */
    --bl-accent-soft: #e0e7ff;    /* tinted objects: pills, Featured rows, the scope chip */
    --bl-band: #eef2ff;           /* the tinted Body Band background */
    --bl-ink: #111827;            /* body text */
    --bl-ink-muted: #6b7280;      /* secondary text */
    --bl-surface: #ffffff;        /* the header and the boxed objects */
    --bl-page: #f9fafb;           /* the page ground */
    --bl-line: #e5e7eb;           /* borders and dividers */
    --bl-font-body: 'FigtreeVariable', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --bl-font-heading: 'FigtreeVariable', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --bl-heading-weight: 700;
    --bl-heading-transform: none;
    --bl-radius: .5rem;

    /*
     * The roles a brand edition exposed that the list above could not
     * express (plan-07 phase-3). EVERY ONE OF THEM DEFAULTS TO AN
     * EXISTING TOKEN — `var(--bl-<existing>)`, the two non-colour ones
     * to today's literal value — so a theme written against phase 1
     * renders byte-for-byte as it did, and the rule that keeps the list
     * honest still holds: a theme that sets nothing but --bl-brand and
     * --bl-accent already looks branded. The chaining is pinned by
     * ThemeContractTest; a new token that breaks it fails the suite.
     *
     *   LINKS vs ACTIONS  --bl-brand was both the colour of a link and
     *   the fill of a button. A brand with a purple link and a magenta
     *   button needs the two apart, so a button reads --bl-action and
     *   the text on it --bl-action-ink.
     *
     *   HEADLINES vs ACCENT  --bl-accent was headlines, badges, the
     *   pill tint's parent and the focus ring at once. Headlines now
     *   read --bl-heading, so an edition can set ink headlines without
     *   dragging the focus ring and the badges to ink with them.
     *
     *   THE CHROME GROUND  the header was --bl-surface, which also
     *   grounds the boxed objects. A coloured header would have taken
     *   every card with it, hence --bl-chrome and --bl-chrome-ink.
     *
     *   THE HERO ACCENT  the strapline's colour on the hero ground
     *   (issue 04 renders it); the hero is not --bl-surface's ground,
     *   so it cannot borrow --bl-surface's role.
     *
     *   THE RADIUS SCALE  one radius could not express an edition with
     *   a tighter corner on controls and a rounder one on cards, so the
     *   tiles, the Contact Card and the images read --bl-radius-card.
     *
     *   THE BODY WEIGHT  there was a --bl-heading-weight and nothing
     *   for running text, so a body face that wants 500 had no slot.
     *
     *   THE LOGO HEIGHT  the header's cap was a literal 2.5rem, and a
     *   near-square mark at that cap is far narrower than a wide lockup
     *   — a value only a forked template could have changed.
     *
     *   THE HERO'S GRADIENT  the hero's ground is a COLOUR here and
     *   always will be: master is neutral (ADR 0008). A brand whose
     *   guidelines carry a gradient lays it over that ground as this
     *   token's value, and `none` — master's default — leaves the flat
     *   band every instance has today. The solid colour stays underneath
     *   it either way, so a gradient that fails to parse cannot leave
     *   paper text on a white hero. Like the tail below, it is a hook
     *   that is NOTHING in master rather than a role with a colour.
     *
     *   THE TAIL (plan-07 phase-5)  the size of a speech-bubble tail on
     *   the two objects on the site that SPEAK — the Browse Hero (the
     *   Directory's own words) and the Contact Card (the business's
     *   facts). Master ships the hook and no brand: at 0px the three
     *   pseudo-elements that read it are zero-sized and invisible, so
     *   every instance that does not ask renders exactly as before. A
     *   brand whose mark is speech bubbles sets a length and the mark
     *   is restated by the page's own structure. A length with a
     *   literal default, the class of --bl-logo-height.
     */
    --bl-action: var(--bl-brand);        /* a button's fill: the Call to Action, the hosted-form submit */
    --bl-action-ink: var(--bl-surface);  /* text on a button */
    --bl-heading: var(--bl-accent);      /* h1, h2, h3 */
    --bl-chrome: var(--bl-surface);      /* the header's ground */
    --bl-chrome-ink: var(--bl-ink-muted); /* the header nav's links */
    --bl-hero-accent: var(--bl-surface); /* the hero's strapline on the hero ground */
    --bl-radius-card: var(--bl-radius);  /* the tiles, the Contact Card, the images */
    --bl-body-weight: 400;               /* running text */
    --bl-logo-height: 2.5rem;            /* the header logo's cap */
    --bl-tail: 0px;                      /* the speech-bubble tail: the hero, the Contact Card, the icon badge */
    --bl-hero-gradient: none;            /* a brand's gradient over the hero's ground */
}

/* ---- Base --------------------------------------------------------- */

body {
    margin: 0;
    font-family: var(--bl-font-body);
    font-size: 1rem;
    line-height: 1.5;
    font-weight: var(--bl-body-weight);
    background: var(--bl-page);
    color: var(--bl-ink);
}
/* Headlines read --bl-heading, which DEFAULTS to --bl-accent — the
   token table's long-standing promise, and what the theme mechanism
   needs: a theme that sets nothing but --bl-brand and --bl-accent must
   already look branded, and until headings stopped reading brand-strong,
   setting --bl-accent moved nothing but the focus ring. The separate
   token is for an edition whose headlines are ink while its badges,
   pills and focus ring stay coloured. The LINK titles below (rows,
   tiles, news items, glossary, related) keep --bl-brand-strong on
   purpose: they are the name of a thing you can click, not a headline
   about it. */
h1, h2, h3 {
    font-family: var(--bl-font-heading);
    font-weight: var(--bl-heading-weight);
    text-transform: var(--bl-heading-transform);
    color: var(--bl-heading);
    line-height: 1.15;
}
/* A DISPLAY scale, not a document one (plan-07 phase-4): big confident
   headings over small quiet body copy is most of what separates a
   directory that reads as a product from one that reads as a report.
   Deliberately NOT tokens (phase-1 d.4) — the scale is master's, so
   every Directory instance gets it and a theme never has to restate
   it. */
h1 { font-size: 2.25rem; margin: 0 0 .75rem; }
h2 { font-size: 1.75rem; margin: 2.5rem 0 1rem; }
h3 { font-size: 1.125rem; margin: 0 0 .25rem; }
a { color: var(--bl-brand); }
img { max-width: 100%; }

/* ---- Icons (plan-07 phase-3) --------------------------------------- */

/*
 * One Lucide glyph, inlined by the `icon` element INSIDE the thing it
 * decorates — inside the tel: link, inside the heading — so it takes the
 * colour of that text by cascade and a theme never needs a selector to
 * recolour it (ADR 0019). Hence NO COLOUR HERE: the element sets
 * stroke="currentColor" and this rule sets size and alignment only.
 *
 * Sized in `em` so a glyph tracks the text it belongs to at every zoom
 * level and inside every heading, and `flex: none` so it is never
 * squeezed when its parent is a flex row. An icon NEVER replaces text:
 * every one is aria-hidden and the page reads identically with every
 * <svg> stripped out (IconsPublicTest).
 */
.bl-icon {
    width: 1em;
    height: 1em;
    vertical-align: -.125em;
    margin-right: .3em;
    flex: none;
}

/*
 * THE BADGE (plan-07 phase-5): the tinted ground a glyph stands in on
 * the trade tile and in the Title Block — the only two places a glyph
 * is an OBJECT rather than a mark inside a line of text.
 *
 * It is a BUBBLE, not a disc. The ground takes --bl-radius-card, the
 * same corner as the tile it sits in and the Contact Card, so the badge
 * belongs to the site's one family of boxed objects instead of being
 * the only circle on the page; and it carries the Signature's tail,
 * sized from --bl-tail like the hero's and the card's, so on a brand
 * whose mark is a speech bubble the badge is that mark at glyph scale.
 * At master's --bl-tail: 0px the tail is a zero-sized triangle and the
 * badge is simply a rounded square.
 *
 * THE ROOM ROUND THE GLYPH IS THE POINT. A Lucide glyph is drawn to the
 * edges of its 24-box and the busiest trade icons — the hammer, the
 * wrench, the bike — put their extremes in the CORNERS, which is
 * exactly where a disc has least room, so they read as cropped by a
 * ground that in fact never clipped them. The padding here is a third
 * of the glyph again; the square's corners do the rest.
 *
 * The wrapper exists because a tail is a pseudo-element and an <svg> is
 * a replaced element that generates none. It is the icon element's
 * `badge` flag, nothing more: the glyph inside is the same aria-hidden,
 * textless <svg>, and no page gains a word.
 */
.bl-icon-badge {
    position: relative;
    display: inline-block;
    border-radius: var(--bl-radius-card);
    background: var(--bl-accent-soft);
    line-height: 0;
}
/* The tail: bottom left, pointing down into the page — the hero's own
   recipe (its coloured left border over a transparent bottom one), at
   the badge's scale rather than the band's, so the two read as the same
   gesture at two sizes. Nothing at master's 0px. */
.bl-icon-badge::after {
    content: '';
    position: absolute;
    left: .75rem;
    bottom: calc(var(--bl-tail) * -.5 + 1px);
    width: 0;
    height: 0;
    border-left: calc(var(--bl-tail) * .5) solid var(--bl-accent-soft);
    border-right: 0 solid transparent;
    border-bottom: calc(var(--bl-tail) * .5) solid transparent;
    pointer-events: none;
}

/* ---- Chrome: header, nav, main, footer ---------------------------- */

/* Every band on the site is two elements: the band carries the colour
   edge to edge, an inner box centres its content in the 80rem page
   container. The Browse Hero below does the same with padding instead of
   a wrapper, so the header, the hero and the footer all line their
   content up on one column whatever the viewport. */
.bl-header {
    background: var(--bl-chrome);
    border-bottom: 1px solid var(--bl-line);
}
.bl-header-inner {
    padding: .875rem 1.25rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 2rem;
    max-width: 80rem;
    margin: 0 auto;
}
.bl-brand {
    color: var(--bl-brand-strong);
    font-family: var(--bl-font-heading);
    font-weight: var(--bl-heading-weight);
    text-transform: var(--bl-heading-transform);
    font-size: 1.25rem;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
}
.bl-logo { display: block; max-height: var(--bl-logo-height); width: auto; }
/* The nav WRAPS under the logo below the breakpoint — no hamburger and
   no script: the links a directory lives on stay visible and crawlable.

   THE AUTO MARGIN IS ON THE <nav>, NOT ON THIS LIST. The list is wrapped
   in a `<nav aria-label="Site">` landmark (plan-07 phase-5 issue 03), so
   the header row's flex ITEM is that wrapper and this list is merely its
   only child. An auto margin here has no free space to absorb and does
   nothing — which is exactly how the landmark silently pulled the whole
   nav back to the left of the header, on the desktop row and on the
   phone's second row alike. Whatever is the flex item carries the push. */
.bl-site-nav {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem 1.75rem;
    font-size: 1rem;
}
/* A nav link is a real target (plan-07 phase-5, the quality floor): an
   inline-block with half a rem above and below, so the row of links
   meets the 40px line the Promoted button beside it already stands on. */
.bl-site-nav a { color: var(--bl-chrome-ink); text-decoration: none; display: inline-block; padding: .5rem 0; }
.bl-site-nav a:hover { color: var(--bl-brand); text-decoration: underline; }
/* The Promoted Page (plan-07 phase-3): the site's ONE action, and the
   only button in the chrome. `margin-left: auto` pushes it to the end of
   a wide header row; when the nav wraps it simply sits last, which is
   where it belongs on a phone too. It is a link wearing a button, so it
   never underlines on hover — the fill is the affordance.

   The selector names the nav case as well as the bare class so the
   button's own colours beat `.bl-site-nav a:hover` above by source
   order rather than by repeating the ink declaration, which would make
   the third button look like a fourth. Its shape — padding, radius,
   weight, the 44px minimum — is the shared button recipe below. */
/* The nav sits at the right edge of the header row: the landmark
   wrapper is the flex item, so the push lives on it. A bare `nav`
   selector rather than a class, for the reason the `li` rule below
   takes a bare `li` — master owns both sides of its own chrome, and a
   contract class is a promise to a theme, not a way to reach an
   element. */
.bl-header-inner > nav { margin-left: auto; }
.bl-site-nav .bl-promoted { margin-left: auto; }
.bl-site-nav a.bl-promoted,
.bl-promoted {
    display: inline-block;
    background: var(--bl-action);
    color: var(--bl-action-ink);
    text-decoration: none;
}
/* Below the breakpoint the nav takes the WHOLE second row (plan-07
   phase-5): the links at the left under the logo, where a reader's eye
   lands after the mark, and the Promoted button pushed to the right
   edge by its own list item — the `li` is the flex item, so the margin
   goes on it. Desktop is untouched: logo left, links and button right. */
@media (max-width: 40rem) {
    .bl-header-inner > nav { margin-left: 0; width: 100%; }
    .bl-site-nav { width: 100%; }
    .bl-site-nav li:has(> .bl-promoted) { margin-left: auto; }
}
/* The page container is no longer here: every band below carries its
   own ground edge to edge and its own container padding (issue 03), so
   `main` is a bare wrapper and a band can reach the viewport's edges
   without escaping anything. */
.bl-main { display: block; }
/*
 * THE FOOTER IS CHROME (plan-07 phase-5): it reads the header's two
 * roles, so the chrome brackets the page — on master's neutral theme a
 * white band under a hairline, on a brand with a coloured header the
 * same bar again at the foot of the page. No new token: a footer that
 * needed its own ground would be a fourth colour the design did not
 * decide on. The hairline stays, so the band has an edge on a page
 * ground the same colour as it.
 */
.bl-footer {
    background: var(--bl-chrome);
    color: var(--bl-chrome-ink);
    border-top: 1px solid var(--bl-line);
    font-size: .9375rem;
}
.bl-footer-inner {
    max-width: 80rem;
    margin: 0 auto;
    padding: 2.5rem 1.25rem;
}
.bl-footer-nav { list-style: none; padding: 0; margin: 0 0 1rem; display: flex; flex-wrap: wrap; gap: .5rem 1.75rem; font-size: 1rem; }
.bl-footer-nav a { color: var(--bl-chrome-ink); text-decoration: none; display: inline-block; padding: .25rem 0; }
.bl-footer-nav a:hover { text-decoration: underline; }
/* The copyright line steps back from the links above it. */
.bl-footer-inner > :last-child { opacity: .8; }

/* ---- Buttons (plan-07 phase-5) ------------------------------------- */

/*
 * ONE BUTTON RECIPE for the site's four buttons — the Promoted Page
 * (header and hero), the Call to Action after an article, the hosted
 * form's Send — so a button reads identically wherever it stands: the
 * radius token (two of them carried a literal .375rem that the theme's
 * own radius could never reach — a token leak), 700, a 44px minimum
 * with the padding inside it. Each button's COLOURS stay in its own
 * rule, where the Theme Contract test counts them; this is shape only.
 * The Contact Card's phone is the fourth button and keeps its own size
 * (it is a number, read at a glance), sharing the weight, the radius
 * and the hover.
 *
 * The nav case is named explicitly because `.bl-site-nav a` sets its
 * own padding above and would otherwise beat the bare class.
 */
.bl-site-nav a.bl-promoted,
.bl-promoted,
.bl-news-cta a,
.bl-hosted-form .egapps-form-submit {
    padding: .625rem 1.375rem;
    min-height: 2.75rem;
    box-sizing: border-box;
    border-radius: var(--bl-radius);
    font-weight: 700;
    line-height: 1.5;
}
/*
 * HOVER IS DERIVED FROM THE TOKEN: the button's own fill, darkened by a
 * measure of ink, instead of a jump to --bl-brand-strong — which on a
 * brand whose links and buttons differ read as a second, different
 * button appearing under the pointer. A theme's action colour now
 * carries its own hover with no second value to set. `color-mix()` of
 * two tokens is not a literal colour: a theme still reaches both ends
 * of it (ADR 0019), and ThemeContractTest pins that every mix in this
 * file is of tokens only.
 */
.bl-site-nav a.bl-promoted:hover,
.bl-promoted:hover,
.bl-contact-phone:hover,
.bl-news-cta a:hover,
.bl-hosted-form .egapps-form-submit:hover {
    background: color-mix(in srgb, var(--bl-action) 85%, var(--bl-ink));
    text-decoration: none;
}

/* ---- The band rhythm (issue 03) ------------------------------------ */

/*
 * Every inner page is three bands, so the site reads as one thing and a
 * theme restyles it with one file: a white **Title Block** stating what
 * the page is, a tinted **Body Band** carrying its substance, and a
 * **Related Strip** on the page ground with what a reader might want
 * next. Each spans the viewport; the shared padding formula puts their
 * content on the same 80rem column as the header, the hero and the
 * footer.
 */
.bl-title,
.bl-band,
.bl-related,
.bl-main > .bl-home {
    padding-left: max(1.25rem, calc((100% - 80rem) / 2));
    padding-right: max(1.25rem, calc((100% - 80rem) / 2));
}
.bl-title { background: var(--bl-surface); padding-top: 3rem; padding-bottom: 2.5rem; }
.bl-band { background: var(--bl-band); padding-top: 4rem; padding-bottom: 4rem; }
.bl-related { background: var(--bl-page); padding-top: 4rem; padding-bottom: 4rem; }
/* A band's own padding is the space above its first thing. */
.bl-title > :first-child,
.bl-band > :first-child,
.bl-related > :first-child { margin-top: 0; }
.bl-children h2, .bl-crossroads h2, .bl-listings h2, .bl-news-latest h2 { margin-top: 0; }
/* One derived line under the title: the count, never a label above it. */
.bl-title-meta { color: var(--bl-ink-muted); font-size: 1rem; margin: .625rem 0 0; }
/* Prose keeps its measure wherever it sits — no band modifier needed. */
.bl-description, .bl-prose, .bl-summary, .bl-news-image { max-width: 65ch; }
/* The Title Block's head (issue 04): a Featured Listing's logo beside
   the name, wrapping under it where there is no room. */
.bl-title-head { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.bl-title-head h1 { margin: 0; }
/* A glyph in the media slot reads at title scale, not body scale — the
   Location Page's pin and the Category Page's Category Icon. */
/*
 * The Title Block's glyph takes the tile's badge at title scale (plan-07
 * phase-4), so the page and the tile that led to it visibly match. Sized
 * in rem rather than em: this ground is a fixed object beside the title,
 * not a mark inside a line of text. The Featured logo that shares this
 * slot is an <img>, so it takes none of it, and a Category with no icon
 * emits no glyph and therefore no badge at all.
 */
.bl-title-head .bl-icon-badge { padding: 1.375rem; }
.bl-title-head .bl-icon-badge::after { left: 1.25rem; }
/* The room under the badge is the tail's, and it is sized by the token
   so it is nothing at master's 0px — the same bargain the hero strikes
   with the home page's padding-top. Without it a tail would hang into
   the intro line below. */
.bl-title-head { margin-bottom: calc(var(--bl-tail) * .5); }
.bl-title-head .bl-icon {
    display: block;
    width: 1.75rem;
    height: 1.75rem;
    margin-right: 0;
}
/* The room under the hero is master's, so a theme's tail never has to
   move the page: 3.5rem clears a 1.375rem tail by the hero's own
   measure (plan-07 phase-5). */
.bl-main > .bl-home { padding-top: 3.5rem; padding-bottom: 3rem; }

/* ---- Pages -------------------------------------------------------- */

/* The page classes are the theme's per-page hook — and the tests'
   page-presence hook, now that the retired card wrapper they used to sit
   beside is gone (there is no card, so the name meant nothing). */
.bl-home,
.bl-category,
.bl-location,
.bl-crossroads-page,
.bl-listing,
.bl-glossary,
.bl-glossary-term,
.bl-info-page,
.bl-news,
.bl-news--listing,
.bl-news-article { display: block; margin: 0; }

/* A taller line so each crumb is a target of at least 24px (phase-5). */
.bl-breadcrumbs { font-size: .875rem; color: var(--bl-ink-muted); margin-bottom: 1rem; line-height: 1.8; }
.bl-breadcrumbs a { color: var(--bl-brand); text-decoration: none; }
.bl-breadcrumbs a:hover { text-decoration: underline; }
.bl-breadcrumbs span { margin: 0 .25rem; }
.bl-description { margin: 0 0 1rem; }
/* The Listing's description is the page's LEDE (plan-07 phase-5) — a
   reading size, not the row scale. The hero's own description keeps its
   size (it is set on `.bl-hero .bl-description` below). */
.bl-listing .bl-description { font-size: 1.125rem; line-height: 1.55; }
.bl-empty { color: var(--bl-ink-muted); }

/* ---- Navigation objects: child pills and Crossroads links ---------- */

/* The band's navigation sections are SIGNPOSTS, not headlines (plan-07
   phase-5): 1.25rem with a tighter margin. The listings' h2 — the
   substance — keeps the full 1.75rem. */
.bl-children h2, .bl-crossroads h2, .bl-also h2 { font-size: 1.25rem; margin-bottom: .75rem; }
/* A child pill is a PAPER OBJECT on the band (plan-07 phase-5) — the
   rule the Body Band exists for: surface fill, line border, a real
   target's padding. The tint moves to the hover, where it says "this
   one". */
.bl-children ul { list-style: none; padding: 0; margin: .75rem 0 0; display: flex; flex-wrap: wrap; gap: .625rem; }
.bl-children li a {
    display: inline-block;
    padding: .5rem 1rem;
    background: var(--bl-surface);
    border: 1px solid var(--bl-line);
    border-radius: 9999px;
    color: var(--bl-brand);
    text-decoration: none;
    font-size: 1rem;
    line-height: 1.4;
}
.bl-children li a:hover { background: var(--bl-accent-soft); border-color: var(--bl-accent-soft); }
/*
 * The Crossroads links, promoted (issue 03): "Plumbers in St Helier" is
 * the page a searcher actually wants, so it outranks a refinement —
 * a real heading and a scannable two-column list, above the child
 * pills' wrapped row.
 */
.bl-crossroads { margin-top: 2.25rem; }
.bl-crossroads-list { list-style: none; padding: 0; margin: 0; columns: 2 16rem; column-gap: 2rem; }
.bl-crossroads-list li { break-inside: avoid; margin: 0 0 .5rem; }
.bl-crossroads-list a { color: var(--bl-brand); text-decoration: none; display: inline-block; padding: .25rem 0; font-size: 1.0625rem; }
.bl-crossroads-list a:hover { text-decoration: underline; }
.bl-also { margin-top: 2rem; }

/* ---- Listing rows -------------------------------------------------- */

.bl-listings { margin-top: 2rem; }
.bl-listing-list { list-style: none; padding: 0; margin: 0; }
.bl-listing-row {
    /* LONGHAND on purpose (plan-07 phase-5): a `padding` shorthand here
       would wipe the Featured row's side padding below. */
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
    border-top: 1px solid var(--bl-line);
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}
.bl-listing-row .bl-row-body { flex: 1; min-width: 0; }
.bl-listing-row h3 { font-size: 1.25rem; margin: 0 0 .375rem; }
.bl-listing-row h3 a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-listing-row h3 a:hover { text-decoration: underline; }
.bl-row-address { color: var(--bl-ink-muted); font-size: 1rem; }
.bl-row-contact { margin-top: .375rem; font-size: 1rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem 1rem; }
/* Underline on hover only — the rule every other link on the site keeps. */
.bl-row-contact a { color: var(--bl-brand); text-decoration: none; }
.bl-row-contact a:hover { text-decoration: underline; }
/* The phone is the row's ACTION — the number IS the button, so it is
   larger and heavier than the links beside it and needs no "Call now"
   chrome (issue 03). */
.bl-row-phone { font-size: 1.125rem; font-weight: 700; text-decoration: none; }
.bl-row-phone:hover { text-decoration: underline; }
/*
 * A ROW, NEVER A CARD (phase-1 d.8, reaffirmed in phase-4): hairline
 * dividers and no box — the Contact Card is the site's one boxed
 * object, and forty boxes is a card grid by another name.
 *
 * Above the breakpoint the body becomes a two-column grid — the name,
 * address and links down the left, THE PHONE PINNED TO THE RIGHT at
 * the name's own line, which is where a reader's eye goes for the
 * action. Auto-placement fills column 1, so a row with no address
 * simply has one fewer line and nothing has to be renumbered. Below
 * it, source order rules: the phone reads directly under the address,
 * above the quieter email and website links.
 */
.bl-row-body > .bl-row-phone { display: inline-block; margin-top: .375rem; font-size: 1.1875rem; }
@media (min-width: 40rem) {
    .bl-listing-row .bl-row-body {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: 1.5rem;
        align-items: start;
    }
    .bl-row-body > h3,
    .bl-row-body > .bl-row-address,
    .bl-row-body > .bl-row-contact { grid-column: 1; }
    .bl-row-body > .bl-row-phone { grid-column: 2; grid-row: 1; margin-top: 0; }
}

/*
 * Featured (a paid Tier in force): PRESENTATION only — the same
 * details as every row. Since plan-07 phase-5 it is a PAPER OBJECT with
 * an accent edge, not a tinted slab: the accent-soft wash it used to
 * carry, spread over a whole row on a coloured band, read as muddy pink
 * on the bizlist amber (the phase-5 review's catch). Paper lifts off
 * the band exactly as the Contact Card does, the hairline frames it,
 * and a 4px `--bl-accent` left rule is the highlight — colour in a
 * shape, never a wash (the band's rule and the edition's). It was
 * already the one row drawn as a box, and it stays the only one
 * (phase-1 d.8). The badge keeps its small tint pill.
 *
 * The negative margin and the side padding put its text on the same
 * left edge as the rows around it (the 3px allows for the thicker
 * rule); the row after it drops its top hairline, which the frame
 * already draws.
 */
.bl-listing-row--featured {
    background: var(--bl-surface);
    border: 1px solid var(--bl-line);
    border-left: 4px solid var(--bl-accent);
    border-radius: var(--bl-radius-card);
    margin: .25rem -1.25rem;
    padding: 1.5rem 1.25rem 1.5rem calc(1.25rem - 3px);
}
.bl-listing-row--featured + .bl-listing-row { border-top-color: transparent; }
.bl-row-logo {
    width: 120px;
    height: 60px;
    object-fit: contain;
    background: var(--bl-surface);
    border: 1px solid var(--bl-line);
    border-radius: var(--bl-radius);
    flex: none;
}
.bl-featured-badge {
    display: inline-block;
    vertical-align: middle;
    margin-left: .375rem;
    padding: .0625rem .5rem;
    border-radius: 9999px;
    background: var(--bl-accent-soft);
    color: var(--bl-brand-strong);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
}
/* The Featured Listing PAGE: the logo and the badge are the whole of the
   Featured presentation, so the amber border is gone and master adds no
   ground of its own. The modifier stays on the markup as a theme hook. */
.bl-listing--featured { background: none; }
.bl-listing-logo {
    display: block;
    max-width: 240px;
    height: auto;
    margin: 0;
    border: 1px solid var(--bl-line);
    border-radius: var(--bl-radius);
}
.bl-listing-image { margin: 0 0 1.25rem; }
.bl-listing-image img { max-width: 100%; height: auto; border-radius: var(--bl-radius-card); }

/* ---- The Listing page: two columns and the Contact Card (issue 04) -- */

/*
 * TWO COLUMNS ON DESKTOP, ONE DOM. The Contact Card is FIRST in the
 * markup — a visitor on a phone came for the number and reads it
 * straight after the name — and the grid moves it to the right-hand
 * column above the breakpoint. Nothing is reordered, duplicated or
 * scripted to achieve that; below the breakpoint source order simply
 * rules.
 */
.bl-listing-body { display: grid; gap: 2rem; align-items: start; margin-top: .5rem; }
/* A single-column grid is an AUTO track, which grows to max-content —
   so a nowrap value in the Contact Card (below) would size the column
   and the page would scroll sideways on a phone. Pin the track below
   the desktop breakpoint; a base-level rule would override the
   two-column query beneath it by cascade order. */
@media (max-width: 59.99rem) {
    .bl-listing-body { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 60rem) {
    .bl-listing-body { grid-template-columns: minmax(0, 1fr) 22rem; }
    .bl-listing-main { grid-column: 1; grid-row: 1; }
    .bl-contact-card { grid-column: 2; grid-row: 1; }
}
.bl-listing-main > :first-child { margin-top: 0; }

/*
 * The Contact Card is the ONE boxed object on the site: the surface,
 * line and radius tokens and nothing else — no shadow kit.
 */
.bl-contact-card {
    position: relative;
    background: var(--bl-surface);
    border: 1px solid var(--bl-line);
    border-radius: var(--bl-radius-card);
    padding: 1.75rem;
    min-width: 0;
}
/* The card's tail (plan-07 phase-5, the Signature): top left, pointing
   up at the business's name in the Title Block — the paper bubble with
   the line's edge. Two triangles: the edge in --bl-line, then the paper
   fill 1px inside it, both sized by --bl-tail and both nothing at 0px.
   The business speaks; this is its bubble. */
.bl-contact-card::before {
    content: '';
    position: absolute;
    top: calc(var(--bl-tail) * -1 + 1px);
    left: 2rem;
    width: 0;
    height: 0;
    border-right: var(--bl-tail) solid var(--bl-line);
    border-top: var(--bl-tail) solid transparent;
}
.bl-contact-card::after {
    content: '';
    position: absolute;
    top: calc(var(--bl-tail) * -1 + 2.5px);
    left: calc(2rem + 1px);
    width: 0;
    height: 0;
    border-right: calc(var(--bl-tail) - 1.5px) solid var(--bl-surface);
    border-top: calc(var(--bl-tail) - 1.5px) solid transparent;
}
.bl-contact-card h2 { margin: 0 0 1rem; font-size: 1.25rem; }
.bl-contact-card .bl-address { margin: 0 0 1rem; font-size: 1.0625rem; line-height: 1.5; }
/*
 * The phone is the page's single strong action, and from plan-07
 * phase-4 it LOOKS like one: a full-width filled block in the action
 * colour, the same fill every other button on the site takes. The
 * label is still the number and its handset and nothing else — "the
 * number IS the button" on its substance, so there is still no "Call
 * now" word anywhere near it. Only the Contact Card's phone does this;
 * a listing row's phone stays a text link, because forty filled bars
 * is a toolbar, not a list.
 */
.bl-contact-phone {
    display: block;
    padding: .875rem 1rem;
    border-radius: var(--bl-radius);
    background: var(--bl-action);
    color: var(--bl-action-ink);
    font-size: 1.375rem;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
}
/* Its hover is the shared button hover in the Buttons section above. */
.bl-contact { list-style: none; padding: 0; margin: 1rem 0 0; font-size: 1rem; }
.bl-contact li { margin: .625rem 0; }
/* The card's labels are LABELS (plan-07 phase-5): small, tracked,
   uppercase, muted — the FEATURED badge's voice — so EMAIL / WEBSITE /
   FACEBOOK read as the names of the values under them, not as a second
   column of text. */
.bl-contact strong {
    display: block;
    color: var(--bl-ink-muted);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    margin-bottom: .125rem;
}
.bl-address { white-space: pre-line; }
/* A value is CUT, never broken (plan-07 phase-5): a long URL used to
   wrap mid-word (`overflow-wrap: anywhere`) and read as three lines of
   fragments. It is now one line with an ellipsis; the href is the
   whole URL, so nothing is lost but the tail of a display string. The
   column must be told not to grow to fit it — see the `minmax(0, 1fr)`
   track and the card's `min-width: 0` above. `bl-link` carries the
   typed link's kind for a theme. */
.bl-contact a,
.bl-link a {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
    overflow-wrap: normal;
    text-decoration: none;
}
.bl-contact a:hover,
.bl-link a:hover { text-decoration: underline; }
/* The opening hours live IN the card (plan-07 phase-4) — hours are a
   contact fact — set off from the contact list by a hairline. */
.bl-contact-card .bl-opening-hours {
    margin: 1.25rem 0 0;
    padding-top: 1rem;
    border-top: 1px solid var(--bl-line);
}
.bl-contact-card .bl-opening-hours h2 { margin: 0 0 .5rem; }

/*
 * The Related Strip's rows: a listing that does not suit the reader must
 * not be a dead end. Compact by design — the name, where it is, the
 * number — because the depth belongs to the page they are already on.
 */
/*
 * The heading and "View all" are ONE ROW — a section's action belongs
 * beside what it acts on. The section is the flex row and the list
 * takes a whole line of it, so the two head items sit at the row's
 * ends without a wrapper element: a wrapper would be a class in the
 * Theme Contract, which is a promise, for a box that holds nothing.
 */
.bl-related-listings {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0 1.5rem;
}
.bl-related-listings h2 { margin-top: 0; }
.bl-related-list {
    flex-basis: 100%;
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
    gap: 1rem 1.5rem;
}
.bl-related-row { border-top: 1px solid var(--bl-line); padding-top: 1rem; }
.bl-related-row h3 { margin: 0 0 .25rem; font-size: 1.125rem; }
.bl-related-row h3 a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-related-row h3 a:hover { text-decoration: underline; }
.bl-related-more { margin: 0; font-size: .9375rem; }
.bl-related-more a { color: var(--bl-brand); text-decoration: none; display: inline-block; padding: .25rem 0; }
.bl-related-more a:hover { text-decoration: underline; }

.bl-opening-hours h2 { margin-bottom: .5rem; }
.bl-hours { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; margin: 0; }
.bl-hours dt { color: var(--bl-ink-muted); }
.bl-hours dd { margin: 0; }
.bl-hours-next-day, .bl-hours-phrase { color: var(--bl-ink-muted); }
.bl-hours-phrase { margin: 0; }

/* ---- Editorial pages (issue 09) ------------------------------------ */

/* Article prose reads at a reading size (plan-07 phase-5); the 65ch
   measure above is untouched. */
.bl-prose { font-size: 1.0625rem; line-height: 1.6; }
.bl-prose p { margin: 0 0 1rem; }
.bl-summary { font-size: 1.125rem; color: var(--bl-ink-muted); margin: 0 0 1.25rem; }
.bl-dateline { display: block; color: var(--bl-ink-muted); font-size: .9375rem; margin: -.25rem 0 1rem; }
.bl-glossary-list dt { font-weight: 600; margin-top: 1rem; }
.bl-glossary-list dt a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-glossary-list dt a:hover { text-decoration: underline; }
.bl-glossary-list dd { margin: .25rem 0 0; color: var(--bl-ink-muted); }
.bl-news-list { list-style: none; padding: 0; margin: 0; }
.bl-news-item { padding: 1rem 0; border-top: 1px solid var(--bl-line); }
.bl-news-item h2 { margin: 0 0 .25rem; font-size: 1.125rem; }
.bl-news-item h2 a, .bl-news-item h3 a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-news-item h2 a:hover, .bl-news-item h3 a:hover { text-decoration: underline; }
.bl-news-item .bl-dateline { margin: 0 0 .5rem; }
.bl-news-item p { margin: 0; color: var(--bl-ink-muted); }
/* The breadcrumb's business link on a Listing article */
.bl-news-business { font-weight: 600; }
/* The Article Image (plan-05 issue 02): the main rendering on the article page, as the editor cropped it */
.bl-news-image { margin: 0 0 1.25rem; }
.bl-news-image img { display: block; max-width: 100%; height: auto; border-radius: var(--bl-radius-card); }
.bl-news-image figcaption { margin: .5rem 0 0; color: var(--bl-ink-muted); font-size: .875rem; line-height: 1.4; }
/* Thumbnails on the list surfaces (plan-05 issue 03): the thumbnail variant beside a pictured item, the text wrapping alongside; an unpictured item (no modifier) takes the full width */
.bl-news-item--pictured { display: flex; align-items: flex-start; gap: 1rem; }
.bl-news-thumb { flex: 0 0 10rem; display: block; }
.bl-news-thumb img { display: block; width: 100%; height: auto; border-radius: var(--bl-radius-card); }
.bl-news-text { flex: 1 1 auto; min-width: 0; }
.bl-news-latest { margin-top: 2rem; }
.bl-news-latest .bl-news-thumb { flex-basis: 6rem; }
@media (max-width: 30rem) { .bl-news-thumb { flex-basis: 6rem; } }
.bl-news-latest .bl-news-item h3 { margin: 0 0 .25rem; font-size: 1.0625rem; }
.bl-news-more { margin: 1rem 0 0; font-size: .9375rem; }
.bl-news-more a { color: var(--bl-brand); text-decoration: none; display: inline-block; padding: .25rem 0; }
/*
 * THE HOME PAGE'S NEWS IS A ROW OF PICTURED STORIES (plan-07 phase-5):
 * the 3:2 thumbnail at the width it was made for, above its title, in a
 * grid — the strongest surface on the site should not carry its news as
 * a text list one click from a pictured News Page. Scoped to `.bl-home`
 * and nowhere else: the News Page, the node pages and the Listing page
 * keep the thumb-beside-text row above, and the markup is the same
 * `news_latest` element on every one of them. An unpictured item simply
 * has no picture — no placeholder, no reserved box.
 */
.bl-home .bl-news-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: 1.75rem 1.5rem;
}
.bl-home .bl-news-item { border-top: 0; padding: 0; }
.bl-home .bl-news-item--pictured { display: block; }
.bl-home .bl-news-thumb { display: block; margin: 0 0 .75rem; }
.bl-home .bl-news-latest .bl-news-thumb { flex-basis: auto; }
.bl-home .bl-news-thumb img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.bl-home .bl-news-latest .bl-news-item h3 { font-size: 1.125rem; }
.bl-home .bl-news-more { margin-top: 1.5rem; }
.bl-news-scope {
    display: inline-block;
    margin-left: .5rem;
    padding: .0625rem .5rem;
    border-radius: 9999px;
    background: var(--bl-accent-soft);
    color: var(--bl-brand-strong);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    vertical-align: middle;
}
/* The Call to Action (plan-06): the button after the body on the article page — its own block, a theme restyles it by cascade */
.bl-news-cta { margin: 1.5rem 0 0; }
.bl-news-cta a {
    display: inline-block;
    background: var(--bl-action);
    color: var(--bl-action-ink);
    text-decoration: none;
}

/* ---- The Hosted Form (plan-07 phase-2) ----------------------------- */

/*
 * An Info Page may carry the form hub's Embed beneath its copy. The
 * Embed renders BARE markup (the element always sends data-no-style),
 * so this is the only stylesheet in play — and it is written entirely
 * on the tokens, so a theme restyles the form by cascade with no forked
 * template, exactly as it restyles everything else.
 *
 * The hooks below (.egapps-form*) are EgappsForm's published API for
 * client sites, documented in the contact-form guide. `bl-hosted-form`
 * is the Theme Contract's one class here; a theme may target the hooks
 * beneath it too.
 */
.bl-hosted-form { max-width: 65ch; margin: 1.5rem 0 0; }
.bl-hosted-form .egapps-form-field { margin: 0 0 1.25rem; }
.bl-hosted-form .egapps-form-label { display: block; margin: 0 0 .375rem; font-weight: 600; }
/* A field has an EDGE that reads on the band and a 44px height (plan-07
   phase-5). The border colour is derived from the muted ink so it exists
   on every ground; it is declared AFTER the line border on purpose, so a
   browser without `color-mix()` keeps `--bl-line`. */
.bl-hosted-form input,
.bl-hosted-form select,
.bl-hosted-form textarea {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: .625rem .875rem;
    min-height: 2.75rem;
    border: 1px solid var(--bl-line);
    border-color: color-mix(in srgb, var(--bl-ink-muted) 35%, var(--bl-surface));
    border-radius: var(--bl-radius);
    background: var(--bl-surface);
    color: var(--bl-ink);
    font-family: inherit;
    font-size: 1rem;
    line-height: 1.5;
}
.bl-hosted-form textarea { min-height: 8rem; resize: vertical; }
.bl-hosted-form input:focus-visible,
.bl-hosted-form select:focus-visible,
.bl-hosted-form textarea:focus-visible {
    outline: 2px solid var(--bl-accent);
    outline-offset: 1px;
}
.bl-hosted-form .egapps-form-error,
.bl-hosted-form .egapps-form-status {
    display: block;
    margin: .25rem 0 0;
    font-size: .875rem;
    color: var(--bl-ink-muted);
}
.bl-hosted-form .egapps-form-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem 1rem;
    margin: 1.25rem 0 0;
}
.bl-hosted-form .egapps-form-submit {
    border: 0;
    background: var(--bl-action);
    color: var(--bl-action-ink);
    font-family: inherit;
    font-size: 1rem;
    cursor: pointer;
}
.bl-hosted-form .egapps-form-submit[disabled] { opacity: .6; cursor: default; }
.bl-hosted-form .egapps-form-privacy { font-size: .875rem; }
.bl-hosted-form .egapps-form-captcha { margin: 1rem 0 0; }
/* The message that REPLACES the form: the success on the soft accent,
   a load failure in the muted step — neither is a page-level banner. */
.bl-hosted-form .egapps-form-message {
    margin: 0;
    padding: 1rem 1.25rem;
    border-radius: var(--bl-radius);
}
.bl-hosted-form .egapps-form-success { background: var(--bl-accent-soft); color: var(--bl-brand-strong); }
.bl-hosted-form .egapps-form-failure { color: var(--bl-ink-muted); }

/* ---- Directory Home Page (phase-3 issue 04, plan-07 issue 02) ------ */

/*
 * The Browse Hero: the one bold object on the site, and the Directory's
 * own words on it — the name, the hand-written intro, the derived count.
 * The band spans the viewport; the padding formula puts its content on
 * the same 80rem column the header and footer use, and the text runs at
 * a ~60ch measure so a long intro never becomes a wall. Left-aligned:
 * a directory is read, not admired.
 */
.bl-hero {
    position: relative;
    /* The ground is the COLOUR; a theme's gradient is an IMAGE laid over
       it (never the `background` shorthand, which would reset the colour
       and leave nothing behind a gradient that failed to parse). The
       colour is also the tail's below, so whatever a theme lays on top,
       the two agree wherever the gradient is absent. */
    background-color: var(--bl-brand-strong);
    background-image: var(--bl-hero-gradient);
    color: var(--bl-surface);
    padding: 5rem max(1.25rem, calc((100% - 80rem) / 2));
}
/* The hero's tail (plan-07 phase-5, the Signature): lower left, in the
   hero's own ground, dropping into the page — so the band reads as a
   speech bubble at page scale on a brand whose mark is one. Sized by
   --bl-tail, nothing at master's 0px; it sits on the content column
   (the same padding formula) and takes no pointer. The Directory
   speaks; this is its bubble. */
.bl-hero::after {
    content: '';
    position: absolute;
    left: calc(max(1.25rem, (100% - 80rem) / 2) + 2.5rem);
    bottom: calc(var(--bl-tail) * -1 + 1px);
    width: 0;
    height: 0;
    border-left: var(--bl-tail) solid var(--bl-brand-strong);
    border-right: 0 solid transparent;
    border-bottom: var(--bl-tail) solid transparent;
    pointer-events: none;
}
.bl-hero > * { max-width: 60ch; }
.bl-hero h1 {
    color: var(--bl-surface);
    font-size: clamp(2.5rem, 5vw, 3.5rem);
    margin: 0 0 1rem;
}
/* The Strapline (plan-07 phase-3): the Directory's own brand phrase,
   under the name and before the intro. It is a MASTHEAD line, so it
   takes the heading face and the hero's accent — the one token whose
   whole job is "a second colour that reads on the hero ground". */
.bl-hero .bl-strapline {
    font-family: var(--bl-font-heading);
    font-weight: var(--bl-heading-weight);
    text-transform: var(--bl-heading-transform);
    color: var(--bl-hero-accent);
    font-size: clamp(1.5rem, 3vw, 2rem);
    line-height: 1.2;
    margin: -.25rem 0 1rem;
}
.bl-hero .bl-description { margin: 0; font-size: 1.0625rem; }
/* The Promoted Page's button in the hero — the page's one action, LAST
   in the hero so the reader is told what the directory is before being
   asked to act. It is the HOME PAGE'S PRIMARY (plan-07 phase-5), so it
   reads one size larger than the header's copy of itself; the two-class
   selector outranks the recipe's bare class wherever the rules sit. */
.bl-hero-action { margin: 2.25rem 0 0; }
.bl-hero-action .bl-promoted { padding: .875rem 1.75rem; font-size: 1.125rem; }

.bl-home-browse { margin-top: 2.5rem; }

/*
 * Browse by trade: TILES. A Category is recognised by its name and
 * explained by its children, so each one gets room to show them.
 * Surface, line and radius tokens only — no shadow kit.
 */
.bl-tiles {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    /* 14rem: five root trades fill one row on the 80rem container, four
       at 64rem, three at 48rem (plan-07 phase-5). */
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 1.25rem;
}
.bl-tile {
    background: var(--bl-surface);
    border: 1px solid var(--bl-line);
    border-radius: var(--bl-radius-card);
    padding: 1.75rem 1.5rem;
}
.bl-tile h3 { margin: 0 0 .625rem; font-size: 1.25rem; }
.bl-tile h3 a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-tile h3 a:hover { text-decoration: underline; }
/*
 * The tile is a COLUMN (plan-07 phase-4 issue 02): the Category Icon in
 * a tinted badge, then the name, then the children. The glyph is
 * the SAME <svg> phase 3 put inside the h3's link and it is still inside
 * it, so both icon rules hold — it never replaces text, and it takes the
 * link's colour by `currentColor`, which is why there is no colour here
 * and no new token. A Category with no icon emits no glyph and therefore
 * no badge: a glyph is absent when its fact is.
 */
.bl-tile h3 .bl-icon-badge {
    display: block;
    width: fit-content;
    padding: 1rem;
    margin: 0 0 calc(1rem + var(--bl-tail) * .5);
}
.bl-tile h3 .bl-icon {
    display: block;
    width: 1.5rem;
    height: 1.5rem;
    margin-right: 0;
}
.bl-tile ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .25rem .875rem;
    font-size: 1rem;
    line-height: 1.6;
}
.bl-tile ul a { color: var(--bl-brand); text-decoration: none; }
.bl-tile ul a:hover { text-decoration: underline; }

/*
 * Browse by place: a dense LIST in columns. A reader already knows the
 * name of their town and needs to find it among many, so the shape that
 * serves them is a short scannable column, not a grid of boxes.
 */
.bl-places { list-style: none; padding: 0; margin: 0; columns: 3 14rem; column-gap: 2rem; }
.bl-places > li { break-inside: avoid; line-height: 1.7; }
.bl-places ul { list-style: none; padding: 0; margin: 0; font-size: 1rem; line-height: 1.7; }
.bl-places ul a { color: var(--bl-brand); text-decoration: none; }
.bl-places ul a:hover { text-decoration: underline; }
.bl-home-branch { margin: 0 0 1.25rem; }
.bl-home-branch h3 { margin: 0 0 .25rem; font-size: 1.125rem; }
.bl-home-branch h3 a { color: var(--bl-brand-strong); text-decoration: none; }
.bl-home-branch h3 a:hover { text-decoration: underline; }

/* ---- Quality floor ------------------------------------------------- */

/* A visible keyboard focus on everything focusable — the outline is the
   accent, so it is visible on a branded instance too. The paper ring
   inside it (plan-07 phase-5) gives the outline an edge on every ground:
   an accent alone on a coloured header can sit at 2.5:1 against it. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--bl-accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--bl-surface);
}
/* Hover transitions ONLY for readers who have not asked for less
   motion; nothing on this site animates by itself. */
@media (prefers-reduced-motion: no-preference) {
    a, .bl-children li a, .bl-news-cta a, .bl-hosted-form .egapps-form-submit { transition: color .15s ease, background-color .15s ease; }
}
