/* =============================================================================================
   WPI Design Template  ·  LAYER 3: COMPONENTS

   Framework-agnostic. Reads semantic --wpi-* tokens ONLY: no palette literals, no framework
   classes. Works identically under Tabler and under Tailwind.

   Load LAST, after wpi-tokens.css and after whichever bridge the app uses.
   ============================================================================================= */


/* =============================================================================================
   LAYER 3: COMPONENTS

   Semantic tokens only below this line. No palette literals.
   ============================================================================================= */

/* Display type. Tabler's .page-title is ~1.25rem; the serif needs display scale to earn its
   place, and the face is single-weight so it is never used below ~28px. */
.wpi-title,
.page-title {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400;
    font-size: 2.25rem;
    line-height: 1.1;
    letter-spacing: -0.01em;
}

/* The hairline card: a soft ornamental edge plus a top shine. The shine is the single detail
   that reads as "premium" more than any other in this system. */
.wpi-card {
    background: var(--wpi-surface);
    border: 1px solid var(--wpi-hairline);
    border-radius: 14px;
    position: relative;
    overflow: hidden;
}
.wpi-card::after,
.wpi-stat::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--wpi-shine), transparent);
}

.wpi-stat {
    border-color: var(--wpi-hairline);
    position: relative;
    overflow: hidden;
}

/* The card's INTERIOR, framework-free.
 *
 * .wpi-card above is only a surface: background, border, radius and the shine. It has no padding
 * of its own, which is correct, because padding belongs to the slots inside it.
 *
 * Those slots existed only as Tabler's .card-header / .card-body / .card-footer, whose padding
 * contract this template declares in wpi-tabler.css. That made the flagship component quietly
 * bridge-dependent: a Tailwind app following the quick start and copying the card markup out of
 * the design-system page got a correct-looking surface with its contents jammed against the
 * edge, and nothing anywhere said why. The architecture claim that components "render identically
 * under either bridge" was false for the one component every screen uses.
 *
 * These three are the framework-free equivalents, with the same values the Tabler bridge sets.
 * Under Tabler, .card-header and friends remain available and identical; use whichever matches
 * the rest of the codebase. Under Tailwind, use these. */
.wpi-card-head {
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--wpi-hairline);
}

.wpi-card-body {
    padding: 1rem 1.25rem;
}

.wpi-card-foot {
    padding: 1rem 1.25rem;
    border-top: 1px solid var(--wpi-hairline);
}

/* Serif numerals in a metallic gradient: the signature element. Always paired with the print
   rule at the bottom of this file, or the numbers vanish on paper. */
.wpi-num {
    font-family: "Instrument Serif", Georgia, serif;
    font-size: 2.75rem;
    line-height: 1.08;
    background: var(--wpi-num-grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    font-variant-numeric: tabular-nums;
}

/* The micro-label above a numeral. Small and wide-tracked, so it runs on ink-3, which is
   contrast-gated for exactly this size. */
.wpi-label {
    font-size: 0.656rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--wpi-ink-3);
    font-weight: 600;
}
.wpi-hint {
    font-size: 0.781rem;
    color: var(--wpi-ink-3);
}

/* The brand mark: a gradient disc with a knocked-out ring. Palette-agnostic by construction,
   so it becomes the WPI logo gradient or the metallic one automatically. */
.wpi-mark {
    width: 1.35rem;
    height: 1.35rem;
    border-radius: 50%;
    background: var(--wpi-brand-grad);
    position: relative;
    flex: none;
}
.wpi-mark::after {
    content: "";
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    border: 1.5px solid var(--wpi-page);
    opacity: 0.55;
}
/* The documented exception to the 28px floor above. 24px, because the lockup has to line up
   beside a 1.35rem mark in a sidebar. Two fixed words that never reflow and carry no detail a
   user must read closely, which is the narrow case this face survives below its floor. */
.wpi-wordmark {
    font-family: "Instrument Serif", Georgia, serif;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--wpi-ink);
}

@media (prefers-reduced-motion: no-preference) {
    .wpi-stat,
    .wpi-card {
        transition: border-color 0.2s ease;
    }
    .wpi-stat:hover,
    .wpi-card:hover {
        border-color: var(--wpi-accent);
    }
}

/* ---- The pitch / hero: the full-bleed marketing moment inside an app shell. ---- */
.wpi-pitch {
    max-width: 46rem;
    margin: 0 auto;
    padding: 3rem 0 1.5rem;
    text-align: center;
}
.wpi-pitch-kicker {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    /* link-hover, not accent: at 11px this needs the full 4.5:1, and in the light skins the
       accent itself does not clear it on the page surface. */
    color: var(--wpi-link-hover);
    margin-bottom: 0.875rem;
}
.wpi-pitch-title {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400;
    font-size: clamp(2.25rem, 5vw, 3.25rem);
    line-height: 1.08;
    letter-spacing: -0.01em;
    margin-bottom: 1rem;
    text-wrap: balance;
    background: var(--wpi-num-grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.wpi-pitch-lede {
    color: var(--wpi-ink-2);
    font-size: 1.0625rem;
    max-width: 38rem;
    margin: 0 auto 2rem;
}
.wpi-pitch-cta {
    border: 1px solid var(--wpi-hairline);
    border-radius: 14px;
    padding: 1.5rem 1.75rem;
    background: var(--wpi-surface);
    position: relative;
    overflow: hidden;
}
.wpi-pitch-cta::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--wpi-shine), transparent);
}
@media (prefers-reduced-motion: no-preference) {
    .wpi-pitch {
        animation: wpi-rise 0.45s ease-out both;
    }
    @keyframes wpi-rise {
        from { opacity: 0; transform: translateY(14px); }
        to   { opacity: 1; transform: none; }
    }
}

/* Signed values in tables. */
.wpi-pos { color: var(--wpi-pos); }
.wpi-neg { color: var(--wpi-neg); }

/* Badge tints. No !important here, so a utility class can still win. The Tabler bridge carries
   an !important copy scoped to .badge.bg-blue-lt, which is what stock Tabler needs to be beaten.
   Plain tokens, not color-mix(): Safari before 16.2 drops a color-mix background entirely. */
.wpi-badge-a {
    background: var(--wpi-badge-a-bg);
    color: var(--wpi-badge-a-ink);
}
.wpi-badge-b {
    background: var(--wpi-badge-b-bg);
    color: var(--wpi-badge-b-ink);
}

/* The focus ring, on the plain elements every stack has. Frameworks that style their own button
   class need one more selector in their bridge file; Tabler's .btn is handled in wpi-tabler.css. */
a:focus-visible,
button:focus-visible,
select:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--wpi-accent);
    outline-offset: 2px;
}


/* =============================================================================================
   PRINT

   Gradient-clipped text prints TRANSPARENT, because browsers omit background graphics by
   default and the "color" of that text is literally transparent. Every gradient-clipped class
   in this file must appear below with a solid fallback, or the numbers, which are usually the
   entire reason a page is printed, come out blank.
   ============================================================================================= */
@media print {
    .wpi-num,
    .wpi-pitch-title {
        background: none;
        color: var(--wpi-print-ink);
    }
}


/* =============================================================================================
   THE COMPONENT LIBRARY

   Everything above this line is carried over from a shipping application and is production-proven.
   Everything
   below is the shared set, added so a team starting a new screen has something to reach for
   other than raw Tabler.

   Same rules as the rest of the file: semantic --wpi-* tokens only, no framework classes, no
   palette literals. These render identically under the Tabler bridge and the Tailwind bridge.

   ---------------------------------------------------------------------------------------------
   READ THIS BEFORE USING A TONE

   The palette carries four semantic tones: --wpi-accent (informational), --wpi-pos (success),
   --wpi-warn (warning) and --wpi-neg (danger). They are NOT reliably distinguishable from each
   other by colour in these skins. Measured, warning against the accent:

       signature dark 1.14:1   signature light 1.10:1   brand dark 1.26:1   brand light 1.12:1

   Identical luminance, different hue. A colour-blind reader, a greyscale print and a projector
   in a bright room all collapse them into one.

   So every component below that carries a tone also carries a TEXT LABEL and an ICON SLOT, and
   the tone is the supporting cue rather than the message. An alert whose only difference from
   the alert above it is a hue is not a warning, it is a decoration. This is WCAG 1.4.1, and in
   this palette it is not a formality.
   ============================================================================================= */


/* ---------------------------------------------------------------------------------------------
   ALERTS AND CALLOUTS
   --------------------------------------------------------------------------------------------- */

/* The tone is carried by a left rule and a tint, never by the text colour: body copy in a
   coloured alert is the fastest way to fail contrast on a surface that is already tinted. */
.wpi-alert {
    --wpi-alert-tone: var(--wpi-accent);
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.875rem 1rem;
    border-radius: 10px;
    border: 1px solid var(--wpi-hairline-soft);
    border-inline-start: 3px solid var(--wpi-alert-tone);
    background: var(--wpi-surface-alt);
    color: var(--wpi-ink);
}

.wpi-alert-pos  { --wpi-alert-tone: var(--wpi-pos); }
.wpi-alert-warn { --wpi-alert-tone: var(--wpi-warn); }
.wpi-alert-neg  { --wpi-alert-tone: var(--wpi-neg); }

/* The icon takes the tone. It is the second cue; the first is the title text. */
.wpi-alert-icon {
    flex: none;
    width: 1.125rem;
    height: 1.125rem;
    color: var(--wpi-alert-tone);
}

.wpi-alert-title {
    font-weight: 600;
    margin-bottom: 0.125rem;
}

.wpi-alert-body {
    color: var(--wpi-ink-2);
    font-size: 0.875rem;
}

/* A quieter aside inside page content. No tone, no icon: if it needs a tone it is an alert. */
.wpi-callout {
    border-inline-start: 2px solid var(--wpi-hairline);
    padding: 0.25rem 0 0.25rem 0.875rem;
    color: var(--wpi-ink-2);
}


/* ---------------------------------------------------------------------------------------------
   EMPTY STATE

   The screen a new merchant sees first and the one most often left until last. It gets a slot
   for an action, because an empty state without a next step is a dead end.
   --------------------------------------------------------------------------------------------- */
.wpi-empty {
    text-align: center;
    padding: 3rem 1.5rem;
    max-width: 26rem;
    margin-inline: auto;
}

.wpi-empty-art {
    width: 3rem;
    height: 3rem;
    margin: 0 auto 1rem;
    color: var(--wpi-ink-3);
}

.wpi-empty-title {
    font-family: "Instrument Serif", Georgia, serif;
    font-size: 1.75rem;
    line-height: 1.15;
    margin-bottom: 0.375rem;
}

.wpi-empty-body {
    color: var(--wpi-ink-2);
    margin-bottom: 1.25rem;
}


/* ---------------------------------------------------------------------------------------------
   AVATARS
   --------------------------------------------------------------------------------------------- */
.wpi-avatar {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--wpi-surface-alt);
    color: var(--wpi-ink-2);
    font-size: 0.75rem;
    font-weight: 600;
    flex: none;
    overflow: hidden;
    user-select: none;
}

.wpi-avatar-lg { width: 3rem; height: 3rem; font-size: 1rem; }
.wpi-avatar-sm { width: 1.5rem; height: 1.5rem; font-size: 0.625rem; }

.wpi-avatar-accent {
    background: var(--wpi-accent);
    color: var(--wpi-accent-ink);
}

/* Overlapped stack. The ring is the page colour, so avatars read as separate discs rather than
   as one blob, and it must be the PAGE not the card: a stack often sits on a table row. */
.wpi-avatar-group {
    display: inline-flex;
    align-items: center;
}

.wpi-avatar-group > .wpi-avatar {
    box-shadow: 0 0 0 2px var(--wpi-surface);
}

.wpi-avatar-group > .wpi-avatar + .wpi-avatar {
    margin-inline-start: -0.5rem;
}


/* ---------------------------------------------------------------------------------------------
   CHIPS, TAGS AND RANK MARKERS
   --------------------------------------------------------------------------------------------- */
.wpi-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.1875rem 0.5rem;
    border-radius: 999px;
    background: var(--wpi-surface-alt);
    border: 1px solid var(--wpi-hairline-soft);
    color: var(--wpi-ink-2);
    font-size: 0.75rem;
    line-height: 1.4;
}

.wpi-chip-on {
    background: var(--wpi-badge-a-bg);
    border-color: var(--wpi-accent);
    color: var(--wpi-badge-a-ink);
}

/* The dismiss control is a real button, never a bare glyph: it must be reachable by keyboard
   and must have an accessible name naming WHAT it removes. */
.wpi-chip-x {
    border: 0;
    background: none;
    padding: 0;
    line-height: 1;
    color: inherit;
    cursor: pointer;
    opacity: 0.7;
}

.wpi-chip-x:hover { opacity: 1; }

.wpi-rank {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    font-weight: 700;
    background: var(--wpi-surface-alt);
    color: var(--wpi-ink-2);
    flex: none;
}

.wpi-rank-top {
    background: var(--wpi-accent);
    color: var(--wpi-accent-ink);
}


/* ---------------------------------------------------------------------------------------------
   METER AND PROGRESS

   A meter shows a proportion. It never carries the number by itself: the value goes in text
   beside it, because a bar is unreadable to anyone who cannot see it and imprecise to everyone
   who can.
   --------------------------------------------------------------------------------------------- */
.wpi-meter {
    height: 0.5rem;
    border-radius: 999px;
    background: var(--wpi-surface-alt);
    overflow: hidden;
}

.wpi-meter > span {
    display: block;
    height: 100%;
    background: var(--wpi-brand-grad);
}

.wpi-meter-sm { height: 0.25rem; }

/* Solid rather than the brand gradient, for meters that sit in a dense list where a gradient
   per row is noise. */
.wpi-meter-plain > span { background: var(--wpi-accent); }


/* ---------------------------------------------------------------------------------------------
   TIMELINE
   --------------------------------------------------------------------------------------------- */
.wpi-timeline {
    list-style: none;
    margin: 0;
    padding: 0 0 0 1.5rem;
    position: relative;
}

.wpi-timeline::before {
    content: "";
    position: absolute;
    inset-inline-start: 0.3125rem;
    top: 0.35rem;
    bottom: 0.35rem;
    width: 1px;
    background: var(--wpi-hairline);
}

.wpi-timeline > li {
    position: relative;
    padding-bottom: 1.25rem;
}

.wpi-timeline > li::before {
    content: "";
    position: absolute;
    inset-inline-start: -1.5rem;
    top: 0.35rem;
    width: 0.6875rem;
    height: 0.6875rem;
    border-radius: 50%;
    background: var(--wpi-surface);
    border: 2px solid var(--wpi-accent);
}

/* An informational entry rather than a deliberate action. */
.wpi-timeline > li.wpi-tl-muted::before { border-color: var(--wpi-ink-3); }


/* ---------------------------------------------------------------------------------------------
   STEPS / WIZARD

   Progress through a multi-stage flow: merchant onboarding, an underwriting decision, a dispute
   response. The current step carries aria-current="step" in the markup; the fill is decoration.
   --------------------------------------------------------------------------------------------- */
.wpi-steps {
    display: flex;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.wpi-steps > li {
    flex: 1;
    position: relative;
    padding-top: 1.75rem;
    color: var(--wpi-ink-3);
    font-size: 0.8125rem;
}

/* The connector sits behind the dots and stops short of the last step. */
.wpi-steps > li::after {
    content: "";
    position: absolute;
    top: 0.5rem;
    inset-inline-start: 0.75rem;
    inset-inline-end: 0;
    height: 2px;
    background: var(--wpi-hairline);
}

.wpi-steps > li:last-child::after { display: none; }

.wpi-steps > li::before {
    content: "";
    position: absolute;
    top: 0;
    inset-inline-start: 0;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background: var(--wpi-surface);
    border: 2px solid var(--wpi-hairline);
    z-index: 1;
}

.wpi-steps > li.wpi-step-done { color: var(--wpi-ink-2); }
.wpi-steps > li.wpi-step-done::before { background: var(--wpi-accent); border-color: var(--wpi-accent); }
.wpi-steps > li.wpi-step-done::after { background: var(--wpi-accent); }

.wpi-steps > li.wpi-step-now { color: var(--wpi-ink); font-weight: 600; }
.wpi-steps > li.wpi-step-now::before { border-color: var(--wpi-accent); }


/* ---------------------------------------------------------------------------------------------
   SEGMENTED CONTROL

   Two to four mutually exclusive options. Past four this is a <select>, because the buttons stop
   fitting on a phone and start wrapping into an unreadable grid.
   --------------------------------------------------------------------------------------------- */
.wpi-segmented {
    display: inline-flex;
    background: var(--wpi-surface-alt);
    border: 1px solid var(--wpi-hairline-soft);
    border-radius: 9px;
    padding: 2px;
    gap: 2px;
}

.wpi-segmented button {
    border: 0;
    background: none;
    border-radius: 7px;
    padding: 0.3rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--wpi-ink-3);
    cursor: pointer;
    white-space: nowrap;
}

.wpi-segmented button:hover { color: var(--wpi-ink); }

/* Selection is aria-pressed, so the state lives in the accessibility tree and not only in the
   fill. Styling off the attribute means the two can never disagree. */
.wpi-segmented button[aria-pressed="true"] {
    background: var(--wpi-accent);
    color: var(--wpi-accent-ink);
}


/* ---------------------------------------------------------------------------------------------
   KEY-VALUE LIST

   The account-record pattern: a definition list that stays aligned and collapses to stacked
   rows on a phone rather than squeezing two columns into 40 characters.
   --------------------------------------------------------------------------------------------- */
.wpi-kv {
    display: grid;
    grid-template-columns: minmax(7rem, 30%) 1fr;
    gap: 0.5rem 1rem;
    margin: 0;
}

.wpi-kv > dt {
    color: var(--wpi-ink-3);
    font-size: 0.8125rem;
    font-weight: 400;
}

.wpi-kv > dd { margin: 0; }

@media (max-width: 36rem) {
    .wpi-kv { grid-template-columns: 1fr; gap: 0.125rem; }
    .wpi-kv > dd { margin-bottom: 0.625rem; }
}


/* ---------------------------------------------------------------------------------------------
   TILE

   A product, reward or integration card with a graphic area on top.
   --------------------------------------------------------------------------------------------- */
.wpi-tile-art {
    aspect-ratio: 16 / 10;
    background: var(--wpi-surface-alt);
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: 1px solid var(--wpi-hairline-soft);
    color: var(--wpi-ink-3);
}


/* ---------------------------------------------------------------------------------------------
   BOARD (kanban)
   --------------------------------------------------------------------------------------------- */
.wpi-column {
    background: var(--wpi-surface-alt);
    border: 1px solid var(--wpi-hairline-soft);
    border-radius: 12px;
    padding: 0.75rem;
    min-height: 12rem;
}

.wpi-column-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.75rem;
}

/* Surface, so a card lifts off the column's alt background. */
.wpi-column-card {
    background: var(--wpi-surface);
    border: 1px solid var(--wpi-hairline);
    border-radius: 10px;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
}


/* ---------------------------------------------------------------------------------------------
   TOOLBAR AND SPLIT LAYOUT
   --------------------------------------------------------------------------------------------- */
.wpi-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--wpi-hairline-soft);
}

.wpi-toolbar-spacer { margin-inline-start: auto; }

/* List-and-detail. Stacks below the breakpoint rather than scrolling two panes side by side on
   a phone, which is the failure mode of every inbox layout. */
.wpi-split {
    display: grid;
    grid-template-columns: minmax(16rem, 22rem) 1fr;
    gap: 1rem;
    align-items: start;
}

@media (max-width: 62rem) {
    .wpi-split { grid-template-columns: 1fr; }
}


/* ---------------------------------------------------------------------------------------------
   MODAL AND TOOLTIP

   Presentation only. Opening, closing, focus trapping and Escape belong to the host framework,
   which already has them: Bootstrap in the portals, a headless library in the Next apps.
   Re-implementing a focus trap here would be a worse focus trap.
   --------------------------------------------------------------------------------------------- */
/* The scrim and the dialog shadow are the file's only colour literals, and deliberately so: a
   scrim darkens whatever arbitrary content sits behind it, and a shadow is an absence of light.
   Neither is a palette decision, and routing them through a token would make a dialog on the
   light skin cast a pale shadow, which is not how shadows work. */
.wpi-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}

.wpi-modal {
    background: var(--wpi-surface);
    border: 1px solid var(--wpi-hairline);
    border-radius: 14px;
    max-width: 32rem;
    width: 100%;
    position: relative;
    overflow: hidden;
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.6);
}

/* The same top shine as .wpi-card: a dialog is a card that arrived. */
.wpi-modal::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--wpi-shine), transparent);
}

.wpi-modal-head,
.wpi-modal-foot {
    padding: 1rem 1.25rem;
}

.wpi-modal-head { border-bottom: 1px solid var(--wpi-hairline-soft); }

.wpi-modal-foot {
    border-top: 1px solid var(--wpi-hairline-soft);
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.wpi-modal-body { padding: 1.25rem; }

.wpi-tooltip {
    background: var(--wpi-ink);
    color: var(--wpi-page);
    padding: 0.3125rem 0.5rem;
    border-radius: 6px;
    font-size: 0.75rem;
    max-width: 18rem;
}


/* ---------------------------------------------------------------------------------------------
   FORM PATTERNS

   The bridge already themes the controls themselves. What is missing is the LAYOUT around one:
   label, control, hint and error, in the order a screen reader should meet them.
   --------------------------------------------------------------------------------------------- */
.wpi-field { margin-bottom: 1rem; }

.wpi-field > label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    margin-bottom: 0.3125rem;
}

.wpi-field-hint {
    font-size: 0.781rem;
    color: var(--wpi-ink-3);
    margin-top: 0.3125rem;
}

/* The error names the problem in words. The red is the second cue, never the first, and the
   message must be wired to the control with aria-describedby. */
.wpi-field-error {
    font-size: 0.781rem;
    color: var(--wpi-neg);
    margin-top: 0.3125rem;
}

.wpi-field-invalid,
.wpi-field-invalid:focus {
    border-color: var(--wpi-neg);
}

/* A search or currency field with a leading glyph. Padding, not a wrapper with a background,
   so the control keeps its own focus ring. */
.wpi-input-icon { position: relative; }

.wpi-input-icon > svg,
.wpi-input-icon > .wpi-input-adorn {
    position: absolute;
    inset-inline-start: 0.625rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1rem;
    height: 1rem;
    color: var(--wpi-ink-3);
    pointer-events: none;
}

.wpi-input-icon > input { padding-inline-start: 2rem; }

/* File dropzone. The visible target is the label, so clicking anywhere in it opens the picker
   and the input itself can stay a real, focusable <input type="file">. */
.wpi-dropzone {
    display: block;
    border: 1px dashed var(--wpi-form-border);
    border-radius: 12px;
    padding: 1.75rem 1rem;
    text-align: center;
    color: var(--wpi-ink-2);
    cursor: pointer;
    background: var(--wpi-surface-alt);
}

.wpi-dropzone:hover { border-color: var(--wpi-accent); }


/* ---------------------------------------------------------------------------------------------
   LIST GROUP AND CODE
   --------------------------------------------------------------------------------------------- */
.wpi-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.wpi-list > li {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--wpi-hairline-soft);
}

.wpi-list > li:last-child { border-bottom: 0; }

.wpi-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8125rem;
    background: var(--wpi-surface-alt);
    border: 1px solid var(--wpi-hairline-soft);
    border-radius: 8px;
    padding: 0.875rem 1rem;
    overflow-x: auto;
    color: var(--wpi-ink);
}

/* Monospaced identifiers in running text and table cells: MIDs, auth codes, batch ids. */
.wpi-mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.75rem;
    color: var(--wpi-ink-2);
}


/* ---------------------------------------------------------------------------------------------
   TREND, for a figure that moved
   --------------------------------------------------------------------------------------------- */
.wpi-trend {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.8125rem;
    font-weight: 600;
}

/* The arrow is a separate glyph in the markup, so direction survives greyscale and the sign is
   in the text as well. Colour alone never states which way a number moved. */
.wpi-trend-up   { color: var(--wpi-pos); }
.wpi-trend-down { color: var(--wpi-neg); }
.wpi-trend-flat { color: var(--wpi-ink-3); }


/* =============================================================================================
   PRINT, for the component library

   Browsers omit background graphics by default, so any component whose meaning lives in a
   background prints blank. These are the ones that do.
   ============================================================================================= */
@media print {
    /* A meter is a filled proportion, and the fill is a background. On paper it would print as
       an empty outline, so it becomes an outlined bar with a solid rule instead. The value is
       always stated in text beside it, which is what actually carries the number. */
    .wpi-meter {
        border: 1px solid var(--wpi-print-ink);
        background: none;
    }
    .wpi-meter > span {
        background: var(--wpi-print-ink);
    }

    /* Tinted chips, badges and alerts lose their tint. Give them a border so they still read as
       discrete objects rather than as loose words in the flow. */
    .wpi-chip,
    .wpi-alert,
    .wpi-avatar,
    .wpi-rank {
        border: 1px solid var(--wpi-print-ink);
        background: none;
        color: var(--wpi-print-ink);
    }

    /* The empty-state serif heading is not gradient-clipped, but the tile art and dropzone are
       decorative surfaces with nothing to say on paper. */
    .wpi-tile-art,
    .wpi-dropzone {
        background: none;
    }

    /* Overlays never print. A backdrop over a page of figures is the worst possible outcome. */
    .wpi-modal-backdrop,
    .wpi-tooltip {
        display: none !important;
    }
}


/* =============================================================================================
   THE LOGO LOCKUP

   The real Wholesale Payments artwork, not a redraw. The brand book is explicit that the mark is
   never retyped or reconstructed, and .wpi-mark plus .wpi-wordmark IS a reconstruction. That
   approximation earns its place in tight spots (a favicon, a collapsed rail); where the real file
   fits, use the real file.

       <a class="wpi-logo" href="/">
         <img class="wpi-logo-light" src="/img/logo-light.png" alt="">
         <img class="wpi-logo-dark"  src="/img/logo-dark.png"  alt="">
         <span class="visually-hidden">Wholesale Payments, home</span>
       </a>

   ---------------------------------------------------------------------------------------------
   WHY <img> AND NOT A background-image FROM A CUSTOM PROPERTY

   The tidy-looking version passes the two files in as custom properties on the element and lets
   this stylesheet paint them with background-image: var(--wpi-logo-light). It renders NOTHING,
   and the reason is worth knowing because it applies to any url() a host app passes in.

   A relative url() inside a custom property is resolved against the stylesheet where the var()
   is SUBSTITUTED, not against the document that declared it. Written on the page as
   url(assets/brand/logo.png) and substituted here in css/wpi-components.css, the browser asks
   for css/assets/brand/logo.png and gets a 404. The element still lays out at its correct size,
   so nothing looks broken in the inspector: there is simply an empty box where the logo should
   be. That shipped here and took a real browser to catch.

   An <img src> is always resolved against the document. The host app writes the path it expects
   and gets the file it named.

   TWO IMAGES, NOT ONE, AND NOT A CSS FILTER. The wordmark is near-black and disappears on a dark
   surface. The usual shortcut, filter: invert(1), also inverts the gradient bars and turns the
   brand's blue-to-green mark orange-to-magenta: a redraw by accident. The white-text lockup is a
   separate official asset so nobody has to do that.

   THE LOGO DOES NOT FOLLOW THE PALETTE, AND THAT IS CORRECT. Everything else here re-skins; the
   logo is fixed artwork. A logo that changed colour to suit the interface would not be a logo.
   ============================================================================================= */
.wpi-logo {
    display: block;
    /* An explicit width: the brand link is usually a flex item of .navbar > .container-fluid, so
       it sizes to its content, and width:100% against an auto-width parent collapses to zero. */
    width: 11rem;
    max-width: 100%;
}

.wpi-logo img {
    display: block;
    width: 100%;
    height: auto;
}

/* Light artwork by default; the reversed lockup only under an explicit dark theme. */
.wpi-logo .wpi-logo-dark { display: none; }

[data-bs-theme="dark"] .wpi-logo .wpi-logo-light { display: none; }
[data-bs-theme="dark"] .wpi-logo .wpi-logo-dark { display: block; }

/* Paper is white, so the reversed lockup would vanish into it. */
@media print {
    .wpi-logo .wpi-logo-light,
    [data-bs-theme="dark"] .wpi-logo .wpi-logo-light { display: block; }
    .wpi-logo .wpi-logo-dark,
    [data-bs-theme="dark"] .wpi-logo .wpi-logo-dark { display: none; }
}


/* =============================================================================================
   OFF-CANVAS DRAWER

   The mobile navigation pattern, and the reason it exists: a vertical sidebar that simply stops
   being fixed below its breakpoint collapses into a block at the TOP of the document, and the
   menu then pushes the whole page down when it opens. Every tap of the toggle shoves the content
   the user was reading off the screen. That is the stock Bootstrap behaviour and it is wrong on
   a phone.

   A drawer slides in over the page instead. The content never moves, the drawer is dismissible
   by tapping away from it, and it can be as tall as the viewport with its own scroll.

   The host app owns open and close; this file owns how it looks and moves. State is a
   data-open attribute rather than a class, so one attribute drives the panel, the backdrop and
   any aria wiring, and they cannot fall out of step.

       <div class="wpi-drawer-backdrop" data-open="false"></div>
       <aside class="wpi-drawer" data-open="false" aria-hidden="true"> ... </aside>
   ============================================================================================= */

.wpi-drawer {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    /* Never the full width. A drawer that covers everything gives no visual anchor back to the
       page, and no margin to tap in to dismiss it. */
    width: min(17rem, 82vw);
    max-width: 100%;
    z-index: 1045;
    display: flex;
    flex-direction: column;
    background: var(--wpi-surface);
    border-inline-end: 1px solid var(--wpi-hairline);
    /* Its own scroll, and momentum scrolling on iOS. overscroll-behavior stops a flick at the
       end of the menu from scrolling the page underneath, which is the single most irritating
       drawer bug on a phone. */
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    transform: translateX(-100%);
}

/* RTL: the drawer comes from the right, so it must leave to the right. */
[dir="rtl"] .wpi-drawer { transform: translateX(100%); }

.wpi-drawer[data-open="true"] { transform: none; }

.wpi-drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1040;
    /* A scrim darkens arbitrary content, so it is black regardless of palette. Same reasoning as
       the modal backdrop above. */
    background: rgba(0, 0, 0, 0.5);
    opacity: 0;
    /* pointer-events, not display: none. The element stays in the layout so its opacity can
       transition; display cannot be transitioned and the fade would never run. */
    pointer-events: none;
}

.wpi-drawer-backdrop[data-open="true"] {
    opacity: 1;
    pointer-events: auto;
}

@media (prefers-reduced-motion: no-preference) {
    .wpi-drawer {
        transition: transform 240ms var(--wpi-ease, cubic-bezier(0.4, 0, 0.2, 1));
    }
    .wpi-drawer-backdrop {
        transition: opacity 240ms var(--wpi-ease, cubic-bezier(0.4, 0, 0.2, 1));
    }
}

/* Nothing off-canvas exists on paper. */
@media print {
    .wpi-drawer,
    .wpi-drawer-backdrop { display: none !important; }
}
