/*
 * Storefront filters and card titles — shipped outside the build on purpose.
 *
 * Everything here used to live in resources/css/app.css, which is compiled by
 * Vite into a hashed file. Two releases in a row the hashed file on the server
 * was older than the templates: the phone filter card rendered its one-row
 * trigger (markup) and the runtime opened the bottom sheet (JavaScript), but
 * the stylesheet that hides the ten selects was never the new one — so the card
 * looked "open" on every phone.
 *
 * A hand-written file under public/ cannot go stale on its own: it is uploaded
 * with the templates, it carries a content hash in its URL (?v=…), and it is
 * requested before the built stylesheet so nothing in app.css can win over it.
 *
 * Rules of this file:
 *   • the filter card is closed everywhere until the visitor asks for it;
 *   • every filter field has the same width — a grid, never a ragged flex row;
 *   • the apply button comes after the last field, never in the middle;
 *   • a phone gets one row and a bottom sheet, by class (runtime) and by media
 *     query (no JavaScript). Nothing here depends on the other working.
 */

/* ── the card ─────────────────────────────────────────────────────────────── */

.catalog-storefront-filters {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 1.2rem;
    margin-bottom: 1.5rem;
}

.catalog-search-row input {
    width: 100%;
    background: var(--surface-raised);
    color: var(--text);
    border: 1px solid var(--line-strong);
    padding: .8rem 1rem;
    border-radius: .7rem;
    font: inherit;
    font-size: .95rem;
}
.catalog-search-row input:focus-visible,
.catalog-filter-more select:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.catalog-suggestions {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    z-index: 30;
    margin: .3rem 0;
    padding: .4rem;
    list-style: none;
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: .7rem;
    box-shadow: var(--shadow-lg);
    max-height: 22rem;
    overflow: auto;
}

.catalog-suggestions a { display: block; color: var(--text); padding: .7rem; text-decoration: none; border-radius: .4rem; }
.catalog-suggestions a:hover,
.catalog-suggestions a:focus-visible { background: var(--surface-muted); outline: 2px solid var(--focus); }

.catalog-storefront-wrapper[aria-busy='true'] { opacity: .7; }

/*
 * The one row: search, search, filters.
 *
 * The card used to paint two selects (and before that, eleven) before the first
 * account, so the live cards opened below the fold and the box read as a form
 * to fill in before anything could be seen. Now the row is a search field, a
 * search button and one button that opens the filters; every filter field lives
 * in the panel behind it, closed until the visitor asks for them.
 *
 * Nothing here sticks to the top of the screen. The card is one row tall and
 * the results are right under it, so a docked copy would only cover them.
 *
 * Width is the whole point of this block, so it is stated once, plainly:
 *   desktop — the search field takes the free space, the two buttons keep
 *             their own size, and all three are the same height;
 *   phone   — the three controls are stacked and each one is exactly 100% of
 *             the card's inner width, so the field and the two buttons are the
 *             same size, not a wide field over two uneven buttons;
 *   fields  — a grid of equal tracks, never a wrapping flex row: four equal
 *             columns on a desktop, two on a phone, one on a very narrow phone.
 */
.catalog-search-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: stretch;
    gap: .6rem;
    position: relative;
    width: 100%;
}

.catalog-search-row .catalog-search {
    display: block;
    margin: 0;
    min-width: 0;
    width: 100%;
}

.catalog-search-row .catalog-search input { height: 100%; min-height: 2.9rem; }

.catalog-search-row > .button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    min-height: 2.9rem;
    padding-inline: 1.15rem;
    white-space: nowrap;
}

.catalog-search-row > .catalog-search-submit { min-width: 7.5rem; }
.catalog-search-row > .catalog-filter-toggle { justify-self: stretch; }

.catalog-filter-more {
    display: none;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
    gap: .85rem .8rem;
    margin-block-start: .9rem;
    padding-block-start: .9rem;
    border-block-start: 1px solid var(--line);
}

.catalog-storefront-filters.is-expanded .catalog-filter-more { display: grid; }

/* Without JavaScript the toggle cannot open anything, so the fields simply
   stay visible: a filter that cannot be reached is worse than a tall card. */
html:not(.js) .catalog-filter-more { display: grid; }
html:not(.js) .catalog-filter-toggle { display: none; }

/*
 * One cell = one caption + one select, and every cell is built the same way, so
 * the selects of a whole row sit on one line. The caption is a single line
 * (clipped, never wrapped: a two-line caption used to push its own select down
 * and break the row) and every select has the same height.
 */
.catalog-filter-more label {
    display: grid;
    grid-template-rows: 1.15rem 2.75rem;
    align-items: center;
    gap: .3rem;
    min-width: 0;
    max-width: none;
    color: var(--text-soft);
    font-size: .8rem;
    line-height: 1.15rem;
    white-space: nowrap;
}

.catalog-filter-more .catalog-filter-caption {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.catalog-filter-more select {
    width: 100%;
    height: 2.75rem;
    min-width: 0;
    max-width: 100%;
    background: var(--surface-raised);
    color: var(--text);
    border: 1px solid var(--line-strong);
    border-radius: .5rem;
    padding: 0 .6rem;
    font: inherit;
    font-size: .85rem;
}

.catalog-filter-more select:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The apply button is the last control in the markup and spans the last row,
   so it is always found after the last filter field. */
.catalog-filter-more .catalog-filter-apply {
    grid-column: 1 / -1;
    justify-self: start;
    min-width: 10rem;
    min-height: 2.75rem;
}

.catalog-filter-toggle { display: inline-flex; align-items: center; gap: .35rem; }

.catalog-filter-toggle__count {
    display: inline-grid;
    place-items: center;
    min-width: 1.3rem;
    height: 1.3rem;
    margin-inline-start: .15rem;
    padding: 0 .3rem;
    border-radius: 999px;
    background: var(--gold);
    color: #fff;
    font-size: .72rem;
    font-weight: 800;
    line-height: 1;
}

.catalog-filter-toggle__count[hidden] { display: none; }
.catalog-filter-toggle__chev { font-size: .8rem; line-height: 1; transition: transform .18s ease; }
.catalog-filter-toggle[aria-expanded='true'] .catalog-filter-toggle__chev { transform: rotate(180deg); }

/* ── the phone: three controls, each exactly the width of the card ────────── */

/*
 * Two ways to know it is a phone, because one of them proved unreliable:
 * `html.is-filter-phone` is written by the runtime from matchMedia (plus a
 * coarse pointer, a short viewport and the user agent), and the media query is
 * the no-JavaScript fallback.
 *
 * The field and the two buttons are one column of equal, full-width controls.
 * `min-width: 0` matters: a grid item defaults to its content width, and a
 * long Persian placeholder was what kept the field short of 100%.
 */
@media (max-width: 48rem), (pointer: coarse) and (max-width: 64rem), (max-height: 30rem) {
    .catalog-filter-more { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }
    .catalog-search-row { grid-template-columns: minmax(0, 1fr); gap: .55rem; }
    .catalog-search-row .catalog-search,
    .catalog-search-row > .button { width: 100%; min-width: 0; justify-self: stretch; }
    /* The apply button is the fourth control of the same stack: full width too. */
    .catalog-filter-more .catalog-filter-apply { width: 100%; min-width: 0; justify-self: stretch; }
}

html.is-filter-phone .catalog-filter-more { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }
html.is-filter-phone .catalog-search-row { grid-template-columns: minmax(0, 1fr); gap: .55rem; }
html.is-filter-phone .catalog-search-row .catalog-search,
html.is-filter-phone .catalog-search-row > .button { width: 100%; min-width: 0; justify-self: stretch; }
html.is-filter-phone .catalog-filter-more .catalog-filter-apply { width: 100%; min-width: 0; justify-self: stretch; }

/* A very narrow phone gets one field per row: two selects side by side would
   be unreadable long before they are unusable. */
@media (max-width: 22rem) {
    /* The class-prefixed rule is listed too: it is more specific than the plain
       one, and on a 320px phone the class is exactly what is set. */
    .catalog-filter-more,
    html.is-filter-phone .catalog-filter-more { grid-template-columns: minmax(0, 1fr); }
}

@media print {
    .catalog-filter-toggle { display: none !important; }
    .catalog-filter-more { display: grid !important; }
}

/* ── the line under the filters, and the material after the cards ─────────── */

/*
 * Under the filters there is exactly one line — how many accounts or requests
 * the current filters left — and then the cards. Nothing sits between them.
 */
.catalog-count {
    margin: .2rem 0 1rem;
    font-size: .92rem;
    font-weight: 650;
    color: var(--text-soft);
}

/*
 * Secondary material (method performance, a hypothetical scenario, the
 * numbers of the page) belongs after the cards, folded and small: it informs
 * the visitor who scrolled to the end, and never pushes the live cards down.
 */
.catalog-secondary {
    margin-block: 2rem 1rem;
    padding: 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.catalog-secondary-title {
    margin: 0 0 .7rem;
    font-size: .95rem;
    font-weight: 800;
    color: var(--text);
}

.catalog-secondary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: .7rem;
    align-items: start;
}

.catalog-secondary-block {
    border: 1px solid var(--line);
    border-radius: .6rem;
    background: var(--surface-raised);
    padding: .6rem .75rem;
    font-size: .85rem;
}

.catalog-secondary-block > summary {
    cursor: pointer;
    font-weight: 700;
    color: var(--text);
    list-style-position: inside;
}

.catalog-secondary-block > summary:hover { color: var(--gold-ink); }
.catalog-secondary-block[open] > summary { margin-block-end: .5rem; }
.catalog-secondary-block .data-table-wrap { overflow-x: auto; }
.catalog-secondary-block .panel-table { font-size: .8rem; }

.catalog-secondary .catalog-kpi-strip { margin-block: .9rem 0; }
.catalog-secondary-links { margin: .9rem 0 0; }

@media (max-width: 40rem) {
    .catalog-secondary { padding: .7rem; }
    .catalog-secondary-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The body of an expandable tab, and the Q&A under it. Both are the closing
   pair of a method page and of the investor-request pages, so they look the
   same on every one of them. */
.closing-tab-body { margin-block-start: .4rem; color: var(--text-soft); line-height: 1.85; }
.closing-tab-body p { margin: 0 0 .5rem; }
.closing-tab-body p:last-child { margin-bottom: 0; }

.closing-faq {
    margin-block-start: .9rem;
    padding: 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.closing-faq__item {
    border-top: 1px solid var(--line);
    padding: .55rem 0;
    font-size: .88rem;
}

.closing-faq__item > summary {
    cursor: pointer;
    font-weight: 700;
    color: var(--text);
    list-style-position: inside;
}

.closing-faq__item > summary:hover { color: var(--gold-ink); }
.closing-faq__item > p { margin: .5rem 0 .2rem; color: var(--text-soft); line-height: 1.85; }

@media (max-width: 40rem) {
    .closing-faq { padding: .7rem; }
}

/* ── card titles ──────────────────────────────────────────────────────────── */

/*
 * Account and investor-request titles were `clamp(1.25rem, 2.5vw, 1.6rem)` and
 * shared their row with the method chip, so a real Persian title ran to three
 * or four lines and pushed the facts of the card out of sight. The title now
 * gets the full width of the card header, a smaller size and a tighter line
 * height; the chip drops to its own line on a phone. Two declarations, and the
 * three-or-four-line title is gone.
 */
.account-card .account-card-head,
.storefront-card .account-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .45rem .7rem;
    margin-block-end: .7rem;
}

/*
 * A card is a bold title and quiet facts under it.
 *
 * The title used to be a heading (1.16rem, and before that 1.6rem): bigger than
 * the platform's own body text, so it ran to three or four lines and pushed the
 * facts off the card. It is now exactly the body size — 1rem, the same as every
 * ordinary paragraph on the platform — and only bold: weight does the work,
 * size does not.
 *
 * Everything else inside a card is one step *below* body text and thin, so the
 * eye lands on the title and then reads the numbers without a second voice
 * competing with it. The selectors are prefixed with the card class, so they
 * always outrank the single-class rules in the built stylesheet.
 */
.account-card .account-card-head h3,
.storefront-card .account-card-head h3,
.account-card h3.card-title,
.storefront-card h3.card-title {
    flex: 1 1 100%;
    min-width: 0;
    margin: 0;
    font-size: 1rem;      /* exactly the body size of the platform */
    font-weight: 700;     /* only bolder, never larger */
    line-height: 1.45;
    letter-spacing: 0;
    text-wrap: balance;
    /*
     * A display title is capped when it is entered (60 characters), and it is
     * capped again here: two lines, then an ellipsis. The title is the one
     * piece of free text on a card, so without this a single long name made
     * its card taller than the whole row. The full name is still on the
     * detail page and in the page title.
     */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

.account-card .account-card-head h3 a,
.storefront-card .account-card-head h3 a { font-size: inherit; font-weight: inherit; }

.account-card .account-card-head .method-chip,
.storefront-card .account-card-head .method-chip { justify-self: start; }

/* The facts: below body size and light, so the title owns the card. */
.account-card .card-meta,
.storefront-card .card-meta,
.account-card .account-card-facts,
.storefront-card .account-card-facts,
.account-card .card-provenance,
.storefront-card .card-provenance {
    font-size: .8rem;
    font-weight: 400;
}

.account-card .account-card-facts dt,
.storefront-card .account-card-facts dt { font-size: .72rem; font-weight: 400; }

.account-card .account-card-facts dd,
.storefront-card .account-card-facts dd { font-size: .8rem; font-weight: 450; }

.account-card .card-provenance,
.storefront-card .card-provenance { font-size: .74rem; }

.account-card .fact-note,
.storefront-card .fact-note { font-size: .66rem; font-weight: 400; }

.account-card .account-card-actions__meta,
.storefront-card .account-card-actions__meta { font-size: .78rem; font-weight: 400; }

/*
 * The foot of a card: short labels only, one line each, equal widths.
 *
 * A long label ("پیوستن به حساب", "جزئیات حساب", "ارسال پیشنهاد مدیریت",
 * "مشاهدهٔ ترکیب و روش‌شناسی") wrapped onto a second line inside a narrow
 * column, so two cards side by side were never the same height and the row of
 * buttons never lined up. The labels are short now, and the buttons may not
 * wrap: whatever the wording, the block stays three tidy rows.
 *
 * `.storefront-card` prefix: app.css is loaded after this file and its
 * single-class button rules would otherwise win.
 */
.storefront-card .account-card-actions { display: grid; gap: .45rem; }

.storefront-card .account-card-actions .button,
.storefront-card .account-card-actions__grid > *,
.storefront-card .account-card-actions__meta > * {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .3rem;
    width: 100%;
    min-width: 0;
    min-height: 2.4rem;
    padding: .35rem .5rem;
    border-radius: .5rem;
    font-size: .8rem;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The join (or proposal) button is a step above the other four. */
.storefront-card .account-card-actions > .button-primary {
    min-height: 2.6rem;
    font-size: .86rem;
    font-weight: 700;
    background: var(--surface-strong);
    color: var(--on-dark);
    border: 1px solid var(--surface-strong);
}

.storefront-card .account-card-actions__grid,
.storefront-card .account-card-actions__meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .4rem;
}

/* Compare and watchlist are the same shape as the two buttons above them. */
.storefront-card .account-card-actions__meta > .compare-toggle,
.storefront-card .account-card-actions__meta > .watchlist-toggle {
    border: 1px solid var(--line-strong);
    background: var(--surface-muted);
    color: var(--text-soft);
    font-size: .78rem;
    font-weight: 600;
}

.storefront-card .account-card-actions__meta > .compare-toggle.is-selected {
    border-color: var(--gold);
    background: var(--gold-soft);
    color: var(--gold-ink);
    font-weight: 700;
}

.storefront-card .account-card-actions__meta > .compare-toggle.is-muted {
    color: var(--text-faint);
    white-space: normal;
    font-weight: 500;
}

/* A lone meta button (a card with no watchlist) spans the row. */
.storefront-card .account-card-actions__meta > :only-child { grid-column: 1 / -1; }

/*
 * The marketplace and contest cards.
 *
 * Both pages build their cards from `.calculator-card` and put their calls to
 * action in `.panel-button`. That class is styled in `assets/panel.css`, which
 * the public layout does not load — so on `/shop` and `/contests` every one of
 * those "buttons" has been a bare text link. These two rules give them the
 * platform's own button shape, and they are scoped to the card, so no other
 * public page changes.
 */
.calculator-card .panel-button,
.calculator-hub-grid .panel-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    min-height: 2.2rem;
    padding: .35rem .85rem;
    border: 1px solid var(--line-strong);
    border-radius: .55rem;
    background: var(--surface-muted);
    color: var(--text);
    font-size: .8rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.calculator-card .panel-button.gold,
.calculator-hub-grid .panel-button.gold {
    background: var(--surface-strong);
    border-color: var(--surface-strong);
    color: var(--on-dark);
    font-weight: 700;
}

/* The contest card is one big link; its closing line is the "open" affordance. */
.calculator-card > span {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .82rem;
    font-weight: 700;
    color: var(--gold-ink);
}

/* The small chips are labels, not sentences. */
.account-card .method-chip,
.storefront-card .method-chip,
.account-card .badge-pill,
.storefront-card .badge-pill,
.account-card .provenance-chip,
.storefront-card .provenance-chip { font-size: .68rem; font-weight: 450; }

.account-card .account-code-pill,
.storefront-card .account-code-pill { font-size: .68rem; font-weight: 600; }

.account-card .affiliation-badge,
.storefront-card .affiliation-badge { font-size: .74rem; font-weight: 400; }
.storefront-card .affiliation-badge { font-size: .76rem; font-weight: 400; }

/* ── card surfaces: every card has to read as a card ─────────────────────── */

/*
 * The complaint was exact: on almost every page the card and the page were the
 * same colour. A card was painted `var(--surface)` (#ffffff) on a page whose
 * wrapper was *also* painted `var(--surface)`, and its only edge was one pixel
 * of `var(--line)` (#e3e4e3) — a hairline a good screen can separate and a
 * normal one cannot. On top of that, half of the module pages (contests,
 * marketplace, academy, prop) style their cards with `.panel-card`, which had
 * no rule at all outside the panel stylesheet.
 *
 * Four changes, and nothing decorative beyond them:
 *
 *   1. The page keeps the platform canvas — `--bg` plus the body's 2.5% grid —
 *      and the card sits one clean step above it, with a border you can see,
 *      a soft two-layer shadow, and a 1px highlight along the top edge that
 *      gives the surface a little depth.
 *
 *   2. One token layer owns it: `--surface-card`, `--surface-card-alt`,
 *      `--edge-card`, `--edge-card-strong`, `--elevation-card`. The names are
 *      deliberately not `--card-bg`/`--line`: the panel stylesheet already
 *      owns those, and two systems fighting over one name is how a dark theme
 *      breaks six months later.
 *
 *   3. The tokens are re-declared *on the card element itself*. This is the
 *      part that makes it work everywhere at once: hundreds of blades write
 *      `background: var(--surface)` and `border: 1px solid var(--line)` inline
 *      on the card, and an inline style beats any stylesheet — but a custom
 *      property declared on the element is what `var(--surface)` inside that
 *      inline style resolves to. So those declarations follow the new values
 *      by themselves: no `!important`, no blade-by-blade rewrite, no card left
 *      behind. `!important` borders already in app.css resolve the same way.
 *
 *   4. `main .panel-card` outranks panel.css's single-class `.panel-card`, so
 *      the panel pages get the same card while the public module pages get a
 *      card they never had.
 */

:root {
    --surface-card: #fffdf6;
    --surface-card-alt: #fbf8ef;
    --edge-card: #d2c8ac;
    --edge-card-strong: #9c8e72;
    --elevation-card: 0 1px 2px rgb(18 19 16 / 7%), 0 12px 26px -18px rgb(18 19 16 / 38%);
    --elevation-card-hover: 0 2px 4px rgb(18 19 16 / 9%), 0 22px 44px -22px rgb(18 19 16 / 44%);
    --sheen-card: linear-gradient(180deg, rgb(255 253 244 / 90%), rgb(255 254 251 / 0) 46%);
}

/*
 * The three themes are handled in the same order app.css handles them, so the
 * cards follow whichever theme the panel chose: html[data-theme] first, then
 * the body.theme-* overrides, then high contrast — where a card keeps a hard
 * black edge and no shadow at all.
 */
html[data-theme='dark'],
body.theme-dark {
    --surface-card: #292518;
    --surface-card-alt: #322d1d;
    --edge-card: #4a4230;
    --edge-card-strong: #7a6e4c;
    --elevation-card: 0 1px 2px rgb(0 0 0 / 45%), 0 16px 32px -20px rgb(0 0 0 / 72%);
    --elevation-card-hover: 0 2px 6px rgb(0 0 0 / 55%), 0 26px 50px -22px rgb(0 0 0 / 85%);
    --sheen-card: linear-gradient(180deg, rgb(255 248 230 / 5%), rgb(255 255 255 / 0) 44%);
}

body.theme-light {
    --surface-card: #fffdf6;
    --surface-card-alt: #fbf8ef;
    --edge-card: #d2c8ac;
    --edge-card-strong: #9c8e72;
    --elevation-card: 0 1px 2px rgb(18 19 16 / 7%), 0 12px 26px -18px rgb(18 19 16 / 38%);
    --elevation-card-hover: 0 2px 4px rgb(18 19 16 / 9%), 0 22px 44px -22px rgb(18 19 16 / 44%);
    --sheen-card: linear-gradient(180deg, rgb(255 253 244 / 90%), rgb(255 254 251 / 0) 46%);
}

body.theme-contrast {
    --surface-card: #ffffff;
    --surface-card-alt: #ffffcc;
    --edge-card: #000000;
    --edge-card-strong: #000000;
    --elevation-card: none;
    --elevation-card-hover: none;
    --sheen-card: none;
}

/*
 * Every card container on the platform. `.panel-card` carries the module
 * pages (contest, marketplace, academy, prop, requests, panel); the rest are
 * the shapes app.css grew one page at a time.
 */
main .panel-card,
main .detail-card,
main .calculator-card,
main .copy-card,
main .prop-tier,
main .capability-card:not(.capability-card-feature),
main .signal-card,
main .hc-card,
main .method-filter-card,
main .module-card-item,
main .principle-card,
main .principle-grid .principle-card,
main .start-paths-grid .principle-card,
main .live-boards-grid .principle-card,
main .calculator-hub-grid .principle-card,
main .calculator-panel {
    --surface: var(--surface-card);
    --surface-raised: var(--surface-card-alt);
    --line: var(--edge-card);
    --line-strong: var(--edge-card-strong);
    --card-bg: var(--surface-card);
    --border-color: var(--edge-card);
    background-color: var(--surface-card);
    background-image: var(--sheen-card);
    border-color: var(--edge-card);
    box-shadow: var(--elevation-card);
}

/*
 * The account and investor-request cards already carry a coloured 3px top edge
 * that names the method the account belongs to, so the colour is set on the
 * other three sides only — a plain `border-color` here would repaint that
 * accent grey and lose the one signal the card was designed around.
 */
.account-card,
.storefront-card {
    --surface: var(--surface-card);
    --surface-raised: var(--surface-card-alt);
    --line: var(--edge-card);
    --line-strong: var(--edge-card-strong);
    --card-bg: var(--surface-card);
    --border-color: var(--edge-card);
    background-color: var(--surface-card);
    background-image: var(--sheen-card);
    border-inline-color: var(--edge-card);
    border-block-end-color: var(--edge-card);
    box-shadow: var(--elevation-card);
}

/*
 * A card that answers the pointer lifts two pixels and firms its edge — the
 * motion is the existing hover behaviour of the catalogue cards, now shared,
 * and it is switched off for anyone who asked the system to reduce motion.
 */
main .panel-card.is-hoverable:hover,
main .detail-card:hover,
main .calculator-card:hover,
main .copy-card:hover,
main .calculator-panel:hover,
main .capability-card:not(.capability-card-feature):hover,
.account-card:hover,
.storefront-card:hover,
main .principle-card:hover {
    border-color: var(--edge-card-strong);
    box-shadow: var(--elevation-card-hover);
}

@media (prefers-reduced-motion: no-preference) {
    main .panel-card,
    main .detail-card,
    main .calculator-card,
    main .prop-tier,
    main .copy-card,
    main .capability-card,
    .account-card,
    .storefront-card {
        transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
    }

    main .detail-card:hover,
    main .calculator-card:hover,
    main .copy-card:hover,
    main .capability-card:hover {
        transform: translateY(-2px);
    }
}

/* A card's own inner wells step down one token, never back to the page. */
main .panel-card .panel-card,
main .detail-card .panel-card {
    --surface: var(--surface-card-alt);
    --line: var(--edge-card);
    background-color: var(--surface-card-alt);
}

/*
 * A two-cell stat strip inside a prop tier is its own well: the cells take the
 * muted surface and the grid gaps keep the card edge colour, so the block
 * still reads as one bordered box instead of melting into the tier.
 */
main .prop-tier .prop-tier-stats {
    background: var(--edge-card);
    border-color: var(--edge-card);
}

main .prop-tier .prop-tier-stats > div {
    background-color: var(--surface-muted);
}

/*
 * ── the code, the ID, the unique thing ────────────────────────────────────
 *
 * A display title is free text: two languages, up to a sentence, different
 * length on every account. In a table column or a row cell it stops being a
 * name and becomes a layout bug. Tables and comparison headers therefore show
 * the unique account code (or request code) and nothing else; the title stays
 * on the card, the detail page and the page title, where it has room.
 */
main a.account-code-badge,
main a.account-code-pill {
    text-decoration: none;
    color: var(--text);
}

main a.account-code-badge:hover,
main a.account-code-pill:hover {
    border-color: var(--gold);
    color: var(--gold-ink);
}

.account-code-note {
    display: block;
    margin-block-start: 2px;
    color: var(--text-soft);
    font-size: 0.74rem;
}

/* A table cell holding a code never wraps the code itself onto a second line. */
main .data-table .account-code-badge,
main .data-table .account-code-pill,
main .compare-table .account-code-pill {
    white-space: nowrap;
}

/*
 * ── `.panel-button` on a public page ──────────────────────────────────────
 *
 * The class is defined in `assets/panel.css`, which a public page does not
 * load — so a "panel button" on a detail page rendered as bare underlined
 * text next to real buttons. It is a button here too, in the same shape and
 * size as `.button`, and the module-specific rules above still win where a
 * card needs something else.
 */
main :is(.panel-button, a.panel-button, button.panel-button) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-height: 2.35rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--edge-card-strong);
    border-radius: 0.55rem;
    background: var(--surface-card);
    color: var(--text);
    font: inherit;
    font-size: 0.84rem;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}

main :is(.panel-button, a.panel-button, button.panel-button):hover {
    border-color: var(--gold);
    background: var(--gold-soft);
    color: var(--gold-ink);
}

main :is(.panel-button, a.panel-button).gold {
    border-color: var(--bd-gold);
    background: var(--gold-soft);
    color: var(--gold-ink);
}

main :is(.panel-button, a.panel-button).secondary {
    background: var(--surface-card-alt);
    color: var(--text-soft);
}

main :is(.panel-button, a.panel-button).danger {
    border-color: var(--bd-danger);
    background: var(--tint-danger);
    color: var(--fg-danger);
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Blocks — everything on a page that is not a card but still has to read as an
 * object rather than a rectangle drawn on the canvas.
 *
 * The card layer above answers "what is a card". This one answers "what is a
 * block": the search card, the field wells inside it, the KPI strip, the table
 * frames, the chips, the pager, the dropdown, the comparison dock and the two
 * closing tabs. They all hang off the same three tokens the cards use, so a
 * theme — including the dark one — only ever changes values in one place.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* 1. A block face: the search/filter card, the KPI strip, a table frame, the
 *    dock, the dropdown and the closing tabs. */
main .catalog-storefront-filters,
main .catalog-kpi-strip,
main .data-table-wrap,
main .catalog-suggestions,
main .catalog-secondary-block,
main .hc-card,
main .compare-dock,
main .empty-state-clean {
    --surface: var(--surface-card);
    --surface-raised: var(--surface-card-alt);
    --line: var(--edge-card);
    --line-strong: var(--edge-card-strong);
    --border-color: var(--edge-card);
    background-color: var(--surface-card);
    border-color: var(--edge-card);
    box-shadow: var(--elevation-card);
}

/* 2. A well: every field the visitor types into sits *inside* a block, so it
 *    steps down one tone instead of competing with the block it lives in. */
main .catalog-search-row input,
main .catalog-filter-more select,
main .catalog-filter-more input,
main .panel-form input,
main .panel-form select,
main .panel-form textarea,
main .form-control,
main .catalog-suggestions a:hover,
main .catalog-suggestions a:focus-visible {
    --surface: var(--surface-muted);
    --line: var(--edge-card);
    --line-strong: var(--edge-card-strong);
    background-color: var(--surface-muted);
    border-color: var(--edge-card);
}

/* 3. A chip and a pager pill: small objects with the same edge, so they are
 *    visible on the page and on a card without a second look. */
main .method-chip,
main .filter-chip,
main .account-code-pill,
main .account-code-badge,
main .pg-pill,
main .compare-dock__clear {
    border-color: var(--edge-card);
    background-color: var(--surface-muted);
    color: var(--text);
}

main .pg-pill.is-current {
    border-color: var(--edge-card-strong);
    background-color: var(--text);
    color: var(--surface-card);
}

/* 4. An alert keeps its semantic tint; only the frame follows the block edge,
 *    so a warning is still a warning in every theme. */
main .panel-alert {
    box-shadow: var(--elevation-card);
}

/* 5. A block inside a block. Prop tiers print their figures in a sub-grid and
 *    baskets print one cell per account; both used to inherit the card surface
 *    and vanish into it. They step down to the well tone instead, so the grid
 *    still reads as a grid and every cell keeps a visible edge. */
main .prop-tier .prop-tier-stats {
    background-color: var(--edge-card);
    border-color: var(--edge-card);
}

main .prop-tier .prop-tier-stats > div {
    background-color: var(--surface-muted);
    border-color: var(--edge-card);
}

main .panel-alert.info { border-color: var(--bd-info); }
main .panel-alert.ok, main .panel-alert.success { border-color: var(--bd-ok); }
main .panel-alert.warn, main .panel-alert.warning { border-color: var(--bd-warn); }
main .panel-alert.danger, main .panel-alert.error { border-color: var(--bd-danger); }

/* ═══════════════════════════════════════════════════════════════════════════
 * QR share (2026.10.18.103)
 *
 * One control on every card that can be scanned off a screen. The code itself
 * is painted by resources/js/qr.js with a real encoder; this file only decides
 * how the button, the sheet and the printed seal look. The image keeps a white
 * field in both themes on purpose: a camera cannot read light modules on a dark
 * card, so the scannable area never follows the theme.
 * ═══════════════════════════════════════════════════════════════════════════ */

.qr-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.12rem 0.5rem;
    border: 1px solid var(--edge-card, #d2c8ac);
    border-radius: 0.45rem;
    background: var(--surface-muted, #ece7d8);
    color: var(--text, #23201a);
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.7;
    white-space: nowrap;
    cursor: pointer;
}

.qr-toggle:hover,
.qr-toggle:focus-visible {
    border-color: var(--edge-card-strong, #9c8e72);
    color: var(--gold-ink, #7c5b0d);
}

.qr-toggle svg {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linejoin: round;
}

.qr-sheet {
    /* A sheet, not a page: it never grows past the viewport, never sits on the
     * edge of the screen, and scrolls inside itself when the phone is short, so
     * the code at the top is never the part that gets cut off. The position in
     * the top layer comes from the dialog's own `:modal` rule — no fixed
     * positioning is declared here, because the filter panel contract forbids
     * one in this stylesheet. */
    inset: 0;
    width: min(22rem, calc(100vw - 1.5rem));
    max-width: none;
    max-height: min(46rem, calc(100dvh - 1.5rem));
    margin: auto;
    padding: 0;
    overflow: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--edge-card, #d2c8ac);
    border-radius: 16px;
    background: var(--surface-card, #fffdf6);
    color: var(--text, #23201a);
    box-shadow: 0 30px 70px -30px rgb(10 9 6 / 45%);
}

.qr-sheet:not([open]) {
    display: none;
}

.qr-sheet::backdrop {
    background: rgb(12 11 8 / 55%);
}

.qr-sheet__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 18px 18px 16px;
}

.qr-sheet__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    text-align: start;
}

.qr-sheet__title {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.5;
}

.qr-sheet__close {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: inline-grid;
    place-items: center;
    padding: 0;
    border: 1px solid var(--edge-card, #d2c8ac);
    border-radius: 8px;
    background: var(--surface-muted, #ece7d8);
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.qr-sheet__mount {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Never wider than the sheet, so the code cannot be clipped by its own box. */
    width: min(190px, 58vw);
    aspect-ratio: 1 / 1;
    height: auto;
    max-width: 100%;
    padding: 8px;
    border: 1px solid var(--edge-card, #d2c8ac);
    border-radius: 12px;
    background: #ffffff;
    color: #16140f;
    overflow-wrap: anywhere;
    font-size: 0.72rem;
}

.qr-sheet__mount svg {
    display: block;
    width: 100%;
    height: auto;
}

.qr-sheet__code {
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--text-soft, #6b6252);
}

.qr-sheet__hint {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.65;
    color: var(--text-soft, #6b6252);
    text-align: center;
}

.qr-sheet .copy-field {
    width: 100%;
    text-align: start;
}

.qr-sheet .copy-field__label {
    font-size: 0.78rem;
}

/* The seal printed on a fact sheet: same code, sitting in the sheet itself. */
.qr-seal {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}

.qr-seal__mount {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 128px;
    height: 128px;
    padding: 4px;
    border: 1px solid var(--edge-card, #cbd5e1);
    border-radius: 8px;
    background: #ffffff;
    color: #16140f;
}

.qr-seal__mount svg {
    display: block;
    width: 100%;
    height: auto;
}

.qr-seal__code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.74rem;
    font-weight: 800;
    color: var(--text-soft, #6b6252);
}

@media print {
    .qr-toggle { display: none !important; }
    .qr-sheet { display: none !important; }
    .qr-seal__mount { border-color: #000000; }
}

@media (max-width: 30rem) {
    .qr-sheet__mount { width: min(168px, 58vw); }
}

@media (max-height: 34rem) {
    .qr-sheet__mount { width: min(150px, 46vw); }
    .qr-sheet__hint { display: none; }
}

/* The closing row of a card: the way in on one side, the QR on the other, so a
 * card that is not a storefront card still ends with the same pair of controls. */
.card-actions-row,
.card-actions-inline {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.card-actions-row {
    justify-content: space-between;
    margin-top: 12px;
}

.card-actions-inline {
    gap: 8px;
}

/*
 * The QR control always ends a card on a line of its own, full width.
 *
 * As a half-width chip beside the compare/watchlist pills it read as something
 * left over, and on a phone it wrapped to its own line anyway — leaving a small
 * button floating at one edge of an otherwise empty row. One width, one line,
 * one position on every card that carries it.
 */
.card-actions-row > .qr-toggle,
.account-card-actions__meta > .qr-toggle,
.card-actions-inline > .qr-toggle,
.tier-qr-row > .qr-toggle,
.qr-toggle:only-child {
    flex: 1 1 100%;
    width: 100%;
    margin-inline-start: 0;
    justify-content: center;
    min-height: 2.1rem;
}

/* A card title that is also the link into the entity keeps the card's own
 * colour and weight; only the affordance changes. */
main .calculator-card h2 a,
main .prop-tier h3 a {
    color: inherit;
    text-decoration: none;
}

main .calculator-card h2 a:hover,
main .prop-tier h3 a:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* The prop tier ends with a full-width start button, so its QR sits on its own
 * line, aligned with the end of the card. */
.tier-qr-row {
    display: flex;
    justify-content: flex-end;
    margin: 0;
}
