/*!
 * BestProsInTown Design System
 * ----------------------------------------------------------------------------
 * Distilled from the V3 home page (/views/new_home_page.php) so the same visual
 * language can be reused on redesigned pages and on new ones.
 *
 * HOW TO USE
 *   1. Load Inter (weights 400,500,700,800,900) — see /design-system/ for the tag.
 *   2. Load this file AFTER any legacy stylesheet:
 *        <link rel="stylesheet" href="/css_main/bestpros-ds.css">
 *   3. Put `bp-scope` on the element that owns the redesigned area — usually
 *      <body>, or a single <section> when a page is migrated piece by piece.
 *      Nothing here styles a bare element outside `.bp-scope`, so a legacy page
 *      keeps rendering exactly as before until you add that class.
 *
 * WHY EVERYTHING IS PREFIXED
 *   Legacy pages load bootstrap.min.css + style.css + vendors.css, which between
 *   them define `.btn` 684 times, `.feature` 208, `.container` 182, `.card` 69,
 *   `.arrow` 62 and `.small` 58. The reference page could use bare names because
 *   it loads none of them; a shared system cannot. Every class here is `bp-*` and
 *   every custom property is `--bp-*`.
 *
 * DO NOT wrap this file in @layer — unlayered legacy CSS would then outrank it.
 *
 * Living reference: /design-system/   ·   Brandbook: /docs/brandbook.md
 */

/* ==========================================================================
   1. TOKENS
   The only place a raw value is allowed to appear.
   ========================================================================== */

:root {
    /* --- Brand ------------------------------------------------------------
       --bp-brand is the logo indigo. It belongs to the mark only: it is not a
       UI colour and never fills a button or a link. --bp-blue is the interface
       accent, --bp-navy the heading/authority colour. */
    --bp-brand: #2e3192;
    --bp-navy: #102a66;
    --bp-blue: #185bd9;
    --bp-blue-hover: #0e51c9;
    --bp-blue-deep: #1651c2;
    /* gradient end that keeps white copy >= 5:1 */
    --bp-blue-soft: #2e6eea;
    /* decoration and gradients ONLY — 4.63:1, never for text */
    --bp-blue-50: #f3f7ff;
    --bp-blue-100: #e8f0ff;

    /* --- Neutrals ---------------------------------------------------------
       --bp-muted is one step darker than the reference #65728a so it clears AA
       on every surface below (4.24:1 -> 4.97:1 on --bp-blue-100). */
    --bp-ink: #172033;
    --bp-body: #3f4e66;
    --bp-muted: #5c677d;
    --bp-white: #fff;
    --bp-surface: #f7f9fc;
    --bp-surface-tint: #fbfdff;

    /* --- Lines ------------------------------------------------------------
       --bp-line / --bp-line-strong are DECORATIVE (1.25:1 / 1.47:1): card
       edges and dividers. Anything a user can operate gets --bp-line-control,
       which is the lightest blue-grey that still clears 3:1 on every surface
       in this palette (worst case 3.09:1 on --bp-blue-100). */
    --bp-line: #dfe7f3;
    --bp-line-strong: #c9d6ea;
    --bp-line-control: #7389b0;

    /* --- Status -----------------------------------------------------------
       Each text colour is AA on its own tint and on white. */
    --bp-success: #14804a;
    --bp-success-bg: #ecfdf5;
    --bp-warning: #9a5b0c;
    --bp-warning-bg: #fffbeb;
    --bp-danger: #b42318;
    --bp-danger-bg: #fef3f2;
    --bp-info: var(--bp-blue);
    --bp-info-bg: var(--bp-blue-100);

    /* --- Amber ------------------------------------------------------------
       --bp-amber is 2.15:1 on white: a decorative fill, never a text colour and
       never the sole carrier of meaning. Star glyphs are always accompanied by
       the rating as text (see .bp-rating). --bp-amber-ink is the readable one. */
    --bp-amber: #f59e0b;
    --bp-amber-ink: #9a5b0c;

    /* --- Type -------------------------------------------------------------
       Inter is loaded at 400/500/700/800/900. Anything between snaps to a
       neighbour — 760, 780 and 800 render identically, so do 850 and 900.
       Use these five and nothing else. */
    --bp-font: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    /* Code the reader copies (embed snippets). System faces only: nothing is
       downloaded for it. */
    --bp-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
    --bp-weight-regular: 400;
    --bp-weight-medium: 500;
    --bp-weight-bold: 700;
    --bp-weight-strong: 800;
    --bp-weight-black: 900;

    --bp-text-2xs: 11px;
    /* micro meta: addresses, captions */
    --bp-text-xs: 12px;
    /* meta, labels, eyebrow */
    --bp-text-sm: 13px;
    /* dense UI, card links */
    --bp-text-base: 14px;
    /* default UI size */
    --bp-text-md: 16px;
    /* body copy */
    --bp-text-lg: 17px;
    /* card titles */
    --bp-text-xl: 19px;
    /* small headings */
    --bp-text-2xl: 24px;
    --bp-text-3xl: 28px;

    --bp-display-1: clamp(50px, 6vw, 78px);
    --bp-display-2: clamp(34px, 4vw, 52px);
    --bp-display-3: clamp(32px, 4vw, 48px);
    --bp-lead: clamp(17px, 2vw, 20px);

    --bp-leading-tight: 1.05;
    --bp-leading-snug: 1.3;
    --bp-leading-normal: 1.55;
    --bp-leading-relaxed: 1.68;
    --bp-tracking-tight: -.04em;
    --bp-tracking-wide: .13em;

    /* --- Space ------------------------------------------------------------ */
    --bp-space-3xs: 4px;
    --bp-space-2xs: 8px;
    --bp-space-xs: 12px;
    --bp-space-sm: 16px;
    --bp-space-md: 24px;
    --bp-space-lg: 32px;
    --bp-space-xl: 48px;
    --bp-space-2xl: 64px;
    --bp-section-y: 82px;
    /* vertical rhythm between page bands */

    /* --- Radii ------------------------------------------------------------ */
    --bp-radius-xs: 10px;
    --bp-radius-sm: 12px;
    /* buttons, inputs */
    --bp-radius-md: 16px;
    /* inner tiles */
    --bp-radius-lg: 18px;
    /* cards */
    --bp-radius-xl: 20px;
    --bp-radius-2xl: 28px;
    /* feature panels */
    --bp-radius-pill: 999px;

    /* --- Elevation --------------------------------------------------------
       One shadow hue (a cool navy) at four depths, plus the blue glow that
       marks the single primary action on a screen. */
    --bp-shadow-sm: 0 8px 24px rgba(26, 48, 89, .05);
    --bp-shadow-md: 0 10px 26px rgba(26, 48, 89, .06);
    --bp-shadow-lg: 0 20px 50px rgba(26, 48, 89, .12);
    --bp-shadow-xl: 0 28px 80px rgba(18, 43, 99, .16);
    --bp-shadow-accent: 0 12px 26px rgba(24, 91, 217, .22);

    /* --- Motion ----------------------------------------------------------- */
    --bp-transition: .16s ease;
    --bp-transition-slow: .3s ease;

    /* --- Layout ------------------------------------------------------------ */
    --bp-container: 1200px;
    --bp-gutter: 20px;
    --bp-z-header: 50;
    --bp-z-dropdown: 900;
    --bp-z-dialog: 1000;
    --bp-z-skip: 1100;
    /* The z-scale had no slot for a bottom bar; it sits below the header. */
    --bp-z-actionbar: 40;

    /* The profile rail, and the offset a sticky header needs so it does not
       swallow every anchor target: 72px of header plus 24px. */
    --bp-rail: 344px;
    --bp-stick: 96px;

    /* --- Focus -------------------------------------------------------------
       One ring for the whole system. Never remove it without a replacement. */
    --bp-focus-ring: 0 0 0 3px rgba(24, 91, 217, .38);
    --bp-focus-color: var(--bp-blue);
}

/* Backgrounds that carry the brand. Declared as tokens so a band never
   hand-rolls its own gradient. */
:root {
    --bp-gradient-hero:
        radial-gradient(circle at 16% 10%, rgba(46, 110, 234, .09), transparent 25rem),
        radial-gradient(circle at 84% 18%, rgba(24, 91, 217, .10), transparent 28rem),
        linear-gradient(180deg, #f8fbff 0%, #edf4ff 72%, #fff 100%);
    --bp-gradient-soft: linear-gradient(180deg, #f9fbff, #eef5ff);
    --bp-gradient-brand: linear-gradient(135deg, var(--bp-navy), var(--bp-blue-deep));
    --bp-gradient-media: linear-gradient(135deg, var(--bp-navy), var(--bp-blue-soft));
    /* The evidence band (see .bp-proof). One flat deep blue rather than the
       brand gradient, so a row of white screenshots sits on an even ground:
       white on it is 8.79:1 and the muted rgba(255,255,255,.78) is 6.03:1. */
    --bp-gradient-proof:
        radial-gradient(circle at 88% 14%, rgba(255, 255, 255, .13), transparent 24rem),
        #0c4897;
}

/* ==========================================================================
   2. SCOPE
   Opt in with `class="bp-scope"`. Everything below is a descendant of it, so
   this file is inert on a page that has not opted in.
   ========================================================================== */

.bp-scope {
    color: var(--bp-ink);
    font-family: var(--bp-font);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-normal);
    -webkit-font-smoothing: antialiased;
}

.bp-scope *,
.bp-scope *::before,
.bp-scope *::after {
    box-sizing: border-box;
}

.bp-scope h1,
.bp-scope h2,
.bp-scope h3,
.bp-scope h4,
.bp-scope h5,
.bp-scope h6 {
    margin-top: 0;
    color: var(--bp-navy);
    font-family: var(--bp-font);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
}

.bp-scope svg {
    display: block;
    max-width: 100%;
}

/* Buttons take the scope's font at zero specificity, so a component that sets
   its own — .bp-btn's 14px/800, .bp-pill, the accordion trigger — wins. As a
   plain `.bp-scope button` (0,1,1) this reset outranked every single-class
   component: a <button class="bp-btn"> rendered at 16px/400 while an
   <a class="bp-btn"> beside it was 14px/800. Fields keep the stronger form on
   purpose: they must stay at the inherited 16px, or iOS zooms in on focus. */
:where(.bp-scope) button {
    font: inherit;
}

.bp-scope input,
.bp-scope select,
.bp-scope textarea {
    font: inherit;
}

/* The one global rule the reference design is missing entirely: it has no
   :focus-visible anywhere, and the hero search actively deletes the browser
   default. Keyboard users get a ring on everything inside the scope. */
.bp-scope :focus-visible {
    outline: 3px solid var(--bp-focus-color);
    outline-offset: 2px;
    border-radius: var(--bp-radius-xs);
}

/* ==========================================================================
   3. LAYOUT
   ========================================================================== */

.bp-container {
    width: min(calc(100% - var(--bp-gutter) * 2), var(--bp-container));
    margin-inline: auto;
}

.bp-section {
    padding-block: var(--bp-section-y);
}

.bp-section--soft {
    background: var(--bp-surface);
}

.bp-section--gradient {
    background: var(--bp-gradient-soft);
}

.bp-section--flush {
    padding-block: 0;
}

/* Heading block + trailing actions. Collapses to a column on small screens so
   the actions never squeeze the copy. */
.bp-section-head {
    margin-bottom: 30px;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--bp-space-md);
}

.bp-section-head>div {
    max-width: 760px;
}

.bp-section-head h2 {
    margin-bottom: var(--bp-space-2xs);
    font-size: var(--bp-display-3);
    line-height: 1.08;
}

.bp-section-head p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
}

.bp-section-actions {
    display: flex;
    align-items: center;
    gap: var(--bp-space-sm);
    flex: 0 0 auto;
}

/* Generic responsive grid: set --bp-cols, it drops to one column on phones. */
.bp-grid {
    --bp-cols: 3;
    display: grid;
    grid-template-columns: repeat(var(--bp-cols), minmax(0, 1fr));
    gap: var(--bp-space-sm) var(--bp-space-md);
}

.bp-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--bp-space-xl);
    align-items: center;
}

.bp-divider {
    height: 1px;
    margin-block: var(--bp-space-md);
    border: 0;
    background: var(--bp-line);
}

/* ==========================================================================
   4. TYPOGRAPHY
   ========================================================================== */

.bp-display-1 {
    margin: 0 0 22px;
    color: var(--bp-navy);
    font-size: var(--bp-display-1);
    font-weight: var(--bp-weight-black);
    line-height: .98;
    letter-spacing: var(--bp-tracking-tight);
    text-wrap: balance;
}

.bp-display-2 {
    margin: 0 0 var(--bp-space-sm);
    color: var(--bp-navy);
    font-size: var(--bp-display-2);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-tight);
    letter-spacing: var(--bp-tracking-tight);
}

.bp-display-3 {
    margin: 0 0 var(--bp-space-2xs);
    color: var(--bp-navy);
    font-size: var(--bp-display-3);
    font-weight: var(--bp-weight-black);
    line-height: 1.08;
    letter-spacing: var(--bp-tracking-tight);
}

.bp-heading {
    margin: 0 0 var(--bp-space-xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-3xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.03em;
}

.bp-subheading {
    margin: 0 0 var(--bp-space-3xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-xl);
    font-weight: var(--bp-weight-black);
}

/* The kicker above a heading. Always uppercase, always ends up as the smallest
   blue thing on the band. */
.bp-eyebrow {
    margin-bottom: 14px;
    display: inline-flex;
    align-items: center;
    gap: 9px;
    color: var(--bp-blue);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-wide);
    text-transform: uppercase;
}

.bp-eyebrow::before {
    content: "";
    width: 8px;
    height: 8px;
    flex: 0 0 8px;
    border-radius: 50%;
    background: var(--bp-amber);
    box-shadow: 0 0 0 5px rgba(245, 158, 11, .14);
}

.bp-lead {
    margin: 0 0 28px;
    color: var(--bp-body);
    font-size: var(--bp-lead);
    line-height: var(--bp-leading-relaxed);
}

.bp-text {
    margin: 0 0 var(--bp-space-sm);
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
}

.bp-text--ink {
    color: var(--bp-body);
}

.bp-emphasis {
    margin: 18px 0 0;
    color: var(--bp-navy);
    font-size: var(--bp-text-xl);
    font-weight: var(--bp-weight-black);
}

.bp-meta {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    line-height: 1.45;
}

.bp-meta--tiny {
    font-size: var(--bp-text-2xs);
}

/* Link colours are repeated across :link/:visited/:hover/:focus/:active on
   purpose. On a legacy page an `a { color: … }` rule can otherwise outrank a
   single-state declaration and silently repaint the label. */
.bp-link,
.bp-link:link,
.bp-link:visited {
    color: var(--bp-blue);
    text-decoration: none;
}

.bp-link:hover,
.bp-link:focus,
.bp-link:active {
    color: var(--bp-blue-hover);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.bp-text-link,
.bp-text-link:link,
.bp-text-link:visited,
.bp-text-link:hover,
.bp-text-link:focus,
.bp-text-link:active {
    color: var(--bp-blue);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-black);
    text-decoration: none;
}

.bp-text-link:hover {
    color: var(--bp-blue-hover);
}

/* ==========================================================================
   5. BUTTONS
   One primary action per view. Secondary is the outlined default; ghost is for
   dense toolbars; --on-dark is the pair used on the CTA band.
   ========================================================================== */

.bp-btn,
.bp-btn:link,
.bp-btn:visited {
    min-height: 48px;
    padding: 0 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--bp-space-2xs);
    border: 1px solid transparent;
    border-radius: var(--bp-radius-sm);
    color: var(--bp-white);
    background: var(--bp-blue);
    box-shadow: var(--bp-shadow-accent);
    font-family: var(--bp-font);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    line-height: 1;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-btn:hover,
.bp-btn:focus,
.bp-btn:active {
    color: var(--bp-white);
    background: var(--bp-blue-hover);
    text-decoration: none;
}

.bp-btn:hover {
    transform: translateY(-1px);
}

.bp-btn svg {
    width: 18px;
    flex: 0 0 18px;
}

.bp-btn--secondary,
.bp-btn--secondary:link,
.bp-btn--secondary:visited,
.bp-btn--secondary:hover,
.bp-btn--secondary:focus,
.bp-btn--secondary:active {
    color: var(--bp-navy);
    background: var(--bp-white);
    /* --bp-line-control, not --bp-line-strong: this border is the button's
       whole shape, so it has to clear 3:1 on the band behind it. */
    border-color: var(--bp-line-control);
    box-shadow: none;
}

.bp-btn--secondary:hover {
    background: var(--bp-blue-50);
}

.bp-btn--ghost,
.bp-btn--ghost:link,
.bp-btn--ghost:visited,
.bp-btn--ghost:hover,
.bp-btn--ghost:focus,
.bp-btn--ghost:active {
    color: var(--bp-blue);
    background: transparent;
    border-color: var(--bp-line-control);
    box-shadow: none;
}

.bp-btn--ghost:hover {
    background: var(--bp-blue-50);
}

/* Inverted pair for use on --bp-gradient-brand. */
.bp-btn--on-dark,
.bp-btn--on-dark:link,
.bp-btn--on-dark:visited,
.bp-btn--on-dark:hover,
.bp-btn--on-dark:focus,
.bp-btn--on-dark:active {
    color: var(--bp-navy);
    background: var(--bp-white);
    border-color: transparent;
    box-shadow: none;
}

.bp-btn--on-dark-outline,
.bp-btn--on-dark-outline:link,
.bp-btn--on-dark-outline:visited,
.bp-btn--on-dark-outline:hover,
.bp-btn--on-dark-outline:focus,
.bp-btn--on-dark-outline:active {
    color: var(--bp-white);
    background: transparent;
    /* .7 alpha, not the reference .4: at .4 the border is 2.19:1 against the
       blue end of the gradient and the button loses its only edge. */
    border-color: rgba(255, 255, 255, .7);
    box-shadow: none;
}

.bp-btn--on-dark-outline:hover {
    background: rgba(255, 255, 255, .12);
}

.bp-btn--sm {
    min-height: 40px;
    padding: 0 14px;
    font-size: var(--bp-text-sm);
}

.bp-btn--lg {
    min-height: 56px;
    padding: 0 28px;
    font-size: var(--bp-text-md);
}

.bp-btn--block {
    width: 100%;
}

.bp-btn:disabled,
.bp-btn[aria-disabled="true"] {
    color: #6b7280;
    background: #e5e9f0;
    border-color: transparent;
    box-shadow: none;
    transform: none;
    cursor: not-allowed;
}

/* Round icon button: sliders, disclosure toggles, close buttons. */
.bp-icon-btn {
    width: 42px;
    height: 42px;
    flex: 0 0 42px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid var(--bp-line-control);
    border-radius: 50%;
    color: var(--bp-navy);
    background: var(--bp-white);
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-icon-btn svg {
    width: 20px;
}

.bp-icon-btn:hover:not(:disabled) {
    color: var(--bp-white);
    border-color: var(--bp-blue);
    background: var(--bp-blue);
}

/* An explicit disabled pair beats opacity: at .35 the reference chevron falls
   to 2.08:1 and its border to 1.13:1, which is not a state anyone chose. */
.bp-icon-btn:disabled {
    color: #9aa5b8;
    border-color: var(--bp-line-strong);
    background: var(--bp-surface);
    cursor: default;
}

.bp-icon-btn--sm {
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
}

.bp-icon-btn--sm svg {
    width: 14px;
}

/* ==========================================================================
   6. FORMS
   Absent from the reference design — it only ever shows the shared search
   widget — but every page being redesigned (add business, contact, payment)
   needs them, so they are built here from the same tokens.
   ========================================================================== */

.bp-field {
    display: grid;
    gap: 6px;
    margin-bottom: var(--bp-space-sm);
}

/* The label is always rendered and always associated with the control.
   A placeholder is a hint, never a label. */
.bp-label {
    color: #35445b;
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
    line-height: var(--bp-leading-snug);
}

.bp-label .bp-required {
    color: var(--bp-danger);
}

.bp-input,
.bp-select,
.bp-textarea {
    width: 100%;
    min-height: 48px;
    padding: 12px 14px;
    border: 1px solid var(--bp-line-control);
    border-radius: var(--bp-radius-sm);
    color: var(--bp-ink);
    background: var(--bp-white);
    font-family: var(--bp-font);
    font-size: var(--bp-text-base);
    transition: var(--bp-transition);
}

.bp-textarea {
    min-height: 120px;
    padding-block: 12px;
    line-height: var(--bp-leading-normal);
    resize: vertical;
}

.bp-select {
    padding-right: 40px;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23102a66' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    background-size: 18px;
}

/* Placeholder uses --bp-muted (5.69:1). The reference #8b96a8 is 2.99:1, and
   because its label is not wired up the placeholder is doing the labelling. */
.bp-input::placeholder,
.bp-textarea::placeholder {
    color: var(--bp-muted);
    opacity: 1;
}

.bp-input:hover,
.bp-select:hover,
.bp-textarea:hover {
    border-color: var(--bp-navy);
}

.bp-input:focus,
.bp-select:focus,
.bp-textarea:focus {
    border-color: var(--bp-blue);
    box-shadow: var(--bp-focus-ring);
    outline: none;
}

.bp-input[aria-invalid="true"],
.bp-select[aria-invalid="true"],
.bp-textarea[aria-invalid="true"] {
    border-color: var(--bp-danger);
}

.bp-input:disabled,
.bp-select:disabled,
.bp-textarea:disabled {
    color: #6b7280;
    background: var(--bp-surface);
    cursor: not-allowed;
}

.bp-help {
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.bp-error {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--bp-danger);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
}

/* Checkbox / radio: native control, sized up to the 20px the rest of the
   system's hit areas imply, with the label as the click target. */
.bp-choice {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding-block: 6px;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
    cursor: pointer;
}

.bp-choice input {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    margin: 1px 0 0;
    accent-color: var(--bp-blue);
    cursor: pointer;
}

.bp-fieldset {
    margin: 0 0 var(--bp-space-md);
    padding: 0;
    border: 0;
}

.bp-fieldset legend {
    padding: 0;
    margin-bottom: var(--bp-space-2xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
}

/* Search bar: the shared /views/layout/search.php widget restyled to the
   system. Wrap the include in <div class="bp-searchbar">. */
.bp-searchbar {
    position: relative;
    z-index: 3;
    width: 100%;
    max-width: 1060px;
    margin-inline: auto;
    padding: 11px;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-lg);
    background: rgba(255, 255, 255, .95);
    box-shadow: 0 30px 80px rgba(18, 43, 99, .19);
}

.bp-searchbar .search-container {
    grid-template-columns: minmax(0, 1.16fr) minmax(0, .84fr) 146px;
    gap: 10px;
    font-family: var(--bp-font);
}

.bp-searchbar .field-group {
    min-width: 0;
    padding: 0 14px;
    border-right: 1px solid var(--bp-line);
    text-align: left;
}

.bp-searchbar .field-group+.field-group {
    border-right: 0;
}

.bp-searchbar .input-wrapper {
    min-height: 50px;
    grid-template-columns: 24px minmax(0, 1fr) auto;
    gap: 11px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
}

/* The component removes its own focus ring; put one back on the whole field. */
.bp-searchbar .input-wrapper:focus-within {
    border: 0;
    border-radius: var(--bp-radius-xs);
    box-shadow: var(--bp-focus-ring);
}

.bp-searchbar .absolute-label {
    position: static;
    max-width: none;
    padding: 0;
    transform: none;
    background: transparent;
    color: #35445b;
    font-family: var(--bp-font);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
    line-height: var(--bp-leading-snug);
}

.bp-searchbar .search-input {
    width: 100%;
    height: 24px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--bp-ink);
    font-family: var(--bp-font);
    font-size: var(--bp-text-base);
}

.bp-searchbar .search-input::placeholder {
    color: var(--bp-muted);
}

.bp-searchbar .search-button {
    min-width: 146px;
    min-height: 46px;
    padding: 0 20px;
    border-radius: var(--bp-radius-sm);
    background: var(--bp-blue);
    box-shadow: var(--bp-shadow-accent);
    font-family: var(--bp-font);
    font-size: 15px;
    font-weight: var(--bp-weight-black);
    letter-spacing: .02em;
}

.bp-searchbar .search-button:hover {
    background: var(--bp-blue-hover);
}

.bp-searchbar .suggestions-container {
    left: 0;
    z-index: var(--bp-z-dropdown);
    border-color: var(--bp-line);
    border-radius: var(--bp-radius-sm);
    box-shadow: 0 18px 44px rgba(18, 43, 99, .16);
    text-align: left;
}

/* ==========================================================================
   7. SURFACES
   ========================================================================== */

/* The white box a section's content sits in. */
.bp-panel {
    padding: 28px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-md);
}

.bp-panel--wide {
    padding: 34px;
}

/* The large two-column band: copy on one side, a visual on the other. */
.bp-feature {
    padding: 46px;
    display: grid;
    grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
    gap: var(--bp-space-xl);
    align-items: center;
    border: 1px solid #d5e2f5;
    border-radius: var(--bp-radius-2xl);
    background: rgba(255, 255, 255, .9);
    box-shadow: var(--bp-shadow-xl);
}

.bp-feature h2 {
    margin-bottom: var(--bp-space-sm);
    font-size: var(--bp-display-2);
    line-height: var(--bp-leading-tight);
}

.bp-feature p {
    margin: 0 0 15px;
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
}

/* Full-bleed closing call to action. */
.bp-cta {
    padding-block: 72px;
    color: var(--bp-white);
    background:
        radial-gradient(circle at 85% 20%, rgba(255, 255, 255, .14), transparent 22rem),
        var(--bp-gradient-brand);
}

.bp-cta__inner {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 50px;
    align-items: center;
}

.bp-cta h2 {
    max-width: 760px;
    margin-bottom: 14px;
    color: var(--bp-white);
    font-size: clamp(36px, 4vw, 54px);
    line-height: 1.06;
}

/* rgba(255,255,255,.8) drops to 4.38:1 on the blue end once the band stacks on
   phones; a solid tint holds above 5:1 across the whole gradient. */
.bp-cta p {
    max-width: 760px;
    margin: 0;
    color: #dbe4f7;
    font-size: var(--bp-text-md);
}

.bp-cta .bp-eyebrow {
    color: var(--bp-white);
}

.bp-cta__actions {
    display: flex;
    flex-direction: column;
    gap: var(--bp-space-xs);
}

.bp-cta__actions .bp-btn {
    min-width: 190px;
}

/* Row of reassurance statements under a hero. */
.bp-trustbar {
    position: relative;
    z-index: 2;
    border-block: 1px solid rgba(201, 214, 234, .8);
    background: rgba(255, 255, 255, .88);
}

.bp-trustbar__grid {
    min-height: 92px;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
    align-items: center;
}

.bp-trustbar__item {
    display: flex;
    align-items: center;
    gap: 13px;
    color: #40506a;
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
}

/* ==========================================================================
   8. ICON TILES
   Inline SVG only: 24x24 viewBox, fill="none", stroke="currentColor",
   stroke-width="2". Never a raster icon, never an icon font.
   ========================================================================== */

.bp-icon-tile {
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    display: grid;
    place-items: center;
    border-radius: var(--bp-radius-sm);
    color: var(--bp-blue);
    background: var(--bp-blue-100);
}

.bp-icon-tile svg {
    width: 20px;
}

.bp-icon-tile--md {
    width: 44px;
    height: 44px;
    flex-basis: 44px;
    border-radius: 14px;
}

.bp-icon-tile--md svg {
    width: 23px;
}

.bp-icon-tile--lg {
    width: 54px;
    height: 54px;
    flex-basis: 54px;
    border-radius: var(--bp-radius-md);
}

.bp-icon-tile--lg svg {
    width: 28px;
}

.bp-icon-tile--solid {
    color: var(--bp-white);
    background: linear-gradient(145deg, var(--bp-blue), var(--bp-navy));
}

/* ==========================================================================
   9. CARDS
   A row of cards is as tall as its tallest card, so every block that can wrap
   gets a fixed line budget. That way the cards come out the same height on
   their own and every footer lands on one baseline.
   ========================================================================== */

.bp-card {
    overflow: hidden;
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-md);
    transition: .18s;
}

.bp-card:hover {
    transform: translateY(-4px);
    border-color: #b9cae1;
    box-shadow: var(--bp-shadow-lg);
}

.bp-card__media {
    height: 150px;
    flex: 0 0 150px;
    overflow: hidden;
    background: var(--bp-gradient-media);
}

.bp-card__media a {
    display: block;
    width: 100%;
    height: 100%;
}

.bp-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.bp-card__body {
    padding: 18px;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}

.bp-card__title {
    margin: 0;
    display: -webkit-box;
    min-height: 2.6em;
    overflow: hidden;
    color: var(--bp-navy);
    font-size: var(--bp-text-lg);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-snug);
    letter-spacing: normal;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.bp-card__title a,
.bp-card__title a:link,
.bp-card__title a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-card__title a:hover,
.bp-card__title a:focus {
    color: var(--bp-blue);
}

/* One line, ellipsis. */
.bp-card__meta {
    margin: 3px 0 0;
    overflow: hidden;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    line-height: 1.45;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Two lines, always occupies two so the cards stay aligned. */
.bp-card__meta--2line {
    display: -webkit-box;
    min-height: 2.9em;
    white-space: normal;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.bp-card__excerpt {
    margin: var(--bp-space-xs) 0 0;
    display: -webkit-box;
    min-height: 4.65em;
    overflow: hidden;
    color: #4b5a70;
    font-size: var(--bp-text-sm);
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

.bp-card__excerpt--5 {
    -webkit-line-clamp: 5;
}

/* Whatever slack a stretched card still has collects above the divider, so the
   footer keeps its own gap and stays flush with the card bottom. */
.bp-card__body>*:nth-last-child(2) {
    margin-bottom: auto;
}

.bp-card__footer {
    margin-top: var(--bp-space-sm);
    padding-top: 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-xs);
    border-top: 1px solid var(--bp-line);
}

.bp-card__action,
.bp-card__action:link,
.bp-card__action:visited,
.bp-card__action:hover,
.bp-card__action:focus,
.bp-card__action:active {
    color: var(--bp-blue);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
    text-decoration: none;
}

.bp-card__aside,
.bp-card__aside:link,
.bp-card__aside:visited {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
    text-decoration: none;
    white-space: nowrap;
}

.bp-card__aside:hover,
.bp-card__aside:focus {
    color: var(--bp-blue);
}

/* Not every aside is a phone number — the blog card's is a plain date, with
   an icon bpIcon() returns at no size of its own (it relies on its
   container, same as everywhere else it is used). */
.bp-card__aside svg {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
}

/* Compact horizontal tile: thumbnail + title, used for stories and related
   links. */
.bp-tile {
    padding: var(--bp-space-xs);
    display: grid;
    grid-template-columns: 86px minmax(0, 1fr);
    gap: var(--bp-space-xs);
    align-items: center;
    min-height: 118px;
    border: 1px solid var(--bp-line);
    border-radius: 14px;
    background: var(--bp-white);
}

.bp-tile__thumb {
    height: 74px;
    overflow: hidden;
    border-radius: var(--bp-radius-xs);
    background: var(--bp-gradient-media);
}

.bp-tile__thumb a,
.bp-tile__thumb img {
    display: block;
    width: 100%;
    height: 100%;
}

.bp-tile__thumb img {
    object-fit: cover;
}

.bp-tile h3 {
    margin: 0 0 6px;
    display: -webkit-box;
    overflow: hidden;
    font-size: var(--bp-text-base);
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.bp-tile h3 a,
.bp-tile h3 a:link,
.bp-tile h3 a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-tile h3 a:hover,
.bp-tile h3 a:focus {
    color: var(--bp-blue);
}

.bp-tile__link,
.bp-tile__link:link,
.bp-tile__link:visited,
.bp-tile__link:hover,
.bp-tile__link:focus {
    color: var(--bp-blue);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
    text-decoration: none;
}

/* Bordered tile with a centred icon over a label. */
.bp-info-tile {
    min-height: 150px;
    padding: 18px 14px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
}

.bp-info-tile .bp-icon-tile {
    margin-bottom: var(--bp-space-xs);
}

.bp-info-tile h3 {
    margin: 0 0 5px;
    font-size: var(--bp-text-base);
}

.bp-info-tile p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
}

/* Wide callout: icon, heading, one line of explanation. */
.bp-callout {
    padding: 22px;
    display: grid;
    grid-template-columns: 54px 1fr;
    gap: var(--bp-space-sm);
    align-items: center;
    border: 1px solid #b9cae4;
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: 0 10px 24px rgba(26, 48, 89, .08);
}

.bp-callout h3 {
    margin: 0 0 var(--bp-space-3xs);
    font-size: var(--bp-text-xl);
}

.bp-callout p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-sm);
}

/* ==========================================================================
   10. RATING
   The stars are decoration. The rating value is text, always, so the amber
   glyphs (2.15:1) never carry the meaning on their own.
     <div class="bp-rating">
       <span class="bp-rating__value">4.9</span>
       <span class="bp-stars" aria-hidden="true">★★★★★</span>
       <span class="bp-rating__count">(82)</span>
     </div>
   ========================================================================== */

.bp-rating {
    margin-top: var(--bp-space-xs);
    display: flex;
    align-items: center;
    gap: 7px;
    color: #526078;
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
}

.bp-stars {
    color: var(--bp-amber);
    letter-spacing: 2px;
}

.bp-rating__value {
    color: var(--bp-navy);
    font-weight: var(--bp-weight-strong);
}

.bp-rating__count {
    color: var(--bp-muted);
}

/* ==========================================================================
   11. REVIEW CARD
   ========================================================================== */

.bp-review {
    min-width: 0;
    padding: 20px;
    display: flex;
    flex-direction: column;
    min-height: 100%;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-sm);
}

.bp-review .bp-rating {
    margin: 0 0 9px;
}

.bp-review blockquote {
    margin: 0;
    display: -webkit-box;
    overflow: hidden;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
    line-height: 1.62;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
}

.bp-review__business {
    margin-top: auto;
    padding-top: 14px;
    display: grid;
    gap: var(--bp-space-3xs);
}

.bp-review__business strong {
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
}

.bp-review__business strong a,
.bp-review__business strong a:link,
.bp-review__business strong a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-review__business strong a:hover,
.bp-review__business strong a:focus {
    color: var(--bp-blue);
}

.bp-review__business address {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-style: normal;
    line-height: 1.45;
}

/* Direct child only: the phone link. The business-name link sits inside the
   <strong> above and keeps that element's navy, so an unscoped descendant
   selector would flatten the name to phone-link styling. */
.bp-review__business>a,
.bp-review__business>a:link,
.bp-review__business>a:visited,
.bp-review__business>a:hover,
.bp-review__business>a:focus {
    width: max-content;
    color: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-strong);
    text-decoration: none;
}

/* ==========================================================================
   12. SLIDER
   Scroll-snap track driven by two arrows; the scrollbar is hidden but the
   track still scrolls with a wheel, a trackpad or a keyboard.
   ========================================================================== */

.bp-slider {
    --bp-per: 4;
    --bp-slider-gap: 18px;
    position: relative;
}

.bp-slider__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - var(--bp-slider-gap) * (var(--bp-per) - 1)) / var(--bp-per));
    gap: var(--bp-slider-gap);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 6px 0 var(--bp-space-sm);
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.bp-slider__track::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
}

.bp-slider__track>* {
    min-width: 0;
    scroll-snap-align: start;
}

.bp-slider__nav {
    display: flex;
    align-items: center;
    gap: var(--bp-space-2xs);
    flex: 0 0 auto;
}

.bp-slider--3 {
    --bp-per: 3;
    --bp-slider-gap: 16px;
}

.bp-slider--2 {
    --bp-per: 2;
}

/* ==========================================================================
   13. DIRECTORY
   Link-dense listing (states -> cities, categories -> subcategories) where each
   group keeps one visible row and a chevron reveals the rest.
   ========================================================================== */

.bp-directory__group+.bp-directory__group {
    margin-top: 36px;
    padding-top: 34px;
    border-top: 1px solid var(--bp-line);
}

.bp-directory__heading {
    margin: 0 0 22px;
    color: var(--bp-navy);
    font-size: var(--bp-text-3xl);
    letter-spacing: -.03em;
}

.bp-directory__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 18px 30px;
}

.bp-region {
    padding-bottom: 13px;
    border-bottom: 1px solid #edf1f7;
}

.bp-region__head {
    min-height: 22px;
    margin-bottom: 7px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-2xs);
}

.bp-region__name {
    margin: 0;
    color: var(--bp-ink);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.01em;
}

.bp-region__name a,
.bp-region__name a:link,
.bp-region__name a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-region__name a:hover,
.bp-region__name a:focus {
    color: var(--bp-blue);
}

.bp-region__toggle {
    display: none;
}

.bp-region.is-collapsible .bp-region__toggle {
    display: grid;
}

.bp-region__toggle svg {
    transition: transform var(--bp-transition);
}

.bp-region.is-open .bp-region__toggle svg {
    transform: rotate(180deg);
}

.bp-region__links {
    display: flex;
    flex-wrap: wrap;
    gap: 5px 11px;
}

/* Collapsed: one row. `visibility` matters — clipping with overflow alone
   leaves the hidden links in the tab order while the toggle still reports
   aria-expanded="false". */
.bp-region.is-collapsible .bp-region__links {
    max-height: 18px;
    overflow: hidden;
}

.bp-region.is-collapsible .bp-region__links>*:not(:first-child) {
    visibility: hidden;
}

.bp-region.is-collapsible.is-open .bp-region__links {
    max-height: none;
    overflow: visible;
}

.bp-region.is-collapsible.is-open .bp-region__links>* {
    visibility: visible;
}

.bp-region__links a,
.bp-region__links a:link,
.bp-region__links a:visited {
    color: var(--bp-blue);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
    line-height: 1.45;
    text-decoration: none;
    /* keeps a city on one line so the collapsed row height is predictable */
    white-space: nowrap;
}

.bp-region__links a:hover,
.bp-region__links a:focus {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* ==========================================================================
   14. BADGES, PILLS, ALERTS
   ========================================================================== */

.bp-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--bp-radius-pill);
    color: var(--bp-navy);
    background: var(--bp-blue-100);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-strong);
    letter-spacing: .02em;
    line-height: 1.4;
    white-space: nowrap;
}

.bp-badge svg {
    width: 13px;
    flex: 0 0 13px;
}

.bp-badge--success {
    color: var(--bp-success);
    background: var(--bp-success-bg);
}

.bp-badge--warning {
    color: var(--bp-warning);
    background: var(--bp-warning-bg);
}

.bp-badge--danger {
    color: var(--bp-danger);
    background: var(--bp-danger-bg);
}

.bp-badge--neutral {
    color: var(--bp-body);
    background: var(--bp-surface);
}

/* Award / verified badge: the one place amber is allowed to carry a label,
   and it does so as --bp-amber-ink on a pale tint, never white on amber. */
.bp-badge--award {
    color: var(--bp-amber-ink);
    background: var(--bp-warning-bg);
}

/* Selectable filter chip. */
.bp-pill,
.bp-pill:link,
.bp-pill:visited {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 14px;
    border: 1px solid var(--bp-line-control);
    border-radius: var(--bp-radius-pill);
    color: var(--bp-body);
    background: var(--bp-white);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
    text-decoration: none;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-pill:hover,
.bp-pill:focus {
    color: var(--bp-navy);
    background: var(--bp-blue-50);
    text-decoration: none;
}

.bp-pill[aria-pressed="true"],
.bp-pill.is-active {
    color: var(--bp-white);
    border-color: var(--bp-blue);
    background: var(--bp-blue);
}

/* The gap above is for this. A chip carries an icon in one of two roles: a
   leading mark for what it selects, or — on a chip that names a filter which
   is already applied — the ✕ that takes it off again. 14px rather than the
   badge's 13 or the button's 18: the chip's text is --bp-text-sm, between the
   two. */
.bp-pill svg {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
}

/* A chip that reads a filter back out rather than offering one. It is not a
   choice, so it is not pressed and it does not take the blue; the ✕ is the
   only thing on it that does anything, and the whole chip is that button. */
.bp-pill--applied,
.bp-pill--applied:hover,
.bp-pill--applied:focus {
    color: var(--bp-navy);
    border-color: transparent;
    background: var(--bp-blue-100);
}

.bp-pill--applied svg {
    color: var(--bp-muted);
}

.bp-pill--applied:hover svg,
.bp-pill--applied:focus svg {
    color: var(--bp-danger);
}

.bp-alert {
    display: flex;
    align-items: flex-start;
    gap: var(--bp-space-xs);
    padding: 14px 16px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-sm);
    color: var(--bp-body);
    background: var(--bp-surface);
    font-size: var(--bp-text-base);
}

.bp-alert svg {
    width: 20px;
    flex: 0 0 20px;
    margin-top: 1px;
}

.bp-alert--info {
    color: var(--bp-navy);
    border-color: #bcd2f7;
    background: var(--bp-info-bg);
}

.bp-alert--success {
    color: var(--bp-success);
    border-color: #a7e0c4;
    background: var(--bp-success-bg);
}

.bp-alert--warning {
    color: var(--bp-warning);
    border-color: #f3d9a3;
    background: var(--bp-warning-bg);
}

.bp-alert--danger {
    color: var(--bp-danger);
    border-color: #f2b8b3;
    background: var(--bp-danger-bg);
}

/* ==========================================================================
   15. NAVIGATION PATTERNS
   ========================================================================== */

.bp-breadcrumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0 0 var(--bp-space-sm);
    padding: 0;
    list-style: none;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.bp-breadcrumbs li {
    display: flex;
    align-items: center;
    gap: 6px;
}

.bp-breadcrumbs li+li::before {
    content: "/";
    color: var(--bp-line-control);
}

.bp-breadcrumbs a,
.bp-breadcrumbs a:link,
.bp-breadcrumbs a:visited {
    color: var(--bp-blue);
    font-weight: var(--bp-weight-bold);
    text-decoration: none;
}

.bp-breadcrumbs a:hover,
.bp-breadcrumbs a:focus {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.bp-breadcrumbs [aria-current="page"] {
    color: var(--bp-muted);
}

.bp-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: var(--bp-space-md) 0 0;
    padding: 0;
    list-style: none;
}

.bp-pagination a,
.bp-pagination a:link,
.bp-pagination a:visited,
.bp-pagination span {
    min-width: 42px;
    min-height: 42px;
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--bp-line-control);
    border-radius: var(--bp-radius-xs);
    color: var(--bp-navy);
    background: var(--bp-white);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
    text-decoration: none;
    transition: var(--bp-transition);
}

.bp-pagination a:hover,
.bp-pagination a:focus {
    color: var(--bp-white);
    border-color: var(--bp-blue);
    background: var(--bp-blue);
}

.bp-pagination [aria-current="page"] {
    color: var(--bp-white);
    border-color: var(--bp-blue);
    background: var(--bp-blue);
}

.bp-pagination .bp-pagination__gap {
    min-width: auto;
    border: 0;
    background: transparent;
    color: var(--bp-muted);
}

.bp-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-md);
    margin-bottom: var(--bp-space-md);
    border-bottom: 1px solid var(--bp-line);
}

.bp-tabs button,
.bp-tabs a {
    padding: 0 2px 12px;
    border: 0;
    border-bottom: 2px solid transparent;
    color: var(--bp-muted);
    background: none;
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    text-decoration: none;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-tabs button:hover,
.bp-tabs a:hover {
    color: var(--bp-navy);
}

.bp-tabs [aria-selected="true"],
.bp-tabs [aria-current="page"] {
    color: var(--bp-navy);
    border-bottom-color: var(--bp-blue);
}

/* Disclosure. The panel is toggled with the `hidden` attribute rather than a
   height animation, so collapsed content leaves the tab order with it. */
.bp-accordion__item {
    border-bottom: 1px solid var(--bp-line);
}

.bp-accordion__trigger {
    width: 100%;
    padding: 17px 2px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-xs);
    border: 0;
    color: var(--bp-navy);
    background: none;
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
    text-align: left;
    cursor: pointer;
}

.bp-accordion__trigger svg {
    width: 18px;
    flex: 0 0 18px;
    transition: transform var(--bp-transition);
}

.bp-accordion__trigger[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

.bp-accordion__panel {
    padding: 0 2px 18px;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
}

.bp-accordion__panel[hidden] {
    display: none;
}

/* ==========================================================================
   16. TABLE
   ========================================================================== */

.bp-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
}

.bp-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--bp-text-base);
}

.bp-table th,
.bp-table td {
    padding: 13px 16px;
    text-align: left;
    border-bottom: 1px solid var(--bp-line);
}

.bp-table th {
    color: var(--bp-navy);
    background: var(--bp-surface);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
    letter-spacing: .04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.bp-table td {
    color: var(--bp-body);
}

.bp-table tbody tr:last-child td {
    border-bottom: 0;
}

.bp-table tbody tr:hover td {
    background: var(--bp-blue-50);
}

/* ==========================================================================
   17. PRICING
   ========================================================================== */

.bp-price {
    position: relative;
    padding: 28px;
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-md);
}

/* Exactly one plan per page may be featured. */
.bp-price--featured {
    border-color: var(--bp-blue);
    box-shadow: var(--bp-shadow-lg);
}

.bp-price__name {
    margin: 0 0 var(--bp-space-3xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-xl);
    font-weight: var(--bp-weight-black);
}

.bp-price__amount {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin: var(--bp-space-2xs) 0 var(--bp-space-sm);
    color: var(--bp-navy);
    font-size: 44px;
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
    line-height: 1;
}

.bp-price__period {
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-bold);
    letter-spacing: normal;
}

.bp-price__features {
    margin: 0 0 var(--bp-space-md);
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--bp-space-2xs);
    color: var(--bp-body);
    font-size: var(--bp-text-base);
}

.bp-price__features li {
    display: flex;
    align-items: flex-start;
    gap: var(--bp-space-2xs);
}

.bp-price__features svg {
    width: 18px;
    flex: 0 0 18px;
    margin-top: 2px;
    color: var(--bp-success);
}

.bp-price .bp-btn {
    margin-top: auto;
}

/* ==========================================================================
   18. STATES
   ========================================================================== */

.bp-empty {
    padding: 48px 24px;
    display: grid;
    justify-items: center;
    gap: var(--bp-space-xs);
    text-align: center;
    border: 1px dashed var(--bp-line-strong);
    border-radius: var(--bp-radius-lg);
    background: var(--bp-surface-tint);
}

.bp-empty h3 {
    margin: 0;
    font-size: var(--bp-text-xl);
}

.bp-empty p {
    margin: 0;
    max-width: 46ch;
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
}

.bp-skeleton {
    border-radius: var(--bp-radius-xs);
    background: linear-gradient(90deg, #eef2f8 25%, #f7f9fc 37%, #eef2f8 63%);
    background-size: 400% 100%;
    animation: bp-skeleton 1.4s ease infinite;
}

@keyframes bp-skeleton {
    0% {
        background-position: 100% 50%;
    }

    100% {
        background-position: 0 50%;
    }
}

.bp-skeleton--text {
    height: 12px;
    margin-bottom: var(--bp-space-2xs);
}

.bp-skeleton--title {
    height: 20px;
    width: 60%;
    margin-bottom: var(--bp-space-xs);
}

.bp-skeleton--media {
    height: 150px;
    border-radius: var(--bp-radius-lg) var(--bp-radius-lg) 0 0;
}

/* ==========================================================================
   19. DIALOG
   Built for the native <dialog> element so focus trapping, Esc and the
   top layer come from the browser instead of a script.
   ========================================================================== */

.bp-dialog {
    width: min(calc(100% - 32px), 560px);
    padding: 28px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    color: var(--bp-ink);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-xl);
}

.bp-dialog::backdrop {
    background: rgba(16, 42, 102, .45);
}

.bp-dialog__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--bp-space-sm);
    margin-bottom: var(--bp-space-xs);
}

.bp-dialog__head h2 {
    margin: 0;
    font-size: var(--bp-text-2xl);
}

.bp-dialog__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--bp-space-2xs);
    margin-top: var(--bp-space-md);
}

/* ==========================================================================
   20. UTILITIES
   ========================================================================== */

.bp-skip {
    position: fixed;
    top: 8px;
    left: 8px;
    z-index: var(--bp-z-skip);
    padding: 10px 14px;
    color: var(--bp-white);
    background: var(--bp-navy);
    border-radius: var(--bp-radius-xs);
    text-decoration: none;
    transform: translateY(-160%);
}

.bp-skip:focus {
    color: var(--bp-white);
    transform: none;
}

.bp-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.bp-stack {
    display: grid;
    gap: var(--bp-space-sm);
}

.bp-stack--tight {
    gap: var(--bp-space-2xs);
}

.bp-stack--loose {
    gap: var(--bp-space-md);
}

.bp-cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bp-space-xs);
}

.bp-actions {
    margin-top: var(--bp-space-md);
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-xs);
}

.bp-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bp-clamp-2,
.bp-clamp-3 {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
}

.bp-clamp-2 {
    -webkit-line-clamp: 2;
}

.bp-clamp-3 {
    -webkit-line-clamp: 3;
}

/* ==========================================================================
   21. RESPONSIVE
   Three breakpoints, matching the reference: 1060 (two columns collapse),
   820 (tablet/phone split, nav collapses) and 580 (phone).
   ========================================================================== */

@media (max-width: 1060px) {

    /* minmax(0, 1fr), never a bare 1fr: a grid item defaults to min-width:auto,
       so one long unbreakable child (a nowrap city link, a wide table) would
       stretch the track past the viewport instead of being contained. */
    .bp-split,
    .bp-feature {
        grid-template-columns: minmax(0, 1fr);
    }

    .bp-grid {
        --bp-cols: 2;
    }

    .bp-directory__grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .bp-slider {
        --bp-per: 2;
    }

    .bp-slider--3 {
        --bp-per: 2;
    }
}

@media (max-width: 820px) {

    .bp-section-head,
    .bp-cta__inner {
        display: flex;
        align-items: flex-start;
        flex-direction: column;
    }

    .bp-section-actions {
        margin-top: 18px;
    }

    .bp-trustbar__grid {
        grid-template-columns: 1fr;
        padding-block: var(--bp-space-sm);
    }

    .bp-cta__actions {
        width: 100%;
        flex-direction: row;
    }

    .bp-cta__actions .bp-btn {
        flex: 1;
    }

    .bp-searchbar .search-container {
        grid-template-columns: 1fr;
    }

    .bp-searchbar .field-group {
        border-right: 0;
        border-bottom: 1px solid var(--bp-line);
    }

    .bp-searchbar .field-group+.field-group {
        border-bottom: 0;
    }

    .bp-searchbar .search-button {
        width: 100%;
    }
}

@media (max-width: 580px) {
    :root {
        --bp-gutter: 14px;
        --bp-section-y: 64px;
    }

    .bp-grid,
    .bp-directory__grid {
        --bp-cols: 1;
        grid-template-columns: minmax(0, 1fr);
    }

    .bp-slider,
    .bp-slider--3 {
        --bp-per: 1;
    }

    .bp-feature {
        padding: var(--bp-space-md);
        border-radius: var(--bp-radius-xl);
    }

    .bp-panel,
    .bp-panel--wide {
        padding: var(--bp-space-sm);
    }

    .bp-callout {
        grid-template-columns: minmax(0, 1fr);
        text-align: center;
    }

    .bp-callout .bp-icon-tile {
        margin-inline: auto;
    }

    .bp-cta__actions {
        flex-direction: column;
    }
}

@media (prefers-reduced-motion: reduce) {
    .bp-scope * {
        transition-duration: .01ms !important;
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
    }
}

/* ==========================================================================
   21. BUSINESS PROFILE
   The profile page: masthead and verdict card, the panels the body is built
   from, the photo collage, the mobile action bar, and the two form dialogs
   it opens. Everything here arrived with the profile redesign and was
   reviewed as /design-system/business-profile.php before it moved in.
   ========================================================================== */

html {
    scroll-padding-top: var(--bp-stick);
}

/* The system resets nothing on <body>, so the user agent's 8px margin
   survives and every full-bleed band gets a white gutter. Legacy pages
   hide this because bootstrap zeroes it; a page on the system alone
   does not.

   The background is the same omission one step further out. Every band the
   system shipped first paints its own — .bp-profile-head a gradient, .bp-page
   the surface tint — so nothing ever showed the canvas underneath. A plain
   .bp-section does not paint anything, and an unpainted canvas is whatever
   the browser feels like: white in the usual case, but dark wherever the UA
   resolves its default against a dark colour-scheme, which turns a white
   section into navy-on-black. */
body.bp-scope {
    margin: 0;
    background: var(--bp-white);
}

/* .bp-scope resets no images at all, while the legacy theme sets
   img{margin-bottom:1.857em} and .feature img{max-width:20px}. */
.bp-scope img {
    max-width: 100%;
    margin-bottom: 0;
}

/* --- Layout: main column + rail ---------------------------------- */
.bp-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--bp-rail);
    gap: var(--bp-space-lg);
    align-items: start;
}

/* Both columns are single-column grids, and an implicit track is sized to
   its contents: an ad whose creative came back wider than the column grew
   the track, and the column and the page with it. minmax(0, 1fr) holds the
   track to the column, so nothing inside one can widen it. */
.bp-layout__main,
.bp-rail {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--bp-space-sm);
    min-width: 0;
}

/* A rail taller than the viewport is worse than no sticky at all: once
   it pins, its lower half is unreachable until the whole page has been
   scrolled. So the rail is kept short — actions and status only, with
   the ad slot moved into the main column — and it only pins on screens
   with room for it. Below that it is an ordinary column. */
@media (min-width: 1061px) and (min-height: 740px) {
    .bp-rail--sticky {
        position: sticky;
        top: var(--bp-stick);
    }
}

@media (max-width: 1060px) {
    .bp-layout {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* --- Profile head -------------------------------------------------
   There is no hero in the system: --bp-gradient-hero is declared and
   used nowhere, .bp-cta is a closing band and .bp-section-head caps
   its copy at 760px. This is the masthead. */
.bp-profile-head {
    padding-block: var(--bp-space-sm) var(--bp-space-lg);
    border-bottom: 1px solid var(--bp-line);
    background: var(--bp-gradient-hero);
}

.bp-profile-head__grid {
    margin-top: var(--bp-space-sm);
    display: grid;
    /* The card is a summary of the reviews now, not a score in a box, so
       it gets room to be one. It no longer lines up with the rail below —
       that alignment is worth less than the bars fitting on one line. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 560px);
    gap: var(--bp-space-lg);
    align-items: center;
}

/* The panels below inset their text by their own padding and border; the
   masthead has no card to do that for it, so its text column takes the
   same inset and the page gets one left edge instead of two. The verdict
   card is left out on purpose: it is a card like the ones below, so its
   edge belongs on the container edge, not on the text line. */
.bp-profile-head nav,
.bp-profile-head__main {
    padding-left: calc(28px + 1px);
}

.bp-profile-head h1 {
    margin: var(--bp-space-2xs) 0 var(--bp-space-xs);
    color: var(--bp-navy);
    font-size: clamp(30px, 3.4vw, 44px);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
    line-height: 1.06;
}

.bp-profile-head__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bp-space-2xs) var(--bp-space-sm);
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-medium);
}

/* Two rows of this: what the business is and what it costs, then where
   it is and how to reach it. The row gap is the one the flex wrap would
   have used anyway, so a wrapped row and a split row read alike. */
.bp-profile-head__meta+.bp-profile-head__meta {
    margin-top: var(--bp-space-2xs);
}

/* Direct children only: an inner wrapper must not become a flex box of
   its own, or the comma between two category links gets the gap too. */
.bp-profile-head__meta>span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.bp-profile-head__meta svg {
    width: 16px;
    height: 16px;
    color: var(--bp-line-control);
}

/* .bp-panel drops to --bp-space-sm down here, so the inset follows it. */
@media (max-width: 580px) {

    .bp-profile-head nav,
    .bp-profile-head__main {
        padding-left: calc(var(--bp-space-sm) + 1px);
    }
}

@media (max-width: 1060px) {
    .bp-profile-head__grid {
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
        gap: var(--bp-space-sm);
    }
}

/* --- Verdict card: the score, how it is distributed, and what the
   reviews agree on. Three registers in one card, so each gets its own
   band: the numbers, then the provenance line, then the three chips. */
.bp-verdict {
    padding: 20px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-lg);
}

/* auto / 1fr, not two equal halves: the score column is as wide as "out
   of 5" needs and every pixel left over goes to the bars, which are the
   part that gets unreadable when it is squeezed. */
.bp-verdict__grid {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--bp-space-lg);
    align-items: center;
}

.bp-verdict__score {
    text-align: center;
}

.bp-verdict__count {
    margin-top: var(--bp-space-3xs);
    color: var(--bp-body);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
}

.bp-verdict .bp-divider {
    margin-block: var(--bp-space-sm);
}

.bp-verdict__trust {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-2xs);
}

.bp-verdict__trust .bp-source {
    margin: 0;
}

.bp-verdict__more {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-size: var(--bp-text-xs);
}

.bp-verdict__more svg {
    width: 14px;
    height: 14px;
}

/* --- Score distribution: label, track, share ----------------------- */
.bp-histogram {
    display: grid;
    gap: 6px;
}

.bp-histogram__row {
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr) 32px;
    align-items: center;
    gap: var(--bp-space-2xs);
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
}

.bp-histogram__track {
    height: 8px;
    border-radius: 999px;
    background: var(--bp-line);
}

.bp-histogram__bar {
    display: block;
    height: 100%;
    min-width: 8px;
    border-radius: inherit;
    background: var(--bp-amber);
}

.bp-histogram__value {
    color: var(--bp-body);
    text-align: right;
}

/* --- What the reviews agree on ------------------------------------ */
.bp-verdict__highlights {
    margin-top: var(--bp-space-sm);
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--bp-space-2xs);
}

.bp-highlight {
    display: flex;
    align-items: center;
    gap: var(--bp-space-2xs);
    padding: 10px 12px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-surface);
}

.bp-highlight svg {
    width: 18px;
    height: 18px;
    flex: 0 0 18px;
    color: var(--bp-blue);
}

.bp-highlight__text {
    display: grid;
    min-width: 0;
}

.bp-highlight__label {
    color: var(--bp-navy);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
}

.bp-highlight__note {
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-medium);
}

/* Stacked, the card would be a white slab under the name for three
   short lines, so below the breakpoint the score goes back to being a
   line and the card steps out. */
.bp-profile-head__rating {
    display: none;
}

@media (max-width: 1060px) {
    .bp-verdict {
        display: none;
    }

    .bp-profile-head__rating {
        display: flex;
        margin-bottom: var(--bp-space-xs);
    }
}

.bp-score__value {
    color: var(--bp-navy);
    font-size: 52px;
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
    line-height: 1;
}

.bp-score__scale {
    margin-bottom: var(--bp-space-2xs);
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-bold);
}

/* Two identical star rows on top of each other; the amber one is clipped
   to --bp-star-fill, so 4.6 shows as 4.6 and not as a rounded 5. */
.bp-stars-frac {
    position: relative;
    display: inline-block;
    font-size: var(--bp-text-xl);
    letter-spacing: 2px;
    line-height: 1;
    white-space: nowrap;
}

.bp-stars-frac__base {
    color: var(--bp-line-strong);
}

.bp-stars-frac__fill {
    position: absolute;
    inset: 0;
    width: var(--bp-star-fill);
    overflow: hidden;
    color: var(--bp-amber);
    /* The row is on screen twice; without this, copying the card copies
       ten stars. */
    user-select: none;
}

/* .bp-rating is hard-locked to 12px with no size modifier. */
.bp-rating--lg {
    margin-top: var(--bp-space-2xs);
    font-size: var(--bp-text-base);
}

.bp-rating--lg .bp-stars {
    font-size: var(--bp-text-xl);
}

/* Secondary affordances that must stay reachable: they sit in the first
   rail card, under a rule, rather than at the foot of the column. */
.bp-rail__foot {
    margin-top: var(--bp-space-sm);
    padding-top: 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-xs);
    border-top: 1px solid var(--bp-line);
}

/* --- Approved badge --------------------------------------------------
   The platform's own mark, so it is the one badge that is filled
   rather than tinted: among the tinted status chips it reads as the
   statement BestProsInTown is making itself. White on the brand
   gradient stays above 7:1 along its whole length. Amber is not
   available here — it belongs to the award seal. */
/* The masthead pair sits on one line and must read as one line. The DS
   badge is 11px on 4px of padding and the approved one 12px on 5px;
   side by side that half-step reads as a mistake, so up here both pills
   are cut to the same size. */
.bp-profile-head .bp-badge {
    padding: 5px 12px;
    font-size: var(--bp-text-xs);
}

/* Status as a dot, not only as text colour: --bp-success on pale green
   is legible but it takes reading to notice. currentColor means the
   closed variant gets its red for free. */
.bp-badge--dot::before {
    content: "";
    width: 8px;
    height: 8px;
    flex: 0 0 8px;
    border-radius: var(--bp-radius-pill);
    background: currentColor;
}

.bp-badge--approved {
    padding: 5px 12px;
    color: var(--bp-white);
    background: var(--bp-gradient-brand);
    box-shadow: var(--bp-shadow-sm);
    font-size: var(--bp-text-xs);
}

.bp-badge--approved svg {
    width: 14px;
    flex: 0 0 14px;
}

/* --- Award seal ------------------------------------------------------
   Rendered as it is issued today: the seal itself, no year, no category
   and no caption, opening the certificate modal that BestTops_form.js
   already builds. */
.bp-award {
    margin-top: var(--bp-space-sm);
    display: grid;
    justify-items: center;
}

.bp-rail__card {
    padding: 20px;
}

.bp-rail__card .bp-divider {
    margin-block: var(--bp-space-sm);
}

.bp-award__seal {
    --bp-seal: 116px;
    /* No ring around the seal, so the disc is the full width and the
       crop below has no border to discount. */
    --bp-seal-ring: var(--bp-seal);
    width: var(--bp-seal);
    height: var(--bp-seal);
    padding: 0;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--bp-radius-pill);
    background: var(--bp-white);
    cursor: pointer;
    transition: var(--bp-transition);
}

/* The ring used to carry the hover state. The shadow carries it now. */
.bp-award__seal:hover {
    box-shadow: var(--bp-shadow-md);
}

/* badgeBestPros.jpg is a 1080x960 canvas carrying an 801px seal disc
   pinned at (160, 90). Its centre is therefore (560.5, 490.5) and not
   the canvas centre (540, 480), so centring the file hung the disc off
   the ring — and height:auto squashed it into an ellipse on the way
   there. Together, the badge that was not centred.

   cover crops the canvas to a square so the disc stays a circle, at
   ring/960 per source pixel. Placing it by the disc rather than by the
   canvas means offsetting the image's corner to (ring/2 - 560.5 x
   ring/960, ring/2 - 490.5 x ring/960), which is the ring scaled by
   (480 - 560.5)/960 and (480 - 490.5)/960 below. Percentages cannot do
   the vertical half: cover leaves no slack on the axis it fitted, so
   50% there resolves to no shift at all. */
.bp-award__seal img {
    /* Sized outright rather than in percentages: a percentage height
       inside a <button> has no definite box to resolve against, and
       leaving height to the img's own height attribute would drop the
       square this crop depends on. */
    width: var(--bp-seal-ring);
    height: var(--bp-seal-ring);
    display: block;
    object-fit: cover;
    object-position:
        calc(var(--bp-seal-ring) * -80.5 / 960) calc(var(--bp-seal-ring) * -10.5 / 960);
    border-radius: var(--bp-radius-pill);
}

/* --- Provenance line ---------------------------------------------- */
.bp-source {
    display: flex;
    align-items: center;
    gap: 7px;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.bp-source svg {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
}

/* Multi-line provenance: the icon belongs on the first line, not
   centred against the whole block. */
.bp-source--block {
    align-items: flex-start;
}

.bp-source--block svg {
    margin-top: 3px;
}

/* .bp-btn sizes its own svg; .bp-text-link does not, so an inline icon
   inflates to the container width. */
.bp-scope .bp-text-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.bp-scope .bp-text-link svg {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
}

/* --- Panel head ---------------------------------------------------- */
.bp-panel__head {
    margin-bottom: var(--bp-space-sm);
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--bp-space-sm);
}

.bp-panel__head h2 {
    margin: 0;
    color: var(--bp-navy);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
}

/* For a head whose title carries the business's name and so can be as long as
   one: "Reviews for …" next to a button squeezed to four lines on a phone. With
   wrapping, a title that does not fit beside its actions takes the row and the
   actions go under it; a short one is still beside them. */
.bp-panel__head--wrap {
    flex-wrap: wrap;
}

/* --- Prose wrapper for the opaque reviews_summaries blob ----------- */
.bp-prose {
    color: var(--bp-body);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-relaxed);
}

.bp-prose p {
    margin: 0 0 var(--bp-space-xs);
}

.bp-prose> :last-child {
    margin-bottom: 0;
}

/* --- Review card, profile variant ---------------------------------
   .bp-review carries the BUSINESS identity — redundant on that
   business's own profile. This one is unattributed: a date and a
   score above the text, and nothing that names anybody. */
.bp-review--profile {
    display: grid;
    gap: var(--bp-space-xs);
}

.bp-review--profile blockquote {
    -webkit-line-clamp: unset;
    display: block;
    overflow: visible;
    font-size: var(--bp-text-md);
    /* A review is whatever a stranger typed, and a link or a run of letters has
       no space to break at. The card is a grid with an auto column, which grows
       to the longest word in it, so one such word made the card, and then the
       page, as wide as the word was. */
    overflow-wrap: anywhere;
}

.bp-review__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-xs);
}

.bp-review__date {
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

/* .bp-rating carries a margin-top for the stacked case; on this row it
   is the second half of a baseline pair and must not drop below it. */
.bp-review__head .bp-rating {
    margin: 0;
}

/* --- Hours --------------------------------------------------------- */
.bp-hours {
    display: grid;
    gap: 2px;
}

.bp-hours__row {
    padding: 9px var(--bp-space-xs);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-sm);
    border-radius: var(--bp-radius-xs);
    font-size: var(--bp-text-base);
}

.bp-hours__day {
    color: var(--bp-body);
}

.bp-hours__time {
    color: var(--bp-ink);
    font-weight: var(--bp-weight-medium);
    font-variant-numeric: tabular-nums;
}

.bp-hours__row.is-today {
    background: var(--bp-blue-50);
}

.bp-hours__row.is-today .bp-hours__day,
.bp-hours__row.is-today .bp-hours__time {
    color: var(--bp-navy);
    font-weight: var(--bp-weight-strong);
}

.bp-hours__row.is-closed .bp-hours__time {
    color: var(--bp-muted);
}

/* --- Checklist ------------------------------------------------------
   .bp-scope applies no list reset, and the only checklist in the
   system is hard-scoped inside .bp-price. */
.bp-checklist {
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 10px var(--bp-space-md);
    list-style: none;
}

.bp-checklist li {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
}

.bp-checklist svg {
    width: 18px;
    height: 18px;
    flex: 0 0 18px;
    margin-top: 2px;
    color: var(--bp-success);
}

/* --- Ratio-locked media --------------------------------------------
   Every media surface in the system is a fixed pixel height. */
.bp-media {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: var(--bp-radius-md);
    background: var(--bp-gradient-media);
}

.bp-media--1x1 {
    aspect-ratio: 1 / 1;
}

.bp-media--16x9 {
    aspect-ratio: 16 / 9;
}

.bp-media img,
.bp-media iframe {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    border: 0;
}

/* --- Photo collage ---------------------------------------------------
   The profile photo block is one generated collage image with an
   <area> per photo, so a click lands on that exact photo in the
   gallery. The image therefore must scale PROPORTIONALLY: resizeMap()
   rescales every area's coords from the rendered width against
   data-original-width, so object-fit:cover or a max-height would move
   the hit areas away from what they sit on. width:100%/height:auto and
   nothing else. */
.bp-collage {
    position: relative;
    overflow: hidden;
    border-radius: var(--bp-radius-md);
    background: var(--bp-gradient-media);
}

.bp-collage img {
    width: 100%;
    height: auto;
    display: block;
}

.bp-collage area {
    cursor: zoom-in;
}

/* Sits over the collage, so it must not intercept clicks meant for the
   image map — only the button itself is clickable. */
.bp-collage__overlay {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--bp-space-sm);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-2xs);
    background: linear-gradient(180deg, transparent, rgba(13, 34, 79, .72));
    pointer-events: none;
}

.bp-collage__overlay>* {
    pointer-events: auto;
}

.bp-collage__btn {
    min-height: 38px;
    padding: 0 14px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border: 1px solid rgba(255, 255, 255, .7);
    border-radius: var(--bp-radius-sm);
    color: var(--bp-white);
    background: rgba(16, 42, 102, .5);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    font: inherit;
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-strong);
    text-decoration: none;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-collage__btn:hover {
    color: var(--bp-white);
    background: rgba(16, 42, 102, .78);
}

.bp-collage__btn svg {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
}

/* --- Link row (nearby businesses) ----------------------------------- */
.bp-linkrow {
    padding: var(--bp-space-xs);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-sm);
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
    text-decoration: none;
    transition: var(--bp-transition);
}

.bp-linkrow:hover {
    border-color: var(--bp-line-strong);
    background: var(--bp-blue-50);
}

/* The text is a business name, or on an appointment link a hostname, and a hostname
   has nothing in it to break on but its dots. A flex item will not shrink below
   its longest word, so without min-width the row grows past its panel; with
   it, a word that does not fit breaks where it has to. A name that fits is
   untouched by either. */
.bp-linkrow>div {
    min-width: 0;
}

.bp-linkrow strong {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    overflow-wrap: anywhere;
}

.bp-linkrow span {
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.bp-linkrow .bp-rating {
    margin: 6px 0 0;
}

.bp-linkrow svg {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    color: var(--bp-line-control);
}

/* --- Fixed mobile action bar ----------------------------------------- */
.bp-actionbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--bp-z-actionbar);
    display: none;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--bp-space-2xs);
    padding: 10px var(--bp-space-sm) calc(10px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--bp-line);
    background: rgba(255, 255, 255, .96);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
}

@media (max-width: 1060px) {
    .bp-actionbar {
        display: grid;
    }

    .bp-page {
        padding-bottom: 84px;
    }
}

/* Call, Directions and Website at their own width are 391px of bar, so on every
   phone narrower than that the third was off the screen. A 1fr track cannot
   stop it: it will not shrink below what is in it. minmax(0, 1fr) can, and the
   padding and the icons are what the room is bought with. The columns follow
   the buttons rather than a fixed three, so a bar with two is two halves and a
   bar with one is the width of the screen. */
@media (max-width: 420px) {
    .bp-actionbar {
        grid-template-columns: none;
        grid-auto-flow: column;
        grid-auto-columns: minmax(0, 1fr);
        gap: 6px;
        padding-inline: 10px;
    }

    .bp-actionbar .bp-btn {
        padding-inline: 6px;
    }

    .bp-actionbar .bp-btn svg {
        display: none;
    }
}

/* --- Page band ------------------------------------------------------- */
.bp-page {
    padding-block: var(--bp-space-lg) var(--bp-space-xl);
    background: var(--bp-surface);
}

.bp-anchor {
    scroll-margin-top: var(--bp-stick);
}



/* --- Dialogs ---------------------------------------------------------
   .bp-dialog ships without a height cap. A review form with a photo
   list is the first thing in the system tall enough to need one, so
   this is proposed as an addition to the component, not a local patch.
   <dialog> handles Esc, focus trapping and the top layer itself. */
.bp-dialog {
    max-height: min(88vh, 760px);
    overflow: auto;
}

.bp-dialog__head .bp-help {
    margin: 4px 0 0;
}

.bp-dialog__sub {
    margin: 0 0 var(--bp-space-sm);
}

/* The browser's own rule for [hidden] is display:none at element weight, so any
   component that declares a display of its own outranks it and a script hiding
   that element achieves nothing. It has caught the review error and the photo
   picker's drop target already, and .bp-accordion__panel above carries its own
   patch for the same reason. Hence the one !important in this file: hidden has
   to mean hidden whatever a component says. */
[hidden] {
    display: none !important;
}

/* A qualifier inside a label is not part of the label. */
.bp-label .bp-help {
    font-weight: var(--bp-weight-medium);
}

/* --- Star rating input -----------------------------------------------
   The radios are in DOM order 5 to 1 so a plain sibling selector can
   fill every star below the one being pointed at; row-reverse puts 1 on
   the left again on screen. The input is hidden, never display:none,
   or the keyboard loses it. */
.bp-rating-input {
    display: inline-flex;
    flex-direction: row-reverse;
    justify-content: flex-end;
    gap: 4px;
}

.bp-rating-input__radio {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.bp-rating-input__star {
    color: var(--bp-line-strong);
    font-size: 34px;
    line-height: 1;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-rating-input__star::before {
    content: "\2605";
}

.bp-rating-input__radio:checked~.bp-rating-input__star {
    color: var(--bp-amber);
}

/* Pointing at a star previews that score, so the committed one clears out
   of the way while the pointer is over the row. Both rules below are then
   scoped to that same hover: .bp-rating-input:hover .bp-rating-input__star
   is three components, and .bp-rating-input__star:hover on its own is two,
   so the reset would outweigh it and the star under the pointer would be
   the one star that stayed grey. */
.bp-rating-input:hover .bp-rating-input__star {
    color: var(--bp-line-strong);
}

.bp-rating-input:hover .bp-rating-input__star:hover,
.bp-rating-input:hover .bp-rating-input__star:hover~.bp-rating-input__star {
    color: var(--bp-amber);
}

.bp-rating-input__radio:focus-visible+.bp-rating-input__star {
    outline: 2px solid var(--bp-blue);
    outline-offset: 3px;
    border-radius: 4px;
}

/* --- Photo picker ----------------------------------------------------
   One drop target over one input that takes several files at once. What
   has been picked shows as a row of thumbnails, each with its own remove
   control; the page script holds the list and puts the file_1..file_5
   names on the request at submit. The drop target is hidden once the cap
   is reached and comes back as soon as something is removed. */
.bp-uploader {
    display: grid;
    gap: var(--bp-space-2xs);
}

.bp-uploader__input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.bp-uploader__drop {
    display: grid;
    justify-items: center;
    gap: 2px;
    padding: 18px;
    border: 1px dashed var(--bp-line-control);
    border-radius: var(--bp-radius-md);
    color: var(--bp-navy);
    background: var(--bp-surface);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    text-align: center;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-uploader__drop:hover {
    border-color: var(--bp-blue);
    background: var(--bp-blue-50);
}

.bp-uploader__drop svg {
    width: 22px;
    height: 22px;
    color: var(--bp-blue);
}

.bp-uploader:has(.bp-uploader__input:focus-visible) .bp-uploader__drop {
    outline: 2px solid var(--bp-blue);
    outline-offset: 2px;
}

.bp-uploader__thumbs {
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-2xs);
    list-style: none;
}

.bp-uploader__thumb {
    position: relative;
    width: 64px;
    height: 64px;
    overflow: hidden;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-sm);
}

.bp-uploader__thumb img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
}

/* Sits on the picture, which is why it carries its own dark ground rather
   than borrowing a surface token: it has to hold against any photo. */
.bp-uploader__remove {
    position: absolute;
    top: 3px;
    right: 3px;
    width: 22px;
    height: 22px;
    padding: 0;
    display: grid;
    place-items: center;
    color: var(--bp-white);
    background: rgba(16, 42, 102, .72);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    transition: var(--bp-transition);
}

.bp-uploader__remove svg {
    width: 14px;
    height: 14px;
}

.bp-uploader__remove:hover {
    background: var(--bp-danger);
}

.bp-uploader__remove:focus-visible {
    outline: 2px solid var(--bp-white);
    outline-offset: -3px;
}

.bp-fieldset legend .bp-required {
    color: var(--bp-danger);
}

/* --- Spacing modifiers ------------------------------------------------
   The proposal carried these as style attributes. They are one-property
   rules, but a one-property rule is still the stylesheet's business. */
.bp-actions--spaced {
    margin-top: var(--bp-space-sm);
}

.bp-stack--spaced {
    margin-top: var(--bp-space-xs);
}

.bp-text--spaced {
    margin: var(--bp-space-sm) 0 0;
}

.bp-meta--spaced {
    margin: 6px 0 0;
}

/* The only one of these that pushes the other way: an alert at the head of a
   form is followed by the first field, not preceded by anything. */
.bp-alert--spaced {
    margin-bottom: var(--bp-space-md);
}

.bp-source__lead {
    color: var(--bp-ink);
}

/* A badge can be a link — today's hours jump to the hours table — and the
   link reset in the theme would otherwise underline it. */
a.bp-badge,
a.bp-badge:link,
a.bp-badge:visited,
a.bp-badge:hover {
    text-decoration: none;
}

/* --- Photo grid -------------------------------------------------------
   What the Photos panel falls back to when no collage was generated for
   this business. auto-fill rather than a fixed count: the panel is the
   full width on a phone and two thirds of it on a desktop. */
.bp-photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--bp-space-2xs);
}

/* For a set of four, which is what the sub-pages of a profile show: four across
   wherever there is room, so that the row is full. auto-fill makes as many
   columns as fit, and on the 766px a panel has beside the rail that is five, so
   four photos stopped a column short of the edge and left a fifth of the panel
   empty; on a tablet it is three, and the fourth went on a row of its own.
   From 680px it is four equal columns, from 480px to there two by two, and
   below 480 the grid is left as it was, which on a phone is already two. */
@media (min-width: 480px) {
    .bp-photo-grid--four {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 680px) {
    .bp-photo-grid--four {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* For every photo a business has, which can be fifty, as its photos page shows
   them. The plain grid makes as many 140px columns as fit, and two of them need
   288px: the panel on a 320px phone is 258, so it is one column there, and fifty
   photos in one column are ten thousand pixels of scrolling. Here the column's
   minimum gives way to half the row less a gap when the row is that narrow, so
   it is two across at the least, and wherever there is room it is the plain
   grid. Two columns need only half the row less half a gap; asking for exactly
   that makes the count two to the last decimal place, which a rounding error can
   turn into one, so the minimum is a little smaller than that. */
.bp-photo-grid--all {
    grid-template-columns: repeat(auto-fill, minmax(min(140px, calc(50% - var(--bp-space-2xs))), 1fr));
}

.bp-photo-grid__item {
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--bp-radius-md);
    background: var(--bp-surface);
}

.bp-photo-grid__item img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    transition: var(--bp-transition);
}

.bp-photo-grid__item:hover img {
    transform: scale(1.03);
}

/* --- Cookie banner ----------------------------------------------------
   popup.js toggles .active on #checkUPcookies and reads it back to decide
   whether consent is still outstanding, so the class is the switch: the
   banner is not in the layout until it is added. */
.footer_banner {
    position: fixed;
    left: 50%;
    bottom: var(--bp-space-sm);
    z-index: var(--bp-z-dialog);
    width: min(calc(100% - 32px), 560px);
    padding: var(--bp-space-sm);
    display: none;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-sm);
    transform: translateX(-50%);
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-xl);
}

.footer_banner.active {
    display: flex;
}

.footer_banner__text {
    margin: 0;
    color: var(--bp-body);
    font-size: var(--bp-text-xs);
}

@media (max-width: 580px) {
    .footer_banner {
        flex-direction: column;
        align-items: stretch;
    }

    /* The bar is pinned to the same edge; above it, not under it. */
    .bp-actionbar~.footer_banner {
        bottom: 84px;
    }
}

/* --- Ad slot ----------------------------------------------------------
   Carried over from the page as it shipped, unchanged: the two heights are
   what the units are booked at and are not this stylesheet's to revise. Two
   of the slots sit in .bp-layout__main and the third in .bp-rail, and both
   columns space them like any other block.

   ads.js writes an <ins class="adsbygoogle ads|ads2"> into each slot, and
   the first of the three carries no data-ad-format, so AdSense sizes it from
   whatever CSS gives the <ins>. Every other page that runs ads.js carries a
   .ads rule of its own — the legacy views inline it, cssTmp/theme.css has the
   same one — and this page loads none of them, so that unit measured zero
   wide and AdSense filled it with its smallest fallback. The size those
   sheets give the two classes therefore has to live here too: same numbers,
   same breakpoint. */
.google-ads,
.google-ads iframe {
    min-height: 250px;
}

/* AdSense sizes a responsive unit from the viewport rather than from the
   box it was given, so a creative can come back wider than the slot. The
   frame it arrives in is held to the slot's width; the <ins> is left alone,
   because a full-width responsive unit sets its own width and pulls itself
   to the edge of the screen with a negative margin, and that one is meant
   to be wider than the column. */
.google-ads {
    max-width: 100%;
}

.google-ads iframe {
    max-width: 100%;
}

/* Nothing came back for this slot: AdSense marks it and hides the <ins>,
   and without this the height reserved for the unit stays behind as a
   blank band — 250px of it on a phone, per slot. */
.google-ads:has(ins[data-ad-status="unfilled"]) {
    min-height: 0;
}

.google-ads .ads,
.google-ads .ads2 {
    display: block;
    width: 100%;
    height: 250px;
}

@media (min-width: 768px) {

    .google-ads,
    .google-ads iframe {
        min-height: 90px;
    }

    .google-ads .ads,
    .google-ads .ads2 {
        height: 90px;
    }
}

/* --- Script-built modals ----------------------------------------------
   BestTops_form.js inserts the report form and the certificate form after
   <footer> on load, and popup.js drops its "Thank You" box in the same
   way. All three are .modal, all three used to be dressed by
   cssTmp/modal.css, and this page does not load that file any more, so
   the certificate form rendered as a plain block under the footer.

   The markup belongs to those scripts and is not this stylesheet's to
   change; everything here is scoped under .modal and does the two things
   the scripts assume: hidden until one of them sets display, and a dialog
   when they do. #set_report ships with display:none of its own and is not
   opened from the profile, so its date and time controls are left as the
   browser renders them. */
.modal {
    position: fixed;
    inset: 0;
    z-index: var(--bp-z-dialog);
    display: none;
    align-items: center;
    justify-content: center;
    padding: var(--bp-space-sm);
    overflow: auto;
    background: rgba(16, 42, 102, .45);
}

.modal .set_report,
.modal .set_report_badge,
.modal .thxBadge,
.modal .popup {
    position: relative;
    width: min(100%, 560px);
    max-height: calc(100vh - 32px);
    padding: 28px;
    overflow: auto;
    color: var(--bp-ink);
    background: var(--bp-white);
    border-radius: var(--bp-radius-xl);
    box-shadow: var(--bp-shadow-xl);
    font-family: var(--bp-font);
}

/* Shown by the script once the form has been sent. */
.modal .thxBadge {
    display: none;
    color: var(--bp-navy);
    font-weight: var(--bp-weight-bold);
    text-align: center;
}

.modal .reviev_title h3 {
    margin: 0 0 4px;
    color: var(--bp-navy);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-tight);
}

.modal .reviev_title p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.modal .congrats {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
}

.modal .groupe-report {
    margin-top: var(--bp-space-sm);
    display: grid;
    gap: 6px;
}

.modal .groupe-report_left span {
    color: #35445b;
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
}

.modal input[type="text"],
.modal input[type="email"],
.modal textarea {
    width: 100%;
    min-height: 48px;
    padding: 12px 14px;
    color: var(--bp-ink);
    background: var(--bp-white);
    border: 1px solid var(--bp-line-control);
    border-radius: var(--bp-radius-sm);
    font-family: var(--bp-font);
    font-size: var(--bp-text-base);
}

.modal textarea {
    min-height: 110px;
    resize: vertical;
}

.modal input[type="text"]:focus,
.modal input[type="email"]:focus,
.modal textarea:focus {
    outline: none;
    border-color: var(--bp-focus-color);
    box-shadow: var(--bp-focus-ring);
}

.modal .permanentli {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
}

.modal .permanentli input {
    width: 20px;
    height: 20px;
    margin: 0;
    accent-color: var(--bp-blue);
}

.modal .send-wrap {
    margin-top: var(--bp-space-md);
    display: flex;
    justify-content: flex-end;
}

.modal .btn {
    min-height: 44px;
    padding: 0 22px;
    color: var(--bp-white);
    background: var(--bp-blue);
    border: 0;
    border-radius: var(--bp-radius-pill);
    font-family: var(--bp-font);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-black);
    cursor: pointer;
    transition: var(--bp-transition);
}

.modal .btn:hover {
    background: var(--bp-blue-hover);
}

/* The close control is two bare spans; the cross is made here. */
.modal .close-modal {
    position: absolute;
    top: 18px;
    right: 18px;
    width: 32px;
    height: 32px;
    cursor: pointer;
}

.modal .close-modal span {
    position: absolute;
    top: 15px;
    left: 6px;
    width: 20px;
    height: 2px;
    background: var(--bp-muted);
    border-radius: 2px;
}

.modal .cris {
    transform: rotate(45deg);
}

.modal .cros {
    transform: rotate(-45deg);
}

/* ==========================================================================
   22. CITY DIRECTORY
   The city page: a compact masthead over the shared search widget, and the
   businesses of a city as link rows grouped by category. Everything else it
   needs it takes from the sections above unchanged — .bp-profile-head for the
   masthead, .bp-panel for a category, .bp-grid for the two columns of rows,
   .bp-linkrow for a row, .bp-directory for the groups and .bp-region__links
   for the link-dense lists at the foot. Three things were missing: the width
   of the search widget in a masthead, the space above the first group of a
   directory, and a badge inside a link row.
   ========================================================================== */

/* The city masthead carries the shared search widget where the profile
   carries its verdict card. The widget is a card, so — for the same reason
   that card is left out of the text column — it takes the container's own
   width and edge rather than the text line's. */
.bp-profile-head .bp-searchbar {
    max-width: none;
    margin-top: var(--bp-space-xs);
}

/* .bp-directory draws the rule between one group and the next but has nothing
   above the first, and on the city page the groups follow a stack of panels.
   Named for the --spaced modifiers above: a top margin and nothing else. */
.bp-directory--spaced {
    margin-top: var(--bp-space-lg);
}

/* A row can carry the award mark. .bp-linkrow paints every span it holds as
   12px muted meta, and at (0,1,1) that outranks the badge's own size and
   colour, so both are restated here. Per variant, so that a row carrying a
   different badge keeps that badge's palette. */
.bp-linkrow .bp-badge {
    flex: 0 0 auto;
    font-size: var(--bp-text-2xs);
}

.bp-linkrow .bp-badge--award {
    color: var(--bp-amber-ink);
}

/* ==========================================================================
   23. STATE DIRECTORY
   The state page: every city of one state as an A-Z index. .bp-directory,
   .bp-region and .bp-linkrow carry all of it unchanged except the head of a
   letter block. .bp-region__name is sized for a word — a state name, a
   category — and one glyph at 13px reads as part of the list instead of the
   head of it, which is the whole job of a letter in an index.
   ========================================================================== */

.bp-region__name--letter {
    /* .bp-region__head is a flex row sized for a name and a toggle; the letter
       takes the whole head so the rule after it can run to the edge. */
    flex: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--bp-navy);
    font-size: var(--bp-text-lg);
    letter-spacing: 0;
}

.bp-region__name--letter::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--bp-line);
}

/* ==========================================================================
   24. LANDING
   A campaign page: one product, one form, no directory around it. Section 21
   says in as many words that the system has no hero — the profile, city and
   state pages all open on .bp-profile-head, which is a masthead for a record
   that already exists. A page whose whole job is to explain something that
   does not exist yet needs the other thing, so --bp-gradient-hero, declared
   in section 1 and until now used nowhere, is finally what it was named for.

   Everything else here is a page band the system was missing: the four-across
   benefit card, the wall of platform names, and the evidence band that came
   off the V3 home page (see views/components/ai_proof.php, which holds that
   block's markup and the slider behaviour it needs).
   ========================================================================== */

/* --- Hero ---------------------------------------------------------------- */
.bp-lander {
    position: relative;
    padding-block: 74px 78px;
    overflow: hidden;
    border-bottom: 1px solid var(--bp-line);
    background: var(--bp-gradient-hero);
    text-align: center;
}

/* Two soft shapes bleeding off the corners. Pseudo-elements rather than an
   image: at 7% opacity the whole effect is a pair of blurred circles, and an
   asset for that is an asset to deploy, cache and get wrong. */
.bp-lander::before,
.bp-lander::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    background: var(--bp-blue-soft);
    opacity: .07;
    pointer-events: none;
}

.bp-lander::before {
    top: -130px;
    left: -150px;
    width: 400px;
    height: 400px;
}

.bp-lander::after {
    right: -170px;
    bottom: -190px;
    width: 440px;
    height: 440px;
}

/* The positioning context for anything set beside the text column, which is
   the container's edge rather than the band's: the band is full-bleed. */
.bp-lander__stage {
    position: relative;
    z-index: 1;
}

.bp-lander__inner {
    max-width: 860px;
    margin-inline: auto;
}

/* The half of the headline that carries the number. Blue on navy in the same
   line, so the claim reads before the sentence does. */
.bp-lander__accent {
    color: var(--bp-blue);
}

/* Hand-written aside in the margin. Pure decoration — it says nothing the
   copy does not, so it is aria-hidden — and it is the first thing to go: below
   1160px there is no margin beside the text column to put it in, and laid over
   the copy it would be noise. Needs Caveat; the page requests it in the same
   Google Fonts call as Inter. */
.bp-lander__scribble {
    position: absolute;
    top: 58px;
    right: -6px;
    width: 196px;
    color: var(--bp-blue-soft);
    font-family: Caveat, "Segoe Script", "Bradley Hand", cursive;
    font-size: 26px;
    font-weight: var(--bp-weight-bold);
    line-height: 1.12;
    text-align: left;
    transform: rotate(-5deg);
    pointer-events: none;
}

.bp-lander__scribble svg {
    width: 82px;
    margin: 4px 0 0 40px;
}

@media (max-width: 1160px) {
    .bp-lander__scribble {
        display: none;
    }
}

/* The component owns its type rather than the page picking a display class,
   the way .bp-section-head h2 and .bp-cta h2 already do. --bp-display-1 is
   the hero token, but it tops out at 78px and a value proposition is a
   sentence, not a word: "Manage your business information across 100+
   directories" is 55 characters, which at 78px is four lines of headline
   before the reader reaches the first fact. --bp-display-2 sets it in two. */
/* Narrower than the paragraph under it, and deliberately: three short lines
   of headline read faster than two long ones, and the margin they leave is
   what .bp-lander__scribble sits in. Without the cap the headline runs the
   full 860px and the aside has nowhere to go but on top of it. */
.bp-lander h1 {
    max-width: 720px;
    margin: 0 auto var(--bp-space-sm);
    color: var(--bp-navy);
    font-size: var(--bp-display-2);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-tight);
    letter-spacing: var(--bp-tracking-tight);
    text-wrap: balance;
}

.bp-lander p {
    max-width: 68ch;
    margin: 0 auto;
    color: var(--bp-body);
    font-size: var(--bp-lead);
    line-height: var(--bp-leading-relaxed);
}

.bp-lander .bp-eyebrow {
    justify-content: center;
}

/* .bp-actions is a left-aligned wrap; centred under a centred hero. */
.bp-lander .bp-actions {
    justify-content: center;
}

/* The honest note under the button on a page for something not yet shipped.
   Muted meta at the foot of the hero rather than a badge inside it: it
   qualifies the offer, it is not a status the reader is asked to act on. */
/* The wall of platforms under the button, still inside the hero: the space
   above it is the gap between two blocks of one band, not a section gap. */
.bp-lander__wall {
    margin-top: var(--bp-space-lg);
}

/* Two classes, because `.bp-lander p` above is (0,1,1) and would otherwise
   out-rank a single class and set this line at hero-lead size. */
.bp-lander .bp-lander__note {
    max-width: 70ch;
    margin: var(--bp-space-sm) auto 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
    line-height: var(--bp-leading-normal);
}

/* --- Centred section head ------------------------------------------------
   Every section head in the system is left-aligned with its actions at the
   far end, because every one of them tops a grid that starts at the left
   edge. A form panel does not: it is narrower than the container and centred
   in it, so a left-aligned head over it reads as a head over something else.
   Only for a band whose content is centred too. */
.bp-section-head--center {
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.bp-section-head--center>div {
    max-width: 720px;
}

.bp-section-head--center .bp-eyebrow {
    justify-content: center;
}

/* The form itself is narrower than the band it sits in: a 1200px row of
   inputs is a row nobody can scan, and a label at the far left of the screen
   is a long way from the field it names. */
.bp-lander__form {
    max-width: 760px;
    margin-inline: auto;
}

/* --- Benefit card --------------------------------------------------------
   .bp-info-tile is the centred 150px version of this — an icon and a label,
   for a row of four words. This one carries a sentence, so it is left-aligned
   and grows with its copy, and a row of them is a .bp-grid. */
.bp-benefit {
    height: 100%;
    padding: 24px;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-sm);
}

.bp-benefit .bp-icon-tile {
    margin-bottom: var(--bp-space-sm);
}

.bp-benefit h3 {
    margin: 0 0 6px;
    color: var(--bp-navy);
    font-size: var(--bp-text-lg);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-snug);
    letter-spacing: -.02em;
}

.bp-benefit p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
    line-height: var(--bp-leading-normal);
}

/* --- Promo banner --------------------------------------------------------
   Points at a landing page from somewhere else on the site, which makes it
   the first component here built to land on a page that is NOT on the system:
   the suggest-an-edit view is bootstrap, Poppins and #004DDA, and the banner
   sits inside it under its own `bp-scope` wrapper. So it takes nothing for
   granted from its surroundings — its own padding, its own radius, its own
   type — and .bp-btn already restates its colour across every link state,
   which is what keeps a legacy `a { color: … }` from repainting the CTA.

   Not .bp-callout: that one is an icon, a heading and a line of explanation
   with nowhere for a button to go, and a banner whose whole purpose is the
   click needs the button on the same row as the promise. */
.bp-promo {
    padding: 24px 26px;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--bp-space-md);
    align-items: center;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-gradient-soft);
}

.bp-promo__title {
    margin: 0 0 var(--bp-space-3xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-snug);
    letter-spacing: var(--bp-tracking-tight);
}

.bp-promo__text {
    max-width: 74ch;
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-normal);
}

/* The label is three words and must stay on one line: the button is the
   narrowest column of the row and a grid track sized to `auto` would let it
   wrap before the paragraph beside it does. */
.bp-promo .bp-btn {
    white-space: nowrap;
}

/* --- Platform wall -------------------------------------------------------
   Where a business's details already live: one row of the platforms a reader
   recognises, each a mark over its name. Twelve across the full container,
   halving at each breakpoint, so it stays one tidy block rather than a row
   and an orphan.

   The marks are the one exception to section 8. Every other icon in this
   system is a 24x24 currentColor stroke; a brand is a multi-colour fill and a
   Google G drawn in one colour is not a Google G. Everything else holds —
   inline SVG, no raster, no request off the page.

   What the block must NOT do is imply the integrations exist. Nothing here
   says "connected" or "supported", and the line under the wall says in as
   many words that the list will be confirmed before launch. */
.bp-logo-wall {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--bp-space-2xs);
}

.bp-logo-wall__item {
    min-height: 78px;
    padding: 12px 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--bp-space-2xs);
    text-align: center;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-sm);
    background: var(--bp-white);
    color: var(--bp-body);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-bold);
    line-height: 1.25;
}

/* Fixed box whatever the mark's own proportions: the logos are square, wide
   and tall by turns, and a row of them only reads as a row when their boxes
   line up rather than their ink. */
.bp-logo-wall__mark {
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
}

@media (max-width: 1060px) {
    .bp-logo-wall {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
}

@media (max-width: 820px) {
    .bp-logo-wall {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* --- Evidence band -------------------------------------------------------
   Screenshots of AI assistants answering with our data. Deep blue rather than
   another white section: the band is a claim being evidenced, and a row of
   white screenshots needs a ground to read against. */
.bp-proof {
    padding-block: 86px;
    color: var(--bp-white);
    background: var(--bp-gradient-proof);
}

.bp-proof .bp-section-head h2,
.bp-proof .bp-eyebrow {
    color: var(--bp-white);
}

.bp-proof .bp-section-head p {
    color: rgba(255, 255, 255, .78);
}

/* The white arrows already read on the band; only the hover needs redoing,
   because .bp-icon-btn hovers to blue and blue on blue disappears. */
.bp-proof .bp-icon-btn {
    border-color: transparent;
}

.bp-proof .bp-icon-btn:hover:not(:disabled) {
    color: var(--bp-blue);
    border-color: var(--bp-white);
    background: var(--bp-white);
}

/* The slides are 4:3 screenshots, not portrait cards: the slider's four per
   row would shrink them past reading size, so this one runs two wide ones. */
.bp-proof .bp-slider {
    --bp-per: 2;
    --bp-slider-gap: 22px;
}

.bp-proof .bp-actions {
    margin-top: var(--bp-space-md);
}

.bp-shot {
    margin: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-xl);
}

/* The screenshots are only roughly 4:3 (925..968 wide at 720 tall), so the
   frame fixes the ratio and the image fills it. Without this every slide
   ends up a different height. */
.bp-shot__frame {
    aspect-ratio: 4 / 3;
    border-bottom: 1px solid var(--bp-line);
    background: var(--bp-white);
}

.bp-shot__frame img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
}

.bp-shot__cap {
    padding: 18px 20px 20px;
}

.bp-shot__q {
    display: block;
    margin-bottom: 6px;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
    line-height: 1.35;
}

.bp-shot__meta {
    display: block;
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
}

/* --- Confirmation --------------------------------------------------------
   The thank-you page: the same hero band, narrower, with the mark above the
   line so the outcome is legible before the sentence is read. */
.bp-lander--confirm {
    padding-block: 64px 68px;
}

.bp-lander--confirm .bp-lander__inner {
    max-width: 720px;
}

.bp-lander__mark {
    width: 62px;
    height: 62px;
    margin: 0 auto var(--bp-space-md);
    display: grid;
    place-items: center;
    border-radius: 50%;
    color: var(--bp-white);
    background: var(--bp-success);
}

.bp-lander__mark svg {
    width: 30px;
}

/* Same badge as the confirmation mark, for the two bands that are not a
   success: 404.php and 500.php. Only the token pair changes. */
.bp-lander__mark--info {
    color: var(--bp-info);
    background: var(--bp-info-bg);
}

.bp-lander__mark--danger {
    color: var(--bp-danger);
    background: var(--bp-danger-bg);
}

/* --- Responsive ----------------------------------------------------------
   The three breakpoints of section 21: 1060, 820, 580. */
@media (max-width: 820px) {

    /* Two 4:3 shots side by side stop being legible below this width. */
    .bp-proof .bp-slider {
        --bp-per: 1;
    }

    /* The button drops to its own row under the icon and the copy rather than
       squeezing them: at this width the paragraph is already two or three
       lines and a button beside it leaves the text nowhere to go. */
    .bp-promo {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .bp-promo .bp-btn {
        grid-column: 1 / -1;
    }
}

@media (max-width: 580px) {
    .bp-lander {
        padding-block: 52px 56px;
    }

    .bp-promo {
        padding: var(--bp-space-md);
        grid-template-columns: minmax(0, 1fr);
        text-align: center;
    }

    .bp-promo .bp-icon-tile {
        margin-inline: auto;
    }

    .bp-proof {
        padding-block: 64px;
    }

    .bp-logo-wall {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .bp-benefit {
        padding: var(--bp-space-md);
    }
}

/* ==========================================================================
   25. CATEGORY RESULTS
   The category-in-city page: the businesses of one category in one city as a
   ranked list, beside a map of the same businesses. Nearly all of it is
   already here — .bp-profile-head for the masthead, .bp-layout for the two
   columns, .bp-card for a business, .bp-directory for the link lists at the
   foot, .bp-dialog and .bp-pill for the filters. Four things were missing:
   a rail wide enough to hold a map, the map in its two states, the rank that
   ties a card to its marker, and the marker itself.

   The map is Leaflet, mounted by js/bestpros/MapComponent.js, which is left
   as it is: .svgWrap and #infowindow below are its markup, not the page's,
   and they are styled here because the stylesheet that used to style them —
   css_main/bestpros/category_in_city_style.css — went with the redesign.
   ========================================================================== */

/* A rail of buttons can be 344px. A street map cannot: below about 380px it
   is a texture rather than a place. The main column keeps a comfortable
   measure at this width because a result is one row, not one of three
   columns — see .bp-card--row. */
.bp-layout--map {
    --bp-rail: 400px;
}

/* Section 21 pins a rail only on a window at least 740px tall, because the
   profile's rail is a stack of blocks with a height of their own: on a short
   window the lower half of a pinned one is unreachable, which is worse than
   not pinning it. A map has no height of its own — #map below is measured
   from the viewport, so the rail fits whatever window it is given — and there
   the gate only means the map slides away on a laptop, which is the one place
   a list beside a map is read. So it is lifted for this rail, and the width
   half of it is kept: below 1060px the rail is not a column at all. */
@media (min-width: 1061px) {
    .bp-layout--map .bp-rail--sticky {
        position: sticky;
        top: var(--bp-stick);
    }
}

/* --- Result row -------------------------------------------------------
   .bp-card turned on its side: the same media, body, rating, excerpt and
   footer, laid out left to right. The line budgets in section 9 exist so a
   row of cards ends up the same height; one card per row has nothing to
   line up with, and the budgets would only leave an empty second line under
   a short name. */
.bp-card--row {
    flex-direction: row;
}

.bp-card--row .bp-card__media {
    /* The rank sits on the media, so the media is its containing block. */
    position: relative;
    width: 190px;
    height: auto;
    min-height: 168px;
    flex: 0 0 190px;
}

.bp-card--row .bp-card__title,
.bp-card--row .bp-card__meta--2line,
.bp-card--row .bp-card__excerpt {
    min-height: 0;
}

/* The two shapes of the card want two crops of the photograph — portrait
   beside the text, landscape across the top — so the media can hold a
   <picture> rather than an <img>. Section 9 sizes .bp-card__media a and the
   image inside it; the wrapper between them needs the same, or it stays an
   inline box and the image has no definite height to fill. */
.bp-card__media picture {
    display: block;
    width: 100%;
    height: 100%;
}

/* The place this business takes in the list, and the number on its marker.
   The pair is the whole reason the list and the map are on one screen, so
   the two are drawn to read as the same thing: white on navy here, white on
   the marker's red there. */
.bp-card__rank {
    position: absolute;
    top: 10px;
    left: 10px;
    min-width: 26px;
    height: 26px;
    padding: 0 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--bp-radius-pill);
    color: var(--bp-white);
    background: var(--bp-navy);
    box-shadow: var(--bp-shadow-sm);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
    line-height: 1;
}

/* Status above the name rather than over the photo: a 104px thumbnail has
   room for the rank or a badge, not both. */
.bp-card__flags {
    margin-bottom: var(--bp-space-3xs);
}

/* An Approved profile's own site, a line under the address and the hours. Not
   a third item in the footer: "View Business", this and a phone number are
   about 335px of text, and the body is 252px wide on a 320px phone and only
   323px at the 580px edge, just before the card stacks — it would wrap at
   exactly the widths the footer is tuned for.

   align-self, because the body is a flex column and would stretch the link
   across the card, making the empty space beside it clickable. The padding is
   the tap target (32px tall) and the text keeps its own line, so the
   block adds the same height at every width. There is no wrapping to plan
   for: the label is fixed. */
.bp-card__site,
.bp-card__site:link,
.bp-card__site:visited {
    align-self: flex-start;
    margin-top: 2px;
    padding: 8px 0;
    color: var(--bp-blue);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
}

.bp-card__site:hover,
.bp-card__site:focus {
    text-decoration: underline;
}

/* On a phone the row turns back into a card: a photograph in a strip beside
   the text is a swatch rather than a picture of the place, and the column it
   costs is most of the width there is — 104px of a 315px card left 184px of
   text, which is too little to hold "View Business" and a phone number on one
   line. Stacked, the photo runs the full width at the top and the body gets
   all of it. This is .bp-card's own shape, and the values are its own too.

   580 is also written into a <source media> in
   views/category_in_city_view.php, which is what picks the portrait crop for
   the row and the landscape one for this. Change the two together. */
@media (max-width: 580px) {
    .bp-card--row {
        flex-direction: column;
    }

    .bp-card--row .bp-card__media {
        width: auto;
        height: 150px;
        min-height: 0;
        flex: 0 0 150px;
    }

    /* A collage is composed rather than photographed, so a 150px strip cut out
       of the middle of it loses half of what it was built to show. This box is
       whatever height the image is at the card's width. */
    .bp-card--row .bp-card__media--full,
    .bp-card--row .bp-card__media--full img {
        height: auto;
    }

    .bp-card--row .bp-card__media--full {
        flex: 0 0 auto;
    }

    /* Section 9 sizes the image to the box and the box clips it, so the
       descender space under an inline image never showed. At its own height
       it does — 7px of card under the collage. */
    .bp-card--row .bp-card__media--full img {
        display: block;
    }
}

/* --- Map --------------------------------------------------------------
   One element in two states. On a screen wide enough for the rail it is a
   card at the top of it; below that breakpoint the rail has stacked under
   the list, so the map becomes a full-screen sheet that slides up from the
   foot and .bp-map__toggle is what raises it.

   The slide is driven by `bottom`, which MapComponent binds inline
   (-100vh or 0). That is why the sheet is only positioned inside the media
   query: on a static element an inline `bottom` does nothing, and on the
   sticky rail above it would be read as a stickiness threshold. */
.bp-map {
    overflow: hidden;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-surface);
    box-shadow: var(--bp-shadow-md);
}

.bp-map #map {
    width: 100%;
    height: min(calc(100vh - var(--bp-stick) - var(--bp-space-lg)), 720px);
}

/* What the control looks like is not a matter of screen width, so only where
   it sits and whether it is shown at all are in the query below. It is navy
   rather than a .bp-btn: it floats over a map, where the outline of a
   secondary button would be lost and a blue primary would compete with the
   markers. */
.bp-map__toggle {
    display: none;
    align-items: center;
    gap: 8px;
    min-height: 46px;
    padding: 0 20px;
    border: 0;
    border-radius: var(--bp-radius-pill);
    color: var(--bp-white);
    background: var(--bp-navy);
    box-shadow: var(--bp-shadow-lg);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
    cursor: pointer;
}

/* MapComponent's icons carry their own 24px, so this is only a floor for
   whatever it renders next. */
.bp-map__toggle svg {
    display: block;
    width: 20px;
    height: 20px;
}

@media (max-width: 1060px) {
    .bp-map {
        position: fixed;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: var(--bp-z-dialog);
        border: 0;
        border-radius: 0;
        box-shadow: none;
        transition: bottom var(--bp-transition-slow);
    }

    .bp-map #map {
        height: 100%;
    }

    /* Over the sheet, so the same control closes it again. */
    .bp-map__toggle {
        position: fixed;
        left: 50%;
        bottom: calc(18px + env(safe-area-inset-bottom, 0px));
        z-index: calc(var(--bp-z-dialog) + 1);
        transform: translateX(-50%);
        display: inline-flex;
    }
}

/* --- Marker and popup -------------------------------------------------
   MapComponent writes both. The marker is a red pin with the business's
   rank laid over it; the span is only on the pin because of the rule
   below, so it is not optional. */
.bp-scope .svgWrap {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
}

.bp-scope .svgWrap span {
    position: absolute;
    top: 4px;
    color: var(--bp-white);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    line-height: 1;
}

.bp-scope #infowindow {
    width: 190px;
    display: grid;
    gap: 6px;
    font-size: var(--bp-text-xs);
}

.bp-scope #infowindow img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--bp-radius-xs);
}

.bp-scope #infowindow .name {
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-snug);
}

.bp-scope #infowindow .name a,
.bp-scope #infowindow .name a:link,
.bp-scope #infowindow .name a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-scope #infowindow .name a:hover,
.bp-scope #infowindow .name a:focus {
    color: var(--bp-blue);
}

.bp-scope #infowindow .address {
    color: var(--bp-muted);
}

/* ==========================================================================
   26. PLANS & PRICING
   The pricing page, band by band: the two-column hero and the example answer
   beside it, the evidence band in its two-column form, the plan row, the
   social-proof block (names, numbers, banner), the comparison table, the row
   of recent profiles, and the closing band.

   Several of these are a second form of something the system already has,
   and the pairs are worth naming so neither gets used by accident:

     .bp-lander  vs .bp-hero      centred band for a page with no visual,
                                  vs the two-column band with one beside it.
     .bp-proof   (section 24)     the band itself, shared; section 24 fills
                                  it with a slider of screenshots, this one
                                  with copy beside two quoted answers.
     .bp-cta     vs .bp-cta--band full-bleed gradient, vs the same gradient
                                  as a rounded band inside the container.
     .bp-trustbar vs .bp-mini-trust
                                  the full-bleed row under a hero, vs the
                                  wrapping row inside a hero's text column.

   The plan card is section 17 plus everything that card stopped short of. A
   page selling one plan needs a name, a price and a list of ticks; a page
   selling three has to say which one it recommends, why it costs what it
   does, and what it adds to the plan before it.
   ========================================================================== */

/* --- Hero ----------------------------------------------------------------
   The claim on one side and an example of the outcome on the other, which is
   the shape .bp-lander cannot take: that one is centred, and centring a
   column of copy leaves nowhere for the thing it is about.

   The visual column has a 390px floor and the text column none, so the card
   beside the headline stops shrinking before it stops being readable and the
   headline takes the squeeze instead. */
.bp-hero {
    position: relative;
    padding-block: 92px 58px;
    overflow: hidden;
    background: var(--bp-gradient-hero);
}

/* One outlined ring bleeding off the top-right corner. A pseudo-element and
   not an image: the whole effect is a 72px ring at 4.5% opacity, and an asset
   for that is an asset to deploy, cache and get wrong. */
.bp-hero::after {
    content: "";
    position: absolute;
    top: -250px;
    right: -180px;
    width: 520px;
    height: 520px;
    border: 72px solid rgba(24, 91, 217, .045);
    border-radius: 50%;
    pointer-events: none;
}

.bp-hero__grid {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1.02fr) minmax(390px, .98fr);
    gap: var(--bp-space-2xl);
    align-items: center;
}

.bp-hero .bp-display-1 {
    max-width: 760px;
}

.bp-hero .bp-lead {
    max-width: 720px;
}

/* A hero with nothing beside the copy: one column, and the headline and lead
   run the full width of the container instead of stopping at their measure. */
.bp-hero__grid--solo {
    grid-template-columns: minmax(0, 1fr);
}

.bp-hero__grid--solo .bp-display-1,
.bp-hero__grid--solo .bp-lead {
    max-width: none;
}

/* The single column centred on the band: eyebrow, headline, lead, button and
   trust row all on one axis. Meant to go with --solo. The headline and lead
   are centred as blocks as well as lines, so that without --solo their caps
   still sit in the middle rather than against the left edge. The trust row's
   own centring also covers the phone layout, where it turns into a column. */
.bp-hero__grid--center {
    text-align: center;
}

.bp-hero__grid--center .bp-display-1,
.bp-hero__grid--center .bp-lead {
    margin-inline: auto;
}

.bp-hero__grid--center .bp-hero__actions,
.bp-hero__grid--center .bp-mini-trust {
    justify-content: center;
}

.bp-hero__grid--center .bp-mini-trust {
    align-items: center;
}

.bp-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-xs);
}

/* --- Mini trust row ------------------------------------------------------
   .bp-trustbar is the full-bleed version: its own edges, its own translucent
   ground, three equal columns across the whole band. This one lives inside
   the hero's text column, so it wraps instead of dividing, and it carries no
   ground of its own. */
.bp-mini-trust {
    margin-top: 30px;
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-md);
}

.bp-mini-trust__item {
    display: flex;
    align-items: center;
    gap: 9px;
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
}

/* --- Example answer ------------------------------------------------------
   An assistant answer quoted as a card: the question, the opening line, the
   businesses it named, and the source it cites. Used beside the hero, and in
   a smaller form inside the evidence band.

   It is a quotation, not a screenshot, so it never gets to look like one:
   .bp-meta in the top-right says what it is, and the page must label it. */
.bp-ai-card {
    position: relative;
    padding: var(--bp-space-md);
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-xl);
    background: rgba(255, 255, 255, .96);
    box-shadow: var(--bp-shadow-xl);
}

.bp-ai-card__top {
    margin-bottom: 18px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-sm);
}

.bp-ai-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
}

.bp-ai-dot {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    display: grid;
    place-items: center;
    border-radius: var(--bp-radius-xs);
    color: var(--bp-white);
    background: var(--bp-navy);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    line-height: 1;
}

.bp-ai-question {
    margin: 0 0 13px;
    color: var(--bp-navy);
    font-size: var(--bp-text-lg);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.02em;
}

.bp-ai-answer {
    margin: 0;
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    line-height: var(--bp-leading-relaxed);
}

/* An ordered list with its own numerals: the answer numbered its results and
   the rank is part of what is being quoted, so it is not a list-style the
   card is free to restyle away. */
.bp-ai-results {
    margin: var(--bp-space-sm) 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--bp-space-2xs);
}

.bp-ai-results li {
    display: grid;
    grid-template-columns: 22px minmax(0, 1fr);
    gap: 9px;
    color: var(--bp-body);
    font-size: var(--bp-text-xs);
}

.bp-ai-results b {
    color: var(--bp-navy);
}

/* --- Source row ----------------------------------------------------------
   What the answer cited, as a chip. The mark inside it is the logo disc, so
   this is the one component outside the masthead allowed to fill anything
   with --bp-brand: section 1 reserves that indigo for the mark, and here the
   chip IS the mark. */
.bp-source-row {
    margin-top: 20px;
    padding-top: var(--bp-space-sm);
    border-top: 1px solid var(--bp-line);
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--bp-space-2xs);
}

.bp-source-label {
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-bold);
}

.bp-source-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 10px;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-pill);
    color: var(--bp-navy);
    background: var(--bp-blue-50);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    line-height: 1.2;
}

.bp-source-chip__mark {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    color: var(--bp-white);
    background: var(--bp-brand);
    font-size: 8px;
    font-weight: var(--bp-weight-black);
    line-height: 1;
}

/* --- Evidence band, two-column form -------------------------------------
   .bp-proof is section 24 and unchanged: the deep-blue ground a row of white
   evidence needs to read against. What is new is this arrangement of it —
   the claim on the left and two quoted answers on the right, rather than a
   section head over a slider. */
.bp-proof__grid {
    display: grid;
    grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
    gap: 52px;
    align-items: center;
}

.bp-proof .bp-display-2 {
    color: var(--bp-white);
}

/* rgba white would drop under 5:1 against the blue end of the gradient once
   the band stacks; a solid tint holds across all of it. */
.bp-proof .bp-lead {
    max-width: 700px;
    margin: 0;
    color: #d9e4fa;
}

.bp-proof__shots {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px;
}

.bp-proof-shot {
    padding: 20px;
    border-radius: var(--bp-radius-lg);
    color: var(--bp-ink);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-lg);
}

.bp-proof-shot__head {
    margin-bottom: var(--bp-space-sm);
    display: flex;
    align-items: center;
    gap: var(--bp-space-2xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
}

.bp-proof-shot__question {
    margin: 0 0 var(--bp-space-2xs);
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-black);
}

.bp-proof-shot p {
    margin: 0;
    color: var(--bp-body);
    font-size: var(--bp-text-2xs);
    line-height: var(--bp-leading-normal);
}

.bp-proof-shot .bp-source-row {
    margin-top: 14px;
    padding-top: var(--bp-space-xs);
}

/* --- Plan row ------------------------------------------------------------
   Not .bp-grid with --bp-cols:3. Section 21 takes that to two columns at
   1060, and two columns of a three-plan row is one plan on its own under the
   other two — the reader compares the pair and scrolls past the orphan.
   Three, then one, and the single column keeps a measure of its own so the
   card does not stretch to 1200px of ticks. */
.bp-pricing-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--bp-space-sm);
    align-items: stretch;
}

/* --- The rest of the plan card ------------------------------------------
   The ribbon over the recommended plan. It sits on the card's top edge, so
   the .bp-price--featured border under it is what makes the two read as one
   shape; .bp-price is already position:relative for exactly this. Only the
   featured plan may carry one, for the same reason only one plan may be
   featured. */
.bp-price__badge {
    position: absolute;
    top: -13px;
    left: 50%;
    padding: 6px 12px;
    transform: translateX(-50%);
    border-radius: var(--bp-radius-pill);
    color: var(--bp-white);
    background: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    letter-spacing: .06em;
    line-height: 1.2;
    text-transform: uppercase;
    white-space: nowrap;
}

.bp-price .bp-icon-tile {
    margin-bottom: 18px;
}

/* The amber pair of section 8, for the plan that is neither the recommended
   one nor the entry one: three identical blue tiles say the three cards are
   three of the same thing. Amber as --bp-amber-ink on the warning tint,
   never white on --bp-amber (2.15:1) — the rule .bp-badge--award follows. */
.bp-icon-tile--amber {
    color: var(--bp-amber-ink);
    background: var(--bp-warning-bg);
}

/* The price of the plan below, restated as the difference. It answers the
   question the middle card exists to answer, so it goes directly under the
   amount and in the accent colour, not into muted meta at the foot. */
.bp-price__anchor {
    margin: 0 0 18px;
    color: var(--bp-blue);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
}

.bp-price__headline {
    margin: 0 0 9px;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-black);
    line-height: var(--bp-leading-snug);
}

.bp-price__desc {
    margin: 0 0 18px;
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    line-height: var(--bp-leading-normal);
}

/* "Everything in Trusted Profile, plus" — the list heading, and the one
   thing that stops three lists of ticks from reading as three unrelated
   lists. */
.bp-price__label {
    margin: 2px 0 10px;
    color: var(--bp-navy);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* Section 17 puts margin-top:auto on the button, which is what lands three
   buttons on one baseline. A note under the button breaks that: the button
   is no longer the last thing in the card, so the slack has to collect above
   the pair instead of above the button. */
.bp-price__foot {
    margin-top: auto;
}

.bp-price__foot .bp-btn {
    width: 100%;
    margin-top: 0;
}

.bp-price__note {
    margin: 10px 0 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    line-height: 1.45;
}

/* --- Social proof --------------------------------------------------------
   The head over a block whose content is centred too, which is why it is not
   .bp-section-head: that one is a left-aligned heading with its actions at
   the far end, because everything it tops starts at the left edge. */
.bp-social-proof__head {
    max-width: 820px;
    margin: 0 auto 34px;
    text-align: center;
}

.bp-social-proof__head .bp-display-3 {
    margin-bottom: 10px;
}

/* The block's own bottom margin is the gap to what follows; .bp-text brings
   a second one and the head ends up 50px clear of the wall under it. */
.bp-social-proof__head .bp-text {
    margin-bottom: 0;
}

/* Names of businesses on a plan, as a wall. A mark over a name is
   .bp-logo-wall in section 24 and this is the wordmark version: the
   businesses here have no logo we hold, so the tile carries the name and the
   trade under it. */
.bp-logos {
    margin-bottom: 32px;
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: var(--bp-space-xs);
    align-items: center;
}

.bp-logo-tile {
    min-height: 74px;
    padding: 14px;
    display: grid;
    place-items: center;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
    line-height: 1.2;
    text-align: center;
}

/* --bp-text-2xs and not the 9px this block was drawn at: 11px is the
   system's floor, and a trade name set below it is decoration rather than a
   word anyone reads. */
.bp-logo-tile small {
    display: block;
    margin-top: var(--bp-space-3xs);
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-bold);
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* Where we do hold the mark, the tile is the image itself. The height is
   fixed rather than left to the artwork, so a wide wordmark and a square
   badge sit in boxes of one height and are fitted inside instead of setting
   the row's height between them. */
img.bp-logo-tile {
    height: 74px;
    width: 100%;
    object-fit: contain;
    object-position: center;
}

/* The same wall, run as one band that never ends. The marquee is the frame:
   it clips the track and fades both ends, so a mark enters and leaves the
   band instead of being cut at the container edge. The track carries the
   marks twice over -- the animation walks it exactly one copy along and
   restarts, which reads as a loop only because the second copy is under the
   eye when the first one ends. */
.bp-logo-marquee {
    margin-bottom: 32px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 72px, #000 calc(100% - 72px), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 72px, #000 calc(100% - 72px), transparent);
}

.bp-logo-marquee__track {
    display: flex;
    width: max-content;
    gap: var(--bp-space-xs);
    animation: bp-logo-marquee 44s linear infinite;
}

/* A fixed tile width and no shrinking: in a flex track a percentage or an
   auto width would let the marks resize as the copies pass, and the band
   would breathe instead of travel. */
.bp-logo-marquee__track .bp-logo-tile {
    flex: 0 0 190px;
    width: 190px;
}

/* The tile is tinted rather than white: the marks we are given carry their
   own light backgrounds and edges, and on a white surface those edges stop
   being visible at all. The border goes, since the tint already separates
   the tile from the band. */
.bp-logo-marquee .bp-logo-tile {
    border-color: transparent;
    background: var(--bp-blue-100);
}

/* Reading a name in the band is worth more than the motion, so pointing at
   it stops the travel. */
.bp-logo-marquee:hover .bp-logo-marquee__track,
.bp-logo-marquee:focus-within .bp-logo-marquee__track {
    animation-play-state: paused;
}

@keyframes bp-logo-marquee {
    from {
        transform: translate3d(0, 0, 0);
    }

    to {
        transform: translate3d(calc(-50% - var(--bp-space-xs) / 2), 0, 0);
    }
}

/* A fact and the sentence that qualifies it, three across. .bp-info-tile is
   the centred icon-over-label version for a single phrase; this one is a row
   because the number has to read before the sentence does. */
.bp-numbers {
    margin-bottom: var(--bp-space-md);
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
}

.bp-number {
    padding: 22px;
    display: flex;
    align-items: center;
    gap: 14px;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
}

.bp-number__value {
    color: var(--bp-navy);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.03em;
    line-height: 1.15;
}

.bp-number p {
    margin: 2px 0 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

/* The banner mid-page. Not .bp-promo: that one leads with an icon tile and
   is built to land on a page that is not on the system, which is why it
   carries its own everything. This one is inside a band that already sets
   the context, so it is a line, a price and the button. */
.bp-inline-cta {
    padding: 26px 30px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-md);
    border: 1px solid #d5e2f5;
    border-radius: var(--bp-radius-xl);
    background: linear-gradient(90deg, var(--bp-blue-50), var(--bp-white));
}

.bp-inline-cta h3 {
    margin: 0 0 5px;
    color: var(--bp-navy);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.03em;
    line-height: var(--bp-leading-snug);
}

.bp-inline-cta p {
    margin: 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-sm);
}

.bp-inline-cta .bp-btn {
    flex: 0 0 auto;
}

/* --- Recent profiles -----------------------------------------------------
   Four .bp-card of section 9 across the container. Its own grid rather than
   .bp-grid because section 21 halves a grid at 1060 and this row wants the
   same two, then one at the phone breakpoint rather than at 580. */
.bp-recent-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 18px;
}

/* A label over a card's photo. Proposed as an addition to section 9 rather
   than a local patch: .bp-card__media has no positioning context of its own,
   and anything laid over the photo needs one. */
.bp-card__media {
    position: relative;
}

.bp-card__tag {
    position: absolute;
    left: 12px;
    top: 12px;
    z-index: 2;
    padding: 6px 9px;
    border-radius: var(--bp-radius-pill);
    color: var(--bp-blue);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-sm);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    line-height: 1.2;
}

/* --- Comparison table ---------------------------------------------------
   A modifier on .bp-table rather than a component of its own: the wrapper,
   the borders, the head and the row rhythm are all already right. What is
   different is that every cell but the first holds one mark, which reads as
   a column only when it is centred, and that one of those columns is the
   recommendation.

   No min-width, which is the one thing .bp-table-wrap's overflow-x would
   otherwise act on. A table of prose scrolls sideways because its cells
   cannot be narrower than their words; this one is three columns of a single
   glyph beside a column of feature names, and a comparison the reader has to
   drag sideways is not a comparison — the three plans have to be on screen
   at once or the marks mean nothing. So it squeezes instead, and the two
   breakpoints below give it the room to: less padding at 820, and at 580 a
   smaller size with the ribbon's qualifier and the foot's buttons dropped. */
.bp-compare th,
.bp-compare td {
    text-align: center;
}

.bp-compare th:first-child,
.bp-compare td:first-child {
    text-align: left;
}

/* .bp-table uppercases its head, which is right for a column label and wrong
   for a product name: "AI PROFILE PLUS" is not what the plan is called, and
   the ribbon above it would be uppercased twice. */
.bp-compare thead th {
    padding-block: var(--bp-space-sm);
    background: var(--bp-surface-tint);
    font-size: var(--bp-text-sm);
    letter-spacing: normal;
    text-transform: none;
    white-space: normal;
}

/* The recommended column, painted from the head to the foot: a tint that
   stopped at the first row would read as a highlighted cell rather than a
   highlighted plan. The head and the foot are restated because
   .bp-compare thead th and .bp-compare tfoot td each set a background of
   their own and each out-ranks a single class. */
.bp-compare__hi,
.bp-compare thead .bp-compare__hi,
.bp-compare tfoot .bp-compare__hi {
    background: var(--bp-blue-100);
}

/* .bp-table lights a hovered row in --bp-blue-50, one step lighter than the
   tint this column already wears, so the recommended column would go flat
   under the pointer. One step darker keeps it visible while the row is lit. */
.bp-compare tbody tr:hover .bp-compare__hi {
    background: #dbe7ff;
}

/* The ribbon in the head of the recommended column. Not .bp-badge: that one
   is navy on --bp-blue-100, which is the tint the column itself is painted
   in, so it would have no edge. Filled blue, like the .bp-price__badge on
   the card the column belongs to.

   .bp-compare__flag-more holds the part of the ribbon that is a qualifier
   rather than the label — "· Only $20 more" after "Best value" — because the
   phone layout below has a column about 50px wide to put it in. */
.bp-compare__flag {
    display: inline-block;
    margin-bottom: 5px;
    padding: 4px 8px;
    border-radius: var(--bp-radius-pill);
    color: var(--bp-white);
    background: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    letter-spacing: .05em;
    line-height: 1.3;
    text-transform: uppercase;
}

/* The marks. The glyph is decoration in both cases — the meaning is the
   hidden word beside it, because a column of ticks says nothing to a reader
   who cannot see which column it is in. */
.bp-compare__yes {
    color: var(--bp-success);
}

.bp-compare__yes svg {
    width: 18px;
    margin-inline: auto;
}

.bp-compare__no {
    color: var(--bp-muted);
}

/* The prices and the buttons repeated at the foot of the table, so a reader
   who has just scrolled 17 rows does not have to scroll back up to act on
   what they read. */
.bp-compare tfoot td {
    padding-block: var(--bp-space-sm);
    border-bottom: 0;
    background: var(--bp-white);
    vertical-align: top;
}

.bp-compare__price {
    display: block;
    margin-bottom: 10px;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-black);
}

/* --- Closing band -------------------------------------------------------
   .bp-cta as a rounded band inside the container instead of a full-bleed
   one. The page ends on white, so a band with edges closes it; a full-bleed
   gradient would read as the start of the footer. */
.bp-cta--band {
    padding-block: 54px 64px;
    background: var(--bp-white);
}

.bp-cta--band .bp-cta__inner {
    padding: 34px 38px;
    border-radius: var(--bp-radius-2xl);
    background: var(--bp-gradient-brand);
}

/* Two buttons on one row at the end of the band. .bp-cta stacks them,
   because the full-bleed band has the height for a column and this one does
   not. */
.bp-cta--band .bp-cta__actions {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* .bp-cta p is #dbe4f7, a tint picked to clear 5:1 across the whole
   gradient. The price is not part of that sentence — it is the one fact the
   reader is being asked to act on — so it is white and on its own line. */
.bp-cta__price {
    margin: 6px 0 0;
    color: var(--bp-white);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-black);
}

/* --- Responsive ----------------------------------------------------------
   The three breakpoints of section 21: 1060, 820, 580. */
@media (max-width: 1060px) {

    .bp-hero__grid,
    .bp-proof__grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 38px;
    }

    .bp-ai-card {
        max-width: 720px;
    }

    .bp-pricing-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .bp-pricing-grid .bp-price {
        width: 100%;
        max-width: 760px;
        margin-inline: auto;
    }

    .bp-logos {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .bp-recent-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 820px) {
    .bp-hero {
        padding-block: 70px 48px;
    }

    .bp-proof__shots {
        grid-template-columns: minmax(0, 1fr);
    }

    .bp-logos {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .bp-numbers {
        grid-template-columns: minmax(0, 1fr);
    }

    .bp-inline-cta {
        align-items: flex-start;
        flex-direction: column;
    }

    /* Below a 780px container the feature names start wrapping to three
       lines at full padding; six of the 32px a cell spends on its own gutters
       buys the column back. */
    .bp-compare th,
    .bp-compare td {
        padding: 12px 10px;
    }
}

@media (max-width: 580px) {

    .bp-hero__actions,
    .bp-mini-trust {
        align-items: stretch;
        flex-direction: column;
    }

    .bp-mini-trust {
        gap: var(--bp-space-2xs);
    }

    .bp-hero__actions .bp-btn {
        width: 100%;
    }

    .bp-recent-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    /* One card per row has the width for a taller photo. */
    .bp-recent-grid .bp-card__media {
        height: 180px;
        flex-basis: 180px;
    }

    /* The table on a phone, fitted to the screen rather than scrolled. A
       comparison the reader has to drag sideways is not a comparison: the
       three plans have to be on screen at once or the marks mean nothing.

       table-layout:fixed is what actually guarantees that, and removing the
       min-width was not enough on its own. The auto algorithm sizes every
       column to its longest unbreakable word and then overflows the box when
       the sum does not fit — "$149/year" holds no space to break at, so at
       12px it set a 67px floor under all three plan columns and the table
       came out 346px wide inside a 330px wrapper on a 360px phone. Fixed
       takes the widths from the first row instead, so the table is exactly
       100% of the wrapper whatever ends up in it.

       On top of that the type steps down a size, the padding halves, and the
       three things that cannot be squeezed give way: the ribbon drops its
       qualifier, the price puts its period on a second line, and the foot
       drops its buttons — the prices stay, and the same three buttons are on
       the cards above. */
    .bp-compare {
        table-layout: fixed;
        font-size: var(--bp-text-xs);
    }

    /* 18% each for the marks leaves 46% for the feature name: 150px on the
       332px container of a 360px phone, which fits "recommendations", the
       longest word in that column. */
    .bp-compare thead th:not(:first-child) {
        width: 18%;
    }

    /* Fixed layout never widens a cell for a word that does not fit, so a
       word wider than its column has to be allowed to break inside it. */
    .bp-compare th,
    .bp-compare td {
        padding: 10px 5px;
        overflow-wrap: break-word;
    }

    .bp-compare thead th {
        padding-block: var(--bp-space-2xs);
        font-size: var(--bp-text-2xs);
    }

    .bp-compare__flag {
        margin-bottom: 3px;
        padding: 3px 6px;
        letter-spacing: 0;
    }

    .bp-compare__flag-more {
        display: none;
    }

    .bp-compare__yes svg {
        width: 15px;
    }

    .bp-compare tfoot td {
        padding-block: var(--bp-space-xs);
    }

    .bp-compare__price {
        margin-bottom: 0;
        font-size: var(--bp-text-sm);
    }

    .bp-compare__per {
        display: block;
    }

    .bp-compare tfoot .bp-btn {
        display: none;
    }

    .bp-cta--band .bp-cta__inner {
        padding: 28px 24px;
    }

    /* Section 21 stacks .bp-cta__actions here, and the row above out-ranks
       it by a class, so the stack has to be restated. */
    .bp-cta--band .bp-cta__actions {
        flex-direction: column;
    }
}

/* ==========================================================================
   27. SEARCH PROMO
   The AI Profile promo that sits between the results — on /search/ and on a
   category page, in both cases after the first business. One row on a wide
   screen: what the product is on the left, what it costs and how to buy it on
   the right, with a rule between them.

   The name is the reference's and it has outlived it: this is the result-list
   promo, whichever list. Renaming it would touch two views, a partial and a
   demo to say the same thing.

   It is a card among cards, so it is shaped like one — the same border,
   radius and shadow a .bp-card has — and it is tinted so it is not read as a
   fourth business. That tint is the whole of the visual difference, which is
   deliberate: an advertisement in a result list that shouts is an
   advertisement people learn to scroll past.

   views/components/ai_profile_promo.php is the markup, and it holds no styles
   of its own. Anything below that looks like a one-off is a one-off; the
   values that had tokens took them.
   ========================================================================== */

.bp-search-promo {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bp-space-md);
    width: 100%;
    /* Tall enough that the left column's four blocks and the right column's
       three land on the same centre line at the widths this runs at. */
    min-height: 190px;
    padding: 22px var(--bp-space-md);
    overflow: hidden;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-lg);
    background: linear-gradient(135deg, var(--bp-blue-50) 0%, var(--bp-white) 56%);
    box-shadow: var(--bp-shadow-md);
}

.bp-search-promo__main {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* The one place amber appears here, as a 7px dot: it is decoration beside a
   label that says the same thing in words. */
.bp-search-promo__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--bp-space-2xs);
    margin: 0 0 10px;
    color: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-black);
    letter-spacing: var(--bp-tracking-wide);
    line-height: 1;
    text-transform: uppercase;
}

.bp-search-promo__eyebrow::before {
    content: "";
    width: 7px;
    height: 7px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--bp-amber);
}

.bp-search-promo__title {
    margin: 0;
    color: var(--bp-ink);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.02em;
    /* The tight tracking above shrinks the spaces too, and in a black weight
       short words like "$1 a day" start to read as one word. Give them back. */
    word-spacing: .1em;
    /* The wider spaces move where the title breaks on narrow columns; balance
       keeps the last line from being a lone "day". */
    text-wrap: balance;
    line-height: 1.15;
}

.bp-search-promo__text {
    /* A measure, not a column width: the paragraph is the longest thing in
       the block and would otherwise set how wide the left column wants to be. */
    max-width: 600px;
    margin: var(--bp-space-2xs) 0 0;
    color: var(--bp-body);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-normal);
}

.bp-search-promo__benefits {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-2xs) 14px;
    margin: var(--bp-space-sm) 0 0;
    padding: 0;
    list-style: none;
}

.bp-search-promo__benefits li {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
    line-height: 1.3;
    /* Three short claims read as three claims only if each stays on one
       line; below 480px they are given a column each instead. */
    white-space: nowrap;
}

/* bpIcon() paints its strokes with currentColor, so the colour is set on the
   svg and the tick inherits it — blue against the navy of the words. */
.bp-search-promo__benefits svg {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
    color: var(--bp-blue);
}

/* Not in the markup as it stands. It is the line the block is missing if the
   claims above ever need a source under them — "Used by 12,000 businesses" —
   and it is kept because that is the shape such a line takes here. */
.bp-search-promo__trust {
    margin: var(--bp-space-sm) 0 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    line-height: 1.4;
}

.bp-search-promo__trust strong {
    color: var(--bp-ink);
    font-weight: var(--bp-weight-strong);
}

.bp-search-promo__offer {
    flex: 0 0 auto;
    min-width: 170px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    padding-left: 22px;
    border-left: 1px solid var(--bp-line);
}

.bp-search-promo__offer-label {
    margin: 0 0 var(--bp-space-3xs);
    color: var(--bp-blue);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
    line-height: 1.2;
}

/* The same size as the title, not a step under it. They are in two columns
   with a rule between, so they do not compete — they balance. */
.bp-search-promo__price {
    margin: 0 0 var(--bp-space-xs);
    color: var(--bp-ink);
    font-size: var(--bp-text-2xl);
    font-weight: var(--bp-weight-black);
    letter-spacing: -.025em;
    line-height: 1;
}

.bp-search-promo__price span {
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
    letter-spacing: 0;
}

/* 720, not the system's 820: the block runs inside .bp-layout__main, which
   is already 400px narrower than the window wherever the map rail is a
   column. The two columns stop fitting well before the page does. */
@media (max-width: 720px) {
    .bp-search-promo {
        align-items: flex-start;
        flex-direction: column;
        gap: 18px;
        min-height: 0;
        padding: var(--bp-space-sm);
    }

    .bp-search-promo__offer {
        width: 100%;
        min-width: 0;
        padding: var(--bp-space-sm) 0 0;
        border-left: 0;
        border-top: 1px solid var(--bp-line);
    }

    .bp-search-promo__offer .bp-btn {
        width: 100%;
    }

    .bp-search-promo__benefits li {
        white-space: normal;
    }
}

@media (max-width: 480px) {
    .bp-search-promo {
        padding: 18px;
        border-radius: var(--bp-radius-md);
    }

    .bp-search-promo__title {
        font-size: var(--bp-text-xl);
    }

    .bp-search-promo__benefits {
        display: grid;
        grid-template-columns: 1fr;
        gap: 9px;
    }
}

/* ==========================================================================
   28. BLOG
   The blog index: every post as a wide card, photo beside the summary, in a
   plain .bp-stack — no map, no filters, nothing beside it, which is what
   makes it simpler than the results pages above. www/blog/index.php is
   where it renders; section 15's .bp-pagination is server-rendered there
   now instead of read back off the URL by a script, and the per-post tags
   are .bp-badge--neutral links, the same badge the tables use for a jump
   link, reused rather than a component of its own.
   The post page (views/news_page_view.php) follows at the end: the post
   panel, the defaults for the editor's HTML inside it, and the rail list.
   ========================================================================== */

/* .bp-card turned on its side like .bp-card--row, but for a post rather than
   a business: nothing competes with the photo for space here — no rank, no
   marker to pair it with — so it takes closer to half the card instead of a
   190px strip, and the excerpt is a summary rather than a review, so it
   keeps six lines instead of three. */
.bp-card--feature {
    flex-direction: row;
}

.bp-card--feature .bp-card__media {
    width: 42%;
    height: auto;
    min-height: 280px;
    flex: 0 0 42%;
}

.bp-card--feature .bp-card__title,
.bp-card--feature .bp-card__excerpt {
    min-height: 0;
}

.bp-card--feature .bp-card__excerpt {
    -webkit-line-clamp: 6;
}

/* 820, not section 25's 580: the photo here is already half the row, so a
   row this wide runs out of space for the text sooner than a 190px
   thumbnail does. */
@media (max-width: 820px) {
    .bp-card--feature {
        flex-direction: column;
    }

    .bp-card--feature .bp-card__media {
        width: auto;
        height: 240px;
        min-height: 0;
        flex: 0 0 240px;
    }
}

/* --- Post page -----------------------------------------------------------
   www/views/news_page_view.php, one post. It is the business profile's
   frame: .bp-profile-head for the masthead, then .bp-page and .bp-layout,
   with the post as one .bp-panel in the main column and more posts in the
   rail. What follows is only what a post has and a profile does not. */

/* A post title is a sentence, not a name: at 44px across the full
   container it ran to 30 words a line. */
.bp-article-head h1 {
    max-width: 920px;
}

/* Tags are .bp-pill links, not badges: a tinted badge is --bp-blue-100 on
   a masthead gradient that ends at #edf4ff, and the pill vanished. */
.bp-article-head__tags {
    gap: var(--bp-space-2xs);
}

/* The last crumb is the title, which the h1 repeats right under it — one
   line is enough to say where the reader is. */
.bp-article-head .bp-breadcrumbs li:last-child {
    min-width: 0;
}

.bp-article-head .bp-breadcrumbs [aria-current="page"] {
    max-width: min(46ch, 60vw);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bp-article-head .bp-profile-head__meta strong {
    color: var(--bp-ink);
    font-weight: var(--bp-weight-bold);
}

/* The author's initial, standing in for a photo nobody has uploaded. Filled
   like the approved badge, for the same reason the tags are pills. */
.bp-avatar {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    display: inline-grid;
    place-items: center;
    border-radius: 50%;
    color: var(--bp-white);
    background: var(--bp-gradient-brand);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-black);
    text-transform: uppercase;
}

/* The photo runs edge to edge across the top of the panel, like a card's,
   so the panel gives up its padding and the body below takes it back. */
.bp-article {
    padding: 0;
    overflow: hidden;
}

.bp-article__figure {
    margin: 0;
}

.bp-article__media {
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: var(--bp-gradient-media);
}

.bp-article__media img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
}

.bp-article__credit {
    margin: 0;
    padding: var(--bp-space-2xs) 28px 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

.bp-article__body {
    padding: var(--bp-space-md) 28px 28px;
}

.bp-article__foot {
    margin-top: var(--bp-space-lg);
    padding-top: var(--bp-space-md);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bp-space-xs) var(--bp-space-sm);
    border-top: 1px solid var(--bp-line);
}

.bp-article__foot-label {
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-strong);
}

/* --- Post content ----------------------------------------------------------
   The body of a post is HTML the editor (Unlayer) wrote, printed as it is.
   The view re-scopes the <style> blocks that come with it under this class,
   so they no longer restyle the page round it. The editor's inline styles
   always win here: it writes a font-size and line-height on every text
   block, and nothing below is !important. These rules are the defaults
   for whatever the editor left unset: the face and colour, links, lists,
   images, and any heading or table that came in without a style.

   Everything is .bp-article__content + one element, (0,1,1). A re-scoped
   rule is the same class + the editor's own selector, so it ties at worst
   and prints later, and a block's own CSS beats these defaults. The one
   rule that loses to them is a bare `*` reset, which is the point: one
   pasted block brings *{margin:0;padding:0}, and that must not strip the
   bullets off every list in the post. */
.bp-article__content {
    overflow-wrap: break-word;
}

.bp-article__content h1,
.bp-article__content h2 {
    margin: var(--bp-space-lg) 0 var(--bp-space-xs);
    font-size: var(--bp-text-2xl);
    line-height: var(--bp-leading-snug);
}

.bp-article__content h3 {
    margin: var(--bp-space-md) 0 var(--bp-space-2xs);
    font-size: var(--bp-text-xl);
    line-height: var(--bp-leading-snug);
}

.bp-article__content h4,
.bp-article__content h5,
.bp-article__content h6 {
    margin: var(--bp-space-md) 0 var(--bp-space-2xs);
    font-size: var(--bp-text-lg);
    line-height: var(--bp-leading-snug);
}

/* Links in running text carry an underline as well as the colour: blue
   against the body grey is 1.4:1, too close to be the only difference. */
.bp-article__content a {
    color: var(--bp-blue);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
    text-underline-offset: .18em;
    transition: color var(--bp-transition), text-decoration-color var(--bp-transition);
}

.bp-article__content a:hover,
.bp-article__content a:focus {
    color: var(--bp-blue-hover);
    text-decoration-color: currentColor;
}

.bp-article__content ul,
.bp-article__content ol {
    margin: 0 0 var(--bp-space-sm);
    padding-left: 1.4em;
}

.bp-article__content ul {
    list-style: disc;
}

.bp-article__content ol {
    list-style: decimal;
}

.bp-article__content li {
    margin-bottom: var(--bp-space-3xs);
}

.bp-article__content li::marker {
    color: var(--bp-line-control);
}

.bp-article__content img {
    height: auto;
    border-radius: var(--bp-radius-sm);
}

.bp-article__content blockquote {
    margin: var(--bp-space-md) 0;
    padding: var(--bp-space-3xs) 0 var(--bp-space-3xs) var(--bp-space-sm);
    border-left: 3px solid var(--bp-blue);
    color: var(--bp-navy);
}

.bp-article__content hr {
    height: 1px;
    margin: var(--bp-space-md) 0;
    border: 0;
    background: var(--bp-line);
}

.bp-article__content table {
    width: 100%;
    margin: 0 0 var(--bp-space-sm);
    border-collapse: collapse;
    font-size: var(--bp-text-base);
}

.bp-article__content th,
.bp-article__content td {
    padding: 10px var(--bp-space-xs);
    border-bottom: 1px solid var(--bp-line);
    text-align: left;
    vertical-align: top;
}

.bp-article__content th {
    color: var(--bp-navy);
    background: var(--bp-surface);
    font-weight: var(--bp-weight-strong);
}

/* Unlayer pads every block by the same 10px top and bottom, so a heading
   sat as far from the paragraph it opens as from the one it closes. The
   extra space goes above it only, on the block's wrapper, which the
   editor never styles. */
.bp-article__content .u_content_heading {
    margin-top: var(--bp-space-sm);
}

.bp-article__content .u_content_heading:first-child {
    margin-top: 0;
}

/* --- Post list -------------------------------------------------------------
   The post page's rail: thumbnail, title, date, one link per row. The rows
   are split by a hairline rather than boxed, because they already sit in a
   .bp-panel, and the title gets three lines instead of a tile's two: a post
   title runs to 90 characters, a business name rarely to 30. */
.bp-article-rail .bp-panel__head {
    align-items: baseline;
}

.bp-article-rail .bp-panel__head h2 {
    font-size: var(--bp-text-xl);
}

.bp-postlist {
    margin: 0;
    padding: 0;
    list-style: none;
}

.bp-postlist li+li {
    border-top: 1px solid var(--bp-line);
}

.bp-postlist__item,
.bp-postlist__item:link,
.bp-postlist__item:visited {
    padding-block: var(--bp-space-xs);
    display: grid;
    grid-template-columns: 72px minmax(0, 1fr);
    gap: var(--bp-space-xs);
    align-items: start;
    color: var(--bp-navy);
    text-decoration: none;
}

.bp-postlist li:first-child .bp-postlist__item {
    padding-top: 0;
}

.bp-postlist li:last-child .bp-postlist__item {
    padding-bottom: 0;
}

.bp-postlist__thumb {
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--bp-radius-xs);
    background: var(--bp-gradient-media);
}

.bp-postlist__thumb img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
}

.bp-postlist__title {
    display: -webkit-box;
    overflow: hidden;
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    line-height: var(--bp-leading-snug);
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    transition: color var(--bp-transition);
}

.bp-postlist__date {
    margin-top: var(--bp-space-3xs);
    display: block;
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-medium);
}

.bp-postlist__item:hover .bp-postlist__title,
.bp-postlist__item:focus-visible .bp-postlist__title {
    color: var(--bp-blue);
}

/* Once the rail drops under the post, two full-width panels of short rows
   leave most of the line empty, so they sit side by side until the phone
   split. */
@media (min-width: 821px) and (max-width: 1060px) {
    .bp-article-rail {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }
}

@media (max-width: 580px) {

    .bp-article__credit,
    .bp-article__body {
        padding-inline: var(--bp-space-sm);
    }

    .bp-article__body {
        padding-bottom: var(--bp-space-sm);
    }

    /* Unlayer stacks its columns at 480px. Inside the phone gutter and the
       panel's padding, a 520px screen already leaves each half of a
       two-column row 230px. (0,5,0), against the editor's own (0,4,0):
       .u-row .u-col.u-col-50 re-scoped. */
    .bp-scope .bp-article__content .u-row:not(.no-stack) {
        flex-wrap: wrap;
    }

    .bp-scope .bp-article__content .u-row:not(.no-stack) .u-col {
        flex: 0 0 100%;
        max-width: 100%;
    }
}


/* ==========================================================================
   29. AWARDS
   The award page (recommended.php): the owner of a recommended business
   picks a badge, copies its embed code, takes a text version, downloads a
   certificate. The steps, the copy field and the letter are general; the
   tile and the stage exist because a badge preview is third-party markup —
   the badge HTML customers paste onto their own sites — that the page shows
   but does not own.
   ========================================================================== */

/* Same disc as the confirmation mark, in the award colours: amber-ink on the
   warning tint, the pair .bp-badge--award uses, so the amber never carries
   a white glyph (see the amber note in section 1). */
.bp-lander__mark--award {
    color: var(--bp-amber-ink);
    background: var(--bp-warning-bg);
    box-shadow: 0 0 0 8px rgba(245, 158, 11, .14);
}

/* --- Badge picker --------------------------------------------------------
   auto-fill rather than .bp-grid's fixed three: a badge is a fixed-size
   object, so the column count follows the room there is, down to one. */
.bp-award-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
    gap: var(--bp-space-sm);
}

/* One badge to choose. The tile's only control is the button in its foot,
   stretched over the whole tile by ::after: the tile is one tab stop with a
   real label, and a click anywhere on it opens the badge. */
.bp-award-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--bp-space-xs);
    padding: var(--bp-space-2xs) var(--bp-space-2xs) var(--bp-space-sm);
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-sm);
    transition: var(--bp-transition);
}

.bp-award-tile:hover {
    border-color: var(--bp-line-control);
    box-shadow: var(--bp-shadow-md);
}

.bp-award-tile__action {
    align-self: center;
}

.bp-award-tile__action::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--bp-radius-lg);
}

/* .bp-btn lifts on hover with a transform, and a transform makes the button
   the containing block of its ::after — the stretched target would shrink to
   the button under the pointer and the hover would flicker off. */
.bp-award-tile .bp-award-tile__action:hover {
    transform: none;
}

/* The ring goes round the tile, not the small button: the ::after is the
   tile's outline box. One ring, so the button's own is dropped. */
.bp-award-tile .bp-award-tile__action:focus-visible {
    outline: 0;
}

.bp-award-tile .bp-award-tile__action:focus-visible::after {
    outline: 3px solid var(--bp-focus-color);
    outline-offset: 2px;
}

/* --- Stage: where a badge preview is drawn ------------------------------
   Its own light ground, centred. The page marks the preview inert: the
   badge's links and its tabindex roots must not become tab stops or click
   targets here. overflow:hidden keeps an oversized badge inside the tile. */
.bp-award-stage {
    min-height: 260px;
    padding: var(--bp-space-2xs);
    display: grid;
    place-items: center;
    overflow: hidden;
    border-radius: var(--bp-radius-md);
    background: var(--bp-surface);
}

.bp-dialog .bp-award-stage {
    min-height: 300px;
    margin-bottom: var(--bp-space-sm);
}

/* --- Colour swatch inside a .bp-pill -------------------------------------
   The pill's text names the colour; the dot only repeats it, so the choice
   never rides on colour alone. The fill is data, passed as --bp-swatch. */
.bp-pill__swatch {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
    border: 1px solid var(--bp-line-control);
    border-radius: 50%;
    background: var(--bp-swatch, var(--bp-white));
}

.bp-pill[aria-pressed="true"] .bp-pill__swatch {
    border-color: var(--bp-white);
}

/* Option rows of a dialog: a .bp-fieldset legend over a .bp-cluster. */
.bp-award-options {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bp-space-sm) var(--bp-space-lg);
}

.bp-award-options .bp-fieldset {
    margin: 0;
}

.bp-dialog .bp-award-options {
    margin-bottom: var(--bp-space-md);
}

/* The how-to under the picker: set off by a rule, steps, then the one
   caution about the code. */
.bp-award-howto {
    margin-top: var(--bp-space-xl);
    padding-top: var(--bp-space-lg);
    border-top: 1px solid var(--bp-line);
}

.bp-award-howto>h3 {
    margin: 0 0 var(--bp-space-md);
}

.bp-award-howto .bp-alert {
    margin-top: var(--bp-space-md);
}

/* .bp-split centres its two columns on each other, which suits a visual
   beside copy; two blocks that are read top-down line up at the top. */
.bp-split--start {
    align-items: start;
}

/* --- Copy field ----------------------------------------------------------
   Read-only code the reader takes away: readonly, never disabled, so the
   text stays selectable; mono, so a snippet reads as code; and the button
   says what happened — "Copied" with a check for a few seconds, announced
   through the status line beside it. (0,2,0) because the scope's own field
   reset, .bp-scope textarea {font:inherit}, is (0,1,1). */
.bp-scope .bp-textarea--code {
    min-height: 116px;
    color: var(--bp-ink);
    background: var(--bp-surface);
    font-family: var(--bp-font-mono);
    font-size: var(--bp-text-sm);
    line-height: var(--bp-leading-normal);
    word-break: break-all;
    resize: vertical;
}

.bp-copy__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bp-space-xs);
}

.bp-copy__status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--bp-success);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
}

.bp-copy__status svg {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
}

/* A sentence shown the way it will read once pasted: the panel is the page
   the reader's visitors see, so it is white and plain, links included. */
.bp-award-quote {
    margin: 0;
    padding: var(--bp-space-md);
    border: 1px solid var(--bp-line);
    border-left: 4px solid var(--bp-blue);
    border-radius: var(--bp-radius-md);
    color: var(--bp-body);
    background: var(--bp-white);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-relaxed);
}

/* --- Steps -----------------------------------------------------------------
   A short ordered how-to. Numbered discs from a CSS counter, so the list is
   still a real <ol> for a screen reader; three across, one column at 820. */
.bp-steps {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: bp-step;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--bp-space-md);
}

.bp-steps>li {
    counter-increment: bp-step;
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr);
    gap: var(--bp-space-xs);
    align-items: start;
    color: var(--bp-body);
    font-size: var(--bp-text-base);
    line-height: var(--bp-leading-normal);
}

.bp-steps>li::before {
    content: counter(bp-step);
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    color: var(--bp-blue);
    background: var(--bp-blue-100);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-black);
}

.bp-steps strong {
    display: block;
    margin-bottom: 2px;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
}

/* --- Certificate preview -------------------------------------------------
   The canvas is drawn at print size (2489x3517) and shown scaled to the
   column; the frame gives the white sheet an edge on a white band. */
.bp-award-certificate {
    width: min(100%, 380px);
    margin-inline: auto;
    padding: var(--bp-space-2xs);
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-lg);
}

.bp-award-certificate canvas {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--bp-radius-xs);
}

.bp-award-certificate__actions {
    justify-content: center;
}

/* --- Letter: a short signed note from the team --------------------------- */
.bp-letter {
    max-width: 720px;
    margin: 0 auto;
    text-align: center;
}

.bp-letter p {
    margin: 0 0 var(--bp-space-sm);
    color: var(--bp-body);
    font-size: var(--bp-text-md);
    line-height: var(--bp-leading-relaxed);
}

.bp-letter__sign {
    display: inline-block;
    min-width: 220px;
    margin-top: var(--bp-space-xs);
    padding-top: var(--bp-space-sm);
    border-top: 1px solid var(--bp-line-strong);
    color: var(--bp-muted);
    font-size: var(--bp-text-base);
}

.bp-letter__sign strong {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
}

/* .bp-benefit had no slot for a link, so pages pushed theirs down with an
   inline margin. This sends it to the foot, so a row of cards lines its links
   up whatever the length of the text above them. */
.bp-benefit__action {
    margin-top: auto;
    padding-top: var(--bp-space-sm);
    align-self: flex-start;
}

@media (max-width: 820px) {
    .bp-steps {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--bp-space-sm);
    }
}


/* ==========================================================================
   30. PRICELIST
   What a business charges, as the profile's sub-pages print it: a heading for
   each category, then a row for each service with its price at the end of the
   line. views/components/pricelist.php is the markup, and the category heading
   is a plain .bp-subheading.

   A list and not a table. A price is read with the name it belongs to rather
   than compared down a column, a service often has no duration and a column
   that is empty on most rows is a column that has to explain itself, and a
   table sizes each column to its longest word, which on a 292px phone is not a
   thing a pricelist can afford.
   ========================================================================== */

.bp-pricelist {
    display: grid;
    gap: var(--bp-space-md);
}

.bp-pricelist__rows {
    margin: var(--bp-space-2xs) 0 0;
    padding: 0;
    list-style: none;
    border-top: 1px solid var(--bp-line);
}

/* Name, duration, price. The price column is allowed to be as wide as its text
   up to 45% of the row, and no wider: "250.00 - 400.00 USD" wraps inside its
   column and leaves the name its room, where a plain auto would give the price
   all it asked for and the name what was left. The columns are placed by
   number, so a row with no duration still has its price at the end. */
.bp-pricelist__row {
    padding: 11px 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto fit-content(45%);
    align-items: baseline;
    column-gap: var(--bp-space-sm);
    border-bottom: 1px solid var(--bp-line);
    font-size: var(--bp-text-base);
}

.bp-pricelist__name {
    grid-column: 1;
    color: var(--bp-ink);
    overflow-wrap: anywhere;
}

.bp-pricelist__time {
    grid-column: 2;
    color: var(--bp-muted);
    font-size: var(--bp-text-sm);
    white-space: nowrap;
}

.bp-pricelist__price {
    grid-column: 3;
    color: var(--bp-navy);
    font-weight: var(--bp-weight-strong);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* On a phone the duration moves under the name, where it is a note on it, and
   the price keeps the full height of the row on the right. */
@media (max-width: 480px) {
    .bp-pricelist__row {
        grid-template-columns: minmax(0, 1fr) fit-content(50%);
    }

    .bp-pricelist__time {
        grid-column: 1;
        grid-row: 2;
        margin-top: 2px;
    }

    .bp-pricelist__price {
        grid-column: 2;
        grid-row: 1 / span 2;
        align-self: start;
    }
}

/* ==========================================================================
   31. REVIEW LIST
   Every review a business has, as its comments page prints them. The card is
   section 21's profile card: a date and a score above the text, naming
   nobody. That is right for the three on the profile, whose cache has no
   author, and not for this page, whose cache and endpoint have one for every
   review. So the name takes the place the date had to itself and the date goes
   under it.

   views/components/review_list.php is the markup, and js_main/review-list.js
   prints the pages after the first in the same shape. The button that asks for
   them is a plain secondary .bp-btn.
   ========================================================================== */

.bp-review__who {
    min-width: 0;
    display: grid;
    gap: 2px;
}

.bp-review__author {
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
    font-weight: var(--bp-weight-strong);
    line-height: 1.3;
    /* A name is whatever was typed, and one with no space in it is as wide as
       it is: the card is a grid with an auto column, which grows to fit it. */
    overflow-wrap: anywhere;
}

/* Two lines on the left and one on the right: the score is level with the
   name and not between the two lines. */
.bp-review__who+.bp-rating {
    align-self: flex-start;
}

/* ==========================================================================
   32. AI PROFILE LANDING
   /ai-profile.php, band by band: the hero with a real profile beside it, the
   evidence band with three captured answers, the strip of businesses, the
   tour of a published profile, the story, the two things sold beyond the
   profile, the work the team has done, and the closing box.

   Most of the page is the system as it already was: .bp-hero from section 26,
   .bp-proof from sections 24 and 26, .bp-section-head, .bp-btn, .bp-card. What
   it brought is the screenshot-in-a-window (the hero and the tour both show a
   page of ours, and one component draws both), the numbered tour, the story
   card, the two diagrams under "Beyond your profile", the work card, the
   closing box, and the image viewer.

   The pairs worth naming, so neither gets used by accident:

     .bp-shot    (section 24)   a 4:3 screenshot with a caption, in a slider.
     .bp-window  (this one)     a tall page cropped to a fixed height, with a
                                title bar over it and a link under it.
     .bp-cta--band (section 26) the dark closing band. This page ends on a
                                light box, .bp-closing, because the footer
                                under it is dark and two dark bands in a row
                                read as one.
   ========================================================================== */

/* --- Hero ----------------------------------------------------------------
   Section 26's two-column hero with a longer headline than that section was
   drawn for, so the display size comes down: --bp-display-1 tops out at 78px
   and this sentence is a dozen words. The text column has no floor and the
   visual one has 390px, so the headline takes the squeeze. */
.bp-hero--profile .bp-display-1 {
    max-width: 560px;
    font-size: clamp(42px, 4.15vw, 61px);
    line-height: 1.06;
}

/* "AI answers." must not split across two lines: the half of the headline that
   carries the claim is the half that reads as a unit. */
.bp-hero__accent {
    color: var(--bp-blue);
    white-space: nowrap;
}

.bp-hero--profile .bp-lead {
    max-width: 520px;
    margin: 0;
    font-size: var(--bp-text-md);
    line-height: 1.8;
}

.bp-hero--profile .bp-hero__actions {
    margin-top: 27px;
    align-items: center;
    gap: 12px 24px;
}

.bp-hero__art {
    min-width: 0;
    padding-block: 14px 24px;
}

/* One line under the window that says who did the work. Green because it is
   a statement of fact about the service, not a link. */
.bp-hero__tag {
    margin: 16px auto 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--bp-space-2xs);
    color: var(--bp-success);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
    text-align: center;
}

.bp-hero__tag svg {
    width: 17px;
    flex: 0 0 17px;
}

/* A text link with an icon after it. The plain .bp-text-link is inline, which
   is what a link inside a sentence wants; this one sits on a row of its own
   and needs the icon level with the words. */
.bp-text-link--icon {
    display: inline-flex;
    align-items: center;
    gap: var(--bp-space-2xs);
}

.bp-text-link--icon svg {
    width: 15px;
    flex: 0 0 15px;
}

/* --- Window --------------------------------------------------------------
   A page of ours, shown as a page: a title bar naming the business, the
   screenshot cropped to a fixed height with a fade at the foot, and a line
   under it with the way in to the real thing. The screenshot is a link to
   itself, and a script turns that into the image viewer at the foot of this
   section; without the script it opens the image.

   --bp-window-shot is the crop height and --bp-window-shift lifts the picture
   so the screenshot's own site header stays out of the crop. The lift is a
   percentage on purpose: a percentage margin is a share of the container's
   width, the same width the image is, so the header strip is cut off at the
   same proportion however wide the window is. */
.bp-window {
    --bp-window-shot: 350px;
    --bp-window-shift: -6%;
    margin: 0;
    overflow: hidden;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-lg);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-xl);
}

.bp-window__bar {
    padding: 14px 18px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 2px 12px;
    border-bottom: 1px solid var(--bp-line);
    color: var(--bp-navy);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-bold);
}

.bp-window__bar span {
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-regular);
}

.bp-window__shot {
    position: relative;
    display: block;
    height: var(--bp-window-shot);
    overflow: hidden;
}

.bp-window__shot img {
    display: block;
    width: 100%;
    height: auto;
    margin-top: var(--bp-window-shift);
}

.bp-window__shot::after {
    content: "";
    position: absolute;
    inset: auto 0 0;
    height: 45px;
    background: linear-gradient(transparent, var(--bp-white));
    pointer-events: none;
}

.bp-window__foot {
    padding: 12px 17px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 4px 15px;
    border-top: 1px solid var(--bp-line);
    background: var(--bp-white);
}

.bp-window__foot>span {
    color: var(--bp-muted);
    font-size: var(--bp-text-xs);
}

/* A line under a picture, not a call to action: the link is set at the size of
   the words beside it. */
.bp-window__foot .bp-text-link {
    font-size: var(--bp-text-xs);
}

/* The whole profile, not the top of it: the crop is tall, the fade is longer
   and lands on the grey the frame sits on, and the picture starts at the top
   because a full-page capture has no header worth hiding. */
.bp-window--tall {
    --bp-window-shot: 580px;
    --bp-window-shift: 0;
    border-radius: var(--bp-radius-xl);
    background: var(--bp-surface);
}

.bp-window--tall .bp-window__bar {
    padding: 16px 20px;
    background: var(--bp-white);
}

.bp-window--tall .bp-window__shot::after {
    height: 70px;
    background: linear-gradient(transparent, var(--bp-surface));
}

.bp-window--tall .bp-window__foot {
    padding: 17px 20px;
}

/* The mark that says a picture opens larger. Decoration over a link that is
   already named by its aria-label, so the icon is aria-hidden. */
.bp-zoom {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 2;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border: 1px solid var(--bp-line);
    border-radius: 8px;
    color: var(--bp-blue);
    background: var(--bp-white);
    box-shadow: 0 2px 12px rgba(18, 52, 99, .1);
}

.bp-zoom svg {
    width: 14px;
}

.bp-window--tall .bp-zoom {
    top: 17px;
    right: 17px;
    width: 35px;
    height: 35px;
}

.bp-window--tall .bp-zoom svg {
    width: 17px;
}

/* --- Evidence band -------------------------------------------------------
   Section 26's .bp-proof__grid with a row of three figures under the claim
   and three captured answers where the two quoted ones went. The shots are
   captures of the real thing, so each is a link to the capture at full size
   rather than a card set by this page. The middle one drops 30px: three
   phones in a line read as a catalogue, and offset they read as a stack that
   was dealt.

   The text colours are the tint section 26 settled on, not a translucent
   white: on the blue end of the gradient that is the only one that holds
   above 5:1. */
.bp-proof__metrics {
    margin-top: 32px;
    padding-top: 24px;
    display: flex;
    border-top: 1px solid rgba(255, 255, 255, .3);
}

.bp-proof__metric {
    min-width: 0;
    flex: 1 1 0;
    padding-inline: 18px;
    border-right: 1px solid rgba(255, 255, 255, .25);
}

.bp-proof__metric:first-child {
    padding-left: 0;
}

.bp-proof__metric:last-child {
    padding-right: 0;
    border-right: 0;
}

.bp-proof__metric strong {
    display: block;
    color: var(--bp-white);
    font-size: 32px;
    font-weight: var(--bp-weight-black);
    line-height: 1.15;
    letter-spacing: -.05em;
}

.bp-proof__metric span {
    margin-top: var(--bp-space-2xs);
    display: block;
    color: #d9e4fa;
    font-size: var(--bp-text-xs);
    line-height: 1.5;
}

.bp-proof__screens {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--bp-space-xs);
    align-items: start;
}

.bp-proof-card {
    min-width: 0;
    margin: 0;
    overflow: hidden;
    border-radius: var(--bp-radius-md);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-lg);
}

.bp-proof-card:nth-child(2) {
    margin-top: 30px;
}

.bp-proof-card__img {
    position: relative;
    display: block;
    padding: 6px;
}

/* Every capture is 472x1023, so the ratio is declared and the band keeps its
   height while they arrive. */
.bp-proof-card__img img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 472 / 1023;
    border-radius: var(--bp-radius-sm);
}

.bp-proof-card figcaption {
    padding: 10px 12px 13px;
    border-top: 1px solid var(--bp-line);
}

.bp-proof-card strong {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-xs);
    font-weight: var(--bp-weight-strong);
    line-height: 1.4;
}

.bp-proof-card figcaption span {
    margin-top: 3px;
    display: block;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
}

.bp-proof__note {
    max-width: 840px;
    margin: 30px 0 0;
    color: #d9e4fa;
    font-size: var(--bp-text-xs);
    line-height: 1.7;
}

/* --- Businesses -----------------------------------------------------------
   The marks of businesses already on the page, with no tile around them: the
   logos we hold carry their own backgrounds, and a border on top of that is
   two frames. Fixed height, fitted inside, so a wide wordmark and a square
   badge share a row without either setting its height. */
.bp-clients {
    padding-block: 34px 37px;
    border-bottom: 1px solid var(--bp-line);
}

/* The sentence over the marks is what says why they are there, so it is set
   as a line of text and not as a caption: 17px, the size of a card title,
   in the body colour rather than the muted one. */
.bp-clients__label {
    margin: 0 0 28px;
    color: var(--bp-body);
    font-size: var(--bp-text-lg);
    font-weight: var(--bp-weight-bold);
    line-height: var(--bp-leading-snug);
    text-align: center;
}

.bp-clients__logos {
    max-width: 1000px;
    margin-inline: auto;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 34px;
    align-items: center;
}

.bp-clients__logos img {
    display: block;
    width: 100%;
    height: 53px;
    padding-inline: 18px;
    object-fit: contain;
}

/* --- Tour ----------------------------------------------------------------
   A published profile on the left and, on the right, what is on it, in the
   order it is on it. The numbers are a list's own: the items are an ordered
   list, so a reader with a screen reader hears "list, six items" and not six
   unrelated headings. */
.bp-tour {
    display: grid;
    grid-template-columns: minmax(0, 1.06fr) minmax(0, 1fr);
    gap: 70px;
    align-items: center;
}

.bp-tour__list {
    margin: 0;
    padding: 0;
    display: grid;
    list-style: none;
}

.bp-tour__item {
    padding-block: 19px;
    display: flex;
    gap: 18px;
    border-bottom: 1px solid var(--bp-line);
}

.bp-tour__item:first-child {
    padding-top: 0;
}

.bp-tour__item:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}

.bp-tour__num {
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    display: grid;
    place-items: center;
    border: 1px solid #dce7fa;
    border-radius: var(--bp-radius-xs);
    color: var(--bp-blue);
    background: var(--bp-blue-50);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-strong);
}

.bp-tour__item h3 {
    margin: 0;
    font-size: var(--bp-text-lg);
    line-height: var(--bp-leading-snug);
    letter-spacing: -.025em;
}

.bp-tour__item p {
    max-width: 400px;
    margin: 7px 0 0;
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    line-height: 1.75;
}

/* --- Story ---------------------------------------------------------------
   One published article, as a card, beside a few lines on why it was worth
   reading. The band is the surface grey with an edge top and bottom, so it
   reads as a separate room rather than a change of background. */
.bp-story {
    border-block: 1px solid var(--bp-line);
}

.bp-story__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: 70px;
    align-items: center;
}

.bp-story__copy .bp-display-3 {
    margin-bottom: var(--bp-space-sm);
}

.bp-story__angle {
    margin-top: 30px;
    padding-top: 24px;
    border-top: 1px solid var(--bp-line-strong);
}

.bp-story__angle h3 {
    margin: 0 0 10px;
    font-size: var(--bp-text-md);
}

.bp-story__angle p {
    margin: 0;
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    line-height: 1.85;
}

.bp-story-card {
    overflow: hidden;
    border: 1px solid var(--bp-line);
    border-radius: var(--bp-radius-xl);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-lg);
}

.bp-story-card__img {
    display: block;
}

.bp-story-card__img img {
    display: block;
    width: 100%;
    height: 265px;
    object-fit: cover;
}

.bp-story-card__body {
    padding: 28px;
}

/* An eyebrow without the dot: the dot marks the top of a band, and this is a
   label on a card inside one. */
.bp-story-card__kicker {
    margin: 0 0 var(--bp-space-xs);
    color: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-strong);
    letter-spacing: .12em;
    text-transform: uppercase;
}

.bp-story-card h3 {
    margin: 0;
    font-size: var(--bp-text-3xl);
    line-height: 1.15;
}

.bp-story-card h3 a,
.bp-story-card h3 a:link,
.bp-story-card h3 a:visited {
    color: inherit;
    text-decoration: none;
}

.bp-story-card h3 a:hover,
.bp-story-card h3 a:focus {
    color: var(--bp-blue);
}

.bp-story-card blockquote {
    margin: 18px 0 20px;
    padding-left: 15px;
    border-left: 2px solid var(--bp-line-strong);
    color: var(--bp-body);
    font-size: var(--bp-text-sm);
    line-height: 1.8;
}

.bp-story-card .bp-meta {
    margin-top: 18px;
}

/* --- Beyond the profile -------------------------------------------------
   Two rows, each a few lines and a picture of the thing: the category as it
   is printed, and the way a press release travels. Both pictures are drawn,
   not captured: the first is a condensed copy of a published category and
   says so under it, the second is a diagram and says that too, because
   "individual placements vary" is the honest half of the claim. */
.bp-vis-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 74px;
    align-items: center;
}

.bp-vis .bp-divider {
    margin-block: 50px;
}

.bp-vis-copy .bp-heading {
    max-width: 450px;
    line-height: 1.18;
}

.bp-vis-copy .bp-text {
    margin-bottom: 0;
}

.bp-vis-copy .bp-text-link {
    margin-top: var(--bp-space-md);
}

.bp-vis-copy__note {
    margin-top: 22px;
    padding-top: 17px;
    border-top: 1px solid var(--bp-line);
}

/* The category as a reader meets it: the place, the heading, three rows. The
   first row is the business the page is about and is drawn as the one that
   matters; the other two are whoever else is listed there. */
.bp-rank-card {
    padding: 25px;
    border: 1px solid #d5e2f5;
    border-radius: var(--bp-radius-xl);
    background: var(--bp-gradient-soft);
}

.bp-rank-card__place {
    margin: 0 0 14px;
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-medium);
}

.bp-rank-card__place svg {
    width: 14px;
    flex: 0 0 14px;
}

.bp-rank-card h4 {
    margin: 0 0 19px;
    font-size: var(--bp-text-2xl);
    line-height: 1.2;
}

.bp-rank-card .bp-meta {
    margin-top: 15px;
}

/* A row is a box and not a link: this is a picture of a category, so a row
   that looked like a way in would promise a page the picture cannot take
   anyone to, and none of them has an arrow. The hover outline stays, because
   it shows which row the pointer is on without promising anything. */
.bp-rank-row {
    margin-top: 9px;
    padding: 14px;
    display: flex;
    align-items: center;
    gap: var(--bp-space-xs);
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-sm);
    background: var(--bp-white);
}

.bp-rank-row:hover {
    border-color: var(--bp-blue-soft);
}

.bp-rank-row--first {
    border-color: var(--bp-blue-soft);
    box-shadow: var(--bp-shadow-sm);
}

.bp-rank-row__pos {
    width: 27px;
    height: 31px;
    flex: 0 0 27px;
    display: grid;
    place-items: center;
    color: var(--bp-muted);
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-strong);
}

.bp-rank-row--first .bp-rank-row__pos {
    border-radius: 8px;
    color: var(--bp-white);
    background: var(--bp-blue);
}

.bp-rank-row>div {
    min-width: 0;
}

.bp-rank-row strong {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-sm);
    line-height: 1.4;
}

.bp-rank-row small {
    margin-top: var(--bp-space-3xs);
    display: block;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
}

/* The path a release takes: one source, a fork, three outlets. The lines are
   drawn with borders rather than an SVG so they stay one pixel wide at any
   width, and --bp-flow-line is local because it is the only place the system
   draws a connector. The fork is 72% wide so its two legs land on the centres
   of the outer outlets. */
.bp-flow {
    --bp-flow-line: #a8bfeb;
    min-height: 310px;
    padding: 29px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    border: 1px solid #d5e2f5;
    border-radius: var(--bp-radius-xl);
    background: var(--bp-blue-50);
}

.bp-flow__source {
    min-width: 235px;
    padding: 17px 21px;
    align-self: center;
    display: flex;
    align-items: center;
    gap: 13px;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-sm);
    background: var(--bp-white);
    box-shadow: var(--bp-shadow-sm);
}

.bp-flow__source strong {
    display: block;
    color: var(--bp-navy);
    font-size: var(--bp-text-base);
}

.bp-flow__source small {
    margin-top: 3px;
    display: block;
    color: var(--bp-muted);
    font-size: var(--bp-text-2xs);
}

.bp-flow__stem {
    width: 1px;
    height: 25px;
    margin: auto;
    background: var(--bp-flow-line);
}

.bp-flow__fork {
    position: relative;
    width: 72%;
    height: 25px;
    margin: auto;
    border: 1px solid var(--bp-flow-line);
    border-bottom: 0;
    border-radius: 8px 8px 0 0;
}

.bp-flow__fork::after {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 1px;
    height: 24px;
    background: var(--bp-flow-line);
}

.bp-flow__outlets {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
}

.bp-flow__outlet {
    padding: 17px 5px;
    border: 1px solid var(--bp-line-strong);
    border-radius: var(--bp-radius-xs);
    color: var(--bp-blue);
    background: var(--bp-white);
    text-align: center;
}

.bp-flow__outlet svg {
    width: 20px;
    margin-inline: auto;
}

.bp-flow__outlet span {
    margin-top: var(--bp-space-2xs);
    display: block;
    color: var(--bp-body);
    font-size: var(--bp-text-2xs);
    overflow-wrap: anywhere;
}

.bp-flow .bp-meta {
    margin-top: 18px;
    text-align: center;
}

/* --- Team ----------------------------------------------------------------
   A heading and a sentence side by side, and three pieces of finished work
   under them. The card is section 9's surface and photo box, as one link:
   the whole card is the way in, so there is no second link inside it to tab
   past. Section 9 keeps the body's last-but-one child at margin-bottom:auto,
   which is what holds the three "View" rows on one baseline. */
.bp-team-head {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 90px;
    align-items: center;
}

.bp-team-head .bp-display-3 {
    max-width: 540px;
}

.bp-team-head .bp-text {
    margin-bottom: 0;
}

.bp-work-grid {
    margin-top: 37px;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 22px;
}

.bp-work-card,
.bp-work-card:link,
.bp-work-card:visited,
.bp-work-card:hover,
.bp-work-card:focus {
    color: inherit;
    text-decoration: none;
}

.bp-work-card .bp-card__media {
    height: 180px;
    flex-basis: 180px;
}

.bp-work-card .bp-card__body {
    padding: 22px;
}

.bp-work-card__kind {
    margin-bottom: 9px;
    display: block;
    color: var(--bp-blue);
    font-size: var(--bp-text-2xs);
    font-weight: var(--bp-weight-strong);
    letter-spacing: .12em;
    text-transform: uppercase;
}

.bp-work-card h3 {
    margin: 0;
    font-size: 20px;
    line-height: 1.2;
}

.bp-work-card p {
    margin: 10px 0 0;
    color: var(--bp-muted);
    font-size: var(--bp-text-sm);
    line-height: 1.65;
}

.bp-work-card__cta {
    margin-top: 19px;
    padding-top: 15px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    border-top: 1px solid var(--bp-line);
    color: var(--bp-blue);
    font-size: var(--bp-text-sm);
    font-weight: var(--bp-weight-bold);
}

.bp-work-card__cta svg {
    width: 15px;
    flex: 0 0 15px;
}

/* --- Closing box ----------------------------------------------------------
   The last thing on the page before the footer: what the package is, and the
   one button. A box inside the container and not a band, for the reason in
   the note at the top of this section. */
.bp-closing {
    position: relative;
    padding: 60px 55px;
    overflow: hidden;
    border: 1px solid #d5e2f5;
    border-radius: var(--bp-radius-2xl);
    background:
        radial-gradient(circle at 6% 10%, rgba(207, 222, 252, .47), transparent 24rem),
        linear-gradient(135deg, #f7faff, #edf4ff);
    text-align: center;
}

.bp-closing .bp-display-3 {
    max-width: 780px;
    margin-inline: auto;
}

.bp-closing .bp-lead {
    max-width: 760px;
    margin: 23px auto 27px;
    font-size: var(--bp-text-md);
}

/* --- Image viewer -------------------------------------------------------
   Section 19's dialog, widened for a picture: no padding of its own, a head
   that stays put while a tall capture scrolls under it, and a body with
   nothing in it but the image. --proof is the one for the phone captures,
   which are 472px wide and are shown at that and no larger.

   It is a native <dialog>, so focus, Esc and the backdrop come from the
   browser; the script only fills it and hands focus back. */
.bp-dialog--image {
    width: min(calc(100% - 32px), 1000px);
    max-height: calc(100dvh - 32px);
    padding: 0;
    overflow: auto;
}

.bp-dialog--proof {
    width: min(calc(100% - 32px), 550px);
}

.bp-dialog--image .bp-dialog__head {
    position: sticky;
    top: 0;
    z-index: 1;
    margin: 0;
    padding: 12px 20px;
    align-items: center;
    border-bottom: 1px solid var(--bp-line);
    background: var(--bp-white);
}

.bp-dialog--image .bp-dialog__head h2 {
    font-size: var(--bp-text-md);
    font-weight: var(--bp-weight-bold);
    letter-spacing: -.02em;
    line-height: 1.4;
}

.bp-dialog__body {
    padding: var(--bp-space-sm);
    background: var(--bp-blue-50);
}

.bp-dialog__body img {
    display: block;
    width: 100%;
    height: auto;
    margin-inline: auto;
}

.bp-dialog--proof .bp-dialog__body img {
    max-width: 472px;
}

/* --- Responsive ----------------------------------------------------------
   The three breakpoints of section 21: 1060, 820, 580, and one more at 360
   for the hero's headline and the crops, which have to give up a size to fit
   a 320px phone. */
@media (max-width: 1060px) {
    .bp-hero--profile .bp-display-1 {
        max-width: 660px;
        font-size: 54px;
    }

    .bp-hero--profile .bp-lead {
        max-width: 620px;
    }

    /* The hero stacks here (section 26), and a window as wide as the container
       would be a screenshot of a desktop page at desktop size. */
    .bp-hero__art {
        width: 100%;
        max-width: 600px;
        margin-inline: auto;
    }

    .bp-window {
        --bp-window-shot: 360px;
    }

    .bp-window--tall {
        --bp-window-shot: 530px;
    }

    .bp-proof__screens {
        width: 100%;
        max-width: 610px;
        margin-inline: auto;
        gap: 20px;
    }

    .bp-proof-card:nth-child(2) {
        margin-top: 0;
    }

    .bp-tour,
    .bp-story__grid,
    .bp-vis-row {
        gap: 40px;
    }

    .bp-team-head {
        gap: 45px;
    }
}

@media (max-width: 820px) {
    .bp-tour,
    .bp-story__grid,
    .bp-vis-row {
        grid-template-columns: minmax(0, 1fr);
        gap: 33px;
    }

    .bp-window--tall {
        --bp-window-shot: 460px;
        width: 100%;
        max-width: 500px;
        margin-inline: auto;
    }

    /* The words first and the picture after, in both rows; the release row has
       its picture on the left on a wide screen. */
    .bp-vis-row--release .bp-flow {
        order: 2;
    }

    .bp-vis .bp-divider {
        margin-block: 37px;
    }

    .bp-team-head {
        grid-template-columns: minmax(0, 1fr);
        gap: 20px;
    }

    .bp-work-grid {
        gap: 15px;
    }

    .bp-work-card .bp-card__media {
        height: 150px;
        flex-basis: 150px;
    }

    .bp-work-card .bp-card__body {
        padding: 18px;
    }

    .bp-work-card h3 {
        font-size: var(--bp-text-xl);
    }

    .bp-clients__logos {
        gap: 20px;
    }

    .bp-clients__logos img {
        height: 47px;
        padding-inline: 10px;
    }

    .bp-closing {
        padding-inline: 36px;
    }
}

/* 680 and not 580 for the work cards: three across stop being cards well
   before a phone, and the horizontal form below has room to be one. */
@media (max-width: 680px) {
    .bp-work-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 19px;
    }

    .bp-work-card {
        display: grid;
        grid-template-columns: 115px minmax(0, 1fr);
    }

    .bp-work-card .bp-card__media {
        height: 100%;
        min-height: 180px;
    }

    .bp-work-card .bp-card__body {
        padding: 21px 18px;
    }

    .bp-work-card h3 {
        font-size: 19px;
    }

    .bp-work-card__cta {
        margin-top: 14px;
        padding-top: 12px;
    }
}

@media (max-width: 580px) {
    .bp-hero--profile .bp-display-1 {
        font-size: 42px;
        line-height: 1.08;
    }

    .bp-hero--profile .bp-lead {
        font-size: var(--bp-text-base);
    }

    .bp-hero--profile .bp-hero__actions .bp-text-link {
        align-self: center;
    }

    .bp-hero__art {
        padding-block: 7px 8px;
    }

    .bp-window {
        --bp-window-shot: 280px;
    }

    .bp-window--tall {
        --bp-window-shot: 460px;
    }

    .bp-window__bar,
    .bp-window--tall .bp-window__bar {
        padding: 12px;
    }

    .bp-window__foot,
    .bp-window--tall .bp-window__foot {
        padding: 12px;
    }

    .bp-zoom {
        top: 8px;
        right: 8px;
        width: 24px;
        height: 24px;
    }

    .bp-zoom svg {
        width: 12px;
    }

    .bp-hero__tag {
        margin-top: 13px;
    }

    .bp-proof__metrics {
        margin-top: 26px;
        padding-top: 22px;
    }

    .bp-proof__metric {
        padding-inline: 13px;
    }

    .bp-proof__metric strong {
        font-size: 27px;
    }

    .bp-proof__screens {
        gap: 10px;
    }

    .bp-proof-card {
        border-radius: var(--bp-radius-sm);
    }

    .bp-proof-card__img {
        padding: 4px;
    }

    .bp-proof-card figcaption {
        padding: 9px 8px 10px;
    }

    .bp-proof__note {
        margin-top: 22px;
    }

    .bp-clients {
        padding-block: 27px;
    }

    .bp-clients__label {
        max-width: 300px;
        margin: 0 auto 23px;
        font-size: var(--bp-text-md);
    }

    /* Three to a row with the last two centred under them: a grid cannot do
       that, a wrapping flex row with a centred last line can. */
    .bp-clients__logos {
        max-width: 420px;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 23px 10px;
    }

    .bp-clients__logos img {
        width: calc((100% - 20px) / 3);
        height: 43px;
        padding-inline: 11px;
    }

    .bp-story-card__img img {
        height: 230px;
    }

    .bp-story-card__body {
        padding: 25px;
    }

    .bp-story__angle {
        margin-top: 23px;
        padding-top: 20px;
    }

    .bp-rank-card {
        padding: 24px;
    }

    .bp-flow {
        min-height: 290px;
        padding: 25px;
    }

    .bp-flow__outlets {
        gap: 12px;
    }

    .bp-closing {
        padding: 35px 24px;
        border-radius: var(--bp-radius-xl);
        text-align: left;
    }

    .bp-closing .bp-display-3 {
        margin-inline: 0;
    }

    .bp-closing .bp-lead {
        margin: 21px 0 25px;
        font-size: var(--bp-text-base);
    }

    .bp-dialog--image {
        width: calc(100% - 20px);
        max-height: calc(100dvh - 20px);
    }

    .bp-dialog--image .bp-dialog__head {
        padding: 10px 15px;
    }

    .bp-dialog--image .bp-dialog__head h2 {
        font-size: var(--bp-text-sm);
    }

    .bp-dialog__body {
        padding: var(--bp-space-2xs);
    }
}

@media (max-width: 360px) {
    .bp-hero--profile .bp-display-1 {
        font-size: 37px;
    }

    .bp-window {
        --bp-window-shot: 250px;
    }

    .bp-window--tall {
        --bp-window-shot: 430px;
    }

    .bp-proof__screens {
        gap: 7px;
    }

    .bp-proof__metric {
        padding-inline: 10px;
    }

    .bp-flow {
        padding: 21px;
    }

    .bp-flow__outlets {
        gap: 7px;
    }
}
