/* ============================================================================================
   SURVANTA WEBSITE — functional ("portal") page design language
   ============================================================================================

   The public marketing pages (Home, Pricing) already carry the product's visual language: an eyebrow
   label, a centred display heading, a soft tinted ground, and a consistent card with a 1px border and a
   low-spread shadow. The signed-in functional pages — My Requests, Notifications, My Profile, order
   details, the plan request — did not: they rendered a bare <h1> on flat #F8FAFC followed by a single
   unstyled table or one tall white box. That is the gap this file closes.

   Three rules govern everything below.

   1. It reuses the master design system in home-static-port.css (--border, --r-md, --shadow-sm,
      --primary, --text-dim, --bg-soft …) rather than introducing a second palette. Those custom
      properties are declared on :root and that stylesheet loads on every Website page, so a token used
      here always resolves.
   2. Bootstrap does the layout. Rows, columns, gaps, flex and spacing come from Bootstrap utilities in
      the markup; this file only styles what Bootstrap has no opinion about — the hero ground, the card
      chrome, the money ladder, the status pills, the table's mobile form. Anything Bootstrap already
      does well is deliberately absent here.
   3. Direction-neutral. Every offset is a logical property (inline-start/end, margin-inline,
      padding-inline, text-align: start), so Arabic mirrors without a single [dir="rtl"] override. The
      handful of RTL rules that do exist are for glyphs that must physically flip, and they are marked.

   Naming is a flat `portal-*` block so a selector can never collide with the marketing pages' `.card`
   / `.btn`, which site.css and home-static-port.css own.
*/

/* ---------------------------------------------------------------------------- heading scope
 *
 * site.css sizes the Website's headings as `.website-main h1/h2/h3` — a descendant selector, so a bare
 * `.portal-card__title` class loses to it no matter which stylesheet loads last. Every heading class
 * below is therefore written as `.portal .portal-x`, which outranks it. Getting this wrong is not subtle
 * but it is silent: the rule appears in devtools as "matched" while the page renders a 42px card title.
 */

/* ---------------------------------------------------------------------------- page shell */

/* The functional pages sit on a soft gradient ground rather than flat grey, which is what makes the
   white cards read as raised. It is very light on purpose: this is a working surface, not a landing
   page, and a strong wash would fight the data. */
.portal {
    --portal-gap: clamp(1rem, 2.2vw, 1.75rem);

    padding-block: clamp(1.5rem, 3vw, 2.5rem) clamp(3rem, 6vw, 5rem);
    background:
        radial-gradient(1100px 420px at 82% -8%, rgba(20, 184, 166, 0.07), transparent 62%),
        radial-gradient(900px 380px at 12% -12%, rgba(37, 99, 235, 0.09), transparent 60%),
        linear-gradient(180deg, var(--bg-tint) 0%, var(--bg) 34%);
}

.portal-container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

/* ---------------------------------------------------------------------------- breadcrumb */

.portal-crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin-block-end: 1rem;
    color: var(--text-mute);
    font-size: 0.82rem;
}

.portal-crumbs a {
    color: var(--text-mute);
    border-radius: var(--r-xs);
}

.portal-crumbs a:hover {
    color: var(--primary);
    text-decoration: underline;
}

.portal-crumbs__sep {
    color: var(--border-strong);
}

/* ---------------------------------------------------------------------------- page intro */

/* Compact by design. The intro states where you are, what the page is for, and offers the one action
   that belongs at page level — then gets out of the way. It is not a marketing hero and must never
   push the actual content below the fold. */
.portal-hero {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--portal-gap);
    margin-block-end: clamp(1.25rem, 2.5vw, 2rem);
}

.portal-hero__text {
    min-width: 0;
    max-width: 62ch;
}

.portal-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-block-end: 0.6rem;
    padding: 0.28rem 0.7rem;
    border: 1px solid var(--primary-100);
    border-radius: var(--r-full);
    background: var(--primary-50);
    color: var(--primary-700);
    font-size: 0.7rem;
    font-weight: 650;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.portal .portal-hero__title {
    margin: 0;
    color: var(--text);
    font-size: clamp(1.65rem, 3.2vw, 2.35rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-wrap: balance;
}

/* A title that is mostly a machine identifier must not be balanced. `text-wrap: balance` optimises for
   even line lengths rather than for fitting, so it split "Order ORD-20260902191703-02070c16" after the
   first hyphen even though the whole string fitted on one line — which read as a broken heading. */
.portal .portal-hero__title--identifier {
    overflow-wrap: anywhere;
    text-wrap: normal;
}

.portal-hero__lead {
    margin-block-start: 0.5rem;
    color: var(--text-dim);
    font-size: 0.98rem;
    line-height: 1.55;
    text-wrap: pretty;
}

.portal-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    align-items: center;
}

/* ---------------------------------------------------------------------------- stat strip */

/* Only ever rendered from real values. An invented metric is worse than no metric, so there is no
   "placeholder" state here — a page with nothing true to report omits the strip entirely. */
.portal-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 0.75rem;
    margin-block-end: clamp(1.25rem, 2.5vw, 1.75rem);
}

.portal-stat {
    padding: 0.85rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: color-mix(in srgb, var(--card) 88%, transparent);
    box-shadow: var(--shadow-xs);
}

.portal-stat__label {
    display: block;
    color: var(--text-mute);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.portal-stat__value {
    display: block;
    margin-block-start: 0.2rem;
    color: var(--text);
    font-size: 1.4rem;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

/* The one stat that means "you need to do something" earns colour; the rest stay neutral so it reads
   as a signal instead of decoration. */
.portal-stat.is-action {
    border-color: #FDE68A;
    background: #FFFBEB;
}

.portal-stat.is-action .portal-stat__value {
    color: #92400E;
}

/* The actionable stat is a link into the offers area, so it has to stop looking like body copy: no
   underline, whole tile clickable, and a visible focus ring for keyboard users. */
a.portal-stat {
    display: block;
    text-decoration: none;
}

a.portal-stat:hover {
    border-color: #F59E0B;
}

a.portal-stat:focus-visible {
    outline: 2px solid var(--brand, #92400E);
    outline-offset: 2px;
}

.portal-stat__hint {
    display: block;
    margin-block-start: 0.15rem;
    color: #92400E;
    font-size: 0.78rem;
    font-weight: 600;
}

/* ---------------------------------------------------------------------------- card system */

.portal-card {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--card);
    box-shadow: var(--shadow-sm);
}

.portal-card + .portal-card {
    margin-block-start: var(--portal-gap);
}

/* A card whose body IS a table has no inner padding to clip the header strip's corners, so the card
   clips them instead. Declared as an explicit modifier rather than with :has(), so the behaviour is
   visible in the markup and does not depend on selector support. */
.portal-card--table {
    overflow: hidden;
}

.portal-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 1rem clamp(1rem, 2vw, 1.4rem);
    border-block-end: 1px solid var(--border);
}

.portal .portal-card__title {
    margin: 0;
    color: var(--text);
    font-size: 0.95rem;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.portal-card__hint {
    margin: 0.2rem 0 0;
    color: var(--text-mute);
    font-size: 0.82rem;
}

.portal-card__body {
    padding: clamp(1rem, 2vw, 1.4rem);
}

.portal-card__body--flush {
    padding: 0;
}

/* A card that carries the page's headline facts. Slightly warmer ground and no shadow, so it reads as
   part of the page rather than as another panel floating on it. */
.portal-card--feature {
    border-color: var(--primary-100);
    background: linear-gradient(180deg, var(--primary-50) 0%, var(--card) 58%);
    box-shadow: none;
}

/* ---------------------------------------------------------------------------- side rail */

/* The commercial/payment-on-creation flow that originally forced this rail back into normal flow is gone.
   Restore the useful desktop summary stickiness, but never make the rail its own scrolling viewport: page
   scrolling must remain the single scroll chain so keyboard focus and validation recovery cannot be clipped. */
@media (min-width: 992px) {
    .portal-rail {
        position: sticky;
        inset-block-start: calc(var(--website-header-min-height) + var(--website-sticky-gap));
        align-self: start;
    }
}

/* Both native validation and server-rerender recovery land on these targets. The JS measures the live
   sticky header precisely; this scroll margin is the CSS/no-script safety net for fragment/native scrolls. */
.portal-form .input-validation-error,
.portal-form [aria-invalid="true"],
.portal-form .validation-summary-errors,
[data-builder-form] .input-validation-error,
[data-builder-form] [aria-invalid="true"],
[data-builder-form] .validation-summary-errors {
    scroll-margin-block-start: calc(var(--website-header-min-height) + var(--website-sticky-gap));
}

/* ---------------------------------------------------------------------------- definition pairs */

.portal-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 1rem 1.5rem;
    margin: 0;
}

.portal-facts > div {
    min-width: 0;
}

.portal-facts dt {
    margin: 0;
    color: var(--text-mute);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.portal-facts dd {
    margin: 0.25rem 0 0;
    color: var(--text);
    font-size: 0.94rem;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------- money ladder */

/* One shared presentation for every commercial total on the Website: the plan request preview, the
   offer review and the order details rail all render this, so a customer sees the same arithmetic in
   the same shape wherever it appears. Figures are tabular so the column of numbers aligns. */
.portal-money {
    margin: 0;
}

.portal-money__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 0.5rem;
}

.portal-money__row + .portal-money__row {
    border-block-start: 1px solid var(--border);
}

.portal-money__label {
    color: var(--text-dim);
    font-size: 0.88rem;
}

.portal-money__note {
    display: block;
    color: var(--text-mute);
    font-size: 0.76rem;
}

.portal-money__value {
    color: var(--text);
    font-size: 0.94rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.portal-money__row--discount .portal-money__value {
    color: #047857;
}

.portal-money__row--total {
    margin-block-start: 0.35rem;
    padding-block-start: 0.75rem;
    border-block-start: 2px solid var(--text);
}

.portal-money__row--total .portal-money__label {
    color: var(--text);
    font-size: 0.95rem;
    font-weight: 650;
}

.portal-money__row--total .portal-money__value {
    font-size: 1.2rem;
    font-weight: 700;
}

/* ---------------------------------------------------------------------------- status pills */

.portal-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.22rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--r-full);
    background: var(--bg-soft);
    color: var(--text-dim);
    font-size: 0.76rem;
    font-weight: 600;
    line-height: 1.45;
    white-space: nowrap;
}

/* State is carried by a leading dot as well as by colour, so the pills stay distinguishable in
   greyscale and to readers who cannot separate the hues. */
.portal-badge::before {
    flex: 0 0 auto;
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: currentColor;
    content: "";
}

.portal-badge--neutral { border-color: var(--border-strong); background: var(--bg-soft); color: var(--text-dim); }
.portal-badge--info    { border-color: #BAE6FD; background: #F0F9FF; color: #075985; }
.portal-badge--progress{ border-color: var(--primary-100); background: var(--primary-50); color: var(--primary-700); }
.portal-badge--success { border-color: #A7F3D0; background: #ECFDF5; color: #065F46; }
.portal-badge--warning { border-color: #FDE68A; background: #FFFBEB; color: #92400E; }
.portal-badge--danger  { border-color: #FECACA; background: #FEF2F2; color: #991B1B; }

.portal-badge--plain {
    border-color: transparent;
    background: transparent;
    color: var(--text-mute);
    padding-inline: 0;
}

.portal-badge--plain::before {
    display: none;
}

/* ---------------------------------------------------------------------------- data table */

/* The canonical Website list presentation, shared by My Requests and My Notifications.

   Desktop is a real table because these rows are comparable records and a table is the honest way to
   show them. Below 768px the same markup becomes stacked cards: each cell keeps its header as a label
   drawn from data-label, so nothing is lost and nothing scrolls sideways. That is why the mobile form
   needs no separate markup and no duplicated Razor. */
.portal-table-wrap {
    overflow-x: auto;
}

.portal-table {
    inline-size: 100%;
    margin: 0;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.portal-table thead th {
    padding: 0.7rem clamp(0.75rem, 1.6vw, 1.15rem);
    border-block-end: 1px solid var(--border);
    background: var(--bg-soft);
    color: var(--text-mute);
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-align: start;
    text-transform: uppercase;
    white-space: nowrap;
}

.portal-table tbody td {
    padding: 0.85rem clamp(0.75rem, 1.6vw, 1.15rem);
    border-block-end: 1px solid var(--border);
    color: var(--text-dim);
    text-align: start;
    vertical-align: middle;
}

.portal-table tbody tr:last-child td {
    border-block-end: 0;
}

.portal-table tbody tr {
    transition: background-color 0.15s ease;
}

.portal-table tbody tr:hover {
    background: color-mix(in srgb, var(--primary-50) 55%, transparent);
}

.portal-table__primary {
    display: block;
    color: var(--text);
    font-weight: 650;
    letter-spacing: -0.01em;
    overflow-wrap: anywhere;
}

.portal-table__sub {
    display: block;
    margin-block-start: 0.15rem;
    color: var(--text-mute);
    font-size: 0.8rem;
}

.portal-table__num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.portal-table__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    justify-content: flex-end;
}

/* An unread row is emphasised by weight, a left rail and a very light wash — enough to find at a
   glance, far short of turning every row into a filled blue panel. */
.portal-table tbody tr.is-unread {
    background: color-mix(in srgb, var(--primary-50) 62%, transparent);
    box-shadow: inset 3px 0 0 0 var(--primary);
}

/* The rail is drawn with an inset shadow so it follows the writing direction: in Arabic the same
   declaration must sit on the right edge. */
[dir="rtl"] .portal-table tbody tr.is-unread {
    box-shadow: inset -3px 0 0 0 var(--primary);
}

.portal-table tbody tr.is-unread .portal-table__primary {
    font-weight: 700;
}

@media (max-width: 767.98px) {
    .portal-table-wrap {
        overflow-x: visible;
    }

    .portal-table thead {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .portal-table,
    .portal-table tbody,
    .portal-table tbody tr,
    .portal-table tbody td {
        display: block;
        inline-size: auto;
    }

    .portal-table tbody tr {
        padding: 0.9rem 1rem;
        border-block-end: 1px solid var(--border);
    }

    .portal-table tbody tr:last-child {
        border-block-end: 0;
    }

    .portal-table tbody td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.28rem 0;
        border-block-end: 0;
    }

    /* The column heading returns as an inline label, so a stacked row still says what each value is. */
    .portal-table tbody td[data-label]::before {
        flex: 0 0 auto;
        color: var(--text-mute);
        font-size: 0.74rem;
        font-weight: 650;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        content: attr(data-label);
    }

    /* The record's own identity spans the row instead of sitting opposite a label. */
    .portal-table tbody td.portal-table__lead {
        display: block;
        padding-block-end: 0.5rem;
    }

    .portal-table tbody td.portal-table__lead::before {
        display: none;
    }

    /* The action cell has no field name to show, and its button should span the stacked row rather than
       sit opposite an empty label. */
    .portal-table tbody td.portal-table__actions-cell {
        display: block;
        padding-block-start: 0.6rem;
    }

    .portal-table tbody td.portal-table__actions-cell::before {
        display: none;
    }

    .portal-table__actions {
        justify-content: stretch;
    }

    .portal-table__actions > * {
        flex: 1 1 auto;
        text-align: center;
    }
}

/* ---------------------------------------------------------------------------- stacked rows */

/* Repeated blocks inside a card body (order lines, for instance) are separated by a rule, but the last
   one must not be, or the card closes with a line floating above its bottom padding. */
.portal-card__body > .portal-stack:last-child {
    margin-block-end: 0;
    padding-block-end: 0;
    border-block-end: 0;
}

.portal-stack {
    margin-block-end: 1rem;
    padding-block-end: 1rem;
    border-block-end: 1px solid var(--border);
}

/* ---------------------------------------------------------------------------- timeline */

.portal-timeline {
    display: grid;
    gap: 0.9rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.portal-timeline li {
    position: relative;
    padding-inline-start: 1.5rem;
    color: var(--text-dim);
}

.portal-timeline li::before {
    position: absolute;
    inset-block-start: 0.3rem;
    inset-inline-start: 0;
    inline-size: 9px;
    block-size: 9px;
    border: 2px solid var(--primary);
    border-radius: 50%;
    background: var(--card);
    content: "";
}

/* The connector joins one dot to the next, so it is drawn on every entry except the last. */
.portal-timeline li:not(:last-child)::after {
    position: absolute;
    inset-block: 1.05rem -0.6rem;
    inset-inline-start: 4px;
    inline-size: 1px;
    background: var(--border);
    content: "";
}

.portal-timeline__label {
    display: block;
    color: var(--text);
    font-size: 0.9rem;
    font-weight: 600;
}

.portal-timeline__time {
    display: block;
    margin-block-start: 0.1rem;
    color: var(--text-mute);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------- empty state */

.portal-empty {
    padding: clamp(2rem, 5vw, 3.25rem) 1.25rem;
    text-align: center;
}

.portal-empty__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 46px;
    block-size: 46px;
    margin-block-end: 0.9rem;
    border: 1px solid var(--primary-100);
    border-radius: var(--r-md);
    background: var(--primary-50);
    color: var(--primary-700);
}

.portal .portal-empty__title {
    margin: 0;
    color: var(--text);
    font-size: 1.02rem;
    font-weight: 650;
}

.portal-empty__text {
    margin: 0.4rem auto 1.1rem;
    max-width: 46ch;
    color: var(--text-dim);
    font-size: 0.9rem;
}

/* ---------------------------------------------------------------------------- forms */

/* Bootstrap owns the control itself. These rules only set the label/help hierarchy and the focus ring
   in the site's own accent, so a Website form does not read as a stock Bootstrap form. */
.portal-field {
    margin-block-end: 1rem;
}

.portal-label {
    display: block;
    margin-block-end: 0.35rem;
    color: var(--text);
    font-size: 0.84rem;
    font-weight: 600;
}

.portal-label__optional {
    margin-inline-start: 0.35rem;
    color: var(--text-mute);
    font-size: 0.78rem;
    font-weight: 500;
}

.portal-help {
    margin-block-start: 0.35rem;
    color: var(--text-mute);
    font-size: 0.8rem;
}

.portal-form .form-control,
.portal-form .form-select {
    border-color: var(--border-strong);
    border-radius: var(--r-sm);
    color: var(--text);
    font-size: 0.92rem;
}

.portal-form .form-control:focus,
.portal-form .form-select:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-100);
}

.portal-form .form-control::placeholder {
    color: var(--text-mute);
}

/* ---------------------------------------------------------------------------- section heads */

.portal .portal-section-title {
    margin: 0 0 0.25rem;
    color: var(--text);
    font-size: 1.05rem;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.portal-section-lead {
    margin: 0 0 1.1rem;
    color: var(--text-dim);
    font-size: 0.88rem;
}

/* ---------------------------------------------------------------------------- choice cards */

/* Used for the subscription-duration options. The whole card is the label, so the entire block is a
   click/tap target rather than just the radio dot. */
.portal-choice {
    position: relative;
    display: block;
    padding: 0.95rem 1.1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--card);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}

.portal-choice:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-xs);
}

.portal-choice:has(:checked) {
    border-color: var(--primary);
    background: var(--primary-50);
    box-shadow: 0 0 0 1px var(--primary);
}

.portal-choice:has(:focus-visible) {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.portal-choice__input {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.portal-choice__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

/* The parts of a choice card are spans inside a <label>, so each one has to be told to occupy its own
   line. Without this the price, the struck original and the per-month caption ran together as a single
   sentence — "2999.00 EGP Works out to 2999.00 EGP per month". */
.portal-choice__price,
.portal-choice__was,
.portal-choice__foot,
.portal-choice__save {
    display: block;
}

.portal-choice__name {
    color: var(--text);
    font-size: 0.92rem;
    font-weight: 650;
}

.portal-choice__meta {
    color: var(--text-mute);
    font-size: 0.78rem;
}

.portal-choice__price {
    margin-block-start: 0.35rem;
    color: var(--text);
    font-size: 1.3rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

.portal-choice__price small {
    color: var(--text-mute);
    font-size: 0.72rem;
    font-weight: 600;
}

.portal-choice__was {
    color: var(--text-mute);
    font-size: 0.8rem;
    text-decoration: line-through;
}

.portal-choice__foot {
    margin-block-start: 0.3rem;
    color: var(--text-dim);
    font-size: 0.79rem;
}

.portal-choice__save {
    display: inline-block;
    margin-block-start: 0.5rem;
    padding: 0.15rem 0.5rem;
    border-radius: var(--r-full);
    background: #ECFDF5;
    color: #065F46;
    font-size: 0.72rem;
    font-weight: 650;
}

/* ---------------------------------------------------------------------------- capability rows */

.portal-spec {
    display: grid;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.portal-spec li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 0.5rem;
    border-block-end: 1px solid var(--border);
    font-size: 0.87rem;
}

.portal-spec li:last-child {
    border-block-end: 0;
}

.portal-spec__name {
    color: var(--text-dim);
}

.portal-spec__value {
    color: var(--text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* An entitlement row: the friendly name over a one-line explanation, value and reset period on the end side. */
.portal-spec--limits li {
    align-items: center;
    padding-block: 0.7rem;
}

.portal-spec__title {
    display: block;
    color: var(--text);
    font-weight: 600;
}

.portal-spec__desc {
    display: block;
    margin-block-start: 0.15rem;
    color: var(--text-mute);
    font-size: 0.78rem;
    line-height: 1.4;
}

.portal-spec--limits .portal-spec__value {
    text-align: end;
}

.portal-spec__yes {
    color: #047857;
    font-weight: 650;
}

/* ---------------------------------------------------------------------------- inline notice */

/* Kept for messages that must stay on screen: a validation summary, or a result the reader has to be
   able to re-read. Transient confirmations use the toast instead. */
.portal-notice {
    display: flex;
    gap: 0.65rem;
    align-items: flex-start;
    padding: 0.85rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--card);
    color: var(--text);
    font-size: 0.9rem;
    line-height: 1.5;
    text-wrap: pretty;
}

.portal-notice--ok   { border-color: #A7F3D0; background: #ECFDF5; color: #065F46; }
.portal-notice--bad  { border-color: #FECACA; background: #FEF2F2; color: #991B1B; }
.portal-notice--warn { border-color: #FDE68A; background: #FFFBEB; color: #92400E; }

.portal-notice__icon {
    flex: 0 0 auto;
    margin-block-start: 0.1rem;
}

/* ---------------------------------------------------------------------------- utilities */

.portal-divider {
    margin-block: 1.1rem;
    border: 0;
    border-block-start: 1px solid var(--border);
    opacity: 1;
}

.portal-muted {
    color: var(--text-mute);
    font-size: 0.82rem;
}

/* Respect a reader who has asked for less motion: the hover transitions above are decorative. */
@media (prefers-reduced-motion: reduce) {
    .portal-table tbody tr,
    .portal-choice {
        transition: none;
    }
}

/* ============================================================================================
   Pricing page refinements
   ============================================================================================

   Pricing was already the closest page to the product's visual language — it is where the eyebrow,
   centred display heading and card system came from — so this is a refinement pass rather than a
   redesign. Three specific things were wrong.

   Everything here is scoped to `.pricing-hero` / `.pricing-plans`, because `.price-card`, `.price` and
   `.grid.cols-3` are shared with the Home page and must not change there.
*/

/* 1. The hero sat on flat #F8FAFC while every other headline surface on the site has some atmosphere.
      Same two-source wash the portal pages use, so the two halves of the site agree. */
.pricing-hero {
    background:
        radial-gradient(1100px 460px at 78% -10%, rgba(20, 184, 166, 0.08), transparent 60%),
        radial-gradient(900px 420px at 15% -12%, rgba(37, 99, 235, 0.10), transparent 58%),
        linear-gradient(180deg, var(--bg-tint) 0%, var(--bg) 46%);
}

/* 2. The cards were as tall as their own content, so three cards with 2, 3 and 4 features each ended at
      three different heights and the CTAs did not line up. Stretching the row and making the card a
      flex column lets the existing `u-mta` on the CTA push it to a common baseline. */
.pricing-plans .grid.cols-3 {
    align-items: stretch;
}

.pricing-plans .price-card {
    display: flex;
    flex-direction: column;
    block-size: 100%;
}

.pricing-plans .price-card .feat {
    flex: 1 1 auto;
}

/* 3. "Contact Sales" was rendered in the same class as a numeric price, so a 12-character phrase came
      out at display size and towered over the "899.00" beside it. It is a label, not a figure: sized as
      one, it sits on the same line as the prices instead of breaking the row's rhythm. */
.pricing-plans .price-card__price--label .price,
.pricing-plans .price-card .price:not(:has(small)):not([data-effective-value]) {
    font-size: 1.35rem;
    font-weight: 650;
    letter-spacing: -0.01em;
}
