/* 02-layout.css - page frame, grid, header, footer, spacing rhythm

   NUMBERED CASCADE. File order IS cascade order. These files are linked from
   App.razor in numeric order and nothing else, so a rule in a later file wins
   over the same-specificity rule in an earlier one.

   APPEND a new file in sequence. Never insert one alphabetically or renumber
   an existing one: both silently change which rule wins on every page.

   07-admin.css is the exception and is linked from AdminLayout only, so a
   member of the public downloads zero bytes of back-office CSS. That is why
   this sequence stops at 04.

   Breakpoints, in one place so they can be read at a glance:
     560px   phone to large phone
     860px   the call-bar boundary and the nav boundary
     1120px  the point the 12 column grid is worth having
   Design at 390px and 1440px (DESIGN.md section 4); everything else is
   interpolation.
*/

/* ===========================================================================
   SKIP LINK. The first tab stop on every page, and visible when focused.
   DESIGN.md section 7.
   =========================================================================== */

.skip-link {
    position: absolute;
    left: var(--space-4);
    top: var(--space-4);
    z-index: 100;
    transform: translateY(-200%);
    background-color: var(--bg-raised);
    color: var(--ink-strong);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    font-weight: 600;
    text-decoration: none;
}

.skip-link:focus {
    transform: translateY(0);
}

/* ===========================================================================
   CONTAINERS AND SECTION RHYTHM

   Sections breathe. DESIGN.md section 4: --space-9 mobile, --space-10 desktop,
   and when in doubt add more. This is where calm editorial is won or lost, so
   a page that wants less padding overrides it deliberately rather than by
   forgetting to apply the class.
   =========================================================================== */

.container {
    width: var(--container);
    margin-inline: auto;
}

.container-narrow {
    width: var(--container-narrow);
    margin-inline: auto;
}

.section {
    padding-block: var(--space-9);
}

.section--tight {
    padding-block: var(--space-7);
}

.section--sage {
    background-color: var(--bg-sage);
}

.section--raised {
    background-color: var(--bg-raised);
}

.section--deep {
    background-color: var(--bg-deep);
    color: var(--ink-on-deep);
}

.section--deep h1,
.section--deep h2,
.section--deep h3,
.section--deep h4 {
    color: var(--ink-on-deep);
}

/* The eyebrow's --ink-muted is a mid slate chosen against the three LIGHT
   grounds. On --bg-deep it measures under 1.5:1 and is effectively invisible,
   which is how a label above a heading turns into a smudge. --ink-on-deep is
   11.33:1 there and is the only ink this ground carries. */
.section--deep .eyebrow {
    color: var(--ink-on-deep);
}

@media (min-width: 860px) {
    .section {
        padding-block: var(--space-10);
    }

    .section--tight {
        padding-block: var(--space-8);
    }
}

/* Vertical rhythm inside a section, so a page does not have to invent a
   margin for every block it stacks. */
.stack > * + * {
    margin-top: var(--space-4);
}

.stack-lg > * + * {
    margin-top: var(--space-6);
}

/* ===========================================================================
   GRID. 12 columns desktop, 4 mobile, --space-5 gutter (DESIGN.md section 4).
   Spans are declared as explicit classes rather than as an inline style so a
   page cannot quietly invent a thirteenth column.
   =========================================================================== */

.grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--gutter);
}

.col-2,
.col-3,
.col-4,
.col-5,
.col-6,
.col-7,
.col-8,
.col-12 {
    grid-column: span 4;
}

@media (min-width: 860px) {
    .grid {
        grid-template-columns: repeat(12, minmax(0, 1fr));
    }

    .col-2 {
        grid-column: span 2;
    }

    .col-3 {
        grid-column: span 3;
    }

    .col-4 {
        grid-column: span 4;
    }

    .col-5 {
        grid-column: span 5;
    }

    .col-6 {
        grid-column: span 6;
    }

    .col-7 {
        grid-column: span 7;
    }

    .col-8 {
        grid-column: span 8;
    }

    .col-12 {
        grid-column: span 12;
    }
}

/* ===========================================================================
   HEADER

   The phone number is a first-class element, not footer trivia (DESIGN.md law
   3). It sits in the header on every viewport: on a phone as a tap target
   beside the logo, on desktop as a full labelled number beside the one
   primary action.
   =========================================================================== */

.site-header {
    background-color: var(--bg);
    border-bottom: 1px solid var(--rule);
}

.site-header__inner {
    width: var(--container);
    margin-inline: auto;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding-block: var(--space-2);
    flex-wrap: wrap;
}

@media (min-width: 560px) {
    .site-header__inner {
        padding-block: var(--space-4);
    }
}

.site-header__brand {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    text-decoration: none;
    margin-right: auto;
    color: var(--ink-strong);
}

/* THE MOBILE BRAND IS THE MARK PLUS REAL TEXT.

   Below 560px the header shows logo-mark at 48px beside the wordmark set in
   Fraunces, and the full-colour lockup does not ship at all. Three reasons,
   and all three were measured on a real 390px viewport:

   1. The lockup rendered 168 CSS px tall at an aspect of 320x278, which is
      146px of a 844px screen spent on a logo, and it was the largest single
      contentful paint on 32 of the 33 pages.
   2. At 168px the lockup's baked-in strapline sets at about 5px. It is not
      legible, it was never in the alt text, and shipping type nobody can read
      is worse than not shipping it.
   3. The 320px derivative was served to a DPR-3 phone that needed 504 device
      pixels, so it was upscaled 1.57x. DESIGN.md 2b says never re-stretch a
      derivative, and there was no srcset to pick a wider one.

   The <picture> in PublicLayout switches the SOURCE on the same media query,
   so exactly one image is ever requested. The wordmark below is real text, so
   it is sharp at every density and costs no bytes.

   Above 560px the lockup returns, with a srcset so a dense screen gets the
   480 or 640 derivative rather than a stretched 320. */
.site-header__logo {
    width: 48px;
    /* The mark is 192x132, so height follows the aspect ratio rather than
       being forced square. Forcing it square is what squashed it before. */
    height: auto;
}

.site-header__wordmark {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.15;
    color: var(--ink-strong);
    /* Two words, two lines, beside a 48px mark.

       width: min-content, not a ch cap. A ch cap is the width of the digit
       zero times seven, which is narrower than the word "Consultants", and
       with the body's overflow-wrap that produced a three-line wordmark
       reading "Blooming / Consultan / ts". min-content is exactly the longest
       word, so the two words stack and neither one breaks. max-width keeps it
       inside the container at 200% text, where breaking is better than
       overflowing. */
    width: min-content;
    max-width: 100%;
}

@media (min-width: 560px) {
    .site-header__logo {
        /* 56px, not the 208px lockup. See the comment in PublicLayout.razor:
           the lockup is a VERTICAL arrangement and at its minimum width it
           stood 181px tall, which made this header 267px and pushed the page
           title to 45 percent of the way down a 900px screen. */
        width: 56px;
        height: auto;
    }

    .site-header__wordmark {
        /* One line on a wide screen. min-content stacks it into two, which is
           right beside a 48px mark on a phone and wrong beside a 56px mark
           with a whole header row to spare. */
        width: auto;
        max-width: none;
        white-space: nowrap;
        font-size: 1.5rem;
    }
}

.site-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.site-header__phone {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
    font-weight: 600;
    color: var(--ink-strong);
    text-decoration: none;
    white-space: nowrap;
    /* 44px minimum touch target, met by padding rather than by a height that
       would fight the flex baseline. */
    padding-block: var(--space-3);
}

.site-header__phone:hover {
    color: var(--teal-text);
    text-decoration: underline;
}

.site-header__phone-label {
    font-size: var(--fs-small);
    font-weight: 500;
    color: var(--ink-muted);
}

/* THE CRISIS NUMBER, ONE TAP FROM ANYWHERE.

   Above 860px there is no fixed call bar, so the header carries 988. Below
   860px the call bar carries it instead, pinned to the bottom of the screen,
   which is the only place that is still one tap away twelve screens down a
   10,000px page. Exactly one of the two is visible at any width.

   It is not styled as an alarm. Quiet, small, the same weight as the phone
   number beside it: it is information, not a klaxon (DESIGN.md section 5). */
.site-header__crisis {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-strong);
    text-decoration: none;
    white-space: nowrap;
    min-height: 44px;
    padding-block: var(--space-3);
}

.site-header__crisis:hover {
    color: var(--teal-text);
    text-decoration: underline;
}

.site-header__crisis-label {
    font-size: var(--fs-small);
    font-weight: 500;
    color: var(--ink-muted);
}

/* Under 860px the call bar carries the phone number, the request action AND
   the crisis number, all three fixed to the bottom of the screen. Repeating
   any of them in the header is 52px of the fold spent on a duplicate.

   This is the ruling on BC-7, taken at 390px with the fold in view: the
   header was 368px, 43.6% of the screen, and nothing but the page title and
   three lines of prose was above the fold. All seven destinations stay, there
   is still no hamburger, and the space comes out of the chrome instead. */
@media (max-width: 859.98px) {
    .site-header__phone,
    .site-header__crisis {
        display: none;
    }

    .site-header__actions .btn {
        display: none;
    }
}

/* ===========================================================================
   PRIMARY NAVIGATION

   No JavaScript menu. Under 860px the nav is a wrapping row of links below
   the brand, which for eight items is shorter to reach than a hamburger and
   has no state to get stuck open. DESIGN.md law 6: mobile is the design.
   =========================================================================== */

.site-nav {
    border-top: 1px solid var(--rule);
    background-color: var(--bg);
}

.site-nav__list {
    width: var(--container);
    margin-inline: auto;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    /* 16px between columns, not 32. Seven links at a 32px gutter wrapped to
       three rows on a 390px phone; at 16px, with "Insurance" in place of
       "Insurance and fees", they wrap to two. No destination was cut: the
       insurance page is the most-asked question on the site. */
    gap: var(--space-2) var(--space-4);
    padding-block: var(--space-1);
}

.site-nav__list li {
    max-width: none;
}

/* 1.2 leading, not the 1.65 the body inherits. A nav link is one line of
   two words, so body leading was buying nothing and costing 8px a row on a
   two-row nav. The 12px of padding above and below stays, which is what
   holds each link at the 44px touch target. */
.site-nav__link {
    display: inline-block;
    padding-block: var(--space-3);
    line-height: 1.2;
    color: var(--ink);
    text-decoration: none;
    font-weight: 500;
}

/* HOVER AND ACTIVE USE THE SAME MECHANISM, and that is the whole point.

   Hover used to be text-decoration: underline while active is an inset rule.
   Those are two different lines at two different heights, so the moment you
   clicked a nav item you arrived on the new page with the pointer still
   resting on it and saw BOTH: a thin underline hugging the text and a thick
   teal bar below it. Not a hover artefact you had to go looking for, the
   default state of the header on every single navigation.

   Both states are now a rule under the link. They occupy the same space, so
   they replace each other instead of stacking, and active simply wins because
   it is declared second. */
.site-nav__link:hover {
    color: var(--teal-text);
    box-shadow: inset 0 -2px 0 0 var(--rule-strong);
}

/* Blazor's NavLink adds this class on the active route. The current page is
   marked by weight AND by a rule, never by colour alone (DESIGN.md section 7:
   colour is never the sole carrier of meaning), and aria-current on the
   element carries it to a screen reader. */
.site-nav__link.active,
.site-nav__link.active:hover {
    color: var(--ink-strong);
    font-weight: 600;
    box-shadow: inset 0 -2px 0 0 var(--teal);
    text-decoration: none;
}

@media (min-width: 860px) {
    .site-nav__list {
        gap: var(--space-6);
    }
}

/* ===========================================================================
   MAIN
   =========================================================================== */

.site-main {
    display: block;
}

/* ===========================================================================
   FOOTER

   --bg-deep, so the REVERSED lockup ships here. The full-colour lockup on a
   dark ground makes the wordmark almost vanish: the pale sage and lavender
   petals are genuinely low alpha in the artwork (DESIGN.md section 2b). That
   is a property of the art, not a bug in the key, and the reversed asset is
   the fix.
   =========================================================================== */

.site-footer {
    background-color: var(--bg-deep);
    color: var(--ink-on-deep);
    padding-block: var(--space-8) var(--space-6);
}

.site-footer a {
    color: var(--ink-on-deep);
}

.site-footer a:hover {
    color: var(--ink-on-deep);
    text-decoration: underline;
}

.site-footer__inner {
    width: var(--container);
    margin-inline: auto;
    display: grid;
    gap: var(--space-7);
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 860px) {
    .site-footer__inner {
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.1fr);
        gap: var(--space-6);
    }
}

/* THE FOOTER BRAND IS THE REVERSED MARK PLUS REAL TEXT, for the same reason
   the header's is: the reversed LOCKUP was a 25KB PNG whose baked-in strapline
   set at about 5px and was in no alt text, and there is no reversed .webp
   beside it. The reversed MARK at 96px is 5.5KB, and the wordmark set in
   Fraunces on --ink-on-deep is sharp at every density.

   Still the reversed art, never the full-colour lockup: on --bg-deep the
   full-colour mark's pale sage and lavender petals are genuinely low alpha in
   the artwork and all but vanish (DESIGN.md section 2b). */
/* Wraps at 200% text, where the wordmark alone is 214px and will not sit
   beside a 64px mark in a 280px column. */
.site-footer__brand {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.site-footer__logo {
    width: 64px;
    height: 64px;
    flex: none;
}

.site-footer__wordmark {
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    font-weight: 600;
    line-height: 1.15;
    color: var(--ink-on-deep);
    /* min-content, for the same reason as the header wordmark: a ch cap is
       narrower than the word "Consultants" and breaks it mid-word. */
    width: min-content;
    max-width: 100%;
}

.site-footer h2 {
    font-family: var(--font-body);
    font-size: var(--fs-small);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--ink-on-deep);
    margin-bottom: var(--space-4);
}

.site-footer__nap {
    font-style: normal;
}

.site-footer__nap p {
    max-width: 34ch;
}

.site-footer__phone {
    display: inline-block;
    font-size: var(--fs-lead);
    font-weight: 600;
    padding-block: var(--space-2);
}

.site-footer__nav {
    list-style: none;
    display: grid;
    gap: var(--space-1);
}

.site-footer__nav li {
    max-width: none;
}

.site-footer__nav a {
    display: inline-block;
    padding-block: var(--space-2);
}

/* Hours are a real table, not a stack of divs: it is tabular data, screen
   readers announce the day beside the time, and Google reads it (DESIGN.md
   section 6, ARCHITECTURE section 6). */
/* The scroll box around the table, not the table itself. At 200% text the
   two columns need 420px in a 350px column, and without this the whole
   DOCUMENT scrolled sideways on all 33 pages, which is a WCAG 1.4.4 and
   1.4.10 failure on every page of the site. The wrapper is capped to the
   container rather than to a rem width, because a rem cap doubles with the
   text and stops capping anything.

   The wrapper is a labelled, focusable region: a scroll container that a
   keyboard cannot reach is a different accessibility bug in place of this
   one. See .table-wrap in 04-public.css, which does the same job for the
   in-page data tables. */
.site-footer__hours-wrap {
    overflow-x: auto;
    max-width: min(24rem, 100%);
}

.site-footer__hours {
    width: 100%;
    font-size: var(--fs-small);
    text-align: left;
}

.site-footer__hours caption {
    text-align: left;
    font-family: var(--font-body);
    font-size: var(--fs-small);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--ink-on-deep);
    padding-bottom: var(--space-4);
}

.site-footer__hours th,
.site-footer__hours td {
    padding: var(--space-1) 0;
    vertical-align: baseline;
}

/* The day column wraps rather than forcing the table wider than the column.
   "Monday to Thursday" set at 30px is 270px on its own; allowed to wrap it is
   150px over two lines, which is what keeps the scroll box above from ever
   actually needing to scroll at 200% on a 390px phone. */
.site-footer__hours th {
    font-weight: 500;
    text-align: left;
    padding-right: var(--space-4);
}

.site-footer__hours td {
    white-space: nowrap;
}

.site-footer__bottom {
    width: var(--container);
    margin-inline: auto;
    margin-top: var(--space-7);
    padding-top: var(--space-5);
    border-top: 1px solid rgba(234, 242, 244, 0.25);
    font-size: var(--fs-small);
}

.site-footer__legal {
    max-width: 78ch;
    color: var(--ink-on-deep);
}

/* ===========================================================================
   THE MOBILE CALL BAR

   The ONLY fixed element on the site (DESIGN.md section 5). Under 860px it
   pins to the bottom; --call-bar-height is set to its real height on :root so
   body padding reserves the space and it never covers the last paragraph.

   Above 860px it is display:none AND the reserved height goes back to zero,
   so a desktop page does not carry 72px of dead space at the foot.
   =========================================================================== */

.call-bar {
    display: none;
}

@media (max-width: 859.98px) {
    :root {
        /* THE RESERVE IS IN REM, NOT PIXELS.

           It used to be a hard-coded 68px. At 200% text the bar measured
           195px, so the last line of the footer legal text sat permanently
           behind it on every page: the one element on the site that is
           allowed to float was covering the copy it is required not to cover.
           A pixel constant cannot track a bar whose height is set by wrapped
           text, and this bar's height is nothing but wrapped text.

           In rem it tracks, because the thing that makes the bar grow is the
           same thing rem is measured in. Both values below were measured, on
           ten combinations of viewport and text size, not estimated:

               390px  100%  bar 140px  8.75rem     320px  100%  bar 140px
               390px  125%  bar 155px  7.75rem     320px  125%  bar 155px
               390px  150%  bar 171px  7.13rem     320px  150%  bar 216px
               390px  175%  bar 186px  6.64rem     320px  175%  bar 300px 10.71rem
               390px  200%  bar 258px  8.06rem     320px  200%  bar 330px 10.31rem

           The worst case is 10.71rem, and it is not the widest screen or the
           largest text: it is a narrow screen at 175%, where both labels wrap
           to three lines at once. 9.75rem covers everything a 390px phone
           does at its default text size; the narrow tier below covers the
           rest with room to spare.

           check-horizontal-overflow.mjs asserts the RELATIONSHIP rather than
           either number: it loads every page at 100% and at 200% and fails if
           the reserved padding is smaller than the bar it reserves for, so a
           future change to the bar's contents fails the build instead of
           quietly sitting on the last line of the page again. */
        --call-bar-height: calc(9.75rem + env(safe-area-inset-bottom, 0px));
    }

    .call-bar {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--space-3);
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 50;
        background-color: var(--bg-raised);
        border-top: 1px solid var(--rule-strong);
        /* The one shadow on the site. Shadows are reserved for genuinely
           floating things so they keep their meaning (DESIGN.md section 5). */
        box-shadow: 0 -2px 12px rgba(22, 48, 60, 0.12);
        padding: var(--space-3) var(--space-4);
        /* Home-indicator inset on an iPhone, on top of the padding. */
        padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    }
}

/* LARGE TEXT, OR A GENUINELY TINY SCREEN.

   The width here is in rem on purpose, so it is a query about how much TEXT
   fits rather than about how many device pixels there are. A 390px phone is
   24.4rem at 100% text and 12.2rem at 200%, so this block is how the
   stylesheet finds out that somebody has turned their text up, without
   guessing at it from a device width.

   It only changes the RESERVE. Stacking the two targets into one column was
   tried here and measured worse at every size that matters: a full-width
   button wraps its label just as readily as a half-width one at 200% and the
   bar came out taller, not shorter. Two columns, and enough room reserved for
   the worst measured case, is the honest answer. */
@media (max-width: 859.98px) and (max-width: 20rem) {
    :root {
        --call-bar-height: calc(11.5rem + env(safe-area-inset-bottom, 0px));
    }
}
