/* ==========================================================================
   EXCHANGE LAYER
   ==========================================================================
   Loaded after app.css on the public storefront only. It restyles; it does not
   redefine the design tokens, and it adds no new colour that is not already a
   var() on :root - so both themes keep working with no second palette to
   maintain.

   Kept as a separate file for one practical reason: rolling the look back is
   removing one <link>, not unpicking a diff.

   Nothing here changes what the buy form DOES. Every hook the JavaScript and
   the server rely on - data-qty-*, data-guard, the CSRF field, the quantity
   input's name - is untouched. This file only decides how it looks.
   ========================================================================== */

/* --- Numerals ------------------------------------------------------------
   An exchange is read as columns of figures. Tabular numerals stop digits
   jittering as a price updates, which is what makes a live figure feel steady
   rather than twitchy. */
.token-price,
.amount-value,
.qty-total b,
.swap-value,
.ticker-item b,
.fact-value {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

/* --- The swap panel ------------------------------------------------------
   "You pay" over "You receive", the shape every exchange uses.

   The quantity field is the ONLY editable one, and that is deliberate. The
   server validates a quantity, converts a quantity to base units exactly once,
   and the delivery verifier compares those base units byte for byte with no
   tolerance. Making "You pay" editable would mean dividing by the unit price in
   the browser to get back to a quantity - which produces more decimal places
   than the token supports, and which with volume bands is circular, because the
   price depends on the quantity. So: pay is computed, receive is typed. */
.swap-card {
    position: relative;
    overflow: hidden;
}

.swap-leg {
    background: var(--surface-2);
    border: 1px solid var(--rule-soft);
    border-radius: var(--r);
    padding: 14px 16px;
}

.swap-leg + .swap-leg { margin-top: 0; }

.swap-leg-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 6px;
}

.swap-leg-label {
    font-family: var(--f-mono);
    font-size: 0.68rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.swap-value {
    font-family: var(--f-display);
    font-size: clamp(1.5rem, 5vw, 1.9rem);
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.02em;
}

.swap-asset {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: var(--glass-strong);
    border: 1px solid var(--rule);
    border-radius: var(--r-pill);
    padding: 5px 12px 5px 6px;
    font-size: 0.82rem;
    font-weight: 600;
    white-space: nowrap;
}

.swap-asset-mark {
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--grad-accent);
    color: var(--accent-ink);
    font-size: 0.6rem;
    font-weight: 700;
    font-family: var(--f-mono);
}

/* The arrow between the legs, overlapping both. */
.swap-divider {
    position: relative;
    height: 10px;
    display: grid;
    place-items: center;
    z-index: 1;
}

.swap-arrow {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--surface-3);
    border: 1px solid var(--rule);
    color: var(--accent);
    box-shadow: var(--shadow-sm);
}

/* The quantity input, restyled to sit inside a leg rather than look like a
   form field bolted on. */
.swap-leg .swap-input {
    width: 100%;
    background: none;
    border: 0;
    padding: 0;
    font-family: var(--f-display);
    font-size: clamp(1.5rem, 5vw, 1.9rem);
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}
.swap-leg .swap-input:focus { outline: none; }
.swap-leg .swap-input::placeholder { color: var(--ink-3); }
.swap-leg:focus-within {
    border-color: var(--accent);
    box-shadow: var(--ring);
}
.swap-leg .swap-input.is-invalid { color: var(--crit); }

/* A slow sheen along the panel's top edge. Purely decorative, and switched off
   under reduced motion below. */
.swap-card::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: var(--grad-line);
    opacity: 0.7;
    animation: swap-sheen 6s ease-in-out infinite;
}

@keyframes swap-sheen {
    0%, 100% { transform: translateX(-30%); opacity: 0.25; }
    50%      { transform: translateX(30%);  opacity: 0.8; }
}

/* --- Fact grid -----------------------------------------------------------
   Replaces the inline-styled 4-cell block on the token page. Same look, no
   inline styles, and reusable. */
.fact-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 1px;
    background: var(--rule-soft);
    border: 1px solid var(--rule-soft);
    border-radius: var(--r);
    overflow: hidden;
}

.fact-cell { background: var(--surface); padding: 12px 14px; }

.fact-label {
    font-family: var(--f-mono);
    font-size: 0.64rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin-bottom: 6px;
}

.fact-value {
    font-family: var(--f-mono);
    font-weight: 600;
    font-size: 0.92rem;
    overflow-wrap: anywhere;
}

/* --- Denser token cards --------------------------------------------------- */
@media (min-width: 900px) {
    .token-card { padding: 18px; gap: 12px; }
}

/* The hover treatment lives in app.css. This file used to re-declare it
   and, without meaning to, weaken it - the border dropped to a 40% mix of
   the accent and the shadow to the smaller one, so the storefront card
   responded LESS than the base design system intended. Removed. */

/* --- Ticker: an exchange tape -------------------------------------------- */
.ticker { border-bottom: 1px solid var(--rule-soft); }

.ticker-item {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
}

/* --- Filter bar ----------------------------------------------------------
   Was a hard-coded four-column inline grid with no media query, which gave
   roughly 80px columns on a phone. */
.filter-bar {
    display: grid;
    gap: 12px;
    align-items: end;
    grid-template-columns: 1fr;
}

@media (min-width: 560px) {
    .filter-bar { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 900px) {
    .filter-bar { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; }
}

/* --- Market table --------------------------------------------------------
   Horizontal scrolling lives in .table-scroll (app.css). The min-width here
   is what makes it actually scroll on a phone rather than crushing seven
   columns into 360px - the page body never scrolls sideways, the table does. */
.table-market { min-width: 720px; }
.table-market th, .table-market td { padding-block: 9px; }
.table-market .num { text-align: right; }
.table-market th.num a { justify-content: flex-end; }

.market-asset { display: flex; align-items: center; gap: 10px; min-width: 0; color: inherit; }
.market-asset:hover .market-name { color: var(--accent); }
.market-asset .token-logo { width: 28px; height: 28px; border-radius: 8px; font-size: 0.66rem; flex: none; }
.market-name { display: block; font-weight: 600; font-size: 0.92rem; line-height: 1.25; transition: color var(--t-fast); }
.market-sym { display: block; font-family: var(--f-mono); font-size: 0.74rem; color: var(--ink-3); }

/* Sorting is a link, so the header has to look like one without looking like
   body copy. The caret carries the state; colour reinforces it. */
.th-sort a { display: inline-flex; align-items: center; gap: 4px; color: inherit; }
.th-sort a:hover { color: var(--ink); }
.th-sort[aria-sort="ascending"] a,
.th-sort[aria-sort="descending"] a { color: var(--accent); }
.sort-caret { opacity: 0.3; flex: none; }
.th-sort[aria-sort="ascending"] .sort-caret,
.th-sort[aria-sort="descending"] .sort-caret { opacity: 1; }

.filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.filter-chip {
    padding: 5px 12px;
    border-radius: var(--r-pill);
    background: var(--glass-strong);
    color: var(--ink-2);
    font-size: 0.8rem;
    font-weight: 500;
    transition: background var(--t-fast), color var(--t-fast);
}
.filter-chip:hover { color: var(--ink); }
.filter-chip.active { background: var(--accent-wash); color: var(--accent); }

/* A single highlight travelling across the table on load. One-shot, and
   it ends fully transparent - so the reduced-motion clamp lands it at its
   resting state with nothing left painted. Named in the kill list anyway. */
.table-sweep { position: relative; overflow: hidden; }

.table-sweep::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--grad-sheen);
    background-size: 220% 100%;
    animation: table-sweep 1100ms ease-out both;
    pointer-events: none;
}

@keyframes table-sweep {
    from { background-position: 200% 0; opacity: 1; }
    to   { background-position: -60% 0; opacity: 0; }
}

/* --- Hero -----------------------------------------------------------------
   The stats are the right column now, not a strip under the CTA, so the
   40px top margin they carried is cancelled. Two columns of figures reads
   as a panel; four in a row reads as a toolbar. */
.hero-stats-panel { margin-top: 0; }

@media (min-width: 980px) {
    .hero-stats-panel { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The hero used to leave up to 168px of stacked padding above the next
   section. It carries its own block-end now and the market section starts
   flush against it. */
.hero-lead { padding-block: clamp(44px, 7vw, 84px) clamp(20px, 3vw, 32px); }
.section-market { padding-block: 0 clamp(32px, 5vw, 56px); }

/* --- Mobile: put the buy panel first -------------------------------------
   .grid-sidebar collapses to one column below 940px, which on every phone puts
   the token's identity and contract details ABOVE the thing the visitor came to
   do. Scoped to a modifier so checkout and the landing page are unaffected. */
@media (max-width: 939.98px) {
    .grid-sidebar-buy > .sticky-side { order: -1; }
    .grid-sidebar-buy { display: flex; flex-direction: column; }
}

/* --- Mobile: real viewport height ---------------------------------------
   Fallback first, so a browser without dvh keeps the old value rather than
   dropping the declaration entirely. */
body { min-height: 100vh; min-height: 100dvh; }

.mobile-nav {
    max-height: calc(100vh - var(--header-h));
    max-height: calc(100dvh - var(--header-h));
}

/* --- Mobile: notch and home indicator ------------------------------------
   The layout already ships viewport-fit=cover, so without these the content
   runs under the rounded corners and the home bar. */
.site-header .wrap {
    padding-inline: max(var(--gutter), env(safe-area-inset-left), env(safe-area-inset-right));
}

.site-footer { padding-bottom: max(24px, env(safe-area-inset-bottom)); }

.mobile-nav { padding-bottom: max(16px, env(safe-area-inset-bottom)); }

/* --- Mobile: the checkout quantity field ---------------------------------- */
.qty-inline { width: 100%; max-width: 9rem; }

@media (max-width: 559.98px) {
    .qty-inline { max-width: none; }
}

/* --- Reduced motion -------------------------------------------------------
   The global clamp in app.css shortens animations but still lets a loop run
   once, which can leave an element parked in its final keyframe. Anything that
   loops has to be named explicitly, so this file names its own. */
@media (prefers-reduced-motion: reduce) {
    .swap-card::before { animation: none !important; opacity: 0.4; transform: none; }
    /* Defensive: table-sweep is a one-shot ending transparent, so the
       clamp already handles it. Named so no highlight can ever be left
       painted across the rows. */
    .table-sweep::after { animation: none !important; opacity: 0; }
}
