@font-face {
    font-family: CircularStd;
    src: url('../fonts/CircularStd-Book.woff') format('woff');
    font-weight: 400;
    font-display: swap;
}

@font-face {
    font-family: CircularStd;
    src: url('../fonts/CircularStd-Medium.woff') format('woff');
    font-weight: 500;
    font-display: swap;
}

@font-face {
    font-family: CircularStd;
    src: url('../fonts/CircularStd-Bold.woff') format('woff');
    font-weight: 700;
    font-display: swap;
}

/* The member portal's entry (/member) and code (/member/verify) screens.

   Page-scoped: those pages opt into this file through the layout's
   portal_styles slot, so the other portal screens keep their own shell. The
   palette and the CircularStd face are the landing page's
   (public/assets/css/landing.css), reused the same way portal-join.css reuses
   them, so a member who arrives from a join link and a member who comes back
   to edit their card meet one product.

   Both screens are one task in two steps, so they share one composition: a
   single centred column whose card is the only raised surface, the brand
   wordmark sitting above it as its caption, and the supporting notes in a
   quieter panel underneath. */

:root {
    --ink: #142b25;
    --accent: var(--brand-color, #087b48);
    /* 85% of the default green is its old #07693d exactly, so the derivation
       reproduces the shipped shade and follows a configured one. */
    --accent-dark: color-mix(in srgb, var(--accent) 85%, #000000);
    --muted: #52655c;
    --surface: #edf5ef;
    --line: #d5e4da;
    --line-strong: #bacfc1;
    --danger: #b3261e;
    --plate: #f3f8f4;
    /* The field's fill and the placeholder ink cannot be derived from the accent
       without drifting from the shipped green, so they are tokens of their own.
       partials.portal-member-brand re-points them, and the rest of the palette,
       when an admin has set a colour. */
    --field: #fbfdfb;
    --placeholder: #73877b;
}

/* White, not a brand wash: the card is the one raised surface, and a tinted
   page put a second, unintended surface behind it -- faint on the shipped
   green, unmistakably pink once a red brand is configured. The brand still
   speaks through the accent, the panels and the hairlines. */
body {
    background: #fff;
}

/* The page has to fill the window, not just its content. A short card leaves
   the shell ending mid-screen, and the strip under it is the page's white
   painted without the body's surface, so the page reads as though it stops
   there. */
.portal-shell {
    min-height: 100vh;
    /* The phone's browser chrome makes 100vh the tallest the window can be; the
       dynamic unit keeps the page filled as that chrome slides away. */
    min-height: 100dvh;
}

/* -------------------------------------------------------------- the column */

/* The wordmark holds the shell's left edge and the language choice its right,
   so the two frame the card below instead of bunching together in the middle. */
.portal-head {
    justify-content: space-between;
    margin-bottom: 1.75rem;
}

/* The pinned header is painted with the portal's canvas so the card slides
   behind it cleanly. This page's canvas is white, so the bar takes the page's
   own surface instead of the shell's grey-blue, which read as a separate band
   across the top. */
.portal-head--sticky {
    background: #fff;
}

.portal-head h1 .h4,
.portal-head .h4 {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -.02em;
}

.portal-head h1 a {
    color: var(--ink);
    text-decoration: none;
}

.portal-head h1 .text-muted {
    color: var(--muted);
    font-weight: 600;
}

.member {
    max-width: 27rem;
    margin: 0 auto;
    color: var(--ink);
    font-family: CircularStd, -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}

.member-layout {
    display: grid;
    gap: 1.15rem;
    align-items: start;
}

/* ------------------------------------------------------------------- card */

.member-card {
    padding: clamp(1.4rem, 4.6vw, 2rem);
    border: 1px solid var(--line);
    border-radius: 1.35rem;
    background: #fff;
    box-shadow: 0 32px 64px -46px color-mix(in srgb, var(--ink) 60%, transparent);
}

.member-intro {
    margin: 0 0 1.5rem;
}

.member-eyebrow {
    margin: 0 0 .55rem;
    color: var(--accent);
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .16em;
    text-transform: uppercase;
}

.member-title {
    margin: 0 0 .5rem;
    color: var(--ink);
    font-size: clamp(1.45rem, 4.4vw, 1.8rem);
    font-weight: 700;
    letter-spacing: -.025em;
    line-height: 1.15;
}

.member-lede {
    margin: 0;
    color: var(--muted);
    font-size: .95rem;
    line-height: 1.6;
}

/* The one thing a member who has never chosen a password needs before typing:
   the form cannot accept a blank one, and a reset code is the way to set it.
   A tinted aside on the card rather than another .member-help line, because it
   stands apart from the field labels and is read once, on the way in. */
.member-guide {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    margin: 0 0 1.4rem;
    padding: .85rem 1rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent);
    border-radius: .75rem;
    background: var(--surface);
    color: var(--muted);
    font-size: .84rem;
    line-height: 1.6;
}

.member-guide > i {
    margin-top: .2rem;
    color: var(--accent);
}

.member-guide p {
    margin: 0;
}

.member-guide a {
    color: var(--accent);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.member-guide a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ------------------------------------------------------------------ field */

.member-field {
    margin-bottom: 1.25rem;
}

.member-label {
    display: block;
    margin-bottom: .5rem;
    color: var(--ink);
    font-size: .875rem;
    font-weight: 600;
}

.member-control {
    position: relative;
}

.member-control > i {
    position: absolute;
    top: 50%;
    left: 1rem;
    transform: translateY(-50%);
    color: var(--muted);
    font-size: .95rem;
    pointer-events: none;
}

.member-control input {
    width: 100%;
    min-height: 52px;
    padding: .75rem 1rem .75rem 2.9rem;
    border: 1px solid var(--line);
    border-radius: .75rem;
    background: var(--field);
    color: var(--ink);
    font: inherit;
    /* 16px, not the form's 15px: anything smaller makes iOS zoom the page when
       the field takes focus. */
    font-size: 1rem;
    transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}

.member-control input::placeholder {
    color: var(--placeholder);
}

.member-control input:hover {
    border-color: var(--line-strong);
}

/* The same ring the join form draws (portal-join.css): a soft shadow for the
   pointer, a solid outline for the keyboard. */
.member-control input:focus {
    border-color: var(--accent);
    background: #fff;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.member-control input.is-invalid {
    border-color: var(--danger);
    background: #fffafa;
}

.member-control input.is-invalid:focus {
    box-shadow: 0 0 0 3px rgba(179, 38, 30, .16);
    outline-color: var(--danger);
}

.member-help {
    margin: .55rem 0 0;
    color: var(--muted);
    font-size: .82rem;
    line-height: 1.5;
}

/* The help line is the live rule as well: red while the value breaks it. */
.member-help[data-invalid],
.pp-help[data-invalid] {
    color: var(--danger);
    font-weight: 500;
}

.member-error {
    display: flex;
    align-items: flex-start;
    gap: .45rem;
    margin: .55rem 0 0;
    color: var(--danger);
    font-size: .82rem;
    font-weight: 600;
    line-height: 1.5;
}

.member-error i {
    margin-top: .15rem;
}

/* ---------------------------------------------------------------- actions */

.member-submit {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .6rem;
    width: 100%;
    min-height: 52px;
    padding: .8rem 1.5rem;
    border: 1px solid var(--ink);
    border-radius: .8rem;
    background: var(--ink);
    color: #fff;
    font: inherit;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color .18s ease, border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}

.member-submit:hover:not(:disabled) {
    border-color: var(--accent);
    background: var(--accent);
    box-shadow: 0 12px 24px -16px color-mix(in srgb, var(--accent) 95%, transparent);
}

.member-submit:active:not(:disabled) {
    transform: translateY(1px);
}

.member-submit:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

.member-submit:disabled {
    cursor: progress;
    opacity: .85;
}

/* The button reports the wait in place: the page is about to leave, so a second
   click is the only thing left to prevent, and "Confirm" no longer describes it. */
.member-submit__spinner {
    display: none;
    width: 1rem;
    height: 1rem;
    border: 2px solid rgba(255, 255, 255, .35);
    border-top-color: #fff;
    border-radius: 50%;
    animation: member-spin .7s linear infinite;
}

.member-submit.is-busy .member-submit__spinner {
    display: inline-block;
}

.member-submit.is-busy .fa-arrow-right {
    display: none;
}

@keyframes member-spin {
    to {
        transform: rotate(360deg);
    }
}

/* The second action on the card — ask for another code, or go back and start
   over — sits under the divider, where the visitor is already looking. */
.member-resend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .4rem .6rem;
    margin: 1.25rem 0 0;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: .85rem;
}

/* The header's action pills are stated by the portal layout
   (layouts/portal.blade.php), because the header is the layout's and the
   portal's own pages carry no member sheet. The rules that remain here are the
   page-local ones: the re-issue wait below, and the reduced-motion override. */

/* Why the re-issue is unavailable, under the control it explains: the code the
   visitor already has may still be in their provider's queue. */
.member-resend__wait {
    flex: 1 0 100%;
    margin: 0;
    color: var(--muted);
    font-size: .8rem;
    text-align: center;
}

/* The first step's dead end: an address that is not on a profile can never be
   mailed a code, so the card offers the two ways out — correct the address, or
   open an account — instead of a field. They sit side by side, and each takes
   the whole row once it is too narrow for both. */
.member-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .7rem;
}

/* The primary action is a link here rather than a button, so it takes the
   fill's own text treatment instead of the browser's underlined link. */
.member-actions .member-submit {
    flex: 1 1 11rem;
    width: auto;
    text-decoration: none;
}

/* The secondary keeps the pill's shape but not its size: two halves of one
   choice read as the same height, and the pill's smaller label is the wrong
   weight beside a 1rem primary. */
.member-actions__alt {
    flex: 1 1 8rem;
    justify-content: center;
    min-height: 52px;
    padding: .8rem 1.25rem;
    border-radius: .8rem;
    font-size: 1rem;
}

/* ------------------------------------------------------------------ steps */

/* The notes are a caption to the card, not a second card: the surface is there
   to separate them, not to compete for the eye. */
.member-aside {
    padding: 1.15rem 1.3rem;
    border: 1px solid var(--line);
    border-radius: 1.1rem;
    background: var(--surface);
}

.member-aside__title {
    margin: 0 0 .9rem;
    color: var(--accent);
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
}

.member-tips {
    display: grid;
    gap: .8rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.member-tips li {
    display: grid;
    grid-template-columns: 1.1rem 1fr;
    gap: .65rem;
    align-items: start;
    color: var(--muted);
    font-size: .875rem;
    line-height: 1.5;
}

.member-tips i {
    margin-top: .2rem;
    color: var(--accent);
    font-size: .85rem;
}

/* The way out of a code that never arrived. It sits inside the muted note, so
   it is drawn in the accent rather than inheriting the browser's blue. */
.member-tips a {
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
}

.member-tips a:hover {
    text-decoration: underline;
}

/* The numbers are drawn with a counter rather than written into the markup, so
   the list stays a list and the steps stay reorderable. */
.member-steps {
    display: grid;
    gap: .85rem;
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: member-step;
}

.member-steps li {
    display: grid;
    grid-template-columns: 1.6rem 1fr;
    gap: .7rem;
    align-items: start;
    color: var(--muted);
    font-size: .875rem;
    line-height: 1.5;
}

.member-steps li::before {
    counter-increment: member-step;
    content: counter(member-step);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: #fff;
    color: var(--accent);
    font-size: .75rem;
    font-weight: 700;
}

/* Both closing notes carry an icon, so both get the same inset. */
.member-note,
.member-aside__foot {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    margin: 1rem 0 0;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: .82rem;
    line-height: 1.5;
}

.member-note i,
.member-aside__foot i {
    margin-top: .15rem;
    color: var(--accent);
}

/* ------------------------------------------------------------------ plate */

/* The address the code went to. It is a fact about the request and not a field,
   so it reads as a chip: the label above, the address below, and the way to the
   other address under them rather than at the foot of the form. */
.member-target {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: .15rem .75rem;
    margin: 0 0 1.4rem;
    padding: .8rem .9rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--surface);
}

.member-target > i {
    grid-area: 1 / 1 / span 2 / auto;
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    border-radius: 999px;
    background: #fff;
    color: var(--accent);
    font-size: .9rem;
}

.member-target__text {
    display: grid;
    gap: .1rem;
    min-width: 0;
}

.member-target__label {
    color: var(--muted);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
}

.member-target__address {
    color: var(--ink);
    font-size: .9rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.member-target__change {
    justify-self: start;
    color: var(--accent);
    font-size: .82rem;
    font-weight: 600;
    text-decoration: none;
}

.member-target__change:hover {
    color: var(--accent-dark);
    text-decoration: underline;
}

.member-target__change:focus-visible {
    border-radius: .25rem;
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

/* The code is the page's one loud element: it is held in a plate, and the field
   inside it is the only control the server reads. Six slots hang under it as a
   meter until the script arrives, and become the plate's own cells after. */
.member-code {
    position: relative;
}

.member-code input {
    width: 100%;
    min-height: 4rem;
    padding: .6rem 1rem;
    border: 1px solid var(--line);
    border-radius: .85rem;
    background: var(--plate);
    color: var(--ink);
    font: inherit;
    font-size: clamp(1.5rem, 6vw, 1.85rem);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: .45em;
    text-align: center;
    /* letter-spacing leaves a gap after the last digit, which pulls a centred
       code off-centre by half of it; the same amount of indent gives it back. */
    text-indent: .45em;
    box-shadow: inset 0 2px 8px -6px color-mix(in srgb, var(--ink) 55%, transparent);
    transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}

.member-code input:hover {
    border-color: var(--line-strong);
}

.member-code input:focus {
    border-color: var(--accent);
    background: #fff;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.member-code input.is-invalid {
    border-color: var(--danger);
    background: #fffafa;
}

.member-code__rail {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: .4rem;
    margin-top: .55rem;
}

.member-code__seg {
    height: .3rem;
    border-radius: 999px;
    background: var(--line);
    transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}

.member-code__seg.is-filled {
    background: var(--accent);
}

/* With the script running the six slots are the plate. The real field lies over
   them transparently: same single control, same autofill and paste behaviour,
   but the digits are drawn in the slots instead of down a long empty box. The
   field keeps the focus ring, so the plate lights up as one control. */
.member-code.is-mirrored .member-code__seg {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 4.35rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--plate);
    color: var(--ink);
    font-size: clamp(1.35rem, 5.4vw, 1.7rem);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    box-shadow: inset 0 2px 8px -6px color-mix(in srgb, var(--ink) 55%, transparent);
}

.member-code.is-mirrored .member-code__seg.is-filled {
    background: #fff;
    border-color: var(--line-strong);
}

/* The caret is invisible under the plate, so the slot the next digit lands in is
   drawn instead. The ring is inset: an outer one would collide with the plate's
   own focus ring and read as two controls. */
.member-code.is-mirrored .member-code__seg.is-active {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

.member-code.is-mirrored .member-code__rail {
    margin-top: 0;
}

.member-code.is-mirrored input {
    position: absolute;
    inset: 0;
    height: 100%;
    min-height: 0;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 1rem;
    background: transparent;
    box-shadow: none;
    color: transparent;
    caret-color: transparent;
    /* The value is the slots' to show; the field only has to hold it. */
    font-size: 1rem;
    letter-spacing: 0;
    text-indent: 0;
    cursor: text;
}

.member-code.is-mirrored input::selection {
    background: transparent;
}

.member-code.is-mirrored input:focus {
    background: transparent;
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent);
}

/* A field that has been refused keeps the refusal in its focus ring too, so the
   plate is red whether or not the visitor has clicked back into it. */
.member-code.is-mirrored input.is-invalid:focus {
    outline-color: var(--danger);
    box-shadow: 0 0 0 4px rgba(179, 38, 30, .14);
}

/* A refused code is wrong, not merely partial: the plate follows the field into
   the error colour instead of reading as a healthy six of six. */
.member-code input.is-invalid ~ .member-code__rail .member-code__seg {
    border-color: #eccac6;
    background: #fffafa;
    color: var(--danger);
}

.member-code input.is-invalid ~ .member-code__rail .member-code__seg.is-filled {
    border-color: var(--danger);
    background: #fffafa;
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 575.98px) {
    .portal-head {
        margin-bottom: 1.25rem;
    }

    .member-card {
        border-radius: 1.15rem;
    }

    .member-code input {
        min-height: 3.5rem;
        letter-spacing: .35em;
        text-indent: .35em;
    }

    /* Six digits plus their gaps have to clear the plate on a 320px screen. */
    .member-code.is-mirrored .member-code__seg {
        height: 3.6rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .member-submit,
    .member-link-btn,
    .member-code input,
    .member-code__seg {
        transition: none;
    }

    .member-submit:active {
        transform: none;
    }

    .member-submit__spinner {
        animation: none;
    }
}
