/* =============================================================================
   MINET BUY ONLINE — client portal
   The screens a customer sees after they have bought: sign-in and code, the
   dashboard, a policy, claims, a claim, and reporting one
   (resources/views/portal/).

   ONE OF THREE, LOADED IN THIS ORDER ON EVERY PAGE (layouts/shop.blade.php):
       base.css  →  shop.css  →  portal.css
   Anything the storefront also renders belongs in base.css, not here.

   Every colour, font, size and radius resolves to a token from
   brand/minet-tokens.css (served at /brand/minet-tokens.css). There is not one
   hex literal in this file, by rule — skill: minet-brand.
   ========================================================================== */


/* ---------- Client portal ----------

   The portal is not the funnel. There is no step, no progress and nothing to
   finish — a customer arrives to answer one question and leave, so the chrome
   is quieter than the journey's and the hierarchy is ordered by what is waiting
   on them rather than by what we have to show. */

.portal-head {
    margin-top: var(--space-8);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--minet-line);
}
.portal-head__row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-4);
}
.portal-head__title {
    margin: var(--space-2) 0 0;
    font-family: var(--font-display);
    font-size: var(--fs-h2);
    color: var(--minet-ink);
}
.portal-head__out { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); }
/* An address is one long unbreakable token to a browser. Without this a
   fifty-character one pushed the Sign out button off the side of a phone —
   the same fault the compare screen shipped with a nowrap price. */
.portal-head__who {
    font-size: var(--fs-small);
    color: var(--minet-muted);
    overflow-wrap: anywhere;
    min-width: 0;
}
.portal-head__out .btn { flex: none; }
/* The answer to "am I covered?", said in words at the top of the dashboard
   rather than left to be inferred from whether a card happens to be below the
   fold. The icon is reinforcement; the sentence carries the meaning. */
.portal-state {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: var(--space-6) 0 0;
    font-size: var(--fs-body);
    color: var(--minet-ink);
}
.portal-state .icon { flex: none; margin-top: var(--space-1); color: var(--minet-red); }
.portal-state--none .icon { color: var(--minet-muted); }
.portal-state strong { font-weight: 700; }
.portal-state__detail { display: block; font-size: var(--fs-small); color: var(--minet-muted); }
.btn--small {
    height: auto;
    padding: var(--space-2) var(--space-4);
    font-size: var(--fs-small);
}
.portal-nav { display: flex; flex-wrap: wrap; gap: var(--space-6); margin-top: var(--space-6); }
/* A tab, not a word: these were 32px tall, under the 44px the brand guide
   requires of anything a thumb has to find (minet-brand §7). The underline
   still sits on the bottom edge, so nothing moves visually on a desktop. */
.portal-nav__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target-min);
    padding-bottom: var(--space-2);
    border-bottom: 2px solid transparent;
    color: var(--minet-text);
    font-size: var(--fs-small);
    font-weight: 600;
    text-decoration: none;
}
.portal-nav__link:hover { color: var(--minet-ink); }
.portal-nav__link.is-current { border-bottom-color: var(--minet-red); color: var(--minet-ink); }
/* Sign-in and code entry. Narrow on purpose: one field, one decision. */
.portal-gate { max-width: 460px; margin: var(--space-12) auto; }
.portal-gate__title {
    margin: var(--space-2) 0 var(--space-3);
    font-family: var(--font-display);
    font-size: var(--fs-h2);
    color: var(--minet-ink);
}
.portal-gate__lede { margin: 0 0 var(--space-8); color: var(--minet-text); }
.portal-gate__form { margin-bottom: var(--space-6); }
.portal-gate__note { margin: 0; font-size: var(--fs-small); color: var(--minet-muted); }
/* A code is read off a notification and typed in one go — spaced out so a
   mistyped digit is visible before it is submitted. */
.control .input--code {
    font-size: var(--fs-h3);
    font-weight: 700;
    letter-spacing: .5em;
    /* letter-spacing adds its space AFTER the last glyph too, so a centred run
       sits half a space left of centre. The indent gives it back. */
    text-indent: .5em;
    text-align: center;
}
.portal-lede { margin: var(--space-8) 0; }
.portal-lede p { margin: 0 0 var(--space-3); color: var(--minet-text); }
.portal-lede__quiet { font-size: var(--fs-small); color: var(--minet-muted); }
/* What is waiting on the customer. Rendered only when there is something —
   a panel that is usually empty teaches people to stop reading it. */
.attention { display: grid; gap: var(--space-4); margin-top: var(--space-8); }
.attention__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding: var(--space-6);
    border-left: 4px solid var(--minet-line-strong);
    border-radius: var(--radius);
    background: var(--minet-surface);
}
/*
 * --warn and --info had been emptied out and left in the markup, so a claim
 * the assessor is waiting on rendered identically to a renewal reminder: two
 * flat grey boxes, neither of them urgent. The rule, and the ground, are
 * reinstated — and each item carries an icon and a sentence, because a
 * difference a colour-blind reader cannot see is not a difference.
 */
.attention__item--warn {
    border-left-color: var(--minet-warning);
    background: var(--minet-warning-tint);
}
.attention__item--info {
    border-left-color: var(--minet-info);
    background: var(--minet-info-tint);
}
/* `min-width: 0` or a flex item will not shrink below its longest unbreakable
   word, and this one renders a configured mailbox address. Same fault, same
   fix, as `.portal-head__who`. */
.attention__lead { display: flex; align-items: flex-start; gap: var(--space-3); min-width: 0; }
.attention__icon { flex: none; margin-top: var(--space-1); color: var(--minet-muted); }
.attention__item--warn .attention__icon { color: var(--minet-warning); }
.attention__item--info .attention__icon { color: var(--minet-info); }
/* The size is declared, not inherited from the element. This rule is worn by a
   <p> on the dashboard and an <h2> on the claim screen — the heading the
   `aria-labelledby` section needs — and with no `font-size` the <h2> took the
   browser's 1.5em and rendered one class at two sizes, larger than the
   `.panel__title` beneath it. */
.attention__title { margin: 0 0 var(--space-1); font-size: var(--fs-body); font-weight: 700; color: var(--minet-ink); }
.attention__body {
    margin: 0 0 var(--space-2);
    font-size: var(--fs-small);
    color: var(--minet-text);
    overflow-wrap: anywhere;
}
.attention__body:last-child { margin-bottom: 0; }
.portal-section { margin-top: var(--space-12); }
.portal-section__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
}
.portal-section__title {
    margin: 0 0 var(--space-6);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--minet-ink);
}
.portal-section__title--quiet { font-size: var(--fs-body); color: var(--minet-muted); }
/* Policy cards */
/* `minmax(320px, …)` keeps its 320px floor even when the column is narrower,
   so on a 360px Android the card was 8px wider than every heading above it and
   below ~368px the page scrolled sideways. `min(100%, 320px)` lets the floor
   collapse to the column. */
.pcards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
    gap: var(--space-6);
    margin: 0;
    padding: 0;
    list-style: none;
}
.pcards--quiet .pcard { background: transparent; }
.pcard {
    display: flex;
    flex-direction: column;
    padding: var(--space-6);
    border: 1px solid var(--minet-line);
    border-radius: var(--radius);
    background: var(--minet-white);
}
.pcard__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.pcard__product { margin: 0; font-weight: 700; color: var(--minet-ink); }
.pcard__plan { margin: var(--space-1) 0 0; font-size: var(--fs-small); color: var(--minet-muted); }
/*
 * Was amber text on white: 4.24:1 at 14px, which fails AA, and the urgency was
 * carried by the colour alone. It is now a flagged line — amber rule, amber
 * ground, ink text, clock — so it survives both a colour-blind reader and a
 * printed page.
 */
.pcard__expiry {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: 0 0 var(--space-4);
    padding: var(--space-2) var(--space-3);
    border-left: 3px solid var(--minet-warning);
    background: var(--minet-warning-tint);
    font-size: var(--fs-small);
    color: var(--minet-ink);
}
.pcard__expiry .icon { flex: none; margin-top: var(--space-1); color: var(--minet-warning); }
.pcard__expiry > span { min-width: 0; overflow-wrap: anywhere; }
.pcard__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: auto; }
/* Claims */
.claim-rows { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--space-4); }
.claim-rows--quiet .claim-row { background: transparent; }
.claim-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-6);
    padding: var(--space-6);
    border: 1px solid var(--minet-line);
    border-radius: var(--radius);
    background: var(--minet-white);
}
/* Emptied out and left in the markup, like the attention tones above: the one
   claim the customer has to act on was indistinguishable from the ones they
   do not. The rule brings it back; "We need:" below carries it in words. */
.claim-row--wants-you { border-left: 4px solid var(--minet-warning); }
.claim-row__main { flex: 1 1 320px; min-width: 0; }
.claim-row__kind { margin: 0; font-weight: 700; color: var(--minet-ink); }
.claim-row__meta { margin: var(--space-1) 0 var(--space-3); font-size: var(--fs-legal); color: var(--minet-muted); }
.claim-row__state { margin: 0; font-size: var(--fs-small); color: var(--minet-text); }
.claim-row__wanted {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    padding: var(--space-2) var(--space-3);
    background: var(--minet-warning-tint);
    font-size: var(--fs-small);
    color: var(--minet-ink);
}
.claim-row__wanted .icon { flex: none; margin-top: var(--space-1); color: var(--minet-warning); }
.claim-row__wanted > span { min-width: 0; overflow-wrap: anywhere; }
.claim-row__wanted strong { color: var(--minet-ink); }
.claim-row__side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-2);
    text-align: right;
}
.claim-row__amount { margin: 0; font-size: var(--fs-small); font-weight: 700; color: var(--minet-ink); }
/* One policy */
.policy {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: var(--space-8);
    margin-top: var(--space-8);
    align-items: start;
}
.policy__main { display: grid; gap: var(--space-6); }
/* `.policy { align-items: start }` left this column exactly as tall as the box
   inside it, so the box had no room to stick and the sticky did nothing.
   Stretching the COLUMN (not the box) gives it the travel it needs. */
.policy__side { align-self: stretch; }
.policy__box {
    position: sticky;
    top: var(--space-8);
    display: grid;
    gap: var(--space-3);
    padding: var(--space-6);
    border: 1px solid var(--minet-line);
    border-radius: var(--radius);
    background: var(--minet-surface);
}
.policy__box-note { margin: 0; font-size: var(--fs-small); color: var(--minet-muted); }
@media (max-width: 1024px) {
    .policy { grid-template-columns: 1fr; }
    .policy__side { position: static; }
    .policy__box { position: static; }}
.portal-empty {
    margin: var(--space-12) 0;
    padding: var(--space-12) var(--space-8);
    border: 1px solid var(--minet-line);
    border-radius: var(--radius);
    text-align: center;
}
.portal-empty__title {
    margin: 0 0 var(--space-3);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--minet-ink);
}
.portal-empty__body { margin: 0 auto var(--space-6); max-width: 48ch; color: var(--minet-text); }
.portal-empty__actions { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--space-3); }
/*
 * The portal on a phone.
 *
 * A customer on mobile data in Nairobi was given 80px of blank space, a 32px
 * greeting, their own email address and a sign-out button before a single word
 * about whether they are covered — the answer they opened the portal for was
 * never on the first screen. This is the portal's own breakpoint: the journey's
 * `.step` padding is shared and is left where it is.
 */
@media (max-width: 720px) {
    .portal-head { margin-top: 0; }
    .portal-head__row { align-items: flex-start; gap: var(--space-3); }
    .portal-head__title { font-size: var(--fs-h3); }
    .portal-head__out { width: 100%; justify-content: space-between; }
    .portal-nav { gap: var(--space-4); margin-top: var(--space-3); }
    .portal-state { margin-top: var(--space-4); }

    .portal-section { margin-top: var(--space-8); }
    .portal-section__title { margin-bottom: var(--space-4); }

    .portal-gate { margin: var(--space-6) auto; }
    .portal-empty { margin: var(--space-8) 0; padding: var(--space-8) var(--space-4); }

    .attention { margin-top: var(--space-6); }
    .attention__item { padding: var(--space-4); }
    /* A full-width target beats a 120px one held in one hand. */
    .attention__item > .btn { width: 100%; }

    /* Badge, amount and link were a right-aligned column floating beside a
       paragraph they belong to. On one line under it, they read as its tail. */
    .claim-row { padding: var(--space-4); gap: var(--space-4); }
    .claim-row__side {
        width: 100%;
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        gap: var(--space-4);
        text-align: left;
    }

    .pcard { padding: var(--space-4); }
    .pcard__actions .btn { flex: 1 1 auto; }}
/*
 * What went wrong, at the top, before the form.
 *
 * Reporting a claim is the longest screen in the portal and the one most often
 * filled in on a phone. A rejected submission reloaded at the top of the page
 * with every message sitting beside its own field, several screens down — so
 * the only signal that anything had failed was that nothing had happened. This
 * is focusable and announced, and each line jumps to the field it is about.
 */
.portal-errors {
    margin: 0 0 var(--space-6);
    padding: var(--space-4) var(--space-6);
    border-left: 4px solid var(--minet-danger);
    border-radius: var(--radius);
    background: var(--minet-danger-tint);
}
.portal-errors__title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
    font-weight: 700;
    color: var(--minet-ink);
}
.portal-errors__title .icon { flex: none; color: var(--minet-danger); }
.portal-errors__list { margin: 0; padding-left: var(--space-6); font-size: var(--fs-small); color: var(--minet-ink); }
.portal-errors__list li { margin-bottom: var(--space-1); }
.portal-errors__list li:last-child { margin-bottom: 0; }
/* One link per line and nothing else on it — a target, not a word inside
   prose, and the error-recovery control on the longest form in the portal.
   `.linklike` already carries the same minimum for the same reason. */
.portal-errors__list a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target-min);
    color: var(--minet-ink);
}
/* It takes the focus on a failed submit, and `:focus-visible` is not reliably
   set on a programmatic focus — so the ring is drawn on plain `:focus` too. */
.portal-errors:focus,
.portal-errors:focus-visible,
.portal-focusable:focus,
.portal-focusable:focus-visible {
    outline: var(--focus-ring);
    outline-offset: var(--focus-offset);
}
