/**
 * Vintedex, web.
 *
 * Every colour, radius, size and duration comes from shared/design/tokens.css.
 * Nothing is decided here. A hex typed into this file is a decision made twice
 * and a theme that will be wrong in one of them, and tools/check_design.py
 * fails the build for it rather than trusting anybody to remember.
 *
 * The visual language is Vinted's, read off their stylesheet: 6px on controls
 * and cards, 12px on sheets, pills for chips, 44px controls, hairlines rather
 * than shadows, Inter. The seller has their tab open beside this one all day.
 *
 * Both themes, and the earlier version had neither: it was light because the
 * phone was dark, which is a preference dressed as a reason. It now follows
 * the machine and can be overridden; see the theme switch in ui.js.
 */

@import url("./tokens.css");

*,
*::before,
*::after { box-sizing: border-box; }

html {
    /* Tells the browser to draw its own furniture - scrollbars, the default
       form controls, the caret - in the running theme. Without it a dark page
       gets a light scrollbar and native checkboxes stay white. */
    color-scheme: light dark;
    accent-color: rgb(var(--vx-accent));
}

body {
    --vx-wordmark: url("../img/wordmark.png");
    margin: 0;
    background: rgb(var(--vx-page));
    color: rgb(var(--vx-ink));
    font-family: var(--vx-font);
    font-size: var(--vx-text-body);
    line-height: var(--vx-leading);
    -webkit-font-smoothing: antialiased;
}

a {
    color: rgb(var(--vx-accent-ink));
    text-underline-offset: 2px;
}

/* Focus is visible everywhere, once, and never removed for looks. The ring is
   outside the element so it does not shift anything as it appears. */
:focus-visible {
    outline: 2px solid rgb(var(--vx-accent));
    outline-offset: 2px;
    border-radius: var(--vx-radius);
}

/* ───────────────────────────────────────────────────────── Shell */

/*
 * The header, in Vinted's arrangement.
 *
 * Two rows, and it is the same two at every width: identity and actions, then
 * the sections. Their site does the same thing, and the only difference at
 * 390px is that the section row turns into pills that scroll sideways and the
 * quiet actions fold behind a menu.
 *
 * No second markup for mobile. One header, one set of rules, so nothing can
 * appear on one width and be forgotten on the other.
 */
.topbar {
    position: sticky;
    top: 0;
    /* The positioning context for the narrow-width menu sheet below. */
    isolation: isolate;
    z-index: var(--vx-z-sticky);
    background: rgb(var(--vx-surface));
    border-bottom: var(--vx-border) solid rgb(var(--vx-line));
}

.topbar-main {
    display: flex;
    align-items: center;
    gap: var(--vx-space-m);
    padding: var(--vx-space-s) var(--vx-space-m);
    min-height: 64px;
    max-width: 76rem;
    margin: 0 auto;
}

/*
 * The wordmark, painted rather than drawn.
 *
 * The PNG is one flat colour on transparency, so it is used as a MASK and the
 * colour comes from the accent token. One asset serves both themes, the mark
 * can never be the wrong teal, and there is no second file to fall out of step
 * with the first. It also fixes the artwork being the print teal on a screen
 * that is not.
 */
.brand {
    display: block;
    height: 26px;
    aspect-ratio: 274 / 66;
    flex: 0 0 auto;
    background: rgb(var(--vx-accent-ink));
    -webkit-mask: var(--vx-wordmark) no-repeat center left / contain;
    mask: var(--vx-wordmark) no-repeat center left / contain;
}

.topbar-sell { margin-left: auto; }

.topbar-actions {
    display: flex;
    align-items: center;
    gap: var(--vx-space-s);
}

.topbar .who {
    color: rgb(var(--vx-ink-faint));
    font-size: var(--vx-text-label);
    white-space: nowrap;
}

/* Chrome, not the work. The full 44px is for what a person came to press. */
.topbar button,
.topbar .btn {
    min-height: var(--vx-control-h-sm);
    padding: 0 var(--vx-space-m);
    font-size: var(--vx-text-label);
    flex: 0 0 auto;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
}

/*
 * The menu button only exists where the actions do not fit.
 *
 * Written as `.topbar .topbar-menu` and not `.topbar-menu`, because the rule
 * above it is `.topbar button`, which is a class plus an element and therefore
 * more specific than a class alone. Left as one class this said display:none
 * and lost, and the hamburger sat in the desktop header looking deliberate.
 */
.topbar .topbar-menu { display: none; }

/*
 * The section row.
 *
 * Text links at desktop, exactly as theirs are, and the current one carries the
 * accent wash rather than an underline: an underline on a nav item competes
 * with every real link on the page below it.
 */
.topbar-nav {
    display: flex;
    gap: var(--vx-space-xs);
    padding: 0 var(--vx-space-m) var(--vx-space-s);
    max-width: 76rem;
    margin: 0 auto;
    overflow-x: auto;
    scrollbar-width: none;
}
.topbar-nav::-webkit-scrollbar { display: none; }

.topbar-nav a {
    padding: var(--vx-space-s) var(--vx-space-m);
    border-radius: var(--vx-radius-pill);
    color: rgb(var(--vx-ink-secondary));
    text-decoration: none;
    font-size: var(--vx-text-label);
    font-weight: var(--vx-weight-medium);
    white-space: nowrap;
    transition: background var(--vx-duration-fast) var(--vx-ease),
                color var(--vx-duration-fast) var(--vx-ease);
}

.topbar-nav a:hover { background: rgb(var(--vx-sunken)); }

.topbar-nav a[aria-current="page"] {
    background: rgb(var(--vx-accent-wash));
    color: rgb(var(--vx-accent-ink));
}

/*
 * Narrow.
 *
 * Theirs keeps the wordmark and a hamburger on the first line and lets the
 * pills scroll on the next. Same here. "Vender" stays out of the menu: it is
 * the one thing a person opens this on a phone to do, and burying the verb of
 * the product behind an icon is how a menu becomes the whole product.
 */
@media (max-width: 720px) {
    .topbar-main { gap: var(--vx-space-s); min-height: 56px; }

    .topbar .topbar-menu { display: inline-flex; }

    .topbar-actions {
        /* Folded away, not removed: the same elements, shown in a sheet under
           the header when the menu is open. */
        display: none;
        position: absolute;
        left: 0;
        right: 0;
        top: 100%;
        flex-direction: column;
        align-items: stretch;
        gap: var(--vx-space-s);
        padding: var(--vx-space-m);
        background: rgb(var(--vx-surface));
        border-bottom: var(--vx-border) solid rgb(var(--vx-line));
        box-shadow: var(--vx-shadow-float);
    }

    /* Anchored to the whole header, not to its first line. Anchored to the
       line, the sheet dropped on top of the section row and the sections
       vanished the moment the menu opened, which reads as the menu having
       replaced the page rather than added to it. */
    .topbar { position: sticky; }

    .topbar.menu-open .topbar-actions {
        display: flex;
        animation: vx-in var(--vx-duration) var(--vx-ease) both;
    }

    .topbar .who { white-space: normal; }

    /* Bleeds to the edges so the last pill is visibly cut off, which is what
       tells a thumb there is more to the right. */
    .topbar-nav {
        padding-left: var(--vx-space-m);
        padding-right: var(--vx-space-m);
    }
}

main {
    max-width: 76rem;
    margin: 0 auto;
    padding: var(--vx-space-l) var(--vx-space-m) var(--vx-space-xxl);
}

h1 {
    font-size: var(--vx-text-display);
    line-height: var(--vx-leading-tight);
    letter-spacing: var(--vx-tracking-tight);
    font-weight: var(--vx-weight-bold);
    margin: 0 0 var(--vx-space-xs);
    text-wrap: balance;
}

h2 {
    font-size: var(--vx-text-title);
    line-height: var(--vx-leading-tight);
    letter-spacing: var(--vx-tracking-tight);
    font-weight: var(--vx-weight-bold);
    margin: var(--vx-space-xl) 0 var(--vx-space-m);
    text-wrap: balance;
}

.lede {
    color: rgb(var(--vx-ink-secondary));
    margin: 0 0 var(--vx-space-l);
    max-width: 60ch;
}

/* ─────────────────────────────────────────────── Cards and tiles */

.card {
    background: rgb(var(--vx-surface));
    border: var(--vx-border) solid rgb(var(--vx-line));
    border-radius: var(--vx-radius);
    box-shadow: var(--vx-shadow-raised);
    padding: var(--vx-space-m);
}

.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--vx-space-s);
    margin-bottom: var(--vx-space-l);
}

/*
 * Written out rather than composed.
 *
 * This rule used to be `.tile { composes: card; }`, which is a CSS Modules
 * feature and not CSS. Browsers drop it without a word, so every tile in the
 * product has been a bare div with no background, no border and no padding
 * since the day it was written, and it looked deliberate enough that nobody
 * questioned it.
 */
.tile {
    background: rgb(var(--vx-surface));
    border: var(--vx-border) solid rgb(var(--vx-line));
    border-radius: var(--vx-radius);
    box-shadow: var(--vx-shadow-raised);
    padding: var(--vx-space-m);
}

.tile b {
    display: block;
    font-size: var(--vx-text-display);
    font-weight: var(--vx-weight-bold);
    letter-spacing: var(--vx-tracking-tight);
    line-height: var(--vx-leading-tight);
    font-variant-numeric: tabular-nums;
}

.tile span {
    color: rgb(var(--vx-ink-secondary));
    font-size: var(--vx-text-label);
}

.tile.good b { color: rgb(var(--vx-good-ink)); }
.tile.bad b  { color: rgb(var(--vx-bad-ink)); }
.tile.warn b { color: rgb(var(--vx-warn-ink)); }

/* ──────────────────────────────────────────────────────── Tables */

.table-wrap {
    overflow-x: auto;
    background: rgb(var(--vx-surface));
    border: var(--vx-border) solid rgb(var(--vx-line));
    border-radius: var(--vx-radius);
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--vx-text-label);
}

th {
    text-align: left;
    font-weight: var(--vx-weight-medium);
    color: rgb(var(--vx-ink-secondary));
    font-size: var(--vx-text-micro);
    text-transform: uppercase;
    letter-spacing: var(--vx-tracking-caps);
    padding: var(--vx-space-s) var(--vx-space-s);
    border-bottom: var(--vx-border) solid rgb(var(--vx-line));
    white-space: nowrap;
}

td {
    padding: var(--vx-space-s);
    border-bottom: var(--vx-border) solid rgb(var(--vx-line));
    vertical-align: middle;
}

/* Vinted's own row height, which is also the smallest comfortable target. */
tbody tr { min-height: var(--vx-control-h); }
tr:last-child td { border-bottom: 0; }

tbody tr {
    transition: background var(--vx-duration-fast) var(--vx-ease);
}
tbody tr:hover { background: rgb(var(--vx-sunken)); }

td.num, th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

td.code {
    font-family: var(--vx-font-mono);
    font-size: var(--vx-text-label);
    color: rgb(var(--vx-ink-secondary));
}

/* ──────────────────────────────────────────────────────── Status */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--vx-space-xs);
    padding: 2px var(--vx-space-s);
    border-radius: var(--vx-radius-pill);
    font-size: var(--vx-text-label);
    font-weight: var(--vx-weight-medium);
    white-space: nowrap;
}

.chip::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/*
 * Six statuses, five colours, and the sharing is on purpose.
 *
 * "Em stock" and "Retirado" are both the absence of a sale, so both are quiet
 * greys; what tells them apart is the word, not a hue invented to fill a gap.
 * Every colour here means the same thing it means everywhere else in the
 * product: teal is us, amber is attention, green is money in, red is wrong.
 */
.chip.in-stock  { color: rgb(var(--vx-ink-secondary)); background: rgb(var(--vx-sunken)); }
.chip.to-ship   { color: rgb(var(--vx-warn-ink));      background: rgb(var(--vx-warn-wash)); }
.chip.shipped   { color: rgb(var(--vx-accent-ink));    background: rgb(var(--vx-accent-wash)); }
.chip.delivered { color: rgb(var(--vx-good-ink));      background: rgb(var(--vx-good-wash)); }
.chip.returned  { color: rgb(var(--vx-bad-ink));       background: rgb(var(--vx-bad-wash)); }
.chip.withdrawn { color: rgb(var(--vx-ink-faint));     background: rgb(var(--vx-sunken)); }

/* The deadline is the one thing here that gets louder over time. */
.due { font-variant-numeric: tabular-nums; white-space: nowrap; }
.due.soon  { color: rgb(var(--vx-warn-ink)); font-weight: var(--vx-weight-medium); }
.due.today { color: rgb(var(--vx-bad-ink)); font-weight: var(--vx-weight-bold); }
.due.late {
    color: rgb(var(--vx-on-accent));
    background: rgb(var(--vx-bad));
    padding: 2px var(--vx-space-s);
    border-radius: var(--vx-radius-pill);
    font-weight: var(--vx-weight-bold);
}

/* ────────────────────────────────────────────────────── Controls */

button, .btn {
    font: inherit;
    font-weight: var(--vx-weight-medium);
    min-height: var(--vx-control-h);
    padding: 0 var(--vx-space-l);
    border-radius: var(--vx-radius);
    border: var(--vx-border) solid transparent;
    background: rgb(var(--vx-accent));
    color: rgb(var(--vx-on-accent));
    cursor: pointer;
    transition: background var(--vx-duration-fast) var(--vx-ease),
                border-color var(--vx-duration-fast) var(--vx-ease);
}

button:hover:not(:disabled),
.btn:hover { background: rgb(var(--vx-accent-strong)); }

/* Pressing moves nothing. A button that shifts under the finger reads as the
   page being knocked rather than as the press landing. */
button:active:not(:disabled) { background: rgb(var(--vx-accent-strong)); }

button:disabled {
    background: rgb(var(--vx-disabled-bg));
    color: rgb(var(--vx-disabled-ink));
    cursor: default;
}

button.quiet, .btn.quiet {
    background: rgb(var(--vx-surface));
    color: rgb(var(--vx-ink));
    border-color: rgb(var(--vx-line-strong));
}
button.quiet:hover:not(:disabled) { background: rgb(var(--vx-sunken)); }

button.danger { background: rgb(var(--vx-bad)); }
button.danger:hover:not(:disabled) { background: rgb(var(--vx-bad-ink)); }

input, select, textarea {
    font: inherit;
    min-height: var(--vx-control-h);
    padding: 0 var(--vx-control-pad);
    border: var(--vx-border) solid rgb(var(--vx-line-strong));
    border-radius: var(--vx-radius);
    background: rgb(var(--vx-surface));
    color: rgb(var(--vx-ink));
    width: 100%;
    transition: border-color var(--vx-duration-fast) var(--vx-ease);
}

textarea {
    padding: var(--vx-space-s) var(--vx-control-pad);
    line-height: var(--vx-leading);
    resize: vertical;
}

input::placeholder, textarea::placeholder { color: rgb(var(--vx-ink-faint)); }

/* Vinted thickens the border on focus rather than adding a ring. Copied,
   because the seller's eye is trained on theirs. No glow: a coloured halo on
   an input is the house style of generated pages and is banned by rule. */
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: rgb(var(--vx-accent));
    border-width: var(--vx-border-strong);
}

label {
    display: block;
    font-size: var(--vx-text-label);
    color: rgb(var(--vx-ink-secondary));
    margin-bottom: var(--vx-space-xs);
}

.row {
    display: flex;
    gap: var(--vx-space-m);
    flex-wrap: wrap;
    align-items: flex-end;
}
.row > * { flex: 1 1 12rem; }
.row > .narrow { flex: 0 0 10rem; }

.filters {
    display: flex;
    gap: var(--vx-space-s);
    flex-wrap: wrap;
    margin-bottom: var(--vx-space-m);
}

.filters button {
    min-height: var(--vx-control-h-sm);
    padding: 0 var(--vx-space-m);
    border-radius: var(--vx-radius-pill);
    background: rgb(var(--vx-surface));
    color: rgb(var(--vx-ink-secondary));
    border-color: rgb(var(--vx-line-strong));
    font-size: var(--vx-text-label);
}

.filters button[aria-pressed="true"] {
    background: rgb(var(--vx-accent-wash));
    color: rgb(var(--vx-accent-ink));
    border-color: rgb(var(--vx-accent));
}

.filters button .n {
    color: rgb(var(--vx-ink-faint));
    margin-left: var(--vx-space-xs);
    font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────── Notices */

.notice {
    padding: var(--vx-space-s) var(--vx-space-m);
    border-radius: var(--vx-radius);
    background: rgb(var(--vx-accent-wash));
    color: rgb(var(--vx-accent-ink));
    margin-bottom: var(--vx-space-m);
}
.notice.bad {
    background: rgb(var(--vx-bad-wash));
    color: rgb(var(--vx-bad-ink));
}

.empty {
    padding: var(--vx-space-xxl) var(--vx-space-m);
    text-align: center;
    color: rgb(var(--vx-ink-secondary));
}
.empty b {
    display: block;
    color: rgb(var(--vx-ink));
    font-size: var(--vx-text-title);
    margin-bottom: var(--vx-space-xs);
}

/* ─────────────────────────────────────────────────────── Sign in */

.gate {
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: var(--vx-space-m);
}
.gate .card { width: 100%; max-width: 24rem; padding: var(--vx-space-l); }
.gate .brand { height: 28px; margin-bottom: var(--vx-space-l); }
.gate label { margin-top: var(--vx-space-m); }
.gate button { width: 100%; margin-top: var(--vx-space-l); }

/* ─────────────────────────────────────────────────── App banner */

/* Sits above the header, on a phone only, and only until it is dismissed. */
.app-banner {
    display: flex;
    align-items: center;
    gap: var(--vx-space-s);
    flex-wrap: wrap;
    padding: var(--vx-space-s) var(--vx-space-m);
    background: rgb(var(--vx-accent-wash));
    color: rgb(var(--vx-accent-ink));
    font-size: var(--vx-text-label);
}
.app-banner span { flex: 1 1 12rem; }
.app-banner .btn,
.app-banner button {
    min-height: var(--vx-control-h-sm);
    padding: 0 var(--vx-space-m);
    font-size: var(--vx-text-label);
    flex: 0 0 auto;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
}

/* ──────────────────────────────────────────────── The animations */

/*
 * The whole motion vocabulary of the product, and there is deliberately not
 * much of it. Three things move: something arriving, a list arriving one row
 * after another, and something that is loading.
 *
 * Everything else is a state change on something already on screen, which is
 * a transition on the property itself and lives with the component.
 *
 * A screen that animates more than this does not feel more alive; it feels
 * slower, because every one of these costs the reader a moment of waiting for
 * the page to settle before they can act.
 */

@keyframes vx-in {
    from { opacity: 0; transform: translateY(var(--vx-lift)); }
    to   { opacity: 1; transform: none; }
}

/** Something arriving: a panel, a card, a notice. */
.vx-in {
    animation: vx-in var(--vx-duration) var(--vx-ease) both;
}

/**
 * A list arriving.
 *
 * The stagger is capped at six because it is a courtesy, not a feature: past
 * that the last row is waiting on an animation nobody is watching, and a table
 * of forty rows would take two seconds to finish appearing.
 */
.vx-stagger > * { animation: vx-in var(--vx-duration) var(--vx-ease) both; }
.vx-stagger > *:nth-child(1) { animation-delay: 0ms; }
.vx-stagger > *:nth-child(2) { animation-delay: 30ms; }
.vx-stagger > *:nth-child(3) { animation-delay: 60ms; }
.vx-stagger > *:nth-child(4) { animation-delay: 90ms; }
.vx-stagger > *:nth-child(5) { animation-delay: 120ms; }
.vx-stagger > *:nth-child(n+6) { animation-delay: 150ms; }

/** Waiting. A pulse, not a spinner: nothing here takes long enough for one. */
@keyframes vx-pulse {
    50% { opacity: .5; }
}
.vx-loading {
    animation: vx-pulse 1.2s var(--vx-ease) infinite;
    pointer-events: none;
}

/*
 * Reduced motion is handled in tokens.css by taking the durations to almost
 * nothing, which covers every rule above at once. Repeating a blanket
 * `* { animation: none }` here would also kill the fade, leaving content to
 * pop in - the jarring thing the setting exists to avoid.
 */
