h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}
/* --------------------------------------------------------------------------
   Brand palette, aligned to Agra.Ops.

   The blues and the orange are Agra.Ops's, so the two apps read as one product:

     --agra-primary is Agra.Ops's --rz-primary / --rz-header-background-color
     (#2b579a), with its --rz-primary-light and --rz-on-primary-lighter as the
     tint and shade. This replaces Ionic's stock #3880ff, which the legacy app
     used only because it never overrode --ion-color-primary.

     --agra-accent is Agra.Ops's --rz-link-color (#eb7413) — the orange on its
     dialog headers and links.

     --agra-brand-* stay sampled from logo.png: they are what the *logo* is,
     which is not the same thing as what the UI is painted in.
   (Agra.Ops/wwwroot/app.css; mwpms-mobile-ordering/src/theme/variables.scss)
   -------------------------------------------------------------------------- */
:root {
    --agra-primary: #2b579a;
    --agra-primary-shade: #154b9e;
    --agra-primary-tint: #3d6fb5;
    --agra-primary-contrast: #ffffff;

    --agra-accent: #eb7413;

    --agra-brand-blue: #2450A6;
    --agra-brand-orange: #f25922;

    --agra-secondary: #3dc2ff;
    --agra-secondary-shade: #36abe0;
    --agra-secondary-tint: #50c8ff;

    --agra-success: #2dd36f;
    --agra-warning: #ffc409;
    --agra-danger: #eb445a;
    --agra-danger-shade: #cf3c4f;

    /* Ionic's neutral steps, for the greys the ported screens lean on. */
    --agra-text: #222428;
    --agra-text-muted: #666666;
    --agra-hairline: rgb(0 0 0 / 13%);
    --agra-item-divider-bg: #f9f9f9;

    /* Chrome vs. content. Both bars are the Agra.Ops header blue carrying light
       content; everything between them is Agra.Ops's page grey (--rz-base-100)
       so the white cards read as raised off the page rather than merging into
       it. */
    --agra-bar-bg: var(--agra-primary);
    --agra-bar-fg: #ffffff;
    --agra-bar-fg-muted: rgb(255 255 255 / 72%);
    --agra-bar-hairline: rgb(255 255 255 / 18%);
    --agra-bar-hover: rgb(255 255 255 / 14%);
    /* The accent orange lightened for use *on* the bar. #eb7413 on this blue is about 2.4:1 —
       below the 4.5:1 a 14px label needs — so the hue is kept and the value raised until it
       clears: this reads as orange and measures 4.6:1. */
    --agra-bar-title: #ffc48f;
    --agra-surface: #f5f5f5;

    /* Radzen's own theme tokens, pointed at the palette above.

       Anything Radzen draws for itself — the Confirm dialog's OK button, a
       dropdown's highlighted row, a progress bar — reads --rz-primary, not
       --agra-primary, so without this the stock Material indigo turns up in the
       middle of an otherwise blue app. Agra.Ops overrides the same four; this is
       that, sourced from one place so the two cannot drift.

       --rz-link-color is deliberately not overridden. Agra.Ops sets it to the
       orange, but #eb7413 on white is about 3:1 — under the 4.5:1 body text
       needs — and links here are body-sized. */
    --rz-primary: var(--agra-primary) !important;
    --rz-primary-light: var(--agra-primary-tint) !important;
    --rz-primary-lighter: rgb(43 87 154 / 16%) !important;
    --rz-on-primary-lighter: var(--agra-primary-shade) !important;

    /* Safe-area insets so the bottom tab bar clears the iOS home indicator. */
    --agra-safe-bottom: env(safe-area-inset-bottom, 0px);
    --agra-safe-top: env(safe-area-inset-top, 0px);
}

html, body {
    /* Phones only — stop horizontal drift from any oversized child. */
    overflow-x: hidden;
    color: var(--agra-text);
    background: var(--agra-surface);
    /* Agra.Ops sets body's font from --rz-text-font-family (Roboto); this app
       never set one at all, so every screen fell back to the browser's serif.
       Roboto first to match Ops, then the platform UI face — nothing here loads
       a webfont, so on iOS this resolves to San Francisco rather than Times. */
    font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI",
                 "Helvetica Neue", Arial, sans-serif;
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

/* The signed-in shell is an app frame, not a document: a fixed-height column whose middle row is
   the only thing that scrolls. The toolbar was sticky before, which pins it but still lets it
   participate in the page scroll — the bar drifts a pixel or two under a rubber-band overscroll,
   and on iOS the address bar collapsing re-lays-out the whole page under it.

   No safe-area padding here any more. The toolbar already pads itself by the top inset and the tab
   bar by the bottom one, so adding it to the shell as well counted both twice on a notched device. */
.agra-mobile-shell {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    background: var(--agra-surface);
}

/* Row one: fixed height, never scrolls. Scoped to this shell so the auth pages, which are ordinary
   scrolling documents, keep the sticky toolbar they were written for. */
.agra-mobile-shell .agra-toolbar {
    flex: 0 0 auto;
    position: static;
}

/* Row two: the only scroll container on the page. min-height:0 is what actually lets it shrink —
   a flex item's default min-height is auto, so without it the content grows the column past the
   viewport and the whole page scrolls again instead. */
.agra-mobile-shell .agra-content {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Stops a scroll that reaches the end of this list from chaining to the page behind it. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

/* --------------------------------------------------------------------------
   Account screens (sign in, sign up, password reset, delete account).

   These reproduce the Ionic pages they replace — ion-header toolbar,
   ion-card, ion-item rows with a hairline underline, ion-button expand="block"
   — at Ionic's Material Design metrics, so a customer who has used the old app
   recognises the screen. Source: mwpms-mobile-ordering/src/app/auth/*.

   Hand-written rather than Radzen form components because these pages render
   in static SSR — they have to post a real form so Identity can set the auth
   cookie, and Radzen's inputs are built for an interactive circuit.
   -------------------------------------------------------------------------- */

.agra-auth-shell {
    min-height: 100dvh;
    background: var(--agra-surface);
    display: flex;
    flex-direction: column;
}

/* ion-header / ion-toolbar, at Ionic's MD metrics but in the Agra.Ops header
   blue with light content rather than Ionic's stock white-on-white. */
.agra-toolbar {
    display: flex;
    align-items: center;
    min-height: 56px;
    padding-top: env(safe-area-inset-top);
    background: var(--agra-bar-bg);
    color: var(--agra-bar-fg);
    box-shadow: 0 2px 4px rgb(0 0 0 / 7%), 0 1px 1px rgb(0 0 0 / 5%);
    position: sticky;
    top: 0;
    z-index: 10;
}

.agra-toolbar-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    color: var(--agra-bar-fg);
    text-decoration: none;
    /* The Ionic markup set color="light" here, which on its white toolbar drew a
       near-white arrow on white. Against the blue bar that intent finally works. */
    flex: 0 0 auto;
}

.agra-toolbar-back:hover { background: var(--agra-bar-hover); }

/* The bar is brand chrome: the head mark on the left, the page name centred. Both side slots are
   the same fixed width whether or not they hold anything, which is what keeps the centred title
   centred on the bar rather than on the space left beside a button. */
.agra-toolbar-slot {
    flex: 0 0 58px;
    box-sizing: border-box;
    padding-left: 12px;
    display: flex;
    align-items: center;
    justify-content: flex-start;
}

.agra-toolbar-slot-end {
    justify-content: flex-end;
    padding-left: 0;
    padding-right: 12px;
}

/* The Agra.Ops head mark, straight onto the bar. Its outer ring is the brand blue and does drop out
   against the blue, but the orange head is the readable part and carries the mark on its own — a
   white disc behind it read as a sticker stuck on the bar. */
.agra-toolbar-brand {
    width: 34px;
    height: 34px;
    display: block;
}

.agra-toolbar-title {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0 8px;
    text-align: center;
    font-size: 14px;
    /* Light and widely tracked: at this size the letterspacing is what makes it read as a mark
       rather than as a heading competing with the page's own. */
    font-weight: 400;
    letter-spacing: .16em;
    text-transform: uppercase;
    line-height: 1.2;
    color: var(--agra-bar-title);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* ion-content.ion-padding */
.agra-content {
    flex: 1 1 auto;
    padding: 16px;
    padding-bottom: max(16px, env(safe-area-inset-bottom));
}

/* ion-card (Material elevation 1) */
.agra-card {
    max-width: 30rem;
    margin: 12px auto;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 3px 1px -2px rgb(0 0 0 / 20%),
                0 2px 2px 0 rgb(0 0 0 / 14%),
                0 1px 5px 0 rgb(0 0 0 / 12%);
    overflow: hidden;
}

.agra-card-header { padding: 16px; }

.agra-card-header-center { text-align: center; }

.agra-card-title {
    margin: 0;
    font-size: 20px;
    font-weight: 500;
    line-height: 1.2;
    color: var(--agra-text);
}

.agra-card-subtitle {
    margin: 4px 0 0;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: .0071em;
    line-height: 1.4;
    color: var(--agra-text-muted);
}

.agra-card-content { padding: 13px 16px 16px; }

/* The title block of a card in a list — a tank on Your Tanks, an order on Order History. Both
   pages had the same flat stack: a 20px near-black title over subtitles set at the same weight as
   each other, so nothing in the card told you where to look first.

   The rule down the left is the accent Agra.Ops puts along the top of a dialog, turned vertical and
   sized to the title line alone. It marks where each card starts without adding another box, which
   is what these lists need — a border per card would just restate the card's own edge. Applied by
   opting in, so the greeting card, which is not an item in a list, keeps the plain title. */
.agra-card-titled {
    position: relative;
    padding-left: 12px;
}

.agra-card-titled::before {
    content: "";
    position: absolute;
    left: 0;
    top: 3px;
    /* The title line only. Running it the full height of the block turns a mark into a sidebar. */
    height: 1.05em;
    width: 3px;
    border-radius: 2px;
    background: var(--agra-accent);
}

.agra-card-titled .agra-card-title {
    font-size: 17px;
    font-weight: 600;
    /* Tightened: at 600 the stock spacing reads loose at this size. */
    letter-spacing: -.01em;
    color: var(--agra-primary);
}

/* Lighter and a step smaller than the title, so the card has three levels — mark, name, detail —
   rather than two weights of the same thing. */
.agra-card-titled .agra-card-subtitle {
    margin-top: 3px;
    font-size: 13px;
    font-weight: 400;
    color: var(--agra-text-muted);
}

/* A field label: small, upper, muted. Started in the order form and is the treatment for any
   control that needs naming. Shared rather than scoped so a second user of it cannot drift. */
.agra-field-label {
    display: block;
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--agra-text-muted);
}

/* A count that repeats on every row in a list reads as more body copy when it
   is set as one. This gives it a shape instead, without making it loud. */
.agra-count-badge {
    display: inline-block;
    margin: 8px 0 0;
    padding: 2px 8px;
    border-radius: 10px;
    background: var(--agra-surface);
    border: 1px solid var(--agra-hairline);
    font-size: 12px;
    font-weight: 500;
    line-height: 1.5;
    color: var(--agra-text-muted);
}

/* The same badge once the tank has something queued against it. Tinted rather than filled, and in
   the accent's darker shade rather than the accent itself: #eb7413 on white is about 3:1, which is
   under what 12px text needs, while this pairing clears it comfortably. */
.agra-count-badge-active {
    background: rgb(235 116 19 / 16%);
    border-color: rgb(235 116 19 / 35%);
    color: #a2500d;
}

/* The logo card header on /login — the Ionic page sized the image at 200px tall. */
.agra-logo {
    display: block;
    width: 100%;
    max-height: 200px;
    object-fit: contain;
    margin: 0 auto;
}

/* ion-item: a 48px row with a hairline underline; the label and control sit on
   one line, or stacked when the Ionic page used position="stacked". */
.agra-field { margin: 0; }

.agra-item {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 48px;
    padding: 4px 0;
    border-bottom: 1px solid var(--agra-hairline);
}

.agra-item-stacked {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-top: 10px;
}

.agra-item-label {
    flex: 0 0 auto;
    font-size: 1rem;
    color: var(--agra-text);
}

/* A value the row states rather than asks for — the email on the settings page, which is the
   username and so is changed by the office rather than in a detail edit. */
.agra-item-readonly {
    flex: 1 1 auto;
    min-width: 0;
    text-align: right;
    color: var(--agra-text-muted);
    overflow-wrap: anywhere;
}

/* The "Change" affordance beside a value the row states rather than asks for — the mobile number,
   which is edited on its own page because changing it needs a code. */
.agra-item-action {
    flex: 0 0 auto;
    margin-left: .75rem;
    font-size: .9rem;
    font-weight: 600;
    color: var(--agra-accent);
    text-decoration: none;
    white-space: nowrap;
}

.agra-item-action:hover { text-decoration: underline; }

.agra-item-stacked .agra-item-label {
    font-size: 12px;
    color: var(--agra-text-muted);
    margin-bottom: 2px;
}

.agra-item-label-wrap { flex: 1 1 auto; line-height: 1.35; font-size: .95rem; }

/* ion-input: the item draws the line, so the control itself is bare. */
.agra-input {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    border: none;
    background: transparent;
    padding: 10px 0;
    /* 16px minimum: iOS Safari zooms the viewport on focus for anything smaller, which on these
       forms leaves the customer scrolled sideways mid-signup. */
    font-size: 1rem;
    font-family: inherit;
    color: var(--agra-text);
}

.agra-input:focus { outline: none; }

/* The static "+1" beside the signup phone field. Not part of the input's value —
   see the comment on the field itself. */
.agra-input-prefix {
    flex: 0 0 auto;
    padding: 10px 0 10px 8px;
    font-size: 1rem;
    color: var(--agra-text-muted);
}

/* Ionic highlights the whole item on focus rather than outlining the input. */
.agra-item:focus-within { border-bottom-color: var(--agra-primary); box-shadow: 0 1px 0 0 var(--agra-primary); }

.agra-input-code {
    letter-spacing: .5em;
    text-align: center;
    font-size: 1.35rem;
}

/* ion-item-divider */
.agra-item-divider {
    margin: 12px 0 0;
    padding: 6px 0;
    min-height: 30px;
    display: flex;
    align-items: center;
    background: var(--agra-item-divider-bg);
    font-size: 14px;
    color: var(--agra-text-muted);
}

.agra-item-divider-inset { padding-left: 16px; margin-left: -16px; margin-right: -16px; padding-right: 16px; }

/* ion-toggle, as a checkbox styled into a switch. */
.agra-toggle {
    appearance: none;
    flex: 0 0 auto;
    width: 51px;
    height: 32px;
    margin: 0;
    border-radius: 16px;
    background: rgb(0 0 0 / 25%);
    position: relative;
    cursor: pointer;
    transition: background .15s ease-in-out;
}

.agra-toggle::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
    transition: transform .15s ease-in-out;
}

.agra-toggle:checked { background: var(--agra-primary); }
.agra-toggle:checked::after { transform: translateX(19px); }
.agra-toggle:focus-visible { outline: 2px solid var(--agra-primary); outline-offset: 2px; }

/* ion-button expand="block" (Material): uppercase, 36px tall, elevation 2. */
.agra-button {
    display: block;
    width: 100%;
    margin: 16px 0 0;
    padding: 0 1rem;
    /* Comfortably above the 44px minimum touch target — Ionic's 36px is not. */
    min-height: 44px;
    border: none;
    border-radius: 4px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: .06em;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 0 3px 1px -2px rgb(0 0 0 / 20%),
                0 2px 2px 0 rgb(0 0 0 / 14%),
                0 1px 5px 0 rgb(0 0 0 / 12%);
}

.agra-button-primary { background: var(--agra-primary); color: #fff; }
.agra-button-primary:hover { background: var(--agra-primary-shade); }

/* Waiting out the minute between two reset codes (code-cooldown.js). The rule covers the native
   disabled state as well, so a button that is counting down and one disabled for any other reason
   never look different from each other. */
.agra-button:disabled,
.agra-button.agra-cooling-down {
    background: var(--agra-text-muted);
    box-shadow: none;
    cursor: default;
    pointer-events: none;
}
.agra-button-danger { background: var(--agra-danger); color: #fff; }
.agra-button-danger:hover { background: var(--agra-danger-shade); }

/* The centred <p><a>...</a></p> links under the Ionic login form. */
.agra-center-link {
    margin: 12px 0 0;
    text-align: center;
    font-size: .95rem;
    color: var(--agra-text);
}

.agra-center-link a { color: var(--agra-primary); text-decoration: none; }
.agra-center-link a:hover { text-decoration: underline; }

/* Counting down: no href, so it is already unclickable — this stops it still *looking* clickable. */
.agra-center-link a.agra-cooling-down,
.agra-center-link a.agra-cooling-down:hover {
    color: var(--agra-text-muted);
    text-decoration: none;
    cursor: default;
}

.agra-link-button {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--agra-primary);
    cursor: pointer;
    text-decoration: none;
}

.agra-link-button:hover { text-decoration: underline; }

.agra-hint {
    display: block;
    margin-top: .3rem;
    font-size: .8rem;
    color: var(--agra-text-muted);
}

.agra-consent {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    padding: 10px 0;
    font-size: .85rem;
    line-height: 1.45;
    color: var(--agra-text);
    border-bottom: 1px solid var(--agra-hairline);
}

.agra-checkbox {
    /* Enlarged from the browser default so it is tappable and visibly unchecked — A2P consent must
       be an affirmative act, so it must be obvious that it starts off. */
    width: 1.15rem;
    height: 1.15rem;
    margin-top: .15rem;
    flex: 0 0 auto;
}

/* The send-my-code-by choice on /forgot-password. Two tappable rows rather than a native select,
   which on iOS opens a wheel picker for what is a two-way switch. */
.agra-choice {
    display: flex;
    gap: .75rem;
    align-items: center;
    padding: 10px 0;
    font-size: .95rem;
    color: var(--agra-text);
    border-bottom: 1px solid var(--agra-hairline);
}

.agra-choice label { flex: 1 1 auto; }

.agra-choice input[type="radio"] {
    width: 1.15rem;
    height: 1.15rem;
    flex: 0 0 auto;
}

.agra-status { margin: 0 0 12px; }

/* ion-text color="danger", centred, as the delete-account page used. */
.agra-text-danger { color: var(--agra-danger); text-align: center; }

.agra-legal h2 { font-size: 1.05rem; margin: 1.25rem 0 .5rem; }
.agra-legal p { font-size: .9rem; line-height: 1.55; color: var(--agra-text); }

/* Blazor's validation output, kept on the Ionic grid. */
.agra-field .validation-message { display: block; margin: 4px 0 0; font-size: .8rem; }

/* --------------------------------------------------------------------------
   App chrome for signed-in customers.

   The Ionic shell: a per-page ion-header toolbar with the page title and a
   menu button, a fixed two-tab ion-tab-bar at the bottom with the wordmark
   between the tabs, and a centre cart FAB that only exists while the cart
   does. The toolbar itself reuses .agra-toolbar from the auth screens above —
   it is the same ion-toolbar at the same Material metrics.
   Source: tabs/tabs.page.html, tanks-tab/tanks-tab.page.html
   -------------------------------------------------------------------------- */

/* The toolbar's title takes the slack so the menu button sits hard right, the
   way ion-buttons slot="end" did. */

.agra-toolbar-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 48px: Ionic's toolbar button, and the minimum comfortable touch target. */
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--agra-bar-fg);
    cursor: pointer;
    flex: 0 0 auto;
}

.agra-toolbar-action:hover { background: var(--agra-bar-hover); }
/* The focus ring has to be light too — --agra-primary is now the bar's own
   colour, which would make the ring invisible. */
.agra-toolbar-action:focus-visible { outline: 2px solid var(--agra-bar-fg); outline-offset: -2px; }

/* --------------------------------------------------------------------------
   Radzen dialog chrome.

   DialogService draws the card and its title bar, and stock Material leaves
   both plain — a black title on white, flush to the content. Agra.Ops gives
   every dialog a coloured header with an orange rule under it; this is that
   idea in this app's palette, set once so any dialog added later inherits it
   rather than restyling itself.
   -------------------------------------------------------------------------- */

.rz-dialog {
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 12px 28px rgb(0 0 0 / 22%), 0 4px 10px rgb(0 0 0 / 14%);
}

.rz-dialog .rz-dialog-titlebar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 16px;
    background: var(--agra-bar-bg);
    color: var(--agra-bar-fg);
    /* The same accent rule Agra.Ops puts across the top of a dialog card. */
    border-bottom: 3px solid var(--agra-accent);
}

.rz-dialog .rz-dialog-title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--agra-bar-fg);
}

.rz-dialog .rz-dialog-titlebar-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    color: var(--agra-bar-fg);
    opacity: .92;
}

.rz-dialog .rz-dialog-titlebar-close:hover {
    background: var(--agra-bar-hover);
    opacity: 1;
}

.rz-dialog .rz-dialog-content {
    padding: 16px;
}

/* ion-popover: a backdrop that dismisses, and a card anchored under the button. */
.agra-menu-backdrop {
    position: fixed;
    inset: 0;
    background: rgb(0 0 0 / 32%);
    z-index: 1000;
}

.agra-menu-popover {
    position: fixed;
    top: calc(56px + env(safe-area-inset-top));
    right: 8px;
    min-width: 200px;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 5px 5px -3px rgb(0 0 0 / 20%),
                0 8px 10px 1px rgb(0 0 0 / 14%),
                0 3px 14px 2px rgb(0 0 0 / 12%);
    overflow: hidden;
    z-index: 1001;
}

/* ion-list-header */
.agra-menu-header {
    padding: 12px 16px 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--agra-text-muted);
}

/* ion-item button */
.agra-menu-item {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 48px;
    padding: 0 16px;
    border: none;
    border-top: 1px solid var(--agra-hairline);
    background: #fff;
    font: inherit;
    font-size: 1rem;
    color: var(--agra-text);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.agra-menu-item:hover { background: rgb(0 0 0 / 4%); }

.agra-menu-form { margin: 0; }

/* ion-tab-bar slot="bottom" — fixed, above the iOS home indicator. */
.agra-tab-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    height: calc(56px + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--agra-bar-bg);
    border-top: 1px solid var(--agra-bar-hairline);
    z-index: 20;
}

/* ion-tab-button: icon over label, greyed until active. */
.agra-tab {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 6px 4px;
    color: var(--agra-bar-fg-muted);
    /* Reserved on every tab so marking one active does not shift the row. */
    border-top: 3px solid transparent;
    box-sizing: border-box;
    text-decoration: none;
    font-size: 12px;
    /* The FAB overlaps the middle of the bar; keep taps on the tabs themselves. */
    min-width: 0;
}

/* White rather than --agra-primary, which is now the bar's own colour. The
   Agra.Ops orange marks the active tab as a rule instead — the same accent it
   puts on a dialog's border-top — because at 12px that orange on this blue is
   about 2.4:1, too low to carry the label itself. */
.agra-tab-active {
    color: var(--agra-bar-fg);
    border-top-color: var(--agra-accent);
}

.agra-tab-icon {
    width: 24px;
    height: 24px;
    /* The SVGs are single-colour black, unreadable on the blue bar. Inverting
       them to white matches the label, and opacity carries active/inactive the
       way it did before. Same technique as .agra-cart-fab-icon. */
    filter: brightness(0) invert(1);
    opacity: .72;
}

.agra-tab-active .agra-tab-icon { opacity: 1; }

.agra-tab-label { line-height: 1.1; text-align: center; }

/* The wordmark between the tabs, at the 40px the Ionic markup set. Drawn straight onto the bar for
   the same reason as the head mark above: the chip behind it was more conspicuous than the parts of
   the artwork it was rescuing. */
.agra-tab-logo {
    height: 40px;
    align-self: center;
    flex: 0 0 auto;
    /* Sits behind the cart FAB when there is one. */
    margin: 0 4px;
}

/* ion-fab vertical="bottom" horizontal="center", riding on top of the tab bar. */
.agra-cart-fab {
    position: fixed;
    left: 50%;
    /* Clear of the bar, not straddling it: 56px is the bar's own height, plus 12px of breathing
       room, plus the inset the bar already pads itself by. The wordmark underneath stays fully
       visible, which straddling defeated. */
    bottom: calc(68px + env(safe-area-inset-bottom));
    transform: translateX(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    /* The accent orange, not --agra-primary: the FAB now rides on a bar of that
       exact blue and would sink into it. */
    background: var(--agra-accent);
    box-shadow: 0 3px 5px -1px rgb(0 0 0 / 20%),
                0 6px 10px 0 rgb(0 0 0 / 14%),
                0 1px 18px 0 rgb(0 0 0 / 12%);
    z-index: 30;
}

.agra-cart-fab-icon {
    width: 28px;
    height: 28px;
    /* The truck SVG is black; the FAB is the accent orange with light content. */
    filter: brightness(0) invert(1);
}

/* ion-badge color="danger" on the FAB's top-right corner. */
.agra-cart-fab .agra-cart-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 22px;
    border-radius: 11px;
    font-size: 12px;
    line-height: 1;
}

/* Content on a tabbed page has to clear the fixed tab bar, or the last card sits
   underneath it and cannot be scrolled into view. */
.agra-content-tabbed {
    padding-bottom: calc(72px + env(safe-area-inset-bottom));
}

.agra-page { padding: 1rem; }

.agra-greeting { font-size: 1.35rem; font-weight: 600; margin: .5rem 0 1rem; color: #1d2330; }

.agra-empty-state { margin-top: 1rem; }

/* --------------------------------------------------------------------------
   Push opt-in and install instructions.

   Both are additions with no Ionic counterpart — the legacy app came from the
   App Store and had neither an install story nor web push — so these are new
   rules rather than ports, built from the same tokens as everything above.
   -------------------------------------------------------------------------- */

.push-optin {
    margin: 0 0 1rem;
    /* Tinted rather than plain white so it reads as a prompt sitting above the
       cart rather than as another cart section. */
    background: #f2f7ff;
    border-left: 3px solid var(--agra-primary);
}

.push-optin-done {
    background: #f1faf3;
    border-left-color: var(--agra-success);
}

.push-optin-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .25rem;
}

/* The button carries .agra-button's own top margin for the auth forms; the
   prompt sets its own rhythm. */
.push-optin-button { margin-top: 0; }

.push-optin-dismiss {
    align-self: center;
    padding: .6rem;
    font-size: .9rem;
    color: var(--agra-text-muted);
}

.install-card { margin: 0 0 1rem; }

.install-steps {
    margin: 0;
    padding-left: 1.25rem;
    font-size: .95rem;
    line-height: 1.6;
    color: var(--agra-text);
}

.install-steps li { margin-bottom: .35rem; }
