h1:focus {
    outline: none;
}

.content {
    padding-top: 1.1rem;
}

/* WCAG AA fix (QUICK-260722-nr5, color-contrast): the current-page (disabled) breadcrumb item
   rendered as MudBlazor's default disabled neutral (#b7b8b8 over the page background = 1.88:1).
   Raise it to the theme's secondary text color, which meets AA in both light (5.19:1) and dark
   (7.8:1) mode while keeping the current item visually de-emphasised relative to the active links.
   Loaded before MudBlazor.min.css, so #app adds specificity and !important beats the base rule. */
#app .mud-breadcrumbs .mud-disabled > a,
#app .mud-breadcrumbs .mud-breadcrumb-item.mud-disabled a {
    color: var(--mud-palette-text-secondary) !important;
}

/* CountrySelect: hide region group labels from page layout; show only inside MudBlazor popover */
.country-group-label {
    display: none !important;
}

.mud-popover .country-group-label {
    display: block !important;
    padding: 6px 16px 2px;
    margin-top: 2px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--mud-palette-text-disabled);
    pointer-events: none;
    user-select: none;
    border-top: 1px solid var(--mud-palette-divider);
    line-height: normal;
}

.mud-popover .country-group-label:first-child {
    border-top: none;
    margin-top: 0;
}


/* Single flat listing surface: subtle border + theme radius, no drop shadow. */
.fp-data-card {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    overflow: hidden;
}

/* DS-1: selection is a border, never elevation. A selected/current content surface (e.g.
   PricingCard's "Current plan") gets a 2px primary-colored border instead of a raised shadow --
   the shadow-based state it replaces was nearly invisible on the dark theme's #1B241F surface.
   The two-class selector (.fp-data-card.fp-card-selected) beats the one-class .fp-data-card rule's
   specificity on its own, so no !important is needed. Theme-token driven so it tracks light/dark. */
.fp-data-card.fp-card-selected {
    border: 2px solid var(--mud-palette-primary);
}

/* PU-1 (owner feedback 2026-08-01): the Current-usage pair — quota panel left, issuance-history
   chart right — stretch to one shared row height, so the shorter card's bottom edge lines up with
   the taller one instead of floating above a gap (same treatment as .fp-detail-layout). Each grid
   item becomes a flex column whose LAST child (the card; QuotaAlerts may render above it) grows. */
.fp-usage-grid .mud-grid-item {
    display: flex;
    flex-direction: column;
}

.fp-usage-grid .mud-grid-item > :last-child {
    flex: 1 1 auto;
}

/* PU-2: PricingCard's header (title + chip row) and body (price/copy/CTA) split into their own
   padded regions, replacing the root MudPaper's flat pa-6 -- lets the header alone take the
   current-tier tint below without also tinting the card's body. */
.fp-pricing-card__header {
    padding: 20px 24px 14px;
}

.fp-pricing-card__body {
    padding: 0 24px 24px;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

/* PU-2: the current tier's header band gets a tinted background instead of a shadow -- reuses the
   SAME tonal palette token already used for the primary tonal-chip backgrounds (DS-4/IR-1), never
   a new hard-coded colour. */
.fp-card-selected .fp-pricing-card__header {
    background: var(--mud-palette-primary-hover);
}

/* Account settings row: label+value on the left, action button on the right.
   Mirrors GitHub / Linear / Vercel's account-settings row pattern. */
.fp-settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 20px;
}

.fp-settings-row__content {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

/* On very small screens, stack the action button below the content. */
@media (max-width: 480px) {
    .fp-settings-row {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* Divider lines that separate the drawer's header/footer from its scrollable body. */
.fp-form-drawer__header {
    border-bottom: 1px solid var(--mud-palette-divider);
}

.fp-form-drawer__footer {
    border-top: 1px solid var(--mud-palette-divider);
}

/* Search grows to fill the toolbar row once it wraps onto its own line (mobile/tablet). */
@media (max-width: 600px) {
    .fp-crud-toolbar__search {
        max-width: 100% !important;
        width: 100%;
    }
}

/* IR-1/FLT-01: removable filter chips row beneath the toolbar's main stack. Spacing only. */
.fp-crud-toolbar__chips {
    margin-top: .5rem;
}

.fp-filter-chip {
    display: inline-flex;
    align-items: center;
}

.fp-filter-chip__remove {
    margin-left: -6px;
}

/* Inset panel for an expanded table row's detail content. */
.fp-details-panel {
    background-color: var(--mud-palette-background-gray);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
}

/* Monospace treatment for shown-once secrets (API keys) and inline key prefixes. */
.fp-mono input,
.fp-mono-inline {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}

/* DS-2: fixed-width digits so metric/quota/date figures don't jitter or drift out of column
   alignment as their values change. Non-numeric text is visually unaffected. */
.fp-num {
    font-variant-numeric: tabular-nums;
}

/* DS-2: app-wide — every MudTd in every MudTable gets tabular digits for free, with zero edits to
   any individual listing page. MudSimpleTable cells (plain <td>, no .mud-table-cell class) are not
   reached by this rule and are wrapped directly where needed. */
.mud-table-cell {
    font-variant-numeric: tabular-nums;
}

/* PASS-3/FMT-01: botanical-nomenclature convention — genus/species pairs render italic everywhere,
   via Shared/BotanicalName.razor. The cultivar segment (after a single quote) is rendered in a plain
   <span> alongside this <i>, so it stays upright without a separate class. */
.fp-botanical {
    font-style: italic;
}

/* PASS-2: the passports list's merged primary identity cell — botanical name and passport number
   stack on their own lines inside the one anchor that is the row's single link. */
.fp-primary-cell {
    display: flex;
    flex-direction: column;
}

.fp-primary-cell .fp-botanical {
    font-weight: 500;
}

.fp-primary-cell .fp-mono-inline {
    color: var(--mud-palette-text-secondary);
    font-size: 0.8125rem;
}

/* PASS-2: signals that the whole row (not just the primary cell's link) navigates to the
   passport's detail page on click. */
.fp-row-clickable {
    cursor: pointer;
}

/* ADM-1 (50-01): the admin tenant list's "Usage this month" cell -- a quota bar over its
   "used / quota" text, both stacked in the row's own column instead of a fixed pixel width. */
.fp-usage-cell {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 96px;
}

.fp-usage-cell__bar {
    border-radius: var(--mud-default-borderradius);
}

/* CP-1: MudTooltip's root is inline-flex and sizes to its content, so a gated full-width input
   wrapped in GateTooltip collapses inside its grid column. Opt-in stretch for those wrappers. */
.fp-gate-fill {
    display: block;
    width: 100%;
}

/* FRM-3: single source of the section-title weight, now that the tag itself carries real heading
   semantics (h2/h3) instead of styled body text. Shared by FormSection, BatchDetail.razor and
   PassportDetail.razor's own section titles. */
.fp-section-title {
    font-weight: 600;
}

/* PASS-2: MudButtonGroup paints its own surface-coloured rounded root behind the segments, and its
   direct MudMenu child wraps the chevron in a .mud-menu div that does not stretch to the buttons'
   height — the group's pale background then peeks out beneath the chevron as a white sliver.
   Stretch the menu wrapper to the group's full height, let the chevron button fill it (carrying
   the group's right-side radius), and keep the group root itself transparent — the segments paint
   their own fills. */
.fp-split-button.mud-button-group-root {
    background: transparent;
}

.fp-split-button .mud-menu {
    display: inline-flex;
    align-self: stretch;
}

.fp-split-button .mud-menu .mud-button-root {
    height: 100%;
    border-radius: 0 var(--mud-default-borderradius) var(--mud-default-borderradius) 0;
}

/* DS-4: Issued passport chips read as a soft tonal fill instead of the fully saturated Material
   Color.Success fill, so a Voided row (unchanged solid filled error) stands out against a list
   that is mostly Issued. MudBlazor 9.7's Variant enum has no native "tonal" member (Text/Outlined/
   Filled only), so this overrides Variant.Filled's background/text color on top. The background is
   the alpha-tint hover token, NOT success-lighten: the mockup draws a pale #EEF4F0-style tint,
   while success-lighten is a saturated green that read as a second filled chip (owner feedback
   2026-08-01). */
.fp-chip-tonal.mud-chip-filled.mud-chip-color-success {
    background-color: var(--mud-palette-success-hover);
    color: var(--mud-palette-success-darken);
}

/* IR-1: active-filter chips are tonal brand pills (mockup: tinted background + primary border),
   not neutral outlined grey — the same tonal override mechanism as DS-4's success arm above. */
.fp-chip-tonal.mud-chip-filled.mud-chip-color-primary {
    background-color: var(--mud-palette-primary-hover);
    color: var(--mud-palette-primary-darken);
    border: 1px solid var(--mud-palette-primary);
}

/* Owner decision 2026-08-01: the tonal treatment is the vocabulary for EVERY status chip —
   error (Voided/Revoked/Suspended/Rejected: a softer red than the old solid fill), info
   (Pending), warning, and the grey fallback alike. Exceptions stay visible through hue, not
   saturation; only Draft/Withdrawn remain outlined neutral (see StatusChipStyle.cs). */
.fp-chip-tonal.mud-chip-filled.mud-chip-color-error {
    background-color: var(--mud-palette-error-hover);
    color: var(--mud-palette-error-darken);
}

.fp-chip-tonal.mud-chip-filled.mud-chip-color-info {
    background-color: var(--mud-palette-info-hover);
    color: var(--mud-palette-info-darken);
}

.fp-chip-tonal.mud-chip-filled.mud-chip-color-warning {
    background-color: var(--mud-palette-warning-hover);
    color: var(--mud-palette-warning-darken);
}

.fp-chip-tonal.mud-chip-filled.mud-chip-color-default {
    background-color: var(--mud-palette-action-default-hover);
    color: var(--mud-palette-text-secondary);
}

/* NVX-02/NAV-3: trailing tier tag on a plan-gated nav item ("Starter+"/"Pro"), rendered only when
   the tenant lacks the item's RequiredCapability -- see NavMenu.razor. One tonal hue for every tag
   regardless of tier (mockup renders its lone "PRO" tag with the same amber swatch the marketing
   site uses for "Starter+"), reusing the same warning token pair as the .fp-chip-tonal warning arm
   above -- never a hard-coded hex value. */
.fp-nav-tag {
    display: inline-block;
    margin-left: auto;
    padding: 0.1rem 0.4rem;
    border-radius: var(--mud-default-borderradius);
    background-color: var(--mud-palette-warning-hover);
    color: var(--mud-palette-warning-darken);
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* ST-3/EMX-01: a plan-gated empty state sits inside a tinted panel (a subtle green wash, the same
   --fp-field surface already used on the marketing home) so it never reads as an error, with the
   capability's tonal tier tag pinned to the corner -- reusing .fp-nav-tag's warning-hover/
   warning-darken tokens verbatim (fp-gated-empty__tag overrides its margin-left:auto so the two
   classes combine correctly on the same span). See CapabilityGate.razor/ApiKeys.razor. */
.fp-gated-empty {
    position: relative;
    background: var(--fp-field);
    border-radius: var(--mud-default-borderradius);
    padding: 1.5rem;
}

.fp-gated-empty__tag {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    margin-left: 0;
}

/* ADT-03/ADM-6: capability tag-chips on the admin plans table reuse .fp-nav-tag's tonal treatment
   outside its nav context -- this modifier only resets the nav-specific margin-left:auto (the same
   override fp-gated-empty__tag makes) so the tags sit in a small wrapping flex row. */
.fp-plan-cap-tag {
    margin-left: 0;
}

/* QUICK-260721-vk6 + QUICK-260722-nr5: classic sticky-footer flexbox pattern. Ownership of the
   flex column moved UP from MudMainContent to the layout root now that the footer is an external
   sibling of MudMainContent (moved out so its contentinfo landmark is top-level). #app .mud-layout
   becomes a flex column at least one viewport tall; the fixed app bar and positioned drawer are out
   of normal flow and unaffected. MudMainContent (.mud-main-content, flex:1 1 auto) grows to fill the
   column, pushing the external footer to the bottom edge on short pages while it still flows below
   content on tall pages. The #app prefix beats MudBlazor's later-loading .mud-layout{height:100%}.
   The footer is never pulled out of normal document flow through a CSS position override -- only flex
   layout (here) and a mirrored drawer margin (below) move it. */
#app .mud-layout {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

/* main grows to fill the flex column; the min-height budget now lives on .mud-layout above (leaving
   it here too would force main alone to a full viewport and create a double-viewport scrollbar). */
#app .fp-main-content {
    flex: 1 0 auto;
}

/* Mirror MudBlazor's responsive left-drawer offset onto the now-external footer region so it stays
   horizontally aligned with page content when the drawer is open on desktop, and un-offsets when the
   drawer collapses to a mobile overlay. Selectors + media-query breakpoints match MudBlazor's own
   .mud-drawer-open-responsive-<bp>-left .mud-main-content rules exactly; --mud-drawer-width-left is
   set at runtime on .mud-layout and inherited by this child region. */
.fp-footer-region {
    transition: margin 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;
}

/* CHR-1: Footer.razor's default (Compact="false") public density. PublicLayout places it once for
   every anonymous route. Reads --mud-palette-* tokens directly; no hard-coded hex anywhere here. */
.fp-footer-full {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
}

.fp-footer-public-content {
    padding-inline: 24px;
}

.fp-footer-full__brand {
    max-width: 44ch;
}

.fp-footer-full__brand-link {
    display: inline-flex;
    color: inherit;
    text-decoration: none;
}

.fp-footer-full__reg {
    margin: 6px 0 0;
    font-size: .82rem;
    color: var(--mud-palette-text-secondary);
}

.fp-footer-full__links {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
}

.fp-footer-full__copy {
    margin-top: 16px;
    font-size: .82rem;
    color: var(--mud-palette-text-secondary);
}

/* CHR-1: mirrors #app .mud-layout's own sticky-footer pattern above, generalised to every
   PublicLayout consumer (Verify + the whole AuthLayout family). PublicLayout becomes a flex column
   at least one viewport tall; .fp-public-main grows to fill it, pinning the footer to the bottom
   edge on short pages while it still flows below content on tall pages. */
.fp-public-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

.fp-public-main {
    flex: 1 0 auto;
}

@media (min-width: 0px) {
    #app .mud-drawer-open-responsive-xs-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

@media (min-width: 600px) {
    #app .mud-drawer-open-responsive-sm-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

@media (min-width: 960px) {
    #app .mud-drawer-open-responsive-md-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

@media (min-width: 1280px) {
    #app .mud-drawer-open-responsive-lg-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

@media (min-width: 1920px) {
    #app .mud-drawer-open-responsive-xl-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

@media (min-width: 2560px) {
    #app .mud-drawer-open-responsive-xxl-left .fp-footer-region {
        margin-left: var(--mud-drawer-width-left);
    }
}

/* QUICK-260721-vk6: wraps the suspended banner + page content so that region grows to fill any
   leftover vertical space in the flex column, pushing the footer down to the bottom edge on
   short pages, while never shrinking below its natural content height so tall pages still scroll
   normally with no clipping. */
.fp-content-grow {
    flex: 1 0 auto;
}

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.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."
    }

/* PLAT-02 / D-03 (15-05): the pre-WASM #app boot loader (previously .loading-progress /
   .loading-progress-text here) was replaced by a self-contained loader whose CSS is inlined
   directly in wwwroot/index.html's <head> (class names .fp-boot / .fp-boot__spinner /
   .fp-boot__text) so first paint needs zero extra network round-trip, not even this stylesheet
   (15-RESEARCH.md Pattern 3 — the FCP-winning lever). Nothing in this codebase's bundled
   blazor.webassembly.js sets --blazor-load-percentage / --blazor-load-percentage-text (verified:
   grep found no occurrence in _framework/*.js), so the removed rules were dead/static anyway. */

/* ---- FloraPass brand identity tokens (2026-07-26 uniform restyle) ----
   Shared by the marketing home and the app kit. Colors that map onto the MudBlazor palette are read
   from --mud-palette-* directly (so light/dark tracks the theme); only values with no palette
   equivalent are defined here. */
:root {
    --fp-serif: "Iowan Old Style", Cambria, Georgia, "Times New Roman", serif;
    --fp-leaf: #3FA372;                 /* brighter interactive green for hovers */
    --fp-forest-deep: #1B4D38;          /* deep forest ground for the closing CTA band */
    --fp-field: #EEF4F0;                /* subtle green-tinted surface */
    --fp-shadow-sm: 0 1px 2px rgba(20, 43, 34, .05), 0 6px 16px -10px rgba(20, 43, 34, .14);
    --fp-shadow: 0 1px 2px rgba(20, 43, 34, .04), 0 12px 32px -12px rgba(20, 43, 34, .16);
}

/* Crisp leaf wordmark, replacing the 🌿 emoji everywhere (app bar, auth card, footer, home). */
.fp-brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; letter-spacing: -.01em; line-height: 1; }
.fp-brand__mark { width: 26px; height: 26px; flex: none; display: block; }
.fp-brand__name { font-weight: 700; letter-spacing: -.02em; }
.fp-brand--sm .fp-brand__mark { width: 20px; height: 20px; }
.fp-brand--sm .fp-brand__name { font-size: .95rem; }

/* PD-1 / LBL-01, LBL-02, LBL-03: the shared on-screen label facsimile (Shared/LabelPreview.razor),
   moved here from Home.razor's private .fph-pp* rules so every consuming page (Home, PassportDetail,
   CreatePassport, Verify) gets identical styling. Mirrors the REAL generated label
   (PassportLabelFrame): the EU flag top-left, "Plant Passport" (+ second-language wording) top-right,
   a single-column A/B/C/D with D = country of origin, and the EAN-13 beneath.
   RFC-0002 CHR-2: this frame represents a printed artefact and must render IDENTICALLY in light and
   dark mode — its background, ink and EU-flag colors are therefore deliberate literal hex values, a
   documented exception to the app's no-hard-coded-colour rule. Do not "fix" them into --mud-palette-*
   or --fp-* tokens; only the frame's structural border/divider chrome (not printed content) tracks the
   theme. POL-01: this frame's box-shadow below is ALSO deliberate, on purpose -- a physical-sticker
   affordance, since the frame represents a printed, adhesive label sitting on a surface rather than
   a flat content card, and is exempt from the app's flat-elevation rule; a future audit should not
   re-flag it. By contrast, the verify card's own box-shadow (see the verify section further down)
   was NOT deliberate and has been removed. */
.fp-label { margin: 0; background: #fff; border: 1px solid var(--mud-palette-lines-default); border-radius: 6px;
    box-shadow: var(--fp-shadow); max-width: 380px; width: 100%; justify-self: center; padding: 1rem 1.1rem; color: #14352A; }
.fp-label-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem;
    padding-bottom: .7rem; border-bottom: 1px solid var(--mud-palette-divider); }
.fp-label-title { text-align: right; line-height: 1.25; }
.fp-label-title strong { display: block; font-size: 1rem; letter-spacing: .01em; }
.fp-label-title span { display: block; font-size: .74rem; color: #5B6B63; }
/* Official EU flag colors, printed on every physical label — must never track the app's theme.
   HOME-3: the single static eu-emblem.svg asset supplies the field colour and stars now, replacing
   the old CSS-approximated star-glyph ring; only box/placement stay pinned here. */
.fp-label-emblem { width: 46px; height: 30px; flex: none; border-radius: 3px; display: block; object-fit: cover; }
.fp-label-rows { display: flex; flex-direction: column; gap: .4rem; padding: .8rem 0; }
.fp-label-row { display: flex; gap: .55rem; align-items: baseline; font-size: .92rem; }
.fp-label-row .k { font-weight: 700; width: .8rem; flex: none; }
.fp-label-pz { font-style: italic; font-size: .88rem; padding-top: .6rem; color: #14352A; }
.fp-label-foot { padding-top: .7rem; border-top: 1px solid var(--mud-palette-divider); }
/* Barcode ink is a fixed literal (matching the frame's own #14352A ink), not the --fph-ink/
   --mud-palette-text-primary alias: that alias flips to a light color in dark mode, which would
   render the bars invisible/inverted against this frame's fixed white background — a latent bug that
   never surfaced while this rule only ever rendered on Home's permanently light-locked page. */
.fp-label-barcode { height: 44px; border-radius: 2px;
    background-image: repeating-linear-gradient(90deg,
        #14352A 0 2px, transparent 2px 3px, #14352A 3px 6px, transparent 6px 8px,
        #14352A 8px 9px, transparent 9px 12px, #14352A 12px 15px, transparent 15px 16px,
        #14352A 16px 17px, transparent 17px 20px, #14352A 20px 23px, transparent 23px 24px);
    background-size: 24px 100%; }
.fp-label-ean { display: block; text-align: center; font-family: ui-monospace, Consolas, monospace; font-size: .82rem;
    letter-spacing: .18em; color: #5B6B63; margin-top: .5rem; font-variant-numeric: tabular-nums; }

/* CRT-01: two-column create-passport layout — the grouped FormSections on the left, the sticky
   LabelPreview on the right; single column below MudBlazor's own md (960px) breakpoint. */
.fp-form-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: start;
}

@media (min-width: 960px) {
    .fp-form-layout {
        grid-template-columns: 1.25fr .75fr;
    }
}

/* Rare edge case: no operator profile loaded, so there is no second (preview) child to grid beside —
   the one remaining card takes the full width instead of being squeezed into the left track. */
.fp-form-layout--single {
    grid-template-columns: 1fr !important;
}

/* CRT-01: the label preview stays pinned in view (offset below the fixed app bar) while the form,
   usually the taller of the two columns, scrolls beside it. */
.fp-sticky-preview {
    position: sticky;
    top: calc(var(--mud-appbar-height, 64px) + 1rem);
    align-self: start;
}

/* PU-1: /plan's route-based scroll lands on #change-plan-section -- offset it below the fixed
   app bar by the same --mud-appbar-height token .fp-sticky-preview already uses above, so the
   "Change plan" heading isn't clipped underneath the bar when scrolled into view. */
.fp-section-anchor {
    scroll-margin-top: calc(var(--mud-appbar-height, 64px) + 1rem);
}

/* PD-4: TraceabilityChain's inline dot-and-connector steps (supplier lots -> batch -> this
   passport), replacing the old collapsed MudExpansionPanel. Theme-token driven so it tracks
   light/dark. */
.fp-trace-step {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
    position: relative;
}

.fp-trace-dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--mud-palette-lines-default);
}

.fp-trace-dot--current {
    background: var(--mud-palette-primary);
}

.fp-trace-step:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 3px;
    top: 100%;
    width: 1px;
    height: 8px;
    background: var(--mud-palette-lines-default);
}

/* PD-5/DET-04: two-column detail composition. On PassportDetail the record card leads in the
   narrower left column and the facsimile uses the wider right column (owner review 2026-08-05);
   single column below the breakpoint (same convention as .fp-form-layout, Phase 40). */
/* align-items stretch (owner feedback 2026-08-01): both cards fill the row's full height so the
   two columns' edges line up instead of the shorter record card floating above a ragged gap. */
.fp-detail-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: stretch;
}

@media (min-width: 960px) {
    .fp-detail-layout {
        grid-template-columns: .85fr 1.15fr;
    }
}

/* V-1/VER-01: public verify becomes a mobile-first credential check -- a stranger's single,
   seconds-long trust check on a phone, outdoors. The verdict band deliberately uses a STRONG/solid
   palette fill (not the DS-4/owner decision 2026-08-01 soft-tonal fill mandated for status CHIPS
   app-wide) because this element serves a different purpose than a chip: maximum scannability at
   arm's length, not a quiet inline status marker. All colors are mud-palette/fp theme tokens --
   no literal hex anywhere in these rules. (NB: never write the token names with their asterisk
   wildcards inside a CSS comment -- the slash-star sequence terminates the comment early and the
   parser then swallows the NEXT rule, which is exactly how .fp-verify-page lost its centering.
   POL-01: min-height: 100dvh removed here -- redundant and actively counter-productive now that
   PublicLayout's own .fp-public-shell provides the "at least one viewport tall, footer pinned to
   the bottom on short pages" behaviour at the outer level (CHR-1); leaving it here would force the
   new footer below an extra full viewport of empty space on every load. */
.fp-verify-page {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 2rem 1rem;
}

/* POL-01: box-shadow removed -- it was not a deliberate design choice (unlike .fp-label's own
   shadow above, which is). */
.fp-verify-card {
    width: 100%;
    max-width: 420px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    overflow: hidden;
    font-size: 1rem;
}

.fp-verify-brand {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--mud-palette-divider);
}

.fp-verify-brand__tag {
    margin-left: auto;
    font-size: .8125rem;
    color: var(--mud-palette-text-secondary);
}

.fp-verify-band {
    text-align: center;
    padding: 1.75rem 1rem;
}

.fp-verify-band--success {
    background: var(--mud-palette-success);
    color: var(--mud-palette-success-text);
}

.fp-verify-band--error {
    background: var(--mud-palette-error);
    color: var(--mud-palette-error-text);
}

.fp-verify-band--warning {
    background: var(--mud-palette-warning);
    color: var(--mud-palette-warning-text);
}

.fp-verify-band__title {
    font-family: var(--fp-serif);
    font-size: 1.2rem;
    font-weight: 600;
    margin: .5rem 0 0;
}

.fp-verify-band__reason,
.fp-verify-band__caption {
    font-size: .8125rem;
    opacity: .85;
    margin-top: .35rem;
}

/* MudSimpleTable emits plain <td>, not reached by the app-wide .mud-table-cell rule -- same pattern
   the .fp-num/.mud-table-cell comment above already documents. */
.fp-verify-rows td {
    font-size: 1rem;
}

.fp-verify-explain {
    font-size: .875rem;
    color: var(--mud-palette-text-secondary);
    padding-top: .75rem;
    border-top: 1px solid var(--mud-palette-divider);
    margin: 0;
}

/* AUL-01/LOG-1/AUTH-1: the light auth-family layout (Layout/AuthLayout.razor), nested inside
   PublicLayout rather than a fourth top-level layout. Reads --mud-palette-* custom properties
   directly, never a literal hex -- same convention as the .fp-verify-* block above.
   CHR-1: .fp-auth-shell no longer owns a viewport-height sticky-footer job -- PublicLayout's own
   .fp-public-shell (above) does that now, once, for every PublicLayout consumer. The selector stays
   in the HTML unstyled (harmless) to avoid an unrelated diff. */
.fp-auth-header {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .75rem 1rem;
}

/* Back-link is the header's second (and last) direct child -- pushed to the right edge without a
   fifth named class, per the plan's exact class list. */
.fp-auth-header > a:last-child {
    margin-left: auto;
}

.fp-auth-body {
    padding: 1rem 1rem 2rem;
}

/* Mockup: the "Forgot?" affordance sits right-aligned on the password label line. */
.fp-auth-forgot-row {
    display: flex;
    justify-content: flex-end;
    margin-bottom: -6px;
}

/* AUL-02/LOG-2: the login card's remember-me row (checkbox + inline hint on one line) and meta row
   (Register / Resend-verification links sharing a single row) -- replaces four stacked text blocks. */
.fp-auth-remember-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

/* Mockup: a single divider-topped, centred line under the primary button. */
.fp-auth-meta-row {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: .5rem;
    padding-top: .9rem;
    border-top: 1px solid var(--mud-palette-divider);
}

/* IRX-02/IR-3: the inspection register's tertiary columns (Batch, Traceability) are real desktop
   columns that collapse below 600px into the per-row expand toggle's ExpandableDetailsPanel child
   row, and vice versa -- the two rules are complementary halves of the same breakpoint. The
   !important guards against MudBlazor's own responsive `display` overrides for Breakpoint.Sm-mode
   cells at the same boundary; 600px matches the existing .fp-crud-toolbar__search media query above
   and MudBlazor's own Sm breakpoint value. */
@media (max-width: 599.98px) {
    .fp-tertiary-col {
        display: none !important;
    }
}

@media (min-width: 600px) {
    .fp-expand-col {
        display: none !important;
    }
}

.fp-recipient-country {
    color: var(--mud-palette-text-secondary);
}

/* LDG-01/ST-1: LoadingSkeleton's Table variant row shape -- the same 1.7/1.1/.7/.5 column ratio
   the register's own real columns and the rfc1-sec-9 mockup both use, so the skeleton and the
   loaded table read as one shape. */
.fp-skeleton-row {
    display: grid;
    grid-template-columns: 1.7fr 1.1fr .7fr .5fr;
    gap: 10px;
    align-items: center;
    padding: 8px 0;
}

.fp-skeleton-row__primary {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* UTIL-1 (Shared/LegalPage.razor): long-form Privacy/Terms content follows the same centered public
   page rhythm as Support. The page container is centered at Support's 760px measure; prose stays
   left-aligned inside it for readability. Token-driven (--mud-palette-*), no hard-coded colours. */
.fp-legal {
    width: 100%;
    max-width: 760px;
    margin-inline: auto;
}

.fp-legal-title {
    margin-bottom: .5rem;
}

.fp-legal-status {
    margin-bottom: 1.5rem;
}

.fp-legal-toc {
    width: 100%;
    margin-bottom: 1.5rem;
    padding: .75rem 1rem;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
}

.fp-legal-toc-title {
    margin-bottom: .25rem;
}

.fp-legal-toc ul {
    margin: 0;
    padding-left: 1.25rem;
}

.fp-legal-toc li {
    margin-bottom: .25rem;
}

/* The capped reading measure: ~70 characters at the app's base font size, per UTIL-1. Applies to
   the consuming page's own prose (p, h2, h3, ul, ol) -- LegalPage itself only renders the h1. */
.fp-legal-body {
    max-width: 70ch;
    margin-inline: auto;
}

.fp-legal-body h2 {
    margin-top: 2rem;
    margin-bottom: .75rem;
    font-size: 1.25rem;
    font-weight: 600;
}

.fp-legal-body h3 {
    margin-top: 1.5rem;
    margin-bottom: .5rem;
    font-size: 1.05rem;
    font-weight: 600;
}

.fp-legal-body p,
.fp-legal-body ul,
.fp-legal-body ol {
    margin-top: 0;
    margin-bottom: 1rem;
    line-height: 1.6;
}

/* ADQ-01 (Pages/Admin/OperatorChangeRequests.razor): the review queue's primary cell shows both
   registration codes in one mono span -- the outgoing value recedes, the incoming value carries the
   theme primary (the brand green in both palettes). Token-driven, no hard-coded colours. */
.fp-code-old {
    color: var(--mud-palette-text-secondary);
}

.fp-code-new {
    font-weight: 600;
    color: var(--mud-palette-primary);
}

/* ADQ-01: urgency treatment for a queue row pending longer than a week -- the age figure goes amber
   and the whole row gets a warm wash, the same warning token pair the nav tier tag uses above. */
.fp-waiting-overdue {
    color: var(--mud-palette-warning-darken);
    font-weight: 500;
}

.fp-row-attention {
    background-color: var(--mud-palette-warning-hover);
}
