/* 03-components.css - reusable pieces: buttons, cards, forms, notices

   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.

   ---------------------------------------------------------------------------
   THIS IS THE WHOLE COMPONENT VOCABULARY (DESIGN.md section 5). It is a small,
   fixed list on purpose. A page that needs a component which is not here adds
   it to DESIGN.md first, then to this file, and never as a one-off rule in a
   per-page stylesheet.

     buttons (three, and no more)   cards            section header
     petal divider                  forms            call bar
     crisis banner                  faq              cta band
     insurer strip
   --------------------------------------------------------------------------- */

/* ===========================================================================
   BUTTONS. Exactly three. No third colour, no gradient fill, no shadow.
   DESIGN.md law 2: one primary action per screen, never two competing buttons
   of equal weight.
   =========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px; /* the touch target floor, on every button on the site */
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-size: var(--fs-body);
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    border: 1.5px solid transparent;
    transition: background-color var(--motion) ease, color var(--motion) ease, border-color var(--motion) ease;
}

.btn-primary {
    background-color: var(--teal-deep);
    color: var(--bg-raised);
    border-color: var(--teal-deep);
}

.btn-primary:hover {
    background-color: var(--ink);
    border-color: var(--ink);
    color: var(--bg-raised);
}

.btn-secondary {
    background-color: transparent;
    color: var(--teal-deep);
    border-color: var(--teal-deep);
}

.btn-secondary:hover {
    background-color: var(--teal-deep);
    color: var(--bg-raised);
}

/* A DISABLED BUTTON MUST LOOK DISABLED.

   It did not. The intake submit ships with the disabled attribute set until
   the Blazor circuit is up, and its computed style was byte for byte the live
   button: full teal fill, white text, cursor: pointer. On a rural connection
   where the 63KB of blazor.web.js times out, that is a perfectly normal
   looking button that a frightened person taps over and over while nothing
   happens.

   So: no fill, muted ink, a dashed edge so the state is not carried by colour
   alone (DESIGN.md section 7), and the not-allowed cursor. --ink-muted on
   --bg-sage is 5.72:1, which clears AA, because a disabled control on this
   site still has to be readable: it is the control that is telling somebody
   the page is not ready.

   The selector covers the attribute, the pseudo-class and aria-disabled, so
   an anchor that cannot be disabled by attribute is covered too. */
.btn[disabled],
.btn:disabled,
.btn[aria-disabled="true"] {
    background-color: var(--bg-sage);
    color: var(--ink-muted);
    border-color: var(--rule-strong);
    border-style: dashed;
    cursor: not-allowed;
}

.btn[disabled]:hover,
.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
    background-color: var(--bg-sage);
    color: var(--ink-muted);
    border-color: var(--rule-strong);
}

/* Text plus a chevron, underlined on hover.

   THE CHEVRON IS DRAWN, NOT TYPED. It used to be a literal greater-than
   character in the markup, set in Inter beside Fraunces headings, and it read
   as an unclosed tag rather than as an arrow. It is now two borders on a
   rotated square: a real shape, in currentColor, that scales with the font
   size, needs no glyph, no SVG file and no data URI, and renders identically
   on an eight-year-old Android as on a current iPhone.

   The rotation is a static transform, not an animation. Nothing here moves
   (DESIGN.md law 4).

   The legacy span is hidden rather than left to double up. One page still
   carries it in markup owned by another lane; when that goes, this rule can
   go with it. */
.btn-quiet {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    /* Same reason as .faq__question: an inline-flex box whose text will not
       shrink below its longest word unless the intrinsic size is allowed to
       break. */
    overflow-wrap: anywhere;
    background: none;
    border: 0;
    padding: var(--space-3) 0;
    min-height: 44px;
    color: var(--teal-text);
    font-weight: 600;
    text-decoration: none;
}

.btn-quiet > span[aria-hidden="true"] {
    display: none;
}

.btn-quiet::after {
    content: "";
    flex: none;
    width: 0.4em;
    height: 0.4em;
    margin-left: 0.1em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
}

.btn-quiet:hover {
    text-decoration: underline;
    color: var(--ink-strong);
}

/* Same problem as the eyebrow: --teal-text is picked for a light ground and
   all but vanishes on --bg-deep. The quiet button keeps its shape and its
   drawn chevron, which follows currentColor, and only the ink changes. */
.section--deep .btn-quiet,
.cta-band--deep .btn-quiet {
    color: var(--ink-on-deep);
}

.section--deep .btn-quiet:hover,
.cta-band--deep .btn-quiet:hover {
    color: var(--ink-on-deep);
    text-decoration: underline;
}

/* On --bg-deep the outline button needs a light edge; the solid one keeps its
   teal ground, which clears 3:1 against the footer. */
.section--deep .btn-secondary,
.cta-band--deep .btn-secondary {
    color: var(--ink-on-deep);
    border-color: var(--ink-on-deep);
}

.section--deep .btn-secondary:hover,
.cta-band--deep .btn-secondary:hover {
    background-color: var(--ink-on-deep);
    color: var(--ink-strong);
}

.btn-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4) var(--space-5);
}

/* ===========================================================================
   CARDS. No drop shadow at rest. On hover the border goes --rule-strong and
   the card lifts 2px over 150ms; reduced motion removes the transition and
   leaves the border change, which is the part that actually carries the
   affordance.
   =========================================================================== */

.card {
    background-color: var(--bg-raised);
    border: 1px solid var(--rule);
    border-radius: var(--radius-card);
    padding: var(--space-5);
    transition: border-color var(--motion) ease, transform var(--motion) ease;
}

.card__title {
    font-size: var(--fs-h3);
    margin-bottom: var(--space-3);
}

.card__title a {
    color: inherit;
    text-decoration: none;
}

.card__body {
    color: var(--ink);
}

.card__body p {
    max-width: none;
}

.card__foot {
    margin-top: var(--space-4);
}

/* Only a card that is itself a link lifts. A static card has nothing to
   afford and a hover state on it is decoration. */
.card--link:hover,
.card--link:focus-within {
    border-color: var(--rule-strong);
    transform: translateY(-2px);
}

/* The whole card is the hit area, with the real anchor still the accessible
   name. No JavaScript row-click handler. */
.card--link {
    position: relative;
}

.card--link .card__title a::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--radius-card);
}

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

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

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

    .card-grid--3 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* ===========================================================================
   SECTION HEADER. Eyebrow, h2, lead. Left aligned by default; centred only on
   the hero and the final call to action (DESIGN.md section 5).
   =========================================================================== */

.section-header {
    margin-bottom: var(--space-6);
    max-width: var(--measure);
}

.section-header .lead {
    margin-top: var(--space-4);
}

.section-header--center {
    text-align: center;
    margin-inline: auto;
}

.section-header--center .lead {
    margin-inline: auto;
}

/* ===========================================================================
   PETAL DIVIDER. The one decorative motif on the site: a single petal lifted
   from the logo's own geometry, in --sage-soft at low opacity. Owned artwork,
   drawn as inline SVG, aria-hidden. Never more than one per viewport
   (DESIGN.md section 5).
   =========================================================================== */

.petal-divider {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    margin-block: var(--space-7);
    color: var(--sage-soft);
}

.petal-divider::before,
.petal-divider::after {
    content: "";
    height: 1px;
    flex: 1 1 auto;
    max-width: 8rem;
    background-color: var(--rule);
}

.petal-divider svg {
    width: 28px;
    height: 28px;
    opacity: 0.85;
    flex: none;
}

/* ===========================================================================
   FORMS. Labels above inputs, always visible, never placeholder-as-label.
   48px input height. The focus ring is 2px with a 2px offset and is never
   removed. Errors sit under the field with an icon and are announced.
   =========================================================================== */

.field {
    margin-bottom: var(--space-5);
    max-width: 34rem;
}

.field__hint {
    display: block;
    font-size: var(--fs-small);
    color: var(--ink-muted);
    font-weight: 400;
    margin-top: var(--space-1);
}

/* Required is spelled out on the label, not carried by a bare asterisk:
   an asterisk is colour-and-glyph shorthand that a screen reader reads as
   "star" and a hurried reader does not read at all (DESIGN.md section 5).

   Every field on this site now says which it is. Not one input carried
   required or aria-required before, and the labels did not distinguish a
   required field from an optional one while the note field said "(optional)"
   in its own label, so the only way to learn the rules was to fail. */
.field__required {
    font-size: var(--fs-small);
    font-weight: 400;
    color: var(--ink-muted);
}

/* THE ERROR SUMMARY. One block at the top of the form listing EVERY problem,
   each as a link to the field it belongs to, beside the per-field message.

   The form used to report one problem at a time, from the top down, with no
   marking on the offending field at all. On a form this long that is three or
   four round trips over a Blazor circuit, each one bouncing focus 1,300px up
   the page, for somebody who is already having a bad night. Validate the lot,
   say all of it, and let them jump straight to whichever one they want.

   The summary keeps its focus ring: it is focused programmatically after a
   failed submit and a person needs to see where they have landed. */
.error-summary {
    border-left: 3px solid var(--error);
    background-color: var(--bg-raised);
    padding: var(--space-4);
    border-radius: 0 var(--radius) var(--radius) 0;
    margin-bottom: var(--space-5);
    max-width: 34rem;
}

.error-summary__title {
    color: var(--error);
    font-weight: 600;
    margin-bottom: var(--space-2);
}

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

.error-summary__list li {
    max-width: none;
}

.error-summary__list a {
    display: inline-block;
    padding-block: var(--space-2);
    color: var(--error);
    font-weight: 600;
}

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

.input,
.textarea,
.select {
    display: block;
    width: 100%;
    min-height: 48px;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background-color: var(--bg-raised);
    color: var(--ink);
    font-size: var(--fs-body);
}

.textarea {
    min-height: 8rem;
    line-height: var(--lh-body);
    resize: vertical;
}

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

.input[aria-invalid="true"],
.textarea[aria-invalid="true"],
.select[aria-invalid="true"] {
    border-color: var(--error);
    border-width: 2px;
}

.field__error {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-2);
    color: var(--error);
    font-size: var(--fs-small);
    font-weight: 600;
}

.field__error svg {
    flex: none;
    width: 1.15em;
    height: 1.15em;
    margin-top: 0.15em;
}

/* The quiet boundary statement on the intake form: what this form is not for,
   and what we do with what you send. Not styled as an alarm, because shouting
   it would be the wrong tone for someone who is already frightened.

   Placement is settled in CLAUDE.md and the copy deck: the crisis notice sits
   ABOVE the form, so someone in trouble sees it before typing rather than
   after writing out the worst thing in their life. The privacy line is the one
   that sits below the submit button. */
.form-notice {
    border-left: 3px solid var(--violet);
    padding: var(--space-3) var(--space-4);
    background-color: var(--bg-sage);
    border-radius: 0 var(--radius) var(--radius) 0;
    margin-bottom: var(--space-5);
    max-width: 34rem;
}

/* ===========================================================================
   THE CALL BAR. Frame and position live in 02-layout.css; this is its
   contents. Two equal-width targets, and nothing else on it.
   =========================================================================== */

.call-bar .btn {
    width: 100%;
    padding-inline: var(--space-3);
    font-size: var(--fs-small);
}

/* On the intake page the second button is dropped, because it would link to
   the page the visitor is already reading. The phone takes the whole bar and
   the label goes up to body size: someone who has opened the form and then
   decided they would rather talk to a person deserves a bigger target, not a
   half-width one. */
.call-bar--single {
    grid-template-columns: 1fr;
}

.call-bar--single .btn {
    font-size: var(--fs-body);
}

/* THE CRISIS NUMBER, ON THE ONE FIXED ELEMENT ON THE SITE.

   Measured before this shipped: on the home page at 320px the only tel:988
   link on the page sat at y=9,543 of a 10,293px document, and it was a 29 by
   19 pixel target inside a paragraph. Twelve screens of scrolling, then a
   19px tap, for the number a person in trouble needs first.

   It spans the full width of the bar under the two actions, so it is a real
   target at any width without taking a column from either of them, and it is
   deliberately quiet: smaller, lighter and unfilled beside the two buttons.
   It is information, not an alarm (DESIGN.md section 5). Above 860px the bar
   is gone and the header carries the same link.

   It stays on the intake page too. The form already carries the notice above
   it, but somebody who has scrolled to the bottom of the form is exactly the
   person who should not have to scroll back up. */
.call-bar__crisis {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-height: 40px;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius);
    font-size: var(--fs-small);
    color: var(--ink);
    text-decoration: none;
    text-align: center;
}

.call-bar__crisis strong {
    color: var(--ink-strong);
}

.call-bar__crisis-label {
    color: var(--ink-muted);
}

.call-bar__crisis:hover {
    color: var(--ink-strong);
    text-decoration: underline;
}

/* ===========================================================================
   CRISIS BANNER. Persistent, quiet, non-dismissible, in the footer of every
   page. Not a modal, not a pop-up, not red. It is information, not an alarm
   (DESIGN.md section 5). The numbers are bold because they are what somebody
   is scanning for, not because the block is urgent.
   =========================================================================== */

.crisis {
    background-color: rgba(234, 242, 244, 0.08);
    border: 1px solid rgba(234, 242, 244, 0.28);
    border-radius: var(--radius-card);
    padding: var(--space-4) var(--space-5);
}

.crisis__title {
    font-family: var(--font-body);
    font-size: var(--fs-body);
    font-weight: 600;
    color: var(--ink-on-deep);
    letter-spacing: 0;
    text-transform: none;
    margin-bottom: var(--space-2);
}

.crisis p {
    max-width: 52ch;
    font-size: var(--fs-small);
    /* Roomier leading than the body default, because the numbers below are
       padded into real tap targets and a 1.65 line box would let two of them
       collide. */
    line-height: 2;
}

/* THE NUMBERS ARE TARGETS, NOT JUST BOLD TEXT.

   Measured in the footer at 390px: 988 was 29 by 19 pixels and 911 was 22 by
   19. DESIGN.md sets a 24px floor and a 44px ideal, and half this traffic is
   a thumb. Padding takes them past the floor without breaking the sentence,
   which is verbatim copy and stays a sentence.

   The 44px version of the same number lives on the call bar and in the
   header, which is also where it is reachable without scrolling. */
.crisis a {
    display: inline-block;
    font-weight: 600;
    min-height: 24px;
    /* 4px all round takes 911, the smallest of them, from 22x19 to 30x27,
       past the 24px floor. The horizontal half is cancelled again by the
       negative margin, so the padding buys target size without opening a gap
       in the middle of a sentence that has to stay a sentence. */
    padding: var(--space-1);
    margin-inline: calc(var(--space-1) * -1);
    border-radius: var(--radius);
}

/* ===========================================================================
   FAQ. Native details and summary. No JavaScript accordion: the disclosure
   widget is keyboard operable, announced, and printable for free, and the
   answer is in the DOM whether or not it is open.
   =========================================================================== */

.faq {
    border-top: 1px solid var(--rule);
    max-width: var(--measure);
}

.faq__item {
    border-bottom: 1px solid var(--rule);
}

.faq__question {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) 0;
    min-height: 44px;
    cursor: pointer;
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--ink-strong);
    line-height: var(--lh-heading);
    list-style: none;
    /* ANYWHERE, NOT BREAK-WORD, AND ONLY BECAUSE THIS IS A FLEX CONTAINER.

       The question text is an anonymous flex item, so its min-width is auto
       and it refuses to shrink below its longest word. At 200% text
       "appointment?" is 240px in a 206px column and the whole document
       scrolled sideways, with no element box out of place to show for it.
       break-word does not help: it permits a break at render time but leaves
       the intrinsic min-content width alone, which is the number flex is
       reading. anywhere changes that number. At every normal text size the
       two behave identically. */
    overflow-wrap: anywhere;
}

.faq__question::-webkit-details-marker {
    display: none;
}

.faq__question::after {
    content: "+";
    flex: none;
    font-family: var(--font-body);
    font-weight: 400;
    color: var(--teal-text);
}

.faq__item[open] > .faq__question::after {
    content: "-";
}

.faq__answer {
    padding-bottom: var(--space-5);
}

/* ===========================================================================
   CTA BAND. The closing call to action. Centred, one primary action, and the
   phone number beside it rather than a second button of equal weight.
   =========================================================================== */

.cta-band {
    background-color: var(--bg-sage);
    padding-block: var(--space-8);
}

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

.cta-band--deep h2 {
    color: var(--ink-on-deep);
}

.cta-band__inner {
    width: var(--container-narrow);
    margin-inline: auto;
    text-align: center;
}

.cta-band__inner p {
    margin-inline: auto;
}

.cta-band__actions {
    margin-top: var(--space-6);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-4) var(--space-5);
}

.cta-band__phone {
    font-weight: 600;
    color: inherit;
    padding-block: var(--space-3);
    display: inline-block;
}

/* ===========================================================================
   INSURER STRIP. Third-party marks reproduced nominatively to state a fact
   about the practice: these are the cards this practice takes. Never styled to
   imply endorsement, sponsorship or a partnership, which means no badges, no
   frames, no "official partner" chrome and no ordering that promotes one payer
   over another. The heading above the row says what the row means, and that is
   what keeps a nominative use nominative.

   THE ROW IS LAID OUT BY HEIGHT, NOT BY WIDTH.

   scripts/process-brand-images.py trims every mark to its ink and scales it to
   a common 80px ink height. So the rule below sets a height and lets width be
   auto, and the flex row centers on the cross axis, which with a common ink
   height IS a shared baseline. The obvious alternative, a grid of equal cells
   with object-fit, is what makes a logo strip look pasted together: a 461px
   wordmark and a 144px shield are the same ink height and wildly different
   widths, and forcing them into one box shrinks the wordmark until nobody can
   read it.

   IMAGES OFF IS NOT A DEGRADED CASE HERE. Each mark's alt is the plan name
   exactly as AcceptedInsurers spells it, so with images blocked the row
   renders as six plan names in the same wrapped layout, which is the text list
   this strip used to be. The heights below apply to the img box either way, so
   nothing jumps.
   =========================================================================== */

.insurer-strip {
    background-color: var(--bg-sage);
    padding-block: var(--space-8);
}

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

.insurer-logos {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* 32px between marks, not 64. At 64 the six came to 1,216px inside a
       1,200px container and HAP fell to a second line on its own, which reads
       as a mistake rather than as a row. */
    gap: var(--space-5);
    margin-top: var(--space-6);
}

.insurer-logos li {
    max-width: none;
    display: flex;
    align-items: center;
}

/* 2rem, not 32px. At 200% text the row grows with everything around it
   instead of becoming a line of stamps under headings twice their size, and
   the marks are 80px of real ink so there is resolution in hand for it. */
.insurer-logos img {
    height: 2rem;
    width: auto;
    max-width: 100%;

    /* With images off the alt text takes the img's place, and an alt box
       pinned to 2rem clips a two-line plan name. Height is a floor for that
       case and a fixed size for the picture, which is what the mark needs. */
    min-height: 2rem;
    object-fit: contain;
    object-position: left center;

    /* The names are the alt text, so a blocked image must read as words
       rather than as a broken-image box wedged into a 2rem line. */
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-strong);
}

@media (min-width: 560px) {
    .insurer-logos img {
        height: 2.5rem;
        min-height: 2.5rem;
    }
}

/* THE NAMED VARIANT, on /insurance-and-fees. The row stops being a strip and
   becomes the answer to the page's question: six tiles, each a mark with its
   plan name under it, laid out so a person can run down them against the front
   of their own card. Two columns on a phone, three from 560px.

   The tiles are --bg-raised with a --rule edge because this page's ground is
   --bg, where a white card is a one percent difference in value and reads as
   nothing at all. */
.insurer-logos--named {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    align-items: stretch;
}

@media (min-width: 560px) {
    .insurer-logos--named {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.insurer-logos--named li {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    background-color: var(--bg-raised);
    border: 1px solid var(--rule);
    border-radius: var(--radius-card);
    padding: var(--space-4);
}

.insurer-logos__name {
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-strong);
    /* A plan name is two or three words and the tile is narrow at 320px with
       the text turned up; it wraps rather than pushing the grid sideways. */
    overflow-wrap: anywhere;
}

.insurer-strip__note {
    margin-top: var(--space-4);
    color: var(--ink-muted);
    font-size: var(--fs-small);
}

.insurer-strip__foot {
    margin-top: var(--space-5);
}

/* ===========================================================================
   THE BLAZOR CIRCUIT CHROME

   Both blocks ship in the DOM of every page and are HIDDEN until the framework
   needs them. That is not a detail: without these rules they render in the
   normal flow at the foot of every page, so the last words a visitor reads are
   "Something went wrong. This page has stopped responding" followed by
   "Reconnecting..." - directly beneath the crisis line offering 988, on a site
   whose entire job is to look like somewhere safe to call. They also add two
   junk tab stops to the end of every page and let a screen reader announce
   "Reconnecting" on a page that is working perfectly.

   Blazor reveals them itself: it sets an inline display on #blazor-error-ui,
   and toggles .components-reconnect-show / -failed / -rejected on the modal.
   Inline styles and the class rules below both beat these defaults, so hiding
   them here costs nothing when they are genuinely needed.
   =========================================================================== */

#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1000;
    padding: var(--space-4);
    background-color: #2C1A1A;
    color: #FCEFEF;
    box-shadow: 0 -2px 12px rgba(22, 48, 60, 0.28);
}

#blazor-error-ui .reload {
    margin-left: var(--space-3);
    padding: var(--space-2) var(--space-4);
    min-height: 44px;
    border: 1px solid #FCEFEF;
    border-radius: var(--radius);
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

#components-reconnect-modal {
    display: none;
}

/* Blazor adds -show while retrying, -failed when the retries run out, and
   -rejected when the server no longer knows the circuit. All three mean the
   person is looking at a dead page and needs to be told. */
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: block;
}

.reconnect-overlay {
    position: fixed;
    inset: 0;
    z-index: 1000;
    background-color: rgba(7, 54, 73, 0.55);
}

.reconnect-card {
    position: fixed;
    z-index: 1001;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(100% - 2.5rem, 26rem);
    padding: var(--space-6);
    border-radius: var(--radius-lg, 12px);
    background-color: var(--bg-raised);
    color: var(--ink);
    box-shadow: 0 8px 32px rgba(22, 48, 60, 0.24);
    text-align: center;
}

.reconnect-title {
    margin: 0 0 var(--space-3);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--ink-strong);
}

.reconnect-message,
.reconnect-sub {
    margin: 0 0 var(--space-2);
    color: var(--ink-muted);
}

.reconnect-sub {
    margin-bottom: 0;
    font-size: var(--fs-small);
}
