@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;
}

/* Join a registration link (/join/{token}).
   Page-scoped: the layout injects this file only for the join form, so the rest
   of the portal keeps its own shell. The palette and the CircularStd face are
   the landing page's (public/assets/css/landing.css), reused rather than
   re-invented, so a link a person was sent looks like the product that sent it. */

body {
    background:
        radial-gradient(1100px 520px at 12% -12%, #e5edfc 0%, rgba(229, 237, 252, 0) 60%),
        #f4f6fb;
}

.join {
    --ink: #14275f;
    --accent: var(--brand-color, #1148c7);
    --muted: #52617a;
    --surface: #eef3fc;
    --line: #dce4f1;
    --danger: #b3261e;
    font-family: CircularStd, -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
    color: var(--ink);
}

/* The signup header keeps the language switcher and the sign-in link on one
   row when they fit, and lets the actions block wrap instead of breaking the
   link's own label across four lines. */
.portal-head__actions {
    flex-wrap: wrap;
    justify-content: flex-end;
}

.join-top-link {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    white-space: nowrap;
    padding: .5rem .75rem;
    border-radius: .6rem;
    color: var(--ink);
    font-size: .875rem;
    font-weight: 600;
    text-decoration: none;
}

.join-top-link:hover {
    background: var(--surface);
    color: var(--accent);
    text-decoration: none;
}

.join-top-link:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

/* The signup header's only action: the reader already knows which audience they
   are, so the way back in is a button carrying the action rather than a
   sentence. The header sits outside .join, so the palette is stated here. */
.join-top-button {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    min-height: 40px;
    padding: .45rem 1rem;
    border: 1px solid #dce4f1;
    border-radius: .6rem;
    background: #fff;
    color: #14275f;
    font-size: .875rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
}

/* The header's Clear control is a <button> wearing the link's class, so it
   brings the two things a button does not inherit. */
button.join-top-button {
    cursor: pointer;
    font-family: inherit;
}

.join-top-button:hover {
    border-color: var(--brand-color, #1148c7);
    background: #f2f6ff;
    color: var(--brand-color, #1148c7);
    text-decoration: none;
}

.join-top-button:focus-visible {
    outline: 3px solid var(--brand-color, #1148c7);
    outline-offset: 2px;
}

/* ------------------------------------------------------------------ intro */

.join-intro {
    margin-bottom: 1.75rem;
}

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

.join-title {
    margin: 0 0 .6rem;
    color: var(--ink);
    font-size: clamp(1.6rem, 3.2vw, 2.15rem);
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1.15;
}

.join-lede {
    max-width: 52ch;
    margin: 0;
    color: var(--muted);
    font-size: 1rem;
}

/* ----------------------------------------------------------------- layout */

.join-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.75rem;
    align-items: start;
}

.join-form {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 1.25rem;
    padding: 1.75rem;
    box-shadow: 0 24px 48px -38px rgba(20, 39, 95, .5);
}

.join-section + .join-section {
    margin-top: 1.9rem;
}

.join-section__title {
    display: flex;
    align-items: center;
    gap: .85rem;
    margin: 0 0 1.2rem;
    color: var(--accent);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
}

.join-section__title::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--line);
}

/* ----------------------------------------------------------------- fields */

.join-form .form-group {
    margin-bottom: 1.1rem;
}

.join-form label {
    margin-bottom: .4rem;
    color: var(--ink);
    /* The label matches the panel's own field size, and stays a step above the
       tooltip bubble that hangs off it. */
    font-size: .9375rem;
    font-weight: 600;
}

/* A label long enough to wrap to a second line ("Identity Card / Passport No.",
   "Kad Pengenalan / No. Pasport") used to push its own control 18px below the
   controls beside it, because each field sized its label row independently.
   On the side-by-side rows every label now reserves two lines and sits at the
   bottom of that space, so the three controls in a row always share one top
   edge. 3em is exactly two .9375rem/1.5 lines. Only where the columns sit side
   by side: stacked on a phone there is no neighbour to line up with.

   A required label keeps its text and its marker inside one child span: as two
   separate flex items the * would be pushed to the column's far edge whenever
   the label wraps to its second line. */
@media (min-width: 768px) {
    .join-form .form-group > label {
        display: flex;
        align-items: flex-end;
        min-height: 3em;
    }
}

.join-required {
    color: var(--danger);
}

.join-form .form-control {
    height: auto;
    min-height: 48px;
    padding: .7rem .9rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fbfcff;
    color: var(--ink);
    font-size: 1rem;
    transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}

.join-form .form-control::placeholder {
    color: #8b97ad;
}

.join-form .form-control:focus {
    border-color: var(--accent);
    background: #fff;
    box-shadow: 0 0 0 3px rgba(17, 72, 199, .18);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.join-form .form-control[readonly] {
    background: var(--surface);
    color: var(--muted);
}

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

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

/* The password field's show/hide control sits inside the field's own row rather
   than beside it, so the field keeps the form's single-column width and the
   password pair still shares one top edge. It is drawn only when scripting is
   on (.js is set in the layout's head), because without its handler it would
   be a button that does nothing. */
.join-password {
    position: relative;
}

.js .join-password .form-control {
    padding-right: 3.25rem;
}

.join-password__toggle {
    position: absolute;
    top: 0;
    right: 0;
    display: none;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 0 .7rem .7rem 0;
    background: transparent;
    color: var(--muted);
    font-size: 1rem;
    cursor: pointer;
    transition: color .15s ease;
}

.js .join-password__toggle {
    display: flex;
}

.join-password__toggle:hover {
    color: var(--ink);
}

.join-password__toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -3px;
    color: var(--ink);
}

/* Select2 replaces each dropdown with a control of its own, so the picker wears
   the same shape as the fields beside it: one 48px row, the same border, radius
   and focus ring. The dropdown itself is portalled to the body, outside .join,
   so it carries the palette it needs rather than borrowing this block's. */
.join-form .select2-container .select2-selection--single {
    display: flex;
    align-items: center;
    height: 48px;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fbfcff;
    transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}

.join-form .select2-container .select2-selection--single .select2-selection__rendered {
    padding: 0 .9rem;
    color: var(--ink);
    font-size: 1rem;
    line-height: normal;
}

.join-form .select2-container .select2-selection--single .select2-selection__placeholder {
    color: #8b97ad;
}

.join-form .select2-container .select2-selection--single .select2-selection__arrow {
    height: 46px;
    right: .55rem;
}

.join-form .select2-container .select2-selection--single .select2-selection__clear {
    position: relative;
    z-index: 1;
    height: 46px;
    margin: 0;
    padding: 0 .4rem;
}

.join-form .select2-container--focus .select2-selection--single,
.join-form .select2-container--open .select2-selection--single {
    border-color: var(--accent);
    background: #fff;
    box-shadow: 0 0 0 3px rgba(17, 72, 199, .18);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ------------------------------------------------------------------ phone */

/* A phone field is usually one control, not three: the country's dialling code
   sits on the left of the number, one border around both. The prefix is the
   same searchable country picker the rest of the form uses, drawn as a prefix
   (no border, no clear button) and reading only the code it carries; the hidden
   field beside the number is what the server posts. */
.phone-field {
    display: flex;
    align-items: stretch;
    min-height: 48px;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fbfcff;
    transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}

/* One ring for the pair: each half suppresses its own, so the control reads as
   a single field while either half has focus. */
.phone-field:focus-within {
    border-color: var(--accent);
    background: #fff;
    box-shadow: 0 0 0 3px rgba(17, 72, 199, .18);
}

.phone-field--invalid {
    border-color: var(--danger);
    background: #fffafa;
}

.phone-field--invalid:focus-within {
    box-shadow: 0 0 0 3px rgba(179, 38, 30, .16);
}

/* The prefix's half. Fixed width: a dialling code is two or three characters
   and the chevron, and every pixel beyond that is taken from the number. */
.phone-field__country {
    flex: 0 0 5.5rem;
    width: 5.5rem;
    border-right: 1px solid var(--line);
}

.phone-field__country .select2-container,
.phone-field__country .form-control {
    width: 100%;
}

.phone-field .form-control {
    min-height: 46px;
}

/* 46px of content + the wrapper's two 1px borders: the same 48px row as the
   field beside it. */
.phone-field .select2-container .select2-selection--single {
    height: 46px;
    min-height: 46px;
}

.phone-field .form-control,
.phone-field .select2-container .select2-selection--single {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}

.phone-field .form-control:focus,
.phone-field .select2-container--focus .select2-selection--single,
.phone-field .select2-container--open .select2-selection--single {
    border: 0;
    background: transparent;
    box-shadow: none;
    outline: 0;
}

.phone-field .select2-container .select2-selection--single .select2-selection__rendered {
    padding: 0 .2rem 0 .8rem;
}

.phone-field .select2-container .select2-selection--single .select2-selection__arrow {
    right: .1rem;
    width: 1.3rem;
}

.phone-field .phone-field__number {
    flex: 1 1 auto;
    min-width: 0;
    padding-left: .85rem;
}

.phone-field__number::placeholder {
    color: #8b97ad;
}

/* The refusal the number earns, printed under the control by the field's own
   script while it is being typed. Same voice as the form's other errors; the
   attribute rule is what hides it, because the display below would otherwise
   beat [hidden]. */
.phone-field__error {
    display: flex;
    gap: .45rem;
    align-items: flex-start;
    margin: .45rem 0 0;
    color: var(--danger);
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.45;
}

.phone-field__error[hidden] {
    display: none;
}

/* The list opens wider than the prefix that opens it: country names clamped to
   five and a half rem would be a list nobody can read. */
.phone-field-dropdown {
    min-width: 17rem;
}

.join-select2-dropdown {
    --join-accent: #1148c7;
    --join-line: #dce4f1;
    overflow: hidden;
    border: 1px solid var(--join-line);
    border-radius: .7rem;
    box-shadow: 0 18px 40px -28px rgba(20, 39, 95, .55);
}

.join-select2-dropdown .select2-search--dropdown { padding: .6rem; }

.join-select2-dropdown .select2-search--dropdown .select2-search__field {
    min-height: 40px;
    border: 1px solid var(--join-line);
    border-radius: .55rem;
    color: #14275f;
}

.join-select2-dropdown .select2-results__option { padding: .55rem .9rem; }

.join-select2-dropdown .select2-results__option--highlighted[aria-selected] {
    background: var(--join-accent);
    color: #fff;
}

.join-help {
    margin: .45rem 0 0;
    color: var(--muted);
    font-size: .8125rem;
    line-height: 1.45;
}

.join-error {
    display: flex;
    gap: .45rem;
    align-items: flex-start;
    margin: .45rem 0 0;
    color: var(--danger);
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.45;
}

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

/* A help line that is also the live rule: it turns red while the value breaks
   it, and stays where it is, so the rule is never hidden from the person
   filling the form in. */
.join-help[data-invalid] {
    color: var(--danger);
    font-weight: 500;
}

/* .join-error sets display, so a hidden error needs the rule the attribute
   alone would otherwise lose to the class. */
.join-error[hidden] {
    display: none;
}

/* ------------------------------------------------------ existing account */

/* The answer to "does this address already have an account?", put on the page
   before the form is submitted. An organization's link seats somebody already
   on record by having them prove that account's password; a platform package
   link opens a new account, so it says so and offers the way in to the account
   they already have. Revealed by join-account.js once the server has answered;
   without script the submit-time refusal still explains itself. */
.join-account {
    display: flex;
    gap: .6rem;
    align-items: flex-start;
    margin: 0 0 1.25rem;
    padding: .8rem .95rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--surface);
    color: var(--ink);
    font-size: .875rem;
    line-height: 1.5;
}

.join-account > i {
    margin-top: .15rem;
    color: var(--accent);
}

.join-account p {
    margin: 0;
}

.join-account a {
    color: var(--accent);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
}

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

/* The package link's answer: an existing account is a dead end for this link,
   so it wears the refusal colour rather than the same calm surface. */
.join-account--blocked {
    border-color: #f0c9c5;
    background: #fdf1f0;
}

.join-account--blocked > i {
    color: var(--danger);
}

.join-account[hidden] {
    display: none;
}

/* The fields join-account.js stands down are a Bootstrap .row (display: flex)
   and form groups; the class rule beats the user agent's own [hidden] rule, so
   the attribute needs the specificity to hide them. */
.join-form .row[hidden],
.join-form .form-group[hidden],
.join-form p[hidden] {
    display: none;
}

/* ----------------------------------------------------------------- info tip */

/* The panel's info tooltip (resources/views/components/info-tip.blade.php),
   styled for this page: the signup form loads portal-join.css and not the admin
   theme.css that carries the panel's copy. Hover and keyboard focus open the
   bubble in CSS; public/assets/js/shared/info-tip.js toggles .is-open on a tap,
   closes it on an outside click or Escape, and places the bubble beside the icon
   inside the viewport. */

.join .info-tip {
    display: inline-flex;
    margin-left: .4rem;
    /* The bubble is positioned against the icon's own box, so a tip opens
       beside the trigger rather than at the bottom of the field. */
    position: relative;
    vertical-align: middle;
}

.join .info-tip__trigger {
    align-items: center;
    background: none;
    border: 0;
    border-radius: 50%;
    color: var(--muted);
    cursor: help;
    display: inline-flex;
    /* A 10px glyph keeps the icon quiet beside the label; the box below stays
       24px for the WCAG 2.5.8 minimum hit area. */
    font-size: .625rem;
    /* 24px square: the WCAG 2.5.8 minimum hit area beside a field label. */
    height: 1.5rem;
    justify-content: center;
    padding: 0;
    width: 1.5rem;
}

.join .info-tip__trigger:hover {
    color: var(--accent);
}

.join .info-tip__trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.join .info-tip__bubble {
    animation: info-tip-fade .15s ease;
    background: var(--ink);
    border-radius: .375rem;
    box-shadow: 0 6px 20px rgba(20, 30, 60, .18);
    color: #fff;
    /* Closed, the bubble is display: none: visibility: hidden removes the
       pixels but keeps the box, so an icon in a right-hand column would widen
       the page even while nothing was open. The fade runs when it is shown. */
    display: none;
    /* One step below the form's own type: the bubble is help, not a field. */
    font-size: .75rem;
    font-weight: 400;
    left: 0;
    line-height: 1.45;
    max-width: min(20rem, calc(100vw - 1rem));
    padding: .5rem .75rem;
    position: absolute;
    text-align: left;
    top: calc(100% + .35rem);
    width: max-content;
    z-index: 1080;
}

.join .info-tip:hover .info-tip__bubble,
.join .info-tip:focus-within .info-tip__bubble,
.join .info-tip.is-open .info-tip__bubble {
    display: block;
}

@keyframes info-tip-fade {
    from {
        opacity: 0;
        transform: translateY(-2px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .join .info-tip__bubble {
        animation: none;
    }
}

/* ------------------------------------------------------------ photo picker */

.join-photo {
    display: flex;
    gap: 1.25rem;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
    padding: 1.1rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: linear-gradient(160deg, #fbfcff 0%, var(--surface) 100%);
}

.join-photo__frame {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 112px;
    height: 112px;
    padding: 4px;
    border: 1px dashed #b9c6df;
    border-radius: 50%;
    background: #fff;
}

.join-photo__frame img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: var(--surface);
    object-fit: cover;
}

.join-photo__fields {
    flex: 1 1 260px;
    min-width: 0;
}

.join-photo__heading {
    display: flex;
    align-items: center;
    gap: .1rem;
    margin-bottom: .6rem;
}

.join-photo__label {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .6rem;
    margin: 0;
    cursor: pointer;
}

.join-photo__input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.join-photo__input:focus-visible ~ .join-button--ghost {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

.join-field__label {
    color: var(--ink);
    font-size: .9375rem;
    font-weight: 600;
}

.join-tag {
    display: inline-block;
    margin-left: .4rem;
    padding: .15rem .5rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: #fff;
    color: var(--muted);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.join-photo__foot {
    display: flex;
    align-items: baseline;
    gap: .9rem;
    flex-wrap: wrap;
    margin-top: .55rem;
}

.join-photo__foot .join-help {
    margin: 0;
}

/* ---------------------------------------------------------------- buttons */

.join-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
    min-height: 48px;
    padding: .7rem 1.4rem;
    border: 1px solid var(--ink);
    border-radius: .7rem;
    background: var(--ink);
    color: #fff;
    font-family: inherit;
    font-size: .9375rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color .18s ease, border-color .18s ease, transform .18s ease;
}

.join-button:hover:not(:disabled) {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.join-button:active:not(:disabled) {
    transform: translateY(1px);
}

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

/* The one state the join button can be held in: the address typed already has
   an account a package link cannot be spent on, so the notice says to sign in
   and this stays out of the way rather than submitting into a refusal. */
.join-button:disabled {
    opacity: .55;
    cursor: not-allowed;
}

.join-button--ghost {
    background: #fff;
    border-color: var(--line);
    color: var(--ink);
}

.join-button--ghost:hover:not(:disabled) {
    background: #fff;
    border-color: var(--accent);
    color: var(--accent);
}

.join-button--text {
    padding: .3rem 0;
    border: 0;
    border-radius: .25rem;
    background: none;
    color: var(--accent);
    font-family: inherit;
    font-size: .875rem;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.join-button--text:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

.join-submit {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: center;
    margin-top: 1.9rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--line);
}

.join-submit .join-button {
    min-width: 180px;
}

/* The trial door, under the payment one. Same form, a different answer to "how
   do I pay", so it sits in the submit block rather than becoming a second page:
   at a booth it is the choice somebody who is unsure actually needs to see, and
   the details they already typed are the details the trial opens with. */
.join-trial {
    flex-basis: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .6rem;
    padding-top: 1.1rem;
    border-top: 1px dashed var(--line);
    text-align: center;
}

.join-trial__note {
    max-width: 34rem;
    margin: 0;
    color: var(--muted);
    font-size: .875rem;
}

.join-trial__note i {
    margin-right: .35rem;
}

/* A gift code applied on the form is not spent by a trial, so the note sits
   beside the trial button -- the place the choice is actually made. */
.join-trial__gift {
    max-width: 34rem;
    margin: 0;
    padding: .6rem .9rem;
    border: 1px solid #c9d9f7;
    border-radius: .7rem;
    background: #f2f7ff;
    color: #1f3a68;
    font-size: .8125rem;
    line-height: 1.5;
}

.join-trial__gift i {
    margin-right: .35rem;
    color: var(--accent);
}

.join-note {
    margin: .75rem 0 0;
    color: var(--muted);
    font-size: .8125rem;
}

/* The preset package is shown, not chosen. The partial is shared, so this only
   restyles it inside the join page. */
.join .preset-package {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .3rem .75rem;
    margin-bottom: 1.5rem;
    padding: .85rem 1rem;
    border: 1px solid #c7d7fb;
    border-radius: .9rem;
    background: var(--surface);
}

.join .preset-package p {
    margin: 0;
}

.join .preset-package .text-muted {
    color: var(--muted) !important;
}

/* --------------------------------------------------------------- gift code */

.join-gift {
    padding: 1.1rem 1.25rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: linear-gradient(160deg, #fbfcff 0%, var(--surface) 100%);
}

.join-gift__intro {
    margin-top: 0;
    margin-bottom: .75rem;
}

.join-gift__row {
    display: flex;
    gap: .6rem;
    align-items: stretch;
}

/* The row holds the field alone -- there is no Apply button beside it any more --
   so the input takes the whole width the button used to leave. */
.join-gift__row .form-control {
    flex: 1 1 auto;
    min-height: 48px;
    font-size: 1rem;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.join-gift__panel {
    margin-top: .9rem;
}

.join-gift__message {
    margin: 0;
    color: var(--muted);
    font-size: .875rem;
    line-height: 1.5;
}

.join-gift__message--error {
    color: var(--danger);
    font-weight: 500;
}

.join-gift__prompt {
    margin-top: .7rem;
    padding: .9rem 1rem;
    border: 1px solid #c7d7fb;
    border-radius: .75rem;
    background: #fff;
}

.join-gift__prompt-title {
    margin: 0 0 .7rem;
    color: var(--ink);
    font-size: .9375rem;
    font-weight: 600;
}

.join-gift__actions {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
}

.join-gift__actions .join-button {
    min-height: 44px;
    padding: .55rem 1rem;
    font-size: .875rem;
}

.join-gift__applied {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0;
    color: #146c43;
    font-size: .875rem;
    font-weight: 500;
    line-height: 1.5;
}

.join-gift__applied i {
    color: #146c43;
}

.join-gift__applied .join-button--text {
    color: var(--danger);
    text-decoration-color: var(--danger);
}

.join-gift [hidden] {
    display: none !important;
}

@media (max-width: 575.98px) {
    .join-gift__row {
        flex-direction: column;
    }
}

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

@media (max-width: 575.98px) {
    /* The sign-in link is the widest header item; let it wrap rather than push
       the header past the viewport on a 320px screen. */
    .join-top-link {
        max-width: 100%;
        white-space: normal;
    }

    .join-form {
        padding: 1.25rem;
        border-radius: 1rem;
    }

    .join-photo {
        padding: .9rem;
    }

    .join-photo__frame {
        width: 88px;
        height: 88px;
    }

    .join-submit .join-button {
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .join *,
    .join *::before,
    .join *::after {
        transition: none !important;
        animation: none !important;
    }
}

/* ----------------------------------------------------------------- signup plans */
/* The landing page hands a package id to signup, and the same package is the
   first thing the form shows, so the plans are radio controls drawn as tiles
   rather than a select: the choice the reader made stays visible.

   An organization is sold one product on several terms, so the tiles are laid
   side by side with the term leading each one and the monthly rate on a shared
   line: the comparison the buyer is making is "more years, less a month", and
   stacking the terms in a single column hides it. The package name stays as the
   tile's footer — it names the plan on the invoice, it does not tell the offers
   apart. */
.signup-plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14.5rem, 1fr));
    gap: .8rem;
    margin-bottom: 1.25rem;
}

/* A lone plan is not a comparison: keep it tile-sized instead of letting one
   card stretch across the form. */
.signup-plans--single {
    max-width: 24rem;
}

.signup-plans .signup-plan {
    position: relative;
    display: flex;
    margin: 0;
    cursor: pointer;
}

.signup-plan input {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.signup-plan__body {
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: 1rem 1.05rem 1.1rem;
    border: 1px solid #d7e0f0;
    border-radius: .85rem;
    background: #fff;
    transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}

.signup-plan:hover .signup-plan__body {
    border-color: #9fb6e8;
    background: #fbfcff;
}

.signup-plan input:checked + .signup-plan__body {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 5%, #fff);
    box-shadow: 0 0 0 1px var(--accent);
}

.signup-plan input:focus-visible + .signup-plan__body {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}

.signup-plan__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem;
}

.signup-plan__term {
    color: var(--ink);
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -.01em;
    line-height: 1.25;
}

.signup-plan input:checked + .signup-plan__body .signup-plan__term {
    color: var(--accent);
}

/* The disc is the visible half of the radio: hollow until the plan is chosen. */
.signup-plan__tick {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin-top: .1rem;
    border: 1px solid #7f8ca1;
    border-radius: 50%;
    background: #fff;
    color: transparent;
    font-size: .55rem;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.signup-plan input:checked + .signup-plan__body .signup-plan__tick {
    border-color: var(--accent);
    background: var(--accent);
    color: #fff;
}

.signup-plan__seats {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-top: .3rem;
    color: var(--muted);
    font-size: .8125rem;
    font-weight: 500;
}

.signup-plan__seats i {
    color: var(--accent);
    font-size: .6875rem;
}

.signup-plan__price {
    margin-top: 1rem;
    color: var(--ink);
    font-size: 1.55rem;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    letter-spacing: -.03em;
    line-height: 1.1;
}

.signup-plan__currency {
    margin-right: 3px;
    font-size: .55em;
    letter-spacing: 0;
    unicode-bidi: isolate;
}

.signup-plan__monthly {
    margin-top: .2rem;
    color: var(--muted);
    font-size: .8125rem;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
}

.signup-plan__saving {
    display: flex;
    align-items: flex-start;
    gap: .35rem;
    margin-top: .4rem;
    color: var(--accent);
    font-size: .75rem;
    font-weight: 600;
    line-height: 1.35;
}

.signup-plan__saving i {
    margin-top: .15rem;
    font-size: .6875rem;
}

.signup-plan__name {
    margin-top: auto;
    padding-top: .9rem;
    color: var(--muted);
    font-size: .75rem;
    font-weight: 400;
    line-height: 1.4;
}

/* A deep link from the pricing page names one plan, so the page prints that
   plan alone. The note says where it came from and offers the way back to the
   whole list — a page change that drops the id, because the link no longer
   describes what is being bought. */
.signup-plans__note {
    grid-column: 1 / -1;
    margin: 0;
    color: var(--muted);
    font-size: .8125rem;
    line-height: 1.5;
}

.signup-plans__reset {
    display: inline-block;
    margin-left: .15rem;
    color: var(--accent);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
    white-space: nowrap;
}

.signup-plans__reset:hover {
    color: var(--ink);
}

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

/* The checked border and tint are colour; in forced-colours mode they are
   replaced by the system palette, so the tick carries the state explicitly. */
@media (forced-colors: active) {
    .signup-plan input:checked + .signup-plan__body {
        outline: 2px solid CanvasText;
    }

    .signup-plan input:checked + .signup-plan__body .signup-plan__tick {
        border-color: CanvasText;
        background: CanvasText;
        color: Canvas;
    }
}

/* ------------------------------------------------------------- audience switch */
/* The same segmented control the landing page uses, rebuilt for the portal's
   light surface. Links, not tab buttons: the two audiences have different forms
   and package lists, so switching is a page change. */
.signup-audience {
    display: inline-flex;
    gap: 4px;
    margin-bottom: 22px;
    padding: 5px;
    border: 1px solid #d7dce7;
    border-radius: 100px;
    background: #fff;
}

.signup-audience__option {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 18px;
    border-radius: 100px;
    color: #5b6474;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color .15s ease, color .15s ease;
}

.signup-audience__option:hover {
    background: #f1f4f9;
    color: #1f2430;
    text-decoration: none;
}

.signup-audience__option.is-active,
.signup-audience__option.is-active:hover {
    background: var(--brand-color, #1148c7);
    color: #fff;
}

.signup-audience__option:focus-visible {
    outline: 3px solid var(--brand-color, #1148c7);
    outline-offset: 3px;
}

@media (max-width: 420px) {
    .signup-audience {
        width: 100%;
    }

    .signup-audience__option {
        flex: 1;
        justify-content: center;
        padding-inline: 10px;
        font-size: 12px;
    }
}
