/* 02 — Base
   Owns: reset deltas, typography, links, headings, buttons, form controls, .collapse primitives, page wrapper
   Depends on: 01-tokens */

/* ── Collapse primitives ─────────────────────────────────────
   base-4 ships .navbar-collapse but not the collapse primitives
   that hide it. Without these the desktop menu shows below 768px
   on top of ShiftNav's own toggle.

   IMPORTANT — this is not the Bootstrap 4 JS contract it looks like.
   bb-theme's own toggle handler (navbarToggleClick in
   ../bb-theme/js/theme.js) is written for Bootstrap 3: it does
   targetPanel.toggleClass('collapse') / .toggleClass('in') on
   .fl-page-nav-collapse. It never adds .show and never adds
   .collapsing. base-4 ships no JavaScript at all (no
   js/base-4.min.js), so no Bootstrap 4 collapse plugin runs to
   fill that gap either. The .collapse:not(.show) rule below hides
   the nav for the reason base-4 lacks any collapse primitive at
   all, not because anything ever adds .show — nothing does.
   .collapsing is consequently inert today: nothing in this theme's
   JS ever applies it. It is kept anyway (not deleted) because it's
   a standard, load-bearing-free primitive that the planned
   off-canvas menu work may want. Do not "fix" .collapse:not(.show)
   into plain .collapse under the assumption BS4 semantics apply —
   that would just be removing a no-op selector, but if a future
   markup/JS change ever does add .show, dropping :not(.show) here
   would break it.

   Separately: on this site, bb-theme's .navbar-toggle-driven
   dropdown path is not even reachable — there is no .navbar-toggle
   element in the rendered markup, so navbarToggleClick never binds
   to anything. The only functioning mobile toggle is ShiftNav's own
   .shiftnav-toggle-button, which drives a completely separate
   off-canvas panel (#shiftnav-main) via the shiftnav-pro plugin's
   own JS and never touches .fl-page-nav-collapse. Verified by
   dispatching click events on .shiftnav-toggle-button at a 500px
   viewport and confirming .fl-page-nav-collapse's classes and
   computed display were unchanged (collapse navbar-collapse /
   display: none) before opening, after opening, and after closing
   the ShiftNav panel. */
.collapse:not(.show) { display: none; }

.collapsing {
    position: relative;
    height: 0;
    overflow: hidden;
    transition: height 0.35s ease;
}

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

/* ── Visually-hidden text ────────────────────────────────────
   base-4 drops Bootstrap's .sr-only, but bb-theme's PHP still emits it for
   social icon labels. Kept under Bootstrap's name because we do not control
   that markup — same reasoning as .btn. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Skip link ───────────────────────────────────────────────
   .fl-screen-reader-text is emitted by the parent theme
   (bb-theme/classes/class-fl-theme.php:1935) and was styled by the generated
   skin we dequeue. Since the rebuild it has rendered as plain visible text at
   the top of every page — measured 112x21px at position:static before this
   rule landed.

   It must stay reachable, not be hidden: this is how a keyboard user skips
   past the nav, so it is moved off-screen rather than display:none'd, and has
   to become visible again on focus. Hiding it outright would remove a working
   accessibility feature rather than fix a styling bug.

   Ported from bb-theme/less/theme.less:126-160 with three deliberate changes:

   1. No `!important` on the background. The parent needed it to out-specify
      something else in the skin; nothing competes with this rule now, and the
      rebuild confines !important to 99-utilities.css.
   2. Logical properties in place of the parent's separate `body.rtl` block —
      inset-inline-start covers both directions in one rule.
   3. The admin-bar offset accounts for the bar being 46px tall below 783px,
      which the parent's flat 32px gets wrong on mobile. Unrelated to the two
      admin-bar bugs in KNOWN-ISSUES: those are theme.js writing inline styles,
      whereas nothing contends with this. */
.fl-screen-reader-text {
    position: absolute;
    inset-inline-start: -10000px;
    top: auto;
    width: 1px;
    height: 1px;
    padding: 10px 15px;
    overflow: hidden;
    background-color: var(--surface);
    color: var(--text);
}

.fl-screen-reader-text:focus {
    position: fixed;
    inset-inline-start: 0;
    top: 0;
    z-index: 99999;
    width: auto;
    height: auto;
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

body.admin-bar .fl-screen-reader-text:focus { top: 46px; }

@media screen and (min-width: 783px) {
    body.admin-bar .fl-screen-reader-text:focus { top: 32px; }
}

/* ── Page wrapper and typography ─────────────────────────────
   Previously supplied by the skin. Intent ported from
   _archive/_base.css, but that file was authored against a 10px
   root — its numeric values are not directly reusable here, where
   the root is 16px. */
html { font-size: 100%; }

body {
    font-family: var(--font-body);
    font-size: var(--p-size);
    line-height: 1.6;
    color: var(--text);
    background-color: var(--surface);
}

/* TRAP (final review, Finding 3): overflow-x: hidden here silently disables
   `position: sticky` for every descendant of .fl-page — any ancestor with a
   non-visible overflow clips the scrolling box that sticky needs to stick
   within. Harmless today (nothing in css/ uses position:sticky yet), but the
   next work on this base is the deferred content modules, and a sticky
   sidebar/CTA placed anywhere inside .fl-page will silently fail to stick.
   If that's needed later, this rule will have to move to a narrower wrapper
   rather than just adding position:sticky and wondering why it's inert. */
.fl-page { overflow-x: hidden; }
.fl-page-content { background-color: var(--surface); }

/* Full-width content wrapper — restores the parent's own reset.
   page.php and tpl-full-width.php both emit
   `<div class="fl-content-full container">` — one element carrying both
   classes — so base-4 caps it at 540/720/960/1140px per breakpoint. The
   parent's reset for that combination (bb-theme/less/theme.less:1311-1332)
   shipped inside the generated skin, which we dequeue, and nothing else
   puts it back.

   Ported verbatim in intent, including the `.fl-builder` scope: page.php
   wraps EVERY page in `.fl-content-full`, Beaver-built or not. Unscoped,
   this would strip the container cap and gutters from plain WordPress
   pages too and run their body text edge-to-edge. `.fl-builder` is on the
   body only when Beaver Builder rendered the layout, which is the only
   case where the rows supply their own width and padding.

   The three inner resets are all load-bearing. `.container` gutters and
   `.row` negative margins already cancel out, but `.fl-content`'s own
   15px `.col-md-12` padding does not — without zeroing it, a
   `.fl-row-full-width` stops 15px short of each edge and never actually
   goes full-bleed. Measured live at 1504px before this rule: rows sat at
   left:15, width:1474.

   Omitted from the parent's block: its `.fl-post-header` / `.fl-comments`
   margins. Those are single-post furniture and belong with the blog work,
   not here. */
.fl-builder .container.fl-content-full {
    width: auto;
    max-width: 100%;
    margin: 0;
    padding: 0;
}

.fl-builder .container.fl-content-full > .row { margin: 0; }
.fl-builder .container.fl-content-full .fl-content { margin: 0; padding: 0; }

/* margin-bottom is 1rem, NOT a --spacing-* token, and that is deliberate: the
   spacing scale is px-based (10/20/30/50) and 16px sits between two of its
   steps. Introducing a token for one value used in one place would be the
   heavier of the two mistakes.

   It was --spacing-lg (30px) until 2026-09-14. That is now the opt-in case
   rather than the default — put .rw-u-mb-lg on a heading that wants the old,
   looser gap, which resolves to exactly the same 30px. Changing the default
   this way round means a heading sitting directly on its paragraph is what you
   get without thinking about it, and the wide gap is a choice someone made. */
h1, h2, h3, h4, h5, h6 {
    margin-top: 0;
    padding-top: 0.9rem;
    margin-bottom: 0.6rem;
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.2;
    text-transform: uppercase;
}

h1 { font-size: clamp(2rem, 1.2rem + 2vw, 3.125rem); color: var(--text); }
h2 { font-size: clamp(1.75rem, 1rem + 1.6vw, 2.75rem); color: var(--text); }
h3 { font-size: clamp(1.375rem, 0.9rem + 1.2vw, 1.875rem); color: var(--accent); }
h4 { font-size: clamp(1.125rem, 0.9rem + 0.6vw, 1.5rem); }
h5 { font-size: clamp(1rem, 0.9rem + 0.4vw, 1.25rem); }
h6 { font-size: clamp(0.875rem, 0.8rem + 0.3vw, 1.125rem); }

a { color: var(--brand); text-decoration: none; transition: var(--transition-base); }
a:hover, a:focus { color: var(--brand-dark); text-decoration: underline; }

/* ── Images ──────────────────────────────────────────────────
   ADDED 2026-09-15. There was no `img` rule in this file at all, and the
   dequeued parent skin is where the usual max-width reset used to come from.
   Every image on the site was therefore unconstrained unless some component
   happened to constrain it — .rw-image img, the two logos, and the marquee do,
   which is why nothing looked broken.

   The first content image to appear proved it: a 1100px featured image on a
   single post rendered at its full natural width inside a 643px column and
   ran straight across the sidebar, covering the widget links.

   `height: auto` is not optional alongside it — WordPress writes width and
   height ATTRIBUTES on the img, and without it a constrained width fights the
   intrinsic height and squashes the artwork.

   Safe against the three component rules that size images deliberately: each
   sits at 0-2-0 or higher against this rule's 0-0-1, so the header logo's
   max-height/object-fit arrangement (04-header.css:221) is untouched. */
img {
    max-width: 100%;
    height: auto;
}

/* ── Focus rings ─────────────────────────────────────────────
   BEAVER BUILDER ALREADY DRAWS ONE, ON :focus, AND THAT IS THE BUG. Global
   Styles emits this into every page's layout cache
   (fl-builder-global-styles/classes/class-fl-builder-global-styles.php:874):

     :where(.fl-builder-content, .fl-page)
       :is(a[href], button, input, select, textarea,
           [tabindex]:not([tabindex="-1"])):focus {
       outline-style: solid; outline-width: 2px; outline-offset: 2px;
     }

   `:focus`, not `:focus-visible`, so it fires on a MOUSE CLICK — which is
   exactly the "box appears when I click a link" symptom. No outline-color is
   emitted (the Global Styles colour field is empty), so it falls back to
   currentColor: near-black on a nav link, brand blue on a body link, and never
   a deliberate choice.

   Two rules deal with it, and both have to out-specify that selector. It
   scores 0-3-0 — :where() contributes nothing, but :is() takes its most
   specific argument, which is `[tabindex]:not([tabindex="-1"])` at 0-2-0, plus
   :focus. Adding one more pseudo-class to the same shape makes these 0-4-0.
   An ordinary `a:focus-visible` CANNOT reach it: at 0-1-1 it loses, which is
   why a rule of that shape that used to sit here was dead on every link inside
   .fl-page — which is all of them.

   1. Mouse and touch get nothing. :focus:not(:focus-visible) is the standard
      way to say "focused, but not by the keyboard". The ring is not removed,
      it is narrowed to the input that needs it. */
:where(.fl-builder-content, .fl-page)
  :is(a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus:not(:focus-visible) {
    outline: none;
}

/* 2. The keyboard ring becomes ours. Only the COLOUR is set: Beaver Builder's
      2px solid at 2px offset is a perfectly good ring, and restating the whole
      shorthand would mean this file silently ignoring the width and offset
      fields in Global Styles if anyone ever sets them. --brand rather than
      currentColor so the ring is one colour everywhere instead of taking the
      colour of whatever it happens to be sitting on.

      .btn and the form controls below set their own --brand ring at 0-2-0.
      That now loses to this 0-4-0 rule, which is harmless — the colour is the
      same, and the offset they wanted differs by a pixel. */
:where(.fl-builder-content, .fl-page)
  :is(a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus-visible {
    outline-color: var(--brand);
}

/* ── Buttons ──────────────────────────────────────────────────
   base-4 has no .btn at all, so this family is ours entirely.
   Buttons are light-on-dark: white text on the accent and brand fills.
   Do NOT add the Bootstrap :not(:disabled):not(.disabled):active
   overrides from the old _buttons.css — base-4 has no .btn rules
   to out-specify, so they would be dead weight now. */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 0.9rem 1.8rem;
    border: 2px solid transparent;
    border-radius: var(--card-border-radius, 4px);
    font-weight: 600;
    font-size: 0.9375rem;
    line-height: 1.4;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: var(--transition-base);
}

.btn:hover, .btn:focus { text-decoration: none; }

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

.btn-primary { background-color: var(--accent); border-color: var(--accent); color: var(--white); }
.btn-primary:hover, .btn-primary:focus { background-color: var(--accent-dark); border-color: var(--accent-dark); color: var(--white); }

.btn-navy { background-color: var(--brand-dark); border-color: var(--brand-dark); color: var(--white); }
.btn-navy:hover, .btn-navy:focus { background-color: var(--brand); border-color: var(--brand); color: var(--white); }

.btn-outline-primary { background-color: transparent; border-color: var(--brand); color: var(--brand); }
.btn-outline-primary:hover, .btn-outline-primary:focus { background-color: var(--brand); color: var(--white); }

/* Neutral outline. Second outline variant rather than a redefinition of
   --primary above: the hero pairs it with the accent fill, where a blue border
   would introduce a third colour into a two-colour block. Border is the light
   token, not --black, so it reads as a quiet secondary next to .btn-primary. */
.btn-outline-neutral { background-color: transparent; border-color: var(--border-strong); color: var(--black); }
.btn-outline-neutral:hover, .btn-outline-neutral:focus { background-color: var(--black); border-color: var(--black); color: var(--white); }

.btn:disabled, .btn.disabled { opacity: 0.6; cursor: not-allowed; }

/* ── Form controls ───────────────────────────────────────────
   base-4 does ship .form-control (30 rules), so this is light —
   only what the theme changes. */
input[type="text"], input[type="email"], input[type="tel"],
input[type="url"], input[type="password"], input[type="search"],
select, textarea {
    border: 1px solid var(--border-strong);
    border-radius: var(--card-border-radius, 4px);
    background-color: var(--surface);
    color: var(--text);
}

input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 1px;
    border-color: var(--brand);
}
