/*
 * The Listamos design language.
 *
 * Bootstrap is still in the build and it earns its place for exactly three things: a
 * modal with a focus trap, a dropdown that answers the keyboard, and a grid that
 * works on a phone. Rewriting those to own the CSS is a month of work and a fresh set
 * of accessibility bugs. So its *behaviour* stays and its *appearance* does not
 * survive this file — its variables are pointed at our tokens in tokens.css, and
 * every component a seller looks at is redrawn below.
 *
 * The composition the whole product is built on is three planes:
 *
 *     shell (white)  →  canvas (tinted, recessed)  →  surface (white, lifted)
 *
 * The navigation is mounted on the shell. The work is a sheet set into it. The
 * objects a seller manipulates — the catalogue, a form, a dialog — are white cards
 * lifted off that sheet. Depth is therefore structural: it says what contains what,
 * before any border is drawn. Take every border out of this product and the planes
 * still read, which is the test.
 *
 * The rules under that:
 *
 * 1. **A card is an object.** A section does not get wrapped in a white rectangle to
 *    look tidy. It gets one when it is a thing — a catalogue, a shop, a listing being
 *    written. Two nested cards means one of them is not an object.
 * 2. **Colour is spent.** Blue means "this is the action" or "this is selected".
 *    Green, amber and red mean something is true about a listing. Everything else is
 *    ink. A screen where five things are coloured is pointing at nothing.
 * 3. **Facts are set in mono.** Counts, prices, identifiers, timestamps, scores and
 *    column headings. It runs through every screen and it is what stops a table
 *    reading as a web page with rows in it.
 * 4. **One control height per row.** A button, a select and a search field standing
 *    side by side share `--control`, so they line up without anybody nudging a
 *    padding. This is most of what "polished" means at close range.
 * 5. **Motion is orientation.** Under a fifth of a second, and only where something
 *    arrived, changed, or is waiting.
 *
 * Sections:
 *    1. Base, typography and layout primitives
 *    2. Buttons
 *    3. Fields
 *    4. Surfaces: cards, panels, tables
 *    5. Status: dots, chips, health
 *    6. Overlays: menus, dialogs, waiting
 *    7. Diffs
 *    8. The application shell
 *    9. The catalogue
 *   10. Public pages
 *   11. Onboarding
 *   12. Depth and motion
 *   13. Folded panels and the variation grid
 *   14. Improving one field
 */

/* ————————————————————————————————————————————————————————————— 1. Base */

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

html {
    -webkit-text-size-adjust: 100%;
}

body {
    background: var(--listamos-shell);
    color: var(--listamos-text);
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* A hair of negative tracking across the whole product. This one line is most of
       the difference between text that reads as a document and text that reads as an
       interface. */
    letter-spacing: -0.006em;
}

::selection {
    background: var(--brand-100);
    color: var(--brand-900);
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    color: var(--listamos-text);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.022em;
    line-height: 1.25;
}

/* ——— The voices ————————————————————————————————————————————————————————
   Every piece of text in the product is one of these, and the list is short on
   purpose: the moment there are fourteen sizes nobody can tell which one a thing
   should be, and the answer becomes whichever looked right that afternoon.

   The tracking is the part that is easy to skip and expensive to skip. Large type
   needs negative tracking to stop looking loose; small mono caps need positive
   tracking or they set as a smear. */

.lm-display {
    font-size: var(--text-5xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.035em;
    line-height: 1.03;
    margin: 0;
}

.lm-title {
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.028em;
    line-height: 1.2;
    /* No margin of its own: the space belongs to the header that arranges it. */
    margin: 0;
}

.lm-title-lg {
    font-size: var(--text-3xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.032em;
    line-height: 1.15;
    margin: 0;
}

.lm-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    line-height: 1.5;
    margin: var(--space-2) 0 0;
    max-width: 62ch;
}

/* A lede on a row of its own — not in the headings column beside the page's controls —
   gets a longer measure. 62ch is the right width for prose being read, and this is not
   that: it is one sentence explaining a screen, read once and then never again, and
   every line it takes is a row of the table beneath it pushed out of sight. Still
   capped, because a lede set to the full width of a table is no longer a paragraph. */
.lm-page > .lm-lede {
    max-width: 96ch;
}

.lm-heading {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.02em;
    margin: 0;
}

.lm-subheading {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.014em;
    margin: 0;
}

/* The name of a thing in a list. Slightly heavier than the body around it, which is
   what makes a row scannable without a rule between every column. */
.lm-row-title {
    color: var(--listamos-text);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    letter-spacing: -0.012em;
    line-height: 1.35;
}

/* Data about a thing rather than the thing: a SKU, a timestamp, a channel.
   Set in mono beside a sans title, it reads as "this is a value, not a sentence"
   with no punctuation or colour needed to say so. */
.lm-meta {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0;
}

/* A column heading, a field label, the name of a group. Mono caps, tracked out, and
   deliberately quiet — a label is the least important text in its vicinity. */
.lm-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.lm-caption {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.45;
}

/* A number that is the point of its own sentence: an import count, a score, a total.
   Mono and tabular so a column of them lines up on the decimal. */
.lm-metric {
    display: block;
    font-family: var(--font-mono);
    font-size: var(--text-3xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-semibold);
    letter-spacing: -0.03em;
    line-height: 1;
}

/* ——— Names the older screens still use ——————————————————————————————————
   Redefined onto the voices above rather than deleted, so a screen that has not been
   through the markup migration still speaks the same language. */

.display {
    font-size: var(--text-4xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.032em;
    line-height: 1.1;
}

.lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-lg);
    line-height: 1.55;
}

.eyebrow {
    color: var(--listamos-primary-text);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.subtle {
    color: var(--listamos-text-secondary);
}

.ink {
    color: var(--listamos-text);
}

.page-title {
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.028em;
    line-height: 1.2;
}

.page-head {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    justify-content: space-between;
}

.page-lead {
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    margin: var(--space-2) 0 0;
    max-width: 62ch;
}

.numeric,
.lm-num,
.table td.numeric {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}

a {
    color: var(--listamos-primary);
    text-decoration: none;
    transition: color var(--fast);
}

a:hover {
    color: var(--listamos-primary-hover);
    text-decoration: underline;
    text-underline-offset: 2px;
}

:focus-visible {
    outline: 2px solid var(--listamos-primary);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

hr {
    background: var(--listamos-border);
    border: 0;
    height: 1px;
    margin: var(--space-6) 0;
    opacity: 1;
}

/* ——— Layout primitives ————————————————————————————————————————————————
   Five classes doing what a page of Bootstrap utilities was doing, named for what
   they are so a page's structure can be read off its markup. */

/* The vertical rhythm of a screen. Every page is a `.lm-page` and its children are
   the sections; the gap between them is decided once, here. */
.lm-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.lm-page-head {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    justify-content: space-between;
}

.lm-page-headings {
    min-width: 0;
}

.lm-page-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    /* Nudged down so a button's optical centre lines up with the title's baseline
       rather than with the box the title sits in. */
    padding-top: 0.1875rem;
}

.lm-rule {
    background: var(--listamos-border);
    border: 0;
    height: 1px;
    margin: 0;
}

/* Two clusters used often enough to name: things in a row, things in a column. */
.lm-cluster {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* A wrapper that is not a box. Used where a `<form>` has to enclose controls for the
   submit to work but must not become a row in the layout. */
.d-contents {
    display: contents;
}


/* ————————————————————————————————————————————————————————— 2. Buttons

   One shape, six voices, three heights.

   The proportions are the tell. A Bootstrap button is padded in ems and ends up
   nearly square-ish and soft; a designed one has a fixed height from the control
   scale, generous horizontal padding, and optical centring. Get those three right and
   a plain white button looks expensive.

   The primary is a flat blue with two details: a dark hairline moulded into the
   bottom inside edge, which is what makes a filled button look like a physical key
   rather than a coloured rectangle, and a shadow tinted with its own hue so it sits
   on the page instead of hovering over it. No gradient — a gradient on a button is
   the single loudest signal that a theme was bought. */

.lm-btn {
    --h: var(--control);
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-xs);
    color: var(--listamos-text);
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 auto;
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.4375rem;
    height: var(--h);
    justify-content: center;
    letter-spacing: -0.008em;
    line-height: 1;
    padding: 0 0.8125rem;
    text-decoration: none;
    transition: background var(--fast), border-color var(--fast), box-shadow var(--fast), color var(--fast), transform var(--fast);
    white-space: nowrap;
}

.lm-btn:hover {
    background: var(--ink-025);
    border-color: var(--listamos-border-hover);
    color: var(--listamos-text);
    text-decoration: none;
}

.lm-btn:active {
    background: var(--ink-050);
    box-shadow: none;
    transform: translateY(0.5px);
}

.lm-btn:focus-visible {
    box-shadow: var(--listamos-ring);
    outline: none;
}

.lm-btn:disabled,
.lm-btn[aria-disabled='true'] {
    cursor: not-allowed;
    opacity: .5;
}

.lm-btn .bi,
.lm-btn .lm-ico {
    font-size: 1em;
    opacity: .75;
}

.lm-btn-primary {
    background: var(--listamos-primary);
    border-color: var(--listamos-primary);
    box-shadow: var(--sheen-bottom), var(--shadow-brand);
    color: var(--listamos-text-on-primary);
}

.lm-btn-primary:hover {
    background: var(--listamos-primary-hover);
    border-color: var(--listamos-primary-hover);
    color: var(--listamos-text-on-primary);
}

.lm-btn-primary:active {
    background: var(--listamos-primary-active);
    border-color: var(--listamos-primary-active);
    box-shadow: var(--sheen-bottom);
}

.lm-btn-primary .bi,
.lm-btn-primary .lm-ico {
    opacity: .9;
}

/* The action that is important but not the one. A tinted field of the brand rather
   than a filled key: it reads as blue at a glance and does not compete with a real
   primary standing next to it. */
.lm-btn-soft {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary-border);
    box-shadow: none;
    color: var(--listamos-primary-text);
}

.lm-btn-soft:hover {
    background: var(--listamos-primary-soft-hover);
    border-color: var(--brand-300);
    color: var(--brand-800);
}

.lm-btn-quiet {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    color: var(--listamos-text-secondary);
}

.lm-btn-quiet:hover {
    background: var(--ink-050);
    border-color: transparent;
    color: var(--listamos-text);
}

.lm-btn-quiet:active {
    background: var(--ink-075);
}

.lm-btn-danger {
    background: var(--listamos-surface);
    border-color: var(--red-200);
    color: var(--listamos-danger-text);
}

.lm-btn-danger:hover {
    background: var(--listamos-danger-soft);
    border-color: var(--red-500);
    color: var(--red-800);
}

.lm-btn-inverse {
    background: var(--listamos-surface-inverse);
    border-color: var(--listamos-surface-inverse);
    box-shadow: var(--sheen-top), var(--shadow-sm);
    color: var(--listamos-text-inverse);
}

.lm-btn-inverse:hover {
    background: var(--ink-800);
    border-color: var(--ink-800);
    color: var(--listamos-text-inverse);
}

.lm-btn-sm {
    --h: var(--control-sm);
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    gap: 0.375rem;
    padding: 0 0.625rem;
}

.lm-btn-lg {
    --h: var(--control-lg);
    border-radius: var(--radius-md);
    font-size: var(--text-md);
    gap: 0.5rem;
    padding: 0 1.125rem;
}

.lm-btn-block {
    width: 100%;
}

/* Square, for a button whose label is its icon. Same heights as the rest so it lines
   up in a toolbar. */
.lm-icon-btn {
    padding: 0;
    width: var(--h);
}

.lm-icon-btn .bi,
.lm-icon-btn .lm-ico {
    font-size: 1.0625em;
    opacity: .7;
}

.lm-icon-btn:hover .bi,
.lm-icon-btn:hover .lm-ico {
    opacity: 1;
}

/* ——— Bootstrap's button, wearing the same clothes ————————————————————————
   The screens still on `.btn` get the language above rather than a second one. */

.btn {
    --h: var(--control);
    --bs-btn-focus-box-shadow: var(--listamos-ring);
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-xs);
    color: var(--listamos-text);
    display: inline-flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.4375rem;
    height: var(--h);
    justify-content: center;
    letter-spacing: -0.008em;
    line-height: 1;
    padding: 0 0.8125rem;
    transition: background var(--fast), border-color var(--fast), box-shadow var(--fast), color var(--fast);
    white-space: nowrap;
}

.btn:hover {
    background: var(--ink-025);
    border-color: var(--listamos-border-hover);
    color: var(--listamos-text);
}

.btn:active {
    background: var(--ink-050);
    box-shadow: none;
}

.btn .bi {
    opacity: .75;
}

.btn-lg {
    --h: var(--control-lg);
    border-radius: var(--radius-md);
    font-size: var(--text-md);
    padding: 0 1.125rem;
}

.btn-sm {
    --h: var(--control-sm);
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    padding: 0 0.625rem;
}

.btn-primary {
    --bs-btn-bg: var(--listamos-primary);
    --bs-btn-border-color: var(--listamos-primary);
    --bs-btn-hover-bg: var(--listamos-primary-hover);
    --bs-btn-hover-border-color: var(--listamos-primary-hover);
    --bs-btn-active-bg: var(--listamos-primary-active);
    --bs-btn-active-border-color: var(--listamos-primary-active);
    background: var(--listamos-primary);
    border-color: var(--listamos-primary);
    box-shadow: var(--sheen-bottom), var(--shadow-brand);
    color: var(--listamos-text-on-primary);
}

.btn-primary:hover {
    background: var(--listamos-primary-hover);
    border-color: var(--listamos-primary-hover);
    color: var(--listamos-text-on-primary);
}

.btn-primary .bi {
    opacity: .9;
}

.btn-outline-secondary {
    --bs-btn-color: var(--listamos-text);
    --bs-btn-border-color: var(--listamos-border-strong);
    --bs-btn-hover-color: var(--listamos-text);
    --bs-btn-hover-bg: var(--ink-025);
    --bs-btn-hover-border-color: var(--listamos-border-hover);
    background: var(--listamos-surface);
    border-color: var(--listamos-border-strong);
    color: var(--listamos-text);
}

.btn-outline-danger {
    --bs-btn-color: var(--listamos-danger-text);
    --bs-btn-border-color: var(--red-200);
    --bs-btn-hover-color: var(--red-800);
    --bs-btn-hover-bg: var(--listamos-danger-soft);
    --bs-btn-hover-border-color: var(--red-500);
    background: var(--listamos-surface);
    border-color: var(--red-200);
    color: var(--listamos-danger-text);
}

.btn-light {
    background: var(--ink-050);
    border-color: transparent;
    box-shadow: none;
    color: var(--listamos-text);
}

.btn-light:hover {
    background: var(--ink-075);
    border-color: transparent;
}

.btn-ghost {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    color: var(--listamos-text-secondary);
}

.btn-ghost:hover {
    background: var(--ink-050);
    border-color: transparent;
    color: var(--listamos-text);
}

.btn-link {
    background: none;
    border: 0;
    box-shadow: none;
    color: var(--listamos-primary);
    height: auto;
    padding: 0;
}

.btn-group > .btn {
    box-shadow: none;
}

.btn-close {
    --bs-btn-close-focus-shadow: var(--listamos-ring);
    border-radius: var(--radius-sm);
    height: var(--control-sm);
    opacity: .45;
    transition: background var(--fast), opacity var(--fast);
    width: var(--control-sm);
}

.btn-close:hover {
    background-color: var(--ink-050);
    opacity: .9;
}


/* ——————————————————————————————————————————————————————————— 3. Fields

   A field is a white surface with a real edge and a hairline of shadow under it, not
   a rectangle drawn in outline. On a tinted canvas that reads as something you can
   type into from across the room, which is the entire job of a form control.

   Focus is the state worth spending on: it is the only moment the field is the most
   important thing on the screen, and a browser default outline throws away the one
   chance the product has to feel responsive. Ours is the border going brand plus a
   soft ring — the ring is large and low-contrast so it reads as light rather than as
   a second border. */

.lm-field,
.form-control,
.form-select {
    background-color: var(--listamos-surface);
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-xs);
    color: var(--listamos-text);
    font-family: inherit;
    font-size: var(--text-sm);
    line-height: 1.4;
    min-height: var(--control);
    padding: 0.4375rem 0.6875rem;
    transition: background-color var(--fast), border-color var(--fast), box-shadow var(--fast);
    width: 100%;
}

.lm-field::placeholder,
.form-control::placeholder {
    color: var(--listamos-text-tertiary);
}

.lm-field:hover:not(:focus),
.form-control:hover:not(:focus),
.form-select:hover:not(:focus) {
    border-color: var(--listamos-border-hover);
}

.lm-field:focus,
.form-control:focus,
.form-select:focus {
    background-color: var(--listamos-surface);
    border-color: var(--listamos-primary);
    box-shadow: var(--listamos-ring);
    color: var(--listamos-text);
    outline: none;
}

.lm-field:disabled,
.form-control:disabled,
.form-select:disabled {
    background-color: var(--ink-050);
    box-shadow: none;
    color: var(--listamos-text-tertiary);
    cursor: not-allowed;
}

/* Error and success on the control itself, so a form that has been submitted reads
   at a glance without every field growing a sentence. */
.lm-field.is-invalid,
.form-control.is-invalid,
.form-select.is-invalid {
    border-color: var(--listamos-danger);
}

.lm-field.is-invalid:focus,
.form-control.is-invalid:focus {
    box-shadow: 0 0 0 3px rgba(205, 54, 48, .22);
}

.lm-field.is-valid,
.form-control.is-valid {
    border-color: var(--green-500);
}

.form-control-lg {
    border-radius: var(--radius-md);
    font-size: var(--text-md);
    min-height: var(--control-lg);
    padding: 0.625rem 0.875rem;
}

.form-control-sm,
.form-select-sm {
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    min-height: var(--control-sm);
    padding: 0.3125rem 0.5625rem;
}

/* Room for the caret: the shared sm padding above would otherwise clip it. */
.form-select-sm {
    padding-right: 2.35rem !important;
}

textarea.form-control {
    line-height: 1.6;
    padding: 0.625rem 0.6875rem;
    resize: vertical;
}

.lm-input-label,
.form-label {
    color: var(--listamos-text);
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    letter-spacing: -0.008em;
    margin-bottom: 0.375rem;
}

.form-text {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
}

.input-group-text {
    background: var(--ink-050);
    border-color: var(--listamos-border-strong);
    border-radius: var(--radius);
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
}

.input-group > .form-control:not(:first-child),
.input-group > .form-select:not(:first-child) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.input-group > :not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* Three numbers and a unit, capped. Left to fill its column the group gave each
   measurement a box wide enough for a sentence, which is the wrong promise for a field
   that takes "36" — and it towered over the weight above it, which is a pair of numbers
   in a third of the width. */
.size-group {
    max-width: 26rem;
}

/* Equal thirds of whatever is left after the separators and the unit, so the boxes stay
   a matched set however long the numbers in them are. */
.size-group > .form-control {
    flex-basis: 0;
}

/* ——— Select ————————————————————————————————————————————————————————————
   One chevron. Native WebKit/macOS still paints its own flush arrow unless
   appearance is cleared *and* the background stack is ours end-to-end — a
   `background-position` that used var() was ignored in Chrome, leaving the
   chevron on the rim (~5px). Literals + !important keep one inset caret. */

.lm-select,
.form-select,
select.lm-field {
    -moz-appearance: none !important;
    -webkit-appearance: none !important;
    appearance: none !important;
    --bs-form-select-bg-img: none;
    background-color: var(--listamos-surface);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath stroke='%235c6378' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.75' d='m3.5 6 4.5 4.5L12.5 6'/%3E%3C/svg%3E") !important;
    background-position: right 20px center !important;
    background-repeat: no-repeat !important;
    background-size: 12px 12px !important;
    cursor: pointer;
    padding-right: 2.75rem !important;
}

.lm-select:hover,
.lm-select:focus,
.lm-select:disabled,
.form-select:hover,
.form-select:focus,
.form-select:disabled,
select.lm-field:hover,
select.lm-field:focus,
select.lm-field:disabled {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath stroke='%235c6378' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.75' d='m3.5 6 4.5 4.5L12.5 6'/%3E%3C/svg%3E") !important;
    background-position: right 20px center !important;
    background-repeat: no-repeat !important;
    background-size: 12px 12px !important;
}

.form-select-sm {
    background-position: right 16px center !important;
    background-size: 11px 11px !important;
    padding-right: 2.35rem !important;
}

.form-select-sm:hover,
.form-select-sm:focus,
.form-select-sm:disabled {
    background-position: right 16px center !important;
    background-size: 11px 11px !important;
}

.lm-select-auto {
    width: auto;
}

/* ——— Search ————————————————————————————————————————————————————————————
   Its own component rather than a field with an icon dropped on top: the icon has to
   dim and brighten with focus, which means it has to be inside the control's state. */

.lm-search {
    align-items: center;
    display: flex;
    position: relative;
}

.lm-search > .bi {
    color: var(--listamos-text-tertiary);
    font-size: 0.875rem;
    left: 0.6875rem;
    pointer-events: none;
    position: absolute;
    transition: color var(--fast);
}

.lm-search .lm-field {
    padding-left: 2.0625rem;
}

.lm-search:focus-within > .bi {
    color: var(--listamos-primary);
}

/* ——— Tick boxes ————————————————————————————————————————————————————————
   Drawn here rather than inherited. The tick is a rotated rectangle with two borders,
   so it scales with the box and carries no image request. */

.form-check-input {
    background: var(--listamos-surface);
    border: 1.5px solid var(--listamos-border-hover);
    border-radius: var(--radius-xs);
    box-shadow: var(--shadow-xs);
    cursor: pointer;
    height: 1.0625rem;
    margin-top: 0;
    position: relative;
    transition: background var(--fast), border-color var(--fast), box-shadow var(--fast);
    width: 1.0625rem;
}

.form-check-input:hover {
    border-color: var(--brand-400);
}

.form-check-input[type='checkbox']:checked,
.form-check-input[type='checkbox']:indeterminate {
    background: var(--listamos-primary);
    border-color: var(--listamos-primary);
    box-shadow: var(--sheen-bottom);
}

.form-check-input[type='checkbox']:checked::after {
    border: 1.75px solid #fff;
    border-left: 0;
    border-top: 0;
    content: '';
    height: 0.5rem;
    left: 50%;
    position: absolute;
    top: 46%;
    transform: translate(-50%, -50%) rotate(42deg);
    width: 0.28125rem;
}

.form-check-input[type='checkbox']:indeterminate::after {
    background: #fff;
    border-radius: 1px;
    content: '';
    height: 1.75px;
    left: 50%;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 0.5rem;
}

.form-check-input[type='radio'] {
    border-radius: 50%;
}

.form-check-input[type='radio']:checked {
    background: var(--listamos-primary);
    border-color: var(--listamos-primary);
    box-shadow: inset 0 0 0 3px #fff;
}

.form-check-input:focus,
.form-check-input:focus-visible {
    border-color: var(--listamos-primary);
    box-shadow: var(--listamos-ring);
    outline: none;
}

.form-check-label {
    cursor: pointer;
    font-size: var(--text-sm);
}

/* ——— Tips ———————————————————————————————————————————————————————————————
   Instant, not the browser's `title` — that waits a second and reads like a system
   dialog. Dark bubble, arrow up, shown on hover and keyboard focus. */

[data-lm-tip] {
    position: relative;
}

[data-lm-tip]::before,
[data-lm-tip]::after {
    left: 50%;
    opacity: 0;
    pointer-events: none;
    position: absolute;
    transform: translateX(-50%);
    transition: opacity 0.1s ease;
    z-index: 30;
}

[data-lm-tip]::before {
    border: 5px solid transparent;
    border-bottom-color: var(--ink-900);
    top: calc(100% + 2px);
}

[data-lm-tip]::after {
    background: var(--ink-900);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    color: #fff;
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    letter-spacing: normal;
    line-height: 1.3;
    max-width: 16rem;
    padding: 0.3125rem 0.5625rem;
    text-align: center;
    text-transform: none;
    top: calc(100% + 7px);
    white-space: nowrap;
}

/* Given something to draw only while shown. A hidden tip with content is still laid out —
   invisible, `nowrap`, half a label wider than its button — and at the right edge of
   the screen that is enough to scroll the whole page sideways: the Russian "next page"
   under the catalogue did it by ten pixels. */
[data-lm-tip]:hover::before,
[data-lm-tip]:focus-visible::before {
    content: '';
    opacity: 1;
}

[data-lm-tip]:hover::after,
[data-lm-tip]:focus-visible::after {
    content: attr(data-lm-tip);
    opacity: 1;
}

/* The last button of a pager sits against the edge; its tip opens leftward. */
.lm-pager > [data-lm-tip]:last-child::after {
    left: auto;
    right: 0;
    transform: none;
}

/* Not drawn at all where nothing can hover: a tap would otherwise leave one shown. */
@media (hover: none) {
    [data-lm-tip]:hover::before,
    [data-lm-tip]:hover::after,
    [data-lm-tip]:focus-visible::before,
    [data-lm-tip]:focus-visible::after {
        content: none;
    }
}

/* ——— Switch ————————————————————————————————————————————————————————————
   For a setting that takes effect immediately, where a tick box would imply there is
   a save button somewhere. */

.form-switch .form-check-input {
    background: var(--ink-200);
    border: 0;
    border-radius: var(--radius-pill);
    box-shadow: var(--sheen-bottom);
    height: 1.125rem;
    width: 1.875rem;
}

.form-switch .form-check-input::after {
    background: #fff;
    border-radius: 50%;
    box-shadow: var(--shadow-xs);
    content: '';
    height: 0.875rem;
    left: 0.125rem;
    position: absolute;
    top: 0.125rem;
    transition: transform var(--medium);
    width: 0.875rem;
}

.form-switch .form-check-input:checked {
    background: var(--listamos-primary);
}

.form-switch .form-check-input:checked::after {
    border: 0;
    transform: translateX(0.75rem);
    height: 0.875rem;
    width: 0.875rem;
}

/* ——— Segmented control ——————————————————————————————————————————————————
   Two or three exclusive choices in the space of one button. The chosen segment is a
   white chip lifted out of a sunken track, which says "this one" without colour. */

.lm-segmented {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    display: inline-flex;
    gap: 2px;
    padding: 3px;
}

.lm-segment {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    gap: 0.375rem;
    height: calc(var(--control) - 6px);
    justify-content: center;
    line-height: 1;
    padding: 0 0.625rem;
    transition: background var(--fast), box-shadow var(--fast), color var(--fast);
    white-space: nowrap;
}

.lm-segment:hover {
    color: var(--listamos-text);
}

.lm-segment.is-on,
.lm-segment[aria-pressed='true'] {
    background: var(--listamos-surface);
    box-shadow: var(--shadow-sm);
    color: var(--listamos-text);
}

.lm-segment .bi {
    font-size: 0.9375rem;
}


/* ——————————————————————————————————————————— 4. Surfaces: cards, panels

   A card is an object, and the three things that make one look designed rather than
   generated are: padding that is the same on all four sides and generous enough to be
   a decision, a radius sized to the object, and one shadow so light you notice it
   only when it is removed.

   Two families. A **card** is a thing — a shop, a listing being written, a dialog's
   worth of settings. A **panel** is a card whose content is a table or a list, so it
   has a head with controls in it and its body runs edge to edge. */

.lm-card,
.card,
.panel {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    color: var(--listamos-text);
}

.lm-card {
    padding: var(--space-5);
}

.lm-card.is-flush {
    padding: 0;
}

.lm-card-head {
    align-items: flex-start;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    margin-bottom: var(--space-4);
}

.lm-card-title {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.014em;
    margin: 0;
}

.lm-card-note {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    margin: 0.1875rem 0 0;
}

.lm-card-foot {
    align-items: center;
    border-top: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    margin: var(--space-5) calc(var(--space-5) * -1) calc(var(--space-5) * -1);
    padding: var(--space-4) var(--space-5);
}

/* Interactive: a card that is a link to the thing it describes. */
.lm-card.is-link {
    transition: border-color var(--fast), box-shadow var(--fast), transform var(--fast);
}

.lm-card.is-link:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}

/* Bootstrap's card body/header/footer, so the screens still using them line up with
   the padding above. */
.card-body {
    padding: var(--space-5);
}

.card-header,
.card-footer {
    background: transparent;
    border-color: var(--listamos-border);
    padding: var(--space-4) var(--space-5);
}

.card-header:first-child {
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.card-footer:last-child {
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* ——— Panel ——————————————————————————————————————————————————————————————
   A card built to hold a table. The head is a strip of controls that belongs to the
   content under it, which is the whole reason for putting the two in one object: a
   toolbar floating above a bare table has to be re-associated by eye every time. */

.lm-panel {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    /* Not clipped. `overflow: hidden` here would make this a scroll container and
       silently kill the sticky head inside it. So whatever sits against the corners has
       to round itself: `.lm-panel-head` and `.lm-panel-foot` do it below, and a bare
       table does it where `.listing-table` is styled. */
}

/* Sticky, and it carries the selection with it. On a catalogue of four hundred rows
   the moment that matters is scrolling down while things are ticked — a bulk bar that
   scrolls away is a bulk bar you have to scroll back to. */
.lm-panel-head {
    background: var(--listamos-surface);
    border-bottom: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    position: sticky;
    top: 0;
    z-index: 12;
}

.lm-panel-title {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.014em;
    margin: 0;
}

.lm-panel-body {
    padding: var(--space-5);
}

.lm-panel-foot {
    align-items: center;
    border-top: 1px solid var(--listamos-border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
}

/* A surface a step *below* the page rather than above it, for the inside of a card:
   a preview area, a summary of what is about to happen, a code block. */
.lm-sunken {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}

.lm-surface,
.lm-raised {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}

/* ——— Tables ————————————————————————————————————————————————————————————
   No vertical rules, no zebra, no outer border — the panel provides that. A column
   heading is tracked mono caps, which is enough to separate it from the data without
   a filled header band. */

.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--listamos-text);
    border-color: var(--listamos-border);
    margin: 0;
    width: 100%;
}

.table > thead {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* Centred, and the same in every table in the product.

   A heading ranged with its cells sounds right and is not, because the cells do not
   agree with each other: a title ranges left, a price ranges right, and a count ranges
   right on one screen and left on a phone. Following each of them gave a header row in
   three different alignments, which reads as three tables joined together rather than as
   one strip naming columns. Centred, the row is a row, and the eye finds a column by
   where its heading sits rather than by which edge it happens to be pinned to. */
.table > thead th {
    background: var(--listamos-surface);
    border-bottom: 1px solid var(--listamos-border);
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.07em;
    padding: 0.625rem var(--space-3);
    text-align: center;
    text-transform: uppercase;
    white-space: nowrap;
}

.table > tbody > tr {
    transition: background var(--fast);
}

.table > tbody > tr:hover {
    background: var(--ink-025);
}

.table > tbody > tr:last-child > * {
    border-bottom: 0;
}

.table > :not(caption) > * > * {
    background: transparent;
    border-bottom-color: var(--listamos-border);
    box-shadow: none;
    color: inherit;
    padding: var(--space-3);
    vertical-align: middle;
}

.table-sm > :not(caption) > * > * {
    padding: 0.5rem var(--space-3);
}

/* ——— Emptiness ——————————————————————————————————————————————————————————
   Centred, quiet, and never a full-page illustration. An empty area says what to do
   next; a picture of a person holding a box says nothing in any language. */

.empty {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    flex-direction: column;
    padding: var(--space-9) var(--space-6);
    text-align: center;
}

/* The direct child only: a button inside an empty state has an icon too, and it must
   not inherit the framed treatment meant for the illustrationless illustration. */
.empty > .bi {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    color: var(--listamos-text-tertiary);
    display: flex;
    font-size: 1.125rem;
    height: 2.5rem;
    justify-content: center;
    margin-bottom: var(--space-4);
    width: 2.5rem;
}

.empty .display,
.empty h2 {
    font-size: var(--text-xl);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.02em;
}

.empty p {
    max-width: 44ch;
}


/* ———————————————————————————————————— 5. Status: dots, chips, health

   The earlier version of this product said every state in a coloured capsule. On a
   grid of fifty rows that is fifty capsules, and a screen with fifty coloured pills
   on it has no emphasis left to spend on the four listings Etsy actually refused.

   So a state is a **dot and a word**: a 6px disc carrying the colour and the word
   carrying the meaning. A capsule is reserved for the places where the thing really
   is a discrete token — a filter that has been applied, a tag on a listing. */

.badge-state,
.lm-status {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    gap: 0.4375rem;
    letter-spacing: -0.006em;
    line-height: 1.3;
    white-space: nowrap;
}

.badge-state::before,
.lm-status::before {
    background: var(--ink-300);
    border-radius: 50%;
    content: '';
    flex: 0 0 auto;
    height: 6px;
    width: 6px;
}

.state-active::before,
.status-applied::before,
.status-completed::before  { background: var(--listamos-success); }

.state-inactive::before,
.state-conflict::before,
.status-failed::before     { background: var(--listamos-warning); }

.state-expired::before,
.status-refused::before    { background: var(--listamos-danger); }

.state-sold_out::before,
.status-pending::before,
.status-queued::before     { background: var(--listamos-primary); }

.state-draft::before,
.state-gone::before,
.status-skipped::before,
.status-unchanged::before,
.status-cancelled::before  { background: var(--ink-300); }

.status-running::before {
    animation: lm-pulse 1.4s var(--ease) infinite;
    background: var(--listamos-primary);
}

@keyframes lm-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: .4; transform: scale(.7); }
}

.state-gone,
.state-draft,
.status-skipped,
.status-unchanged,
.status-cancelled {
    color: var(--listamos-text-tertiary);
}

/* ——— Chips ——————————————————————————————————————————————————————————————
   A capsule of tinted colour with the signal's own text on it. Used where the thing
   is genuinely a token: a count of refusals, a filter in force, a tag. */

.lm-tag {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    gap: 0.3125rem;
    letter-spacing: -0.004em;
    line-height: 1;
    padding: 0.25rem 0.4375rem;
    white-space: nowrap;
}

.lm-tag.is-primary { background: var(--listamos-primary-soft); color: var(--listamos-primary-text); }
.lm-tag.is-success { background: var(--listamos-success-soft); color: var(--listamos-success-text); }
.lm-tag.is-warning { background: var(--listamos-warning-soft); color: var(--listamos-warning-text); }
.lm-tag.is-danger  { background: var(--listamos-danger-soft);  color: var(--listamos-danger-text); }

/* ——— Health ————————————————————————————————————————————————————————————
   Listamos's reading of a listing: a score, the word for the band it falls in, a meter,
   and a count of what is outstanding.

   The score is not invented for the row — it is the number the advisor already
   computes from the same checks the checklist draws, so "54" and "nine requirements
   unmet" are two views of one fact. That is the difference between this and a letter
   grade: the grade tells a seller they are a B and stops, and hovering this one opens
   the sixteen lines it was worked out from.

   Which is the whole reason the reading is styled as a thing you can point at. A
   number that hides an explanation and looks like plain text is an explanation nobody
   finds, so the word carries a dotted underline, the cursor changes over it, and the
   whole reading lifts onto a tinted pill under the pointer.

   The meter is four pixels of colour and it is doing real work — a column of them
   turns four hundred rows into a shape the eye can sort before reading a number. */

.lm-health {
    --health-hue: 120;

    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

.lm-health-read {
    align-items: center;
    border-radius: var(--radius-sm);
    cursor: help;
    display: flex;
    gap: 0.4375rem;
    /* Padded and pulled back by the same amount, so the pill can appear under the
       pointer without the column moving when it does. */
    margin: -0.1875rem -0.375rem;
    padding: 0.1875rem 0.375rem;
    transition: background var(--fast);
    width: fit-content;
}

.lm-health-read:hover {
    background: var(--listamos-surface-sunken);
}

/* Score drives hue on a red → yellow → green spectrum (0–120).

   The dotted rule is the affordance, and it is under the number because the number is
   now the only thing to point at: the word for the band used to carry it and no longer
   exists. It is the one mark a reader already knows means "there is more of this
   somewhere", and it costs no width in a column that has none to give. */
.lm-health-score {
    border-bottom: 1px dotted var(--listamos-border-strong);
    color: hsl(var(--health-hue) 55% 34%);
    font-family: var(--font-mono);
    font-size: var(--text-base);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-bold);
    letter-spacing: -0.03em;
    line-height: 1.1;
    white-space: nowrap;
}

.lm-health-read:hover .lm-health-score {
    border-bottom-color: currentcolor;
}

/* Quiet, and deliberately not tinted: the hue is the reading, and a red "/100" beside
   a red "34" would say the denominator was the bad news. Smaller and lighter, so the
   eye lands on the number and the scale is there for the one glance that needs it. */
.lm-health-of {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0;
}

.lm-health-bar {
    background: var(--listamos-health-track);
    border-radius: var(--radius-pill);
    flex: 0 0 auto;
    height: 5px;
    margin-left: 0.125rem;
    overflow: hidden;
    width: 3rem;
}

.lm-health-bar > i {
    background: hsl(var(--health-hue) 60% 44%);
    border-radius: inherit;
    display: block;
    height: 100%;
    transition: width var(--slow);
}

/* What is left to do, and only the number of it. Naming one of nine problems under a
   score of 94 was a row arguing with itself; the count works at both ends and the
   checklist above says which. */
.lm-health-count {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
    line-height: 1.3;
    white-space: nowrap;
}

/* ——— The checklist behind the number ——————————————————————————————————————
   Hovering the meter shows every requirement the score is made of, grouped by field,
   ticked or crossed. The score says how bad, the reason under it says at what, and
   this says compared to what — the question a beginner actually has, and the one a
   letter grade refuses to answer.

   Two columns because six groups in one are five hundred pixels of tooltip, which is
   a panel that covers the rows it is describing. */

.lm-checklist {
    background: var(--ink-900);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    color: #fff;
    display: grid;
    font-size: var(--text-xs);
    gap: var(--space-4) var(--space-5);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding: var(--space-4);
    /* Nothing in here is clickable, and a panel that can be hovered is a panel that
       keeps itself open over the row underneath. */
    pointer-events: none;
    position: fixed;
    width: 29rem;
    z-index: 60;
}

.lm-checklist[hidden] {
    display: none;
}

.lm-checklist h4 {
    color: var(--ink-400);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.04em;
    margin: 0 0 var(--space-2);
    text-transform: uppercase;
}

.lm-checklist ul {
    display: grid;
    gap: 0.375rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.lm-checklist li {
    align-items: start;
    display: grid;
    gap: var(--space-2);
    grid-template-columns: 1rem 1fr;
    line-height: 1.35;
}

.lm-checklist li i {
    align-items: center;
    border-radius: var(--radius-pill);
    display: flex;
    font-size: 0.5625rem;
    height: 1rem;
    justify-content: center;
    /* The marks sit on a line of text and would otherwise ride high on it. */
    margin-top: 0.0625rem;
    width: 1rem;
}

.lm-checklist .is-met i {
    background: var(--green-600);
}

/* Met requirements are dimmed and unmet ones are not. A seller opens this to find
   the crosses, and a list where sixteen lines are equally bright is sixteen lines
   they have to read to find four. */
.lm-checklist .is-met span {
    color: var(--ink-300);
}

.lm-checklist .is-unmet i {
    background: var(--red-600);
}

/* ——— Notices ———————————————————————————————————————————————————————————
   Tinted at the signal's lightest step with a two-pixel edge in the signal itself.
   Loud enough to stop somebody, quiet enough to sit above a form without becoming the
   subject of the page. */

.alert {
    background: var(--listamos-surface-subtle);
    border: 1px solid var(--listamos-border);
    border-left: 2px solid var(--ink-300);
    border-radius: var(--radius);
    color: var(--listamos-text);
    font-size: var(--text-sm);
    margin: 0 0 var(--space-4);
    padding: 0.875rem 1.25rem;
}

.alert-danger {
    background: var(--listamos-danger-soft);
    border-color: var(--red-100);
    border-left-color: var(--listamos-danger);
    color: var(--listamos-danger-text);
}

.alert-warning {
    background: var(--listamos-warning-soft);
    border-color: var(--amber-100);
    border-left-color: var(--listamos-warning);
    color: var(--listamos-warning-text);
}

.alert-success {
    background: var(--listamos-success-soft);
    border-color: var(--green-100);
    border-left-color: var(--listamos-success);
    color: var(--listamos-success-text);
}

.alert-light {
    background: var(--listamos-surface-subtle);
    border-left-color: var(--ink-200);
    color: var(--listamos-text-secondary);
}


/* ——————————————————————————————————————————————————— 6. Overlays

   The only places in the product allowed a real shadow, because they are the only
   things genuinely in front of the page. A menu is `--shadow-lg`, a dialog is
   `--shadow-xl`, and nothing else gets either. */

/* ——— Menus ————————————————————————————————————————————————————————————— */

.dropdown-menu {
    --bs-dropdown-bg: var(--listamos-surface);
    --bs-dropdown-border-color: var(--listamos-border);
    --bs-dropdown-link-hover-bg: var(--ink-050);
    --bs-dropdown-link-active-bg: var(--listamos-primary-soft);
    --bs-dropdown-link-active-color: var(--listamos-primary-text);
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-sm);
    min-width: 12rem;
    padding: var(--space-1);
}

.dropdown-item {
    align-items: center;
    border-radius: var(--radius-sm);
    color: var(--listamos-text);
    display: flex;
    font-weight: var(--weight-medium);
    gap: 0.5rem;
    padding: 0.4375rem 0.5625rem;
    transition: background var(--fast), color var(--fast);
}

.dropdown-item .bi {
    color: var(--listamos-text-tertiary);
    font-size: 0.9375rem;
}

.dropdown-item:active,
.dropdown-item.active {
    background: var(--listamos-primary-soft);
    color: var(--listamos-primary-text);
}

.dropdown-item:active .bi,
.dropdown-item.active .bi {
    color: var(--listamos-primary);
}

.dropdown-header {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.07em;
    padding: 0.5rem 0.5625rem 0.25rem;
    text-transform: uppercase;
}

.dropdown-divider {
    border-color: var(--listamos-border);
    margin: var(--space-1) 0;
    opacity: 1;
}

/* Bootstrap draws its own caret. Ours are real icons in the markup, where they can be
   the right one for the job. */
.dropdown-toggle::after {
    display: none;
}

/* ——— Dialogs ——————————————————————————————————————————————————————————— */

.modal-content {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
}

.modal-backdrop.show {
    background: var(--listamos-overlay);
    opacity: 1;
}

.modal-header,
.modal-footer {
    border-color: var(--listamos-border);
    padding: var(--space-4) var(--space-5);
}

.modal-title {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.014em;
}

.modal-body {
    padding: var(--space-5);
}

/* A lede in a window is the whole of what there is to read, not a subtitle under a
   heading — which is what the page version's margins are for, and they are the wrong
   way round here: a little above, none below. The eight pixels above put the window's
   padding out against its own sides, and the nothing below left the label for a box
   touching the sentence explaining it, so the explanation and the thing to fill in
   read as one run-on block. */
.modal-body > .lm-lede {
    margin-top: 0;
    white-space: pre-line;
}

/* Schedule consent: mode, then the fields, then one quiet sentence. Full-width
   segments so the two ways of answering take the same space, and the fields sit
   on one row so the window is a form rather than a stack of leftovers. */
.lm-schedule-when {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.lm-schedule-when-mode {
    display: flex;
    width: 100%;
}

.lm-schedule-when-mode > .lm-segment {
    flex: 1;
}

.lm-schedule-when-fields {
    align-items: flex-end;
    flex-wrap: nowrap;
}

.lm-schedule-when-day {
    flex: 1 1 auto;
    min-width: 0;
}

.lm-schedule-when-day input[type="date"] {
    width: 100%;
}

.lm-schedule-when-fields input[name="per_day"] {
    width: 5.5rem;
}

.lm-schedule-when-fields input[name="at_time"] {
    width: 7.5rem;
}

.lm-schedule-when-preview,
.lm-schedule-when-note {
    margin: 0;
}

.lm-schedule-when-preview {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.45;
}

.lm-schedule-when-note {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.45;
    white-space: pre-line;
}

/* Whatever the sentence was explaining, at arm's length from it. Set on the thing that
   follows rather than under the lede, so that a window with nothing after it — a line
   to read and a button — still ends where its padding says it does. Skipped when the
   lede is hidden, which is the shared dialog asking something short enough to fit in
   its own heading: the field is then the first thing in the window and wants no gap
   above it. */
.modal-body > .lm-lede:not(.d-none) + * {
    margin-top: var(--space-5);
}

.modal-footer {
    background: var(--listamos-surface-subtle);
    border-radius: 0 0 var(--radius-xl) var(--radius-xl);
    gap: var(--space-2);
}

.modal-footer > * {
    margin: 0;
}

/* ——— Waiting ———————————————————————————————————————————————————————————
   Two answers, and which one is right depends on whether we know the shape of what
   is coming. A skeleton where we do — a table of rows, a grid of cards — because it
   holds the layout still. A bar where the work has a length. */

.progress {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-pill);
    height: 6px;
    overflow: hidden;
}

.progress-bar {
    background: var(--listamos-primary);
    border-radius: inherit;
    transition: width var(--slow);
}

.progress-bar.bg-success {
    background: var(--listamos-success) !important;
}

.lm-skeleton,
.skeleton-row,
.rail-skeleton {
    animation: lm-shimmer 1.4s linear infinite;
    background: linear-gradient(90deg, var(--ink-075) 25%, var(--ink-050) 37%, var(--ink-075) 63%);
    background-size: 400% 100%;
    border-radius: var(--radius-xs);
    height: 0.75rem;
}

.skeleton-row {
    margin-bottom: 0.5rem;
}

.skeleton-row:last-child {
    margin-bottom: 0;
    width: 70%;
}

/* In a table the bar must reach the last column. A short shimmer under a five-column
   head looks like the load stopped halfway across the row. The stack-of-varied-widths
   look is for free-standing skeletons, not for a colspan cell. */
.listing-table td .skeleton-row,
.listing-table td .skeleton-row:last-child,
.listing-table td .skeleton-row:nth-child(2n),
.listing-table td .skeleton-row:nth-child(3n) {
    margin-bottom: 0;
    width: 100%;
}

@keyframes lm-shimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.spinner-border {
    border-width: 2px;
    color: currentColor;
}

.spinner-border-sm {
    height: 0.875rem;
    width: 0.875rem;
}


/* ——————————————————————————————————————————————————————————— 7. Diffs

   Before and after, in one line where it fits. The old value is struck through in the
   secondary ink rather than tinted red: a value being replaced is not an error, and
   colouring it as one makes a routine price change read as a warning. */

.diff {
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-xs);
    gap: 0.375rem;
    line-height: 1.4;
}

.diff-field {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.02em;
}

.diff-before {
    color: var(--listamos-text-tertiary);
    text-decoration: line-through;
}

.diff-after {
    color: var(--listamos-text);
    font-weight: var(--weight-medium);
}

/* The bulk editor's instruction rows. Each change is a sunken strip so that a stack
   of three reads as three separate rules rather than as one long form, and so the
   remove button at the end of each one has something to belong to. */

.lm-edits {
    display: grid;
    gap: var(--space-2);
}

.lm-edit {
    align-items: center;
    background: var(--listamos-surface-subtle);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3);
}

.lm-edit-inputs {
    display: flex;
    flex: 1 1 16rem;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-width: 0;
}

.lm-edit-inputs > * {
    flex: 1 1 8rem;
    min-width: 0;
}

.lm-preview {
    max-height: 22rem;
    overflow: auto;
    padding: 0;
}

.lm-preview > .table > thead th {
    background: var(--listamos-surface-sunken);
    position: sticky;
    top: 0;
    z-index: 1;
}


/* ——————————————————————————————————————— 8. The application shell

   Three planes, and this is where they are built.

   The **shell** is white and holds the navigation. The **workspace** is a tinted
   sheet set into it: inset from the top and right, rounded at the top corners, and
   running off the bottom of the window so that scrolling never reveals an edge. It is
   *darker* than the frame, which is the part that matters — work is recessed into the
   product, and the white cards that sit on it therefore lift without needing a shadow
   heavy enough to notice.

   A rail rather than a bar across the top, and the reason is not taste: a top bar
   spends about 56 pixels of vertical space on every screen forever, and on the
   catalogue that is two listings a seller never sees, forty times a day. The rail
   spends horizontal space, which a laptop has and a seven-column table does not need.

   It is also short. The product has three destinations and the rail names three.
   Bulk is deliberately absent — it is what the catalogue becomes when rows are
   selected, not somewhere to go. */

.lm-shell {
    display: grid;
    /* `auto`, not the width variable in the track itself. A grid column set to a custom
       property does not interpolate when the property changes — the rail snaps. The
       track follows whatever width the rail is animating to instead. */
    grid-template-columns: auto minmax(0, 1fr);
    min-height: 100vh;
}

/* Stacked above the work, and `position: sticky` is the reason it has to be said out
   loud: sticky always opens a stacking context, so everything the rail drops — the shop
   menu, the account menu — is trapped inside the rail's own layer. While the rail was
   wide that never showed, because a menu the width of the rail never left it. Collapse
   the rail and the same menu keeps its real width, reaches over the catalogue, and
   disappears under the first sticky thing it meets: the toolbar at 12, the listing
   detail's bar at 20. Thirty clears both. */
.lm-rail {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    height: 100vh;
    padding: var(--space-5) var(--space-4) var(--space-4);
    position: sticky;
    top: 0;
    z-index: 30;
}

/* Flush, not floating. This used to be a rounded sheet held eight pixels clear of the
   top and right edges, so that it read as a panel set into the white frame. The idea
   was sound and the execution kept announcing itself: at the top of every screen the
   corners curved away from the window and left two small wedges of frame above the
   work, which is the sort of detail that looks like a rendering fault rather than a
   decision. The rail is a plain vertical band and the work starts where it ends. */
.lm-work {
    background: var(--listamos-canvas);
    border-left: 1px solid var(--listamos-border);
    /* Recessed rather than raised. One inset hairline is the whole effect: it reads as
       the sheet sitting a fraction below the frame beside it. */
    box-shadow: inset 1px 0 2px rgba(15, 19, 32, .035);
    min-height: 100vh;
    min-width: 0;
}

.lm-work-inner {
    margin: 0 auto;
    max-width: var(--measure-app);
    padding: var(--space-5) var(--space-7) var(--space-10);
}

.lm-work-inner:has(.lm-page--catalogue) {
    padding-top: var(--space-5);
}

/* ——— The catalogue header ———————————————————————————————————————————————
   One row: the title, the view strip, and create — without a second bar above. */

.lm-page--catalogue {
    gap: var(--space-4);
}

.lm-page-head--catalogue {
    align-items: center;
    gap: var(--space-4);
}

.lm-page-head--catalogue .lm-title {
    flex: 0 0 auto;
    font-size: var(--text-xl);
}

.lm-catalogue-bar {
    align-items: center;
    display: flex;
    flex: 1 1 auto;
    gap: var(--space-3);
    justify-content: flex-end;
    min-width: 0;
}

.lm-page-head--catalogue .lm-view {
    font-size: var(--text-xs);
    gap: 0.3125rem;
    height: calc(var(--control) - 10px);
    padding: 0 0.5625rem;
}

.lm-page-head--catalogue .lm-view.is-on {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    box-shadow: none;
}

.lm-catalogue-bar .lm-btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Invisible on a wide screen so the buttons stay children of the catalogue bar's
   flex row. On a phone the bar itself goes `contents` and this box becomes real —
   without it the four Connect buttons land in the head's space-between and fan out. */
.lm-connect-actions {
    display: contents;
}

/* Below a phone the five states and Create listing stop sharing a line. Sharing it, the
   tab strip was down to about a third of the width and had to be scrolled sideways to
   reach Expired — and a row of tabs cut off mid-word reads as a rendering fault rather
   than as something that scrolls. The button goes up beside the title, which has room
   for it, and the states get the width to themselves. */
@media (max-width: 34em) {
    .lm-page-head--catalogue {
        flex-wrap: wrap;
    }

    .lm-page-head--catalogue .lm-title {
        flex: 1 1 auto;
    }

    .lm-catalogue-bar {
        display: contents;
    }

    .lm-connect-actions {
        display: grid;
        flex: 1 1 100%;
        gap: var(--space-2);
        grid-template-columns: repeat(2, minmax(0, 1fr));
        order: 2;
        width: 100%;
    }

    .lm-connect-actions .lm-btn,
    .lm-connect-actions [data-role='soon'] {
        justify-content: flex-start;
        min-width: 0;
        width: 100%;
    }

    .lm-page-head--catalogue .lm-views {
        flex: 1 1 100%;
        order: 3;
    }
}

/* A screen with one decision on it gets no rail and no sheet — the frame is the
   page. See start.blade.php. */
.lm-shell.is-focused {
    grid-template-columns: minmax(0, 1fr);
}

.lm-shell.is-focused .lm-work {
    background: var(--listamos-shell);
    border: 0;
    border-radius: 0;
    box-shadow: none;
    margin: 0;
    min-height: 100vh;
}

.lm-shell.is-focused .lm-work-inner {
    max-width: none;
    padding: 0;
}

/* ——— The mark ——————————————————————————————————————————————————————————
   The one place a logo is allowed to be the loudest thing in its vicinity, and even
   here it is 34 pixels tall. */

.lm-brand {
    align-items: center;
    color: var(--listamos-text);
    display: flex;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    gap: 0.5rem;
    letter-spacing: -0.028em;
    padding: 0 0.375rem;
    text-decoration: none;
}

.lm-brand:hover {
    color: var(--listamos-text);
    text-decoration: none;
}

.brand-lockup {
    align-items: center;
    display: inline-flex;
    flex: 0 0 auto;
    min-width: 0;
}

.brand-mark {
    display: block;
    flex: 0 0 auto;
    height: 34px;
    object-fit: contain;
    width: 34px;
}

.brand-word {
    color: var(--listamos-text);
    font-size: 1.125rem;
    font-weight: var(--weight-bold);
    letter-spacing: -0.03em;
    line-height: 1;
    white-space: nowrap;
}

.brand-lockup.is-mark .brand-word {
    display: none;
}

.public-nav .brand-mark,
.auth-shell .brand-mark {
    height: 36px;
    width: 36px;
}

.public-nav .brand-word,
.auth-shell .brand-word {
    font-size: 1.2rem;
}

.navbar-brand {
    align-items: center;
    color: var(--listamos-text);
    display: inline-flex;
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    gap: 0.5rem;
    letter-spacing: -0.028em;
}

.navbar-brand:hover {
    color: var(--listamos-text);
    text-decoration: none;
}

/* ——— Shop switcher —————————————————————————————————————————————————————
   The shop at the top of the rail: current marketplace in a pill, the rest in a
   menu. Vela puts the seller's context here rather than on a separate page. */

.lm-shop-switch {
    position: relative;
}

.lm-shop-trigger {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xs);
    color: var(--listamos-text);
    cursor: pointer;
    display: flex;
    font-family: inherit;
    gap: 0.625rem;
    padding: 0.5625rem 0.6875rem;
    text-align: left;
    transition: background var(--fast), border-color var(--fast), box-shadow var(--fast);
    width: 100%;
}

.lm-shop-trigger:hover,
.lm-shop-trigger[aria-expanded='true'] {
    background: var(--ink-025);
    border-color: var(--listamos-border-hover);
}

.lm-shop-trigger::after {
    display: none;
}

.lm-shop-mark {
    align-items: center;
    background: var(--ink-050);
    border-radius: var(--radius-pill);
    display: inline-flex;
    flex: 0 0 auto;
    height: 2rem;
    justify-content: center;
    position: relative;
    width: 2rem;
}

.lm-shop-mark[data-channel='etsy'] {
    background: #fff4ec;
}

.lm-shop-mark[data-channel='shopify'] {
    background: #eefbf3;
}

.lm-shop-mark[data-channel='ebay'] {
    background: #fdeeee;
}

.lm-shop-mark.is-empty {
    color: var(--listamos-text-tertiary);
}

.lm-shop-mark .lm-channel-logo {
    height: 1.125rem;
    width: auto;
}

.lm-shop-mark .lm-channel-logo--icon {
    display: block;
    height: 1.125rem;
    object-fit: contain;
    width: 1.125rem;
}

.lm-shop-status {
    background: var(--listamos-success);
    border: 2px solid var(--listamos-surface);
    border-radius: var(--radius-pill);
    height: 0.5625rem;
    position: absolute;
    right: -0.0625rem;
    top: -0.0625rem;
    width: 0.5625rem;
}

.lm-shop-status.is-warn {
    background: var(--listamos-warning);
}

.lm-shop-labels {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

.lm-shop-channel {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.02em;
    line-height: 1.2;
    text-transform: uppercase;
}

.lm-shop-name {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.012em;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-shop-chevron {
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
    font-size: 0.8125rem;
}

/* ——— The switcher beside a page title ————————————————————————————————————
   The same control as the phone's top bar, sized for a row of small controls rather
   than for a column fifteen rems wide. One line instead of two: the marketplace is
   already said by the logo in the mark, so the kicker above the name is a word the
   eye reads twice, and the two-line pill stands a head taller than every tab beside
   it. The menu it opens is unchanged — it is a list of shop names and is sized by
   them. */
.lm-shop-switch--inline {
    flex: 0 1 auto;
    /* Beside the title, including on the widths where the header wraps. Without it the
       head's space-between sends the pill to the far right of a line it shares with the
       title alone, and "Store listings ............ MapleAndTwine" reads as two
       unrelated things rather than as one heading. The strip below takes the slack
       whenever everything fits on one line, so this only shows once it does not. */
    margin-right: auto;
    min-width: 0;
}

.lm-shop-switch--inline .lm-shop-trigger {
    border-radius: var(--radius-md);
    gap: 0.4375rem;
    padding: 0.25rem 0.5rem 0.25rem 0.3125rem;
    width: auto;
}

.lm-shop-switch--inline .lm-shop-mark {
    height: 1.5rem;
    width: 1.5rem;
}

.lm-shop-switch--inline .lm-shop-mark .lm-channel-logo,
.lm-shop-switch--inline .lm-shop-mark .lm-channel-logo--icon {
    height: 0.875rem;
    width: 0.875rem;
}

.lm-shop-switch--inline .lm-shop-status {
    height: 0.4375rem;
    width: 0.4375rem;
}

.lm-shop-switch--inline .lm-shop-channel {
    display: none;
}

.lm-shop-switch--inline .lm-shop-name {
    font-size: var(--text-xs);
    max-width: 10rem;
}

.lm-shop-switch--inline .lm-shop-menu {
    min-width: 15rem;
    width: auto;
}

.lm-shop-menu {
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    margin-top: 0.375rem !important;
    padding: 0.375rem;
}

.lm-shop-menu-label {
    color: var(--listamos-text-secondary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.04em;
    margin: 0.25rem 0.4375rem 0.125rem;
    text-transform: uppercase;
}

.lm-shop-option {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius);
    color: inherit;
    cursor: pointer;
    display: flex;
    font-family: inherit;
    gap: 0.625rem;
    padding: 0.5rem 0.4375rem;
    text-align: left;
    transition: background var(--fast);
    width: 100%;
}

.lm-shop-option:hover,
.lm-shop-option.is-current {
    background: var(--ink-050);
}

.lm-shop-option.is-static {
    cursor: default;
    pointer-events: none;
}

.lm-shop-option.is-static:hover {
    background: transparent;
}

.lm-shop-check {
    color: var(--listamos-primary);
    flex: 0 0 auto;
    font-size: 1rem;
}

.lm-shop-divider {
    border-color: var(--listamos-border);
    margin: 0.375rem 0;
    opacity: 1;
}

.lm-shop-connect-row {
    align-items: center;
    display: flex;
    gap: 0.625rem;
    padding: 0.375rem 0.4375rem;
}

.lm-shop-connect-name {
    flex: 1 1 auto;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    min-width: 0;
}

.lm-shop-connect-btn {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 0;
    border-radius: var(--radius-pill);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 auto;
    font-family: inherit;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    gap: 0.25rem;
    letter-spacing: 0.01em;
    padding: 0.3125rem 0.625rem;
    transition: background var(--fast), color var(--fast);
}

.lm-shop-connect-btn:hover {
    background: var(--ink-100);
    color: var(--listamos-text);
}

.lm-shop-connect-btn .bi {
    font-size: 0.75rem;
}

.lm-shop-manage {
    align-items: center;
    border-radius: var(--radius);
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.5rem;
    padding: 0.5rem 0.4375rem;
    text-decoration: none;
    transition: background var(--fast), color var(--fast);
}

.lm-shop-manage:hover {
    background: var(--ink-050);
    color: var(--listamos-text);
    text-decoration: none;
}

.lm-add-shop {
    background: var(--listamos-surface-inverse);
    border-color: var(--listamos-surface-inverse);
    box-shadow: var(--shadow-xs);
    color: #fff;
    margin-bottom: var(--space-3);
}

.lm-add-shop:hover {
    background: var(--ink-800);
    border-color: var(--ink-800);
    color: #fff;
}

.lm-add-shop::after {
    display: none;
}

.lm-add-shop .bi {
    opacity: .85;
}

.lm-rail-foot .lm-shop-menu {
    margin-bottom: 0.375rem !important;
}

/* ——— Destinations ——————————————————————————————————————————————————————
   The current one is a tinted field with a bar against the rail's edge. Tint plus
   marker rather than a filled pill: a solid blue block in the navigation is a second
   primary action competing with the real one two inches above it. */

.lm-nav {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-5);
    min-height: 0;
    overflow-y: auto;
}

.lm-nav-group {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.lm-nav-tail {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-top: auto;
}

.lm-nav-divider {
    background: transparent;
    border: 0;
    border-top: 1px solid var(--listamos-border);
    margin: var(--space-3) 0.5rem var(--space-2);
    opacity: 1;
}

.settings-panel .lm-card-title {
    margin-bottom: var(--space-3);
}

.settings-facts {
    display: grid;
    gap: var(--space-3);
    margin: 0;
}

.settings-facts dt {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.04em;
    margin: 0 0 0.125rem;
    text-transform: uppercase;
}

.settings-facts dd {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    margin: 0;
}

.settings-languages {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.settings-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 22rem;
}

.settings-field {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.settings-field-error {
    color: var(--listamos-danger-text);
    font-size: var(--text-sm);
    margin: 0;
}

/* The keys an assistant holds.

   Same table as the catalogue and the shops, for the reason written on both of those:
   down a list, looking for the one to act on. The one thing acted on here is
   withdrawing, so the row's own name carries the weight and everything to its right is
   held to a fixed width. */
.lm-col-agent-shop {
    width: 12rem;
}

.lm-col-agent-scope {
    width: 11rem;
}

.lm-col-agent-when {
    width: 7.5rem;
}

.lm-col-agent-actions {
    width: 12.5rem;
}

/* Settings is not the catalogue. The listing-table heading style — mono, tiny,
   centred, uppercase — is for a sticky strip above hundreds of rows. Four columns
   of one key leave "KEY" floating in the middle of a wide cell above a left-ranged
   name, which reads as a broken table rather than as a label. Both classes, because the
   catalogue's rule comes later in this file and would otherwise win. */
.agents-table.listing-table > thead th {
    color: var(--listamos-text-tertiary);
    font-family: inherit;
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0;
    padding: 0.5rem var(--space-3);
    text-align: left;
    text-transform: none;
}

.agents-table.listing-table > tbody > tr > td {
    font-size: var(--text-sm);
    padding: 0.75rem var(--space-3);
    text-align: left;
    vertical-align: middle;
}

.agents-table.listing-table > thead th:last-child,
.agents-table.listing-table > tbody > tr > td:last-child {
    text-align: right;
}

.agent-who {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    min-width: 0;
}

.agent-words {
    display: grid;
    gap: 0.125rem;
    min-width: 0;
}

/* The assistant's own mark when the server could tell which it is; its initial otherwise. */
.agent-mark {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    flex: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    height: 2.25rem;
    justify-content: center;
    width: 2.25rem;
}

.agent-mark img {
    display: block;
    height: 1.25rem;
    width: 1.25rem;
}

.agent-mark.is-chatgpt,
.agent-mark.is-cursor {
    background: #0d0d0d;
    border-color: #0d0d0d;
}

.agent-mark.is-claude {
    background: #faf1ec;
    border-color: #f0dfd5;
}

.agent-mark--sm {
    border-radius: var(--radius-sm);
    height: 1.125rem;
    width: 1.125rem;
}

.agent-mark--sm img {
    height: 0.6875rem;
    width: 0.6875rem;
}

.agent-mark--lg {
    border-radius: var(--radius-lg);
    height: 3rem;
    width: 3rem;
}

.agent-mark--lg img {
    height: 1.75rem;
    width: 1.75rem;
}

.agent-clients .lm-segment {
    align-items: center;
    display: inline-flex;
    gap: 0.375rem;
}

.agent-name {
    font-weight: var(--weight-semibold);
    line-height: 1.3;
}

.agent-standing,
.agent-done {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.35;
}

.agent-history-link {
    background: none;
    border: 0;
    color: var(--listamos-primary-text);
    font: inherit;
    padding: 0;
}

.agent-history-link:hover,
.agent-history-link:focus-visible {
    text-decoration: underline;
}

.agent-shops {
    display: grid;
    gap: 0.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.agent-shops li {
    align-items: center;
    display: flex;
    gap: 0.375rem;
    min-width: 0;
}

.agent-shops li span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.agent-shops .destination-mark {
    flex: none;
    height: 14px;
    width: 14px;
}

.agents-table .lm-tag {
    line-height: 1.3;
    max-width: 100%;
    white-space: normal;
    width: fit-content;
}

.agents-table.listing-table > tbody > tr > td.agent-when {
    color: var(--listamos-text-secondary);
    white-space: nowrap;
}

.agent-withdraw:hover,
.agent-withdraw:focus-visible {
    border-color: var(--listamos-danger);
    color: var(--listamos-danger-text);
}

/* Connecting comes before the table: the address, which client, and that client's
   three steps. One client's steps at a time, because the three sets differ in every
   menu name and reading the wrong one is the usual way this goes wrong. */
.agent-connect {
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3);
}

.agent-address {
    display: flex;
    gap: var(--space-2);
}

.agent-address .form-control {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
}

.agent-address .lm-btn {
    flex: none;
}

.agent-clients {
    align-self: flex-start;
}

.agent-steps ol {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0 0 var(--space-2);
    padding-left: 1.25rem;
}

.agent-steps .agent-key {
    font-size: var(--text-xs);
}

.agent-subhead {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    margin: 0 0 var(--space-2);
}

.agent-upsell {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    padding: var(--space-3);
}

.agents-table.listing-table > tbody > tr > td.agent-empty {
    padding: var(--space-3);
    text-align: left;
}

.agent-examples {
    margin: var(--space-2) 0 0;
    padding-left: 1.25rem;
}

.agent-actions {
    display: inline-flex;
    gap: var(--space-2);
}

/* When on the left at a fixed width, so a page of rows reads down one column of
   times and one of what happened. */
.agent-history {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 0;
    padding: 0;
}

.agent-history-row {
    border-bottom: 1px solid var(--listamos-border);
    display: grid;
    font-size: var(--text-sm);
    gap: var(--space-3);
    grid-template-columns: 9.5rem 1fr;
    padding: var(--space-2) 0;
}

.agent-history-row:last-child {
    border-bottom: 0;
}

.agent-history-when {
    color: var(--listamos-text-secondary);
    font-variant-numeric: tabular-nums;
}

@media (max-width: 575.98px) {
    .agent-history-row {
        gap: 0.125rem;
        grid-template-columns: 1fr;
    }
}

/* Linked accounts. A list rather than the table the keys above use: there are at most
   three of these, each is one line, and a table of three rows and two columns is a
   grid drawn around a sentence. */
.identity-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    list-style: none;
    margin: 0 0 var(--space-3);
    padding: 0;
}

.identity {
    align-items: center;
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
}

.identity-who {
    flex: 1 1 auto;
    min-width: 0;
}

.identity-name {
    font-weight: var(--weight-medium);
}

.identity-mark {
    flex: none;
    height: 1.25rem;
    object-fit: contain;
    width: 1.25rem;
}

.identity-mark--sm {
    height: 1rem;
    width: 1rem;
}

/* Where the Unlink button would be on every other row. Said rather than left blank,
   because a missing button reads as a bug and "this is the only way in" reads as a
   reason. */
.identity-kept {
    flex: none;
    text-align: right;
}

.identity-add {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* A form inside a window, given the rhythm one on a page already has.

   A modal body has no layout of its own, so fields dropped into it sit on whatever
   margins their own labels happen to carry — which is none between them, and the three
   questions read as one block with the lines in the wrong places. The same spacing as
   `.settings-form`, without its measure: a window is already narrow. */
.lm-field-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.lm-field-stack > .lm-lede,
.lm-field-stack > .alert {
    margin: 0;
}

.lm-field-stack fieldset {
    border: 0;
    margin: 0;
    padding: 0;
}

.lm-field-stack legend {
    float: none;
    width: auto;
}

/* A rung, with the line that says what it crosses. The note is the part that is read —
   "edit" on its own sounds like it reaches the shop, and the whole point of that rung
   is that it does not — so the two sit in a column and the note is not squeezed onto
   the end of the label. */
.agent-scope {
    align-items: start;
    cursor: pointer;
    display: grid;
    gap: var(--space-2);
    grid-template-columns: auto 1fr;
    padding-block: var(--space-2);
}

.agent-scope > span {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.agent-scope input {
    margin-top: 0.25rem;
}

/* Shown once and meant to be copied, so it wraps rather than scrolls: a key with its
   middle hidden in an overflow is a key pasted in half. */
.agent-key {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-sm);
    display: block;
    font-size: var(--text-sm);
    margin-bottom: var(--space-2);
    overflow-wrap: anywhere;
    padding: var(--space-2);
}

.lm-nav-group-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.09em;
    margin: 0 0 var(--space-2);
    padding: 0 0.5rem;
    text-transform: uppercase;
}

.lm-nav-item {
    align-items: center;
    border-radius: var(--radius);
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.625rem;
    letter-spacing: -0.008em;
    min-height: var(--control);
    padding: 0 0.5rem;
    position: relative;
    text-decoration: none;
    transition: background var(--fast), color var(--fast);
}

.lm-nav-item .lm-ico,
.lm-nav-item .bi {
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
    font-size: 1.0625rem;
    transition: color var(--fast);
}

.lm-nav-item:hover {
    background: var(--ink-050);
    color: var(--listamos-text);
    text-decoration: none;
}

.lm-nav-item:hover .lm-ico,
.lm-nav-item:hover .bi {
    color: var(--listamos-text-secondary);
}

.lm-nav-item.is-current {
    background: var(--listamos-primary-soft);
    color: var(--listamos-primary-text);
    font-weight: var(--weight-semibold);
}

.lm-nav-item.is-current .lm-ico,
.lm-nav-item.is-current .bi {
    color: var(--listamos-primary);
}

/* The marker, in the gutter between the item and the edge of the rail. */
.lm-nav-item.is-current::before {
    background: var(--listamos-primary);
    border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
    content: '';
    height: 1rem;
    left: calc(var(--space-4) * -1);
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
}

.lm-nav-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-nav-count {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-xs);
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-variant-numeric: tabular-nums;
    padding: 0.125rem 0.3125rem;
}

.lm-nav-item.is-current .lm-nav-count {
    background: var(--brand-100);
    color: var(--listamos-primary-text);
}

/* ——— The account ———————————————————————————————————————————————————————
   Pinned to the foot of the rail, holding everything that is about the person rather
   than about the work: their address, their language, the way out. They were in a top
   bar before, competing for attention every second and wanted about once a month. */

.lm-rail-foot {
    border-top: 1px solid var(--listamos-border);
    margin-top: auto;
    padding-top: var(--space-3);
}

/* ——— The plan ——————————————————————————————————————————————————————————
   Which plan, how much AI is left this month, how many shop slots — at the foot of
   every screen, so the ceiling is something a seller sees coming rather than meets
   as a refusal. Quiet while there is room; amber past 80%, red when the month is out. */

.lm-plan {
    --plan-fill: var(--brand-500);
    background: var(--ink-050, #f7f7f8);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg, 0.75rem);
    display: grid;
    gap: 0.375rem;
    margin-bottom: var(--space-3);
    padding: 0.625rem 0.75rem 0.75rem;
}

.lm-plan[data-tone="low"] {
    --plan-fill: var(--amber-500, #f59e0b);
}

.lm-plan[data-tone="out"] {
    --plan-fill: var(--red-500, #ef4444);
    background: var(--red-050, #fef2f2);
    border-color: var(--red-100, #fee2e2);
}

.lm-plan-head {
    align-items: center;
    color: inherit;
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.125rem;
    text-decoration: none;
}

.lm-plan-name {
    font-size: var(--text-sm);
    font-weight: 650;
    letter-spacing: -0.01em;
}

.lm-plan-tag {
    color: var(--listamos-text-muted, var(--ink-500));
    font-size: var(--text-xs);
}

.lm-plan-tag.is-closed {
    color: var(--listamos-danger-text);
    font-weight: 500;
}

.lm-plan-head .bi {
    color: var(--listamos-text-muted, var(--ink-500));
    font-size: 0.6875rem;
    margin-left: auto;
    transition: transform 160ms ease;
}

.lm-plan-head:hover .bi {
    transform: translateX(2px);
}

.lm-plan-row {
    align-items: baseline;
    color: var(--listamos-text-muted, var(--ink-500));
    display: flex;
    font-size: var(--text-xs);
    gap: 0.5rem;
    justify-content: space-between;
}

.lm-plan-value {
    color: var(--listamos-text, var(--ink-900));
    font-variant-numeric: tabular-nums;
    font-weight: 550;
    white-space: nowrap;
}

.lm-plan-value.is-over {
    color: var(--amber-700, #b45309);
}

.lm-plan-bar {
    background: var(--ink-100, #ececef);
    border-radius: 999px;
    height: 0.3125rem;
    margin-bottom: 0.25rem;
    overflow: hidden;
}

.lm-plan-bar > i {
    background: var(--plan-fill);
    border-radius: inherit;
    display: block;
    height: 100%;
    transition: width 420ms cubic-bezier(0.2, 0.7, 0.2, 1), background-color 200ms ease;
}

.lm-plan-upgrade {
    align-items: center;
    background: var(--brand-600);
    border-radius: var(--radius, 0.5rem);
    box-shadow: 0 1px 0 rgb(255 255 255 / 0.18) inset, var(--shadow-xs);
    color: #fff;
    display: flex;
    font-size: var(--text-xs);
    font-weight: 600;
    gap: 0.375rem;
    justify-content: center;
    margin-top: 0.25rem;
    min-height: 2rem;
    padding: 0.375rem 0.625rem;
    text-decoration: none;
    transition: background-color 140ms ease;
}

.lm-plan-upgrade:hover,
.lm-plan-upgrade:focus-visible {
    background: var(--brand-700);
    color: #fff;
}

.lm-plan-manage {
    color: var(--listamos-text-muted, var(--ink-500));
    font-size: var(--text-xs);
    margin-top: 0.125rem;
    text-decoration: none;
}

.lm-plan-manage:hover {
    color: var(--listamos-text, var(--ink-900));
    text-decoration: underline;
}

/* The collapsed rail keeps one thing: a ring of what is left, coloured like the bar. */
.lm-plan-mini {
    display: none;
}

.lm-plan-mini-ring {
    background:
        radial-gradient(closest-side, var(--listamos-surface, #fff) 68%, transparent 70%),
        conic-gradient(var(--plan-fill) calc(var(--p, 100) * 1%), var(--ink-100, #ececef) 0);
    border-radius: 50%;
    display: block;
    height: 1.375rem;
    width: 1.375rem;
}

/* ——— The banner ————————————————————————————————————————————————————————
   The one plan problem in the way right now, above the page. One line, one button. */

.lm-plan-alert {
    align-items: center;
    background: var(--ink-050, #f7f7f8);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg, 0.75rem);
    display: flex;
    gap: 0.75rem;
    margin-bottom: var(--space-4);
    padding: 0.625rem 0.625rem 0.625rem 0.875rem;
}

.lm-plan-alert[hidden] {
    display: none;
}

.lm-plan-alert > .bi {
    flex: 0 0 auto;
    font-size: 1.0625rem;
}

.lm-plan-alert.is-info {
    background: var(--brand-050);
    border-color: var(--brand-100);
}

.lm-plan-alert.is-info > .bi {
    color: var(--brand-600);
}

.lm-plan-alert.is-warning {
    background: var(--amber-050, #fffbeb);
    border-color: var(--amber-100, #fef3c7);
}

.lm-plan-alert.is-warning > .bi {
    color: var(--amber-600, #d97706);
}

.lm-plan-alert.is-danger {
    background: var(--red-050, #fef2f2);
    border-color: var(--red-100, #fee2e2);
}

.lm-plan-alert.is-danger > .bi {
    color: var(--red-600, #dc2626);
}

.lm-plan-alert-text {
    flex: 1 1 auto;
    font-size: var(--text-sm);
    line-height: 1.45;
    margin: 0;
    min-width: 0;
}

.lm-plan-alert--block .lm-plan-alert-text {
    display: grid;
    gap: 0.125rem;
}

.lm-plan-alert .lm-plan-alert-action {
    background: var(--listamos-surface-inverse, var(--ink-900));
    border-color: var(--listamos-surface-inverse, var(--ink-900));
    color: #fff;
    flex: 0 0 auto;
    white-space: nowrap;
}

.lm-plan-alert .lm-plan-alert-action:hover {
    color: #fff;
    opacity: 0.9;
}

.lm-plan-alert.is-danger .lm-plan-alert-action {
    background: var(--red-600, #dc2626);
    border-color: var(--red-600, #dc2626);
}

.lm-plan-alert-close {
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm, 0.375rem);
    color: var(--listamos-text-muted, var(--ink-500));
    flex: 0 0 auto;
    height: 2rem;
    width: 2rem;
}

.lm-plan-alert-close:hover {
    background: rgb(0 0 0 / 0.05);
    color: var(--listamos-text, var(--ink-900));
}

@media (max-width: 47.9375em) {
    .lm-plan-alert {
        align-items: flex-start;
        flex-wrap: wrap;
    }

    .lm-plan-alert-text {
        flex-basis: calc(100% - 4.5rem);
    }

    .lm-plan-alert .lm-plan-alert-action {
        margin-left: 1.8125rem;
        order: 3;
    }
}

/* Arriving from "Upgrade": the billing panel says it is the one you came for. */
.settings-panel:target {
    animation: lm-plan-target 1.6s ease-out 1;
    scroll-margin-top: var(--space-6);
}

@keyframes lm-plan-target {
    0%, 30% { box-shadow: 0 0 0 3px var(--brand-100), var(--shadow-xs); }
    100% { box-shadow: var(--shadow-xs); }
}

.lm-account {
    width: 100%;
}

.lm-account-btn {
    align-items: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--listamos-text);
    cursor: pointer;
    display: flex;
    font-family: inherit;
    font-size: var(--text-sm);
    gap: 0.5rem;
    padding: 0.375rem 0.4375rem;
    text-align: left;
    transition: background var(--fast), border-color var(--fast);
    width: 100%;
}

.lm-account-btn:hover,
.lm-account-btn[aria-expanded='true'] {
    background: var(--ink-050);
    border-color: var(--listamos-border);
}

.lm-account-name {
    flex: 1 1 auto;
    font-weight: var(--weight-medium);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-account-btn .bi-chevron-expand {
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
    font-size: 0.75rem;
}

.avatar {
    align-items: center;
    background: var(--listamos-surface-inverse);
    border-radius: var(--radius-sm);
    box-shadow: var(--sheen-top);
    color: #fff;
    display: inline-flex;
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    height: 1.625rem;
    justify-content: center;
    text-transform: uppercase;
    width: 1.625rem;
}

/* ——— Putting the rail away ————————————————————————————————————————————
   One rail, two ways of not being in the way, and the line between them is whether the
   screen is wide enough to hold it and the work at the same time.

   On a laptop it is: the rail keeps its column and collapses to its icons when asked,
   which is a preference and therefore remembered. Below a laptop it is not, so the rail
   leaves the layout entirely and becomes a drawer over the page.

   What used to sit between these two was a third answer — the rail lying down as a
   strip across the top — and it was the wrong shape in both directions. On a tablet it
   put six controls in a row that had to be scrolled sideways to finish reading; on a
   phone it dropped every label to fit and left a navigation of unnamed icons. A
   navigation reduced to icons because there is no room for its labels is telling you it
   does not belong there. Now it does not go there. */

.lm-topbar,
.lm-rail-scrim,
.lm-rail-toggle {
    display: none;
}

body.lm-locked {
    overflow: hidden;
}

.lm-rail-head {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    justify-content: space-between;
}

.lm-rail-collapse {
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
}

.lm-rail-collapse i {
    transition: transform var(--medium);
}

/* ——— Collapsed ————————————————————————————————————————————————————————
   The rail at its icons. Everything in it already has one, because everything in it is
   a destination or an object — so this hides words rather than swapping the contents,
   and nothing moves anywhere it was not already.

   Set on the document rather than the shell so that the class can be applied in the
   head, before the first paint. A remembered rail that draws itself open and then snaps
   shut on every page load is worse than one that does not remember. */
@media (min-width: 64em) {
    /* No overflow on the rail. It was here to clip half-shrunk labels mid-collapse, and
       it clipped the menus with them: a shop list four inches wide opening out of a
       72-pixel column was sliced off at the column's edge. Everything that shrinks
       already hides its own overflow, so the rail does not need to. */
    .lm-rail {
        transition: width var(--medium), padding var(--medium);
        width: var(--listamos-rail-width);
    }

    .lm-rail-collapsed .lm-shell {
        --listamos-rail-width: 4.5rem;
    }

    /* One slot size for every control in the column. They were each sizing themselves —
       a 26-pixel mark, a 36-pixel collapse button, a 44-pixel destination, a shop pill
       stretched full width — and `align-items: center` on the rail cannot fix a row that
       is wider than its neighbour. Same box everywhere, icon dead centre in it. */
    .lm-rail-collapsed .lm-rail {
        --lm-rail-icon: var(--control-lg);
        align-items: center;
        padding-left: var(--space-2);
        padding-right: var(--space-2);
    }

    .lm-rail-collapsed .lm-rail-head {
        align-items: center;
        flex-direction: column;
        gap: var(--space-2);
        width: 100%;
    }

    .lm-rail-collapsed .lm-brand {
        justify-content: center;
        padding: 0;
        width: var(--lm-rail-icon);
    }

    .lm-rail-collapsed .lm-rail-collapse {
        --h: var(--lm-rail-icon);
        width: var(--lm-rail-icon);
    }

    .lm-rail-collapsed .lm-rail-collapse i {
        transform: scaleX(-1);
    }

    .lm-rail-collapsed .lm-plan {
        background: transparent;
        border: 0;
        margin-bottom: var(--space-2);
        padding: 0;
    }

    .lm-rail-collapsed .lm-plan > :not(.lm-plan-mini) {
        display: none;
    }

    .lm-rail-collapsed .lm-plan-mini {
        align-items: center;
        border-radius: var(--radius, 0.5rem);
        display: flex;
        height: var(--lm-rail-icon);
        justify-content: center;
        width: var(--lm-rail-icon);
    }

    .lm-rail-collapsed .lm-plan-mini:hover {
        background: var(--ink-100, #ececef);
    }

    .lm-rail-collapsed .lm-rail-foot > .dropup {
        display: flex;
        justify-content: center;
        margin-inline: auto;
        max-width: var(--lm-rail-icon);
        width: auto !important;
    }

    .lm-rail-collapsed .lm-nav,
    .lm-rail-collapsed .lm-nav-group,
    .lm-rail-collapsed .lm-nav-tail {
        align-items: center;
        width: 100%;
    }

    .lm-rail-collapsed .lm-rail-foot {
        align-items: center;
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
        width: 100%;
    }

    .lm-rail-collapsed .lm-account {
        margin-inline: auto;
        width: auto;
    }

    /* The current-page bar lives in the gutter and only makes sense when there is a
       gutter wide enough to read as one. In the icon column it was a sliver hanging
       off the left edge of the one destination that happened to be active. */
    .lm-rail-collapsed .lm-nav-item.is-current::before {
        display: none;
    }

    /* Scoped to the trigger, not to the switcher. The menu that drops out of it lists
       every connected shop using the same three classes the trigger does, so hiding
       `.lm-shop-labels` outright collapsed the rail and emptied the shop list with it —
       a row of channel marks and no names. What collapses is the control; what it opens
       is a menu, and a menu has room.

       `display: none` was why collapse had no animation: the words vanished the instant
       the class landed while the column was still wide. Max-width and opacity shrink
       with the rail instead, and overflow on the rail clips what is left mid-transition. */
    .lm-rail .lm-nav-group-label,
    .lm-rail .lm-nav-label,
    .lm-rail .lm-nav-count,
    .lm-rail .lm-shop-trigger .lm-shop-labels,
    .lm-rail .lm-shop-trigger .lm-shop-chevron,
    .lm-rail .lm-add-shop-label,
    .lm-rail .lm-account-btn .lm-account-name,
    .lm-rail .lm-account-btn .bi-chevron-expand {
        max-width: 12rem;
        opacity: 1;
        overflow: hidden;
        transition: max-width var(--medium), opacity var(--fast);
        white-space: nowrap;
    }

    .lm-rail .lm-nav-count {
        max-width: 3rem;
    }

    /* The word is a span next to the mark, so it collapses the same way every other
       rail label does — rather than by cropping a lockup image to its left square. */
    .lm-rail .brand-word {
        overflow: hidden;
        transition: max-width var(--medium), opacity var(--medium);
        max-width: 8rem;
    }

    .lm-rail-collapsed .brand-word {
        max-width: 0;
        opacity: 0;
    }

    .lm-rail-collapsed .lm-nav-group-label,
    .lm-rail-collapsed .lm-nav-label,
    .lm-rail-collapsed .lm-nav-count,
    .lm-rail-collapsed .lm-shop-trigger .lm-shop-labels,
    .lm-rail-collapsed .lm-shop-trigger .lm-shop-chevron,
    .lm-rail-collapsed .lm-add-shop-label,
    .lm-rail-collapsed .lm-account-btn .lm-account-name,
    .lm-rail-collapsed .lm-account-btn .bi-chevron-expand {
        max-width: 0;
        opacity: 0;
    }

    /* The chevron is drawn by Bootstrap as a pseudo-element on the toggle, so hiding the
       label is not enough to stop the button reserving room for one. */
    .lm-rail-collapsed .lm-add-shop.dropdown-toggle::after,
    .lm-rail-collapsed .lm-shop-trigger.dropdown-toggle::after {
        display: none;
    }

    .lm-rail-collapsed .lm-brand,
    .lm-rail-collapsed .lm-rail-collapse,
    .lm-rail-collapsed .lm-nav-item,
    .lm-rail-collapsed .lm-add-shop,
    .lm-rail-collapsed .lm-shop-trigger,
    .lm-rail-collapsed .lm-account-btn {
        flex: 0 0 auto;
        gap: 0;
        height: var(--lm-rail-icon);
        justify-content: center;
        margin-inline: auto;
        max-width: var(--lm-rail-icon);
        min-width: var(--lm-rail-icon);
        overflow: hidden;
        padding: 0;
        width: var(--lm-rail-icon);
    }

    /* Expanded it is a pill across the rail. Collapsed it is the channel mark in the same
       square tile as every other icon — not a wide oval with invisible words still
       setting min-width under flex. `max-width: 0` alone does not shrink past min-content;
       the border was wrapping a row the eye cannot see and calling it a button. */
    .lm-rail-collapsed .lm-shop-trigger {
        background: transparent;
        border-color: transparent;
        border-radius: var(--radius);
        box-shadow: none;
    }

    .lm-rail-collapsed .lm-shop-trigger:hover,
    .lm-rail-collapsed .lm-shop-trigger[aria-expanded='true'] {
        background: var(--ink-050);
        border-color: var(--listamos-border);
        box-shadow: var(--shadow-xs);
    }

    .lm-rail-collapsed .lm-shop-trigger .lm-shop-labels,
    .lm-rail-collapsed .lm-shop-trigger .lm-shop-chevron {
        flex: 0 0 0;
        margin: 0;
        max-width: 0;
        min-width: 0;
        overflow: hidden;
        padding: 0;
        width: 0;
    }

    /* Zero width is not always zero in a flex row — min-content can still nudge the icon
       off centre while the box around it lines up. */
    .lm-rail-collapsed .lm-nav-label,
    .lm-rail-collapsed .lm-add-shop-label {
        flex: 0 0 0;
    }

    /* Padding was still buying ten pixels after max-width hit zero, which shoved the
       destination icon left while the box around it sat centred. */
    .lm-rail-collapsed .lm-nav-count {
        flex: 0 0 0;
        margin: 0;
        max-width: 0;
        min-width: 0;
        overflow: hidden;
        padding: 0;
        width: 0;
    }


    /* The menus still open at the rail's old width. They are lists of shop names and
       languages, and a list of names in a 72-pixel column is not a list of names. */
    .lm-rail-collapsed .lm-rail-foot .dropdown-menu {
        min-width: 15rem;
        width: auto;
    }
}

/* ——— The drawer ——————————————————————————————————————————————————————————
   Below a laptop the rail keeps the shape it has above one — vertical, labelled, in the
   order it is always in — and moves off the side of the screen. A seller who learns the
   navigation on a laptop does not have to learn a second one on an iPad; it is the same
   rail, and the only thing that changed is whether it is showing. */
@media (max-width: 63.9375em) {
    /* There is no column to collapse into down here, and the button that would do it has
       nothing to say: the drawer is already either out or away. */
    .lm-rail-collapse {
        display: none;
    }

    .lm-shell {
        display: flex;
        flex-direction: column;
        min-height: 100dvh;
    }

    .lm-topbar {
        align-items: center;
        background: var(--listamos-sidebar);
        border-bottom: 1px solid var(--listamos-border);
        display: flex;
        flex: 0 0 auto;
        gap: var(--space-3);
        padding: var(--space-3);
        position: sticky;
        top: 0;
        z-index: 40;
    }

    /* Sized by the name in it, not by the bar around it.
       
       In the rail the pill is a column-wide control and `w-100` is right; here the same
       markup was being told to fill a bar it shares with two other things, and the
       result was a shop name floating in eight inches of button with the chevron parked
       against the far edge. It gives rather than grows: it is the only thing in the bar
       that can, the other two being a 34-pixel button and a mark, so a long shop name
       shortens the pill's own text instead of pushing anything off the screen. */
    .lm-topbar .lm-shop-switch {
        flex: 0 1 auto;
        margin-left: auto;
        min-width: 0;
        width: auto !important;
    }

    .lm-topbar .lm-shop-trigger {
        max-width: 100%;
        width: auto;
    }

    /* The menu is a list of shop names and marketplaces, so it is sized by what is in
       it rather than by the control it hangs from. */
    .lm-topbar .lm-shop-menu {
        min-width: 15rem;
        width: auto;
    }

    .lm-rail-toggle {
        display: inline-flex;
        flex: 0 0 auto;
    }

    /* The shop pill is in the top bar down here and stays there: it is the one thing a
       seller needs to see without opening anything, and two of them on one screen would
       mean the control that changes shops is somewhere different depending on the width
       of the window. So the one beside the catalogue's title stands down. */
    .lm-page-head--catalogue .lm-shop-switch {
        display: none;
    }

    .lm-rail {
        background: var(--listamos-sidebar);
        border-right: 1px solid var(--listamos-border);
        height: 100dvh;
        left: 0;
        pointer-events: none;
        position: fixed;
        top: 0;
        transform: translateX(-100%);
        /* Transform only. Visibility was why close looked unanimated: the instant the
           open class dropped, hidden fired and the drawer vanished while translate was
           still mid-flight — a slide the eye never got to see. Off-screen is enough to
           keep it out of the way; pointer-events finishes the job for taps. */
        transition: transform var(--medium);
        width: min(17rem, 82vw);
        z-index: 60;
    }

    .lm-shell.is-rail-open .lm-rail {
        pointer-events: auto;
        transform: translateX(0);
    }

    .lm-rail-scrim {
        background: rgba(15, 19, 32, .4);
        border: 0;
        display: block;
        inset: 0;
        opacity: 0;
        pointer-events: none;
        position: fixed;
        transition: opacity var(--medium);
        z-index: 50;
    }

    .lm-shell.is-rail-open .lm-rail-scrim {
        opacity: 1;
        pointer-events: auto;
    }

    /* Nothing to the left of it any more, so nothing to be divided from. Flex-grow keeps
       the canvas grey all the way down; `min-height: 0` was leaving body white below
       short pages like bulk history. */
    .lm-work {
        border-left: 0;
        box-shadow: none;
        flex: 1 1 auto;
    }

    .lm-work-inner {
        padding: var(--space-6) var(--space-4) var(--space-9);
    }
}

@media (max-width: 47.9375em) {
    .lm-work-inner {
        padding: var(--space-5) var(--space-3) var(--space-8);
    }

    .lm-panel-head {
        padding: var(--space-3);
    }
}


/* ——————————————————————————————————————————————— 9. The catalogue

   The most important screen in the product, and the one a seller has open all day.

   Its structure is: the page says what this is, a row of views says which slice of it
   you are looking at, and one panel holds the toolbar and the rows together. The
   toolbar being *inside* the panel is the point — a strip of controls floating above a
   bare table has to be re-associated by eye every time the page loads, and it is the
   single most common reason an admin screen feels assembled rather than designed. */

/* ——— Views ——————————————————————————————————————————————————————————————
   Five slices of the same catalogue, each carrying its own count. The counts are what
   make this navigation rather than decoration: "Needs attention 17" is a reason to
   press it, "Needs attention" alone is a guess. */

/* The strip is two boxes, not one: the first holds what is only here, the rest is
   what the shop last said. The gap between them is the whole separation — a rule
   inside one box reads as a divider between two of the eight tabs. */
.lm-views {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    overflow-x: auto;
    scrollbar-width: none;
    width: fit-content;
    max-width: 100%;
}

.lm-view-group {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    display: flex;
    flex: 0 0 auto;
    gap: 2px;
    padding: 3px;
}

.lm-views::-webkit-scrollbar {
    display: none;
}

.lm-view {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 auto;
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.4375rem;
    height: calc(var(--control) - 6px);
    letter-spacing: -0.008em;
    padding: 0 0.6875rem;
    transition: background var(--fast), box-shadow var(--fast), color var(--fast);
    white-space: nowrap;
}

.lm-view:hover {
    color: var(--listamos-text);
}

.lm-view.is-on {
    background: var(--listamos-surface);
    box-shadow: var(--shadow-sm);
    color: var(--listamos-text);
    font-weight: var(--weight-semibold);
}

.lm-view-count {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
}

.lm-view.is-on .lm-view-count {
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-xs);
    color: var(--listamos-primary-text);
    padding: 0.125rem 0.3125rem;
}

.lm-view[data-view="rejected"] .lm-view-count {
    color: var(--listamos-danger);
}

.lm-view[data-view="rejected"].is-on .lm-view-count {
    color: var(--listamos-primary-text);
}

/* ——— The problems line (retired) ————————————————————————————————————————
   Gone / Refused / Edited moved into the view strip's second box, after Not sent.
   The bar is kept in the DOM empty so older paint calls stay harmless. */

.lm-problems {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-problems:empty {
    display: none;
}

.lm-problems-label {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.lm-problem {
    align-items: center;
    background: transparent;
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    gap: 0.375rem;
    line-height: 1;
    padding: 0.3125rem 0.5rem;
    transition: background var(--fast), border-color var(--fast), color var(--fast);
    white-space: nowrap;
}

.lm-problem:hover {
    background: var(--listamos-surface-sunken);
    border-color: var(--listamos-border-strong);
    color: var(--listamos-text);
}

.lm-problem.is-on {
    background: var(--listamos-primary-soft);
    border-color: transparent;
    color: var(--listamos-primary-text);
}

.lm-problem-count {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
}

.lm-problem[data-problem="rejected"] .lm-problem-count {
    color: var(--listamos-danger);
}

.lm-problem.is-on .lm-problem-count {
    color: inherit;
}

/* ——— The toolbar ————————————————————————————————————————————————————————
   One designed surface holding search, filter, sort, density and the overflow, rather
   than five controls that happen to be adjacent. Everything in it shares `--control`,
   which is why it reads as a single object. */

.lm-toolbar {
    align-items: center;
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-toolbar .lm-search {
    flex: 1 1 16rem;
    max-width: 24rem;
}

.lm-toolbar-group {
    align-items: center;
    display: flex;
    gap: var(--space-2);
}

.lm-toolbar-divider {
    background: var(--listamos-border);
    flex: 0 0 auto;
    height: 1.25rem;
    width: 1px;
}

.lm-toolbar-spacer {
    flex: 1 1 auto;
}

/* Allowed to wrap and allowed to shrink. It was `flex: 0 0 auto`, which on a phone
   meant the refresh button and the range beside it kept their full width and took the
   whole page with them: 453 pixels of content in a 390 pixel window, and the count of
   listings sitting off the right edge on every screen size we ship to. */
.lm-toolbar-meta {
    align-items: center;
    display: flex;
    flex: 0 1 auto;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    min-width: 0;
}

.lm-toolbar-range {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.lm-sync-btn {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    box-shadow: var(--shadow-sm);
    color: var(--listamos-text);
    font-weight: var(--weight-semibold);
    gap: 0.4375rem;
    white-space: nowrap;
}

.lm-sync-btn .bi-arrow-repeat {
    color: var(--listamos-primary);
    font-size: 0.9375rem;
}

.lm-sync-btn:hover:not(:disabled) {
    background: var(--listamos-surface);
    border-color: color-mix(in srgb, var(--listamos-primary) 35%, var(--listamos-border));
    color: var(--listamos-text);
}

.lm-sync-btn:disabled {
    cursor: default;
    opacity: 0.65;
}

.lm-sync-btn.is-running .bi-arrow-repeat {
    animation: lm-sync-spin 0.9s linear infinite;
}

.lm-sync-btn-when {
    color: var(--listamos-text-secondary);
    font-weight: var(--weight-regular);
}

.lm-sync-btn-sep {
    color: var(--listamos-text-tertiary);
    opacity: 0.6;
}

/* Last bulk run still worth acting on — progress or Undo — after the editor closes. */
.lm-bulk-pill {
    align-items: center;
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-width: 0;
}

.lm-bulk-pill.is-running .bi-arrow-repeat {
    animation: lm-sync-spin 0.9s linear infinite;
}

/* A count riding on the button that produced it: "Filter 2". */
.lm-count {
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-xs);
    color: var(--listamos-primary-text);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-variant-numeric: tabular-nums;
    margin-left: 0.125rem;
    padding: 0.0625rem 0.25rem;
}

.lm-btn.is-on {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary-border);
    color: var(--listamos-primary-text);
}

.lm-btn.is-on .bi {
    opacity: 1;
}

/* ——— The filter menu ————————————————————————————————————————————————————
   Everything a rail of filters used to hold, in a menu that is only open while it is
   being used. The counts come with it: "Refused 4" is the only way to find out that
   four edits never reached the shop. */

.lm-filter {
    max-height: min(30rem, 70vh);
    min-width: 17rem;
    overflow-y: auto;
    padding: var(--space-3);
}

.lm-facet-group + .lm-facet-group {
    border-top: 1px solid var(--listamos-border);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
}

.lm-facet-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.08em;
    margin-bottom: var(--space-2);
    text-transform: uppercase;
}

.lm-facet {
    align-items: center;
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--listamos-text);
    cursor: pointer;
    display: flex;
    font-family: inherit;
    font-size: var(--text-sm);
    gap: var(--space-2);
    padding: 0.375rem 0.4375rem;
    text-align: left;
    transition: background var(--fast), color var(--fast);
    width: 100%;
}

.lm-facet:hover {
    background: var(--ink-050);
}

.lm-facet.is-on {
    background: var(--listamos-primary-soft);
    color: var(--listamos-primary-text);
    font-weight: var(--weight-medium);
}

.lm-facet-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-facet-count {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
}

.lm-facet-more .lm-facet-name {
    color: var(--lm-accent);
}

.lm-facet-more i {
    color: var(--lm-accent);
    font-size: 0.75rem;
}

.lm-facet.is-on .lm-facet-count {
    color: var(--listamos-primary-text);
}

/* ——— Selection ——————————————————————————————————————————————————————————
   When rows are chosen the toolbar becomes the thing you can do to them. Replacing
   rather than adding: a bar that appears below the toolbar pushes four hundred rows
   down the page every time somebody ticks a box, and the two rows of controls then
   compete for the same decision. */

.lm-selection {
    align-items: center;
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-selection-count {
    align-items: center;
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-sm);
    color: var(--listamos-primary-text);
    display: inline-flex;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    height: var(--control);
    padding: 0 0.625rem;
    white-space: nowrap;
}

.lm-selection-divider {
    background: var(--listamos-border);
    flex: 0 0 auto;
    height: 1.25rem;
    width: 1px;
}

.lm-selection-all {
    background: none;
    border: 0;
    color: var(--listamos-primary);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.lm-selection-all:hover {
    color: var(--listamos-primary-hover);
}

/* ——— Applied filters ————————————————————————————————————————————————————
   One capsule per filter in force, each removable. This is the only honest way to
   show state that lives in the URL: without it, a seller who has scrolled past the
   toolbar is looking at a filtered catalogue with no sign that it is filtered. */

.lm-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-chips:empty,
.chips:empty {
    display: none;
}

/* Filled, not outlined. These were white on white — a tag inside the white chip box, a
   filter capsule on a white panel — with a hairline doing the whole job of saying where
   one ended and the next began. Thirteen of those in two rows is a grey mesh, and the
   thing a seller is actually scanning for ("did 'linen cushion' go in twice?") is the
   word, not the box around it.

   Neutral and not brand-tinted, though these are chosen values. There can be thirteen
   tags beside thirteen materials, and twenty-six blue capsules would make the seller's
   own words look like the interface's opinion. It also leaves the red hover free to
   mean the one thing it means: this one is about to be removed. */
.lm-chip,
.chip {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border-strong);
    border-radius: var(--radius-sm);
    color: var(--listamos-text);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    gap: 0.375rem;
    line-height: 1;
    padding: 0.3125rem 0.5rem;
    transition: background var(--fast), border-color var(--fast), color var(--fast);
    white-space: nowrap;
}

.lm-chip:hover,
.chip:not(.is-static):hover {
    background: var(--listamos-danger-soft);
    border-color: var(--red-200);
    color: var(--listamos-danger-text);
}

.lm-chip .bi,
.chip .bi {
    color: var(--listamos-text-tertiary);
    font-size: 0.6875rem;
}

.lm-chip:hover .bi,
.chip:not(.is-static):hover .bi {
    color: var(--listamos-danger);
}

/* ——— The rows ———————————————————————————————————————————————————————————
   Fixed layout, hairlines between rows, no vertical rules and no zebra. The panel
   around it draws the outer edge, so the table itself draws almost nothing. */

.listing-table {
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--text-sm);
    table-layout: fixed;
    width: 100%;
}

.listing-table > thead {
    position: sticky;
    /* Clear of the strip above, which is also sticky and owns the top of the window.
       Measured rather than guessed: the strip's height depends on the language and on
       whether rows are selected, so a hard-coded offset leaves the headings either
       overlapped in Russian or floating in English. See the observer in
       listings/index. On the row group rather than on the cells because that is where
       `position: sticky` is set, and an offset on a statically positioned `th` is
       ignored. */
    top: var(--lm-strip-h, 0px);
    z-index: 2;
}

@keyframes lm-sync-spin {
    to {
        transform: rotate(360deg);
    }
}

.listing-table > thead th {
    background: var(--listamos-surface);
    border-bottom: 1px solid var(--listamos-border);
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium);
    hyphens: none;
    letter-spacing: 0.08em;
    padding: 0;
    text-align: center;
    text-transform: uppercase;
    vertical-align: middle;
    white-space: normal;
}

/* The headings that sort are buttons filling their cell, so the centring above has to be
   said again as a flex rule — `text-align` does not place a flex item. */
.lm-sort-head {
    align-items: center;
    background: transparent;
    border: 0;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    gap: 0.25rem;
    justify-content: center;
    letter-spacing: inherit;
    padding: 0.5rem var(--space-3);
    text-align: inherit;
    text-transform: inherit;
    width: 100%;
}

.lm-sort-head:hover,
.lm-sort-head:focus-visible {
    color: var(--listamos-text-secondary);
}

.lm-sort-head.is-active {
    color: var(--listamos-text);
}

.lm-sort-icon {
    flex: 0 0 auto;
    font-size: 0.625rem;
    opacity: 0.45;
}

.lm-sort-head.is-active .lm-sort-icon {
    color: var(--listamos-primary);
    opacity: 1;
}

.listing-table > tbody > tr > td {
    border-bottom: 1px solid var(--listamos-border);
    padding: 0.625rem var(--space-3);
    vertical-align: middle;
}

/* Header and body tick boxes line up because they are centred in a column of one
   fixed width, rather than each inset by a padding that thead and tbody disagree
   about. `text-align` does the centring, so no cell needs a height to be right. */
.listing-table col.lm-col-check {
    width: 2.75rem;
}

.listing-table > thead > tr > th.lm-col-check,
.listing-table > tbody > tr > td.lm-col-check {
    box-sizing: border-box;
    padding: 0;
    text-align: center;
    vertical-align: middle;
    width: 2.75rem;
}

.listing-table .lm-check-slot {
    display: inline-flex;
    line-height: 1;
    vertical-align: middle;
}

.listing-table .lm-check-slot .form-check-input {
    display: block;
    flex: none;
    margin: 0;
}

.listing-table .form-check-input:focus-visible {
    outline: 2px solid var(--listamos-focus);
    outline-offset: 1px;
}

.listing-table > thead th:last-child,
.listing-table > tbody > tr > td:last-child {
    padding-right: var(--space-4);
}

.listing-table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/* A table that is the whole panel has to carry the panel's corners.

   The catalogue puts a head and a foot inside the panel, and those two are what touch
   the rounded corners there — which is why the panel is not clipped, and why it must not
   be: `overflow: hidden` would make it a scroll container and silently kill the sticky
   heading inside it. The four screens that came later — shops, templates, the release
   queue, the keys in Settings — put the table in on its own, so the table is what
   touches the corners, and nothing had told it.

   The top squared off always, because `thead th` is painted opaque to sit under the
   sticky strip. The bottom squared off only sometimes, which is what made it look like a
   rendering fault rather than a missing rule: a last row is transparent until it is
   hovered or ticked, and then it paints its own square corner over the panel's curve. */
.lm-panel > .listing-table:first-child > thead > tr:first-child > th:first-child {
    border-top-left-radius: var(--radius-xl);
}

.lm-panel > .listing-table:first-child > thead > tr:first-child > th:last-child {
    border-top-right-radius: var(--radius-xl);
}

.lm-panel > .listing-table:last-child > tbody > tr:last-child > td:first-child {
    border-bottom-left-radius: var(--radius-xl);
}

.lm-panel > .listing-table:last-child > tbody > tr:last-child > td:last-child {
    border-bottom-right-radius: var(--radius-xl);
}

.listing-table > tbody > tr {
    transition: background var(--fast);
}

/* Asked as a capability, because a touch browser answers `:hover` with the last thing
   tapped and then keeps answering it. Unguarded, scrolling a list left one row tinted
   behind you — a highlight that means "here is the cursor" on a device with no cursor,
   pointing at a row the reader has already left. */
@media (hover: hover) {
    .listing-table > tbody > tr:hover {
        background: var(--ink-025);
    }
}

/* A chosen row is tinted and carries a bar on its leading edge, so a selection
   survives being scrolled past the header that counts it. */
.listing-table > tbody > tr:has(.form-check-input:checked) {
    background: var(--listamos-primary-soft);
}

.listing-table > tbody > tr:has(.form-check-input:checked) > .lm-col-check {
    box-shadow: inset 2px 0 0 var(--listamos-primary);
}

.listing-cell {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    min-width: 0;
}

.listing-text {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

/* The photograph is the anchor of the row: it is how a seller finds a listing among
   four hundred, long before they read a title. */
.listing-thumb {
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    flex: 0 0 auto;
    height: 2.5rem;
    object-fit: cover;
    width: 2.5rem;
}

.listing-thumb.is-empty {
    align-items: center;
    color: var(--ink-250);
    display: flex;
    font-size: 0.875rem;
    justify-content: center;
}

.listing-title {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    letter-spacing: -0.01em;
    line-height: 1.35;
    overflow: hidden;
    text-decoration: none;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.listing-title:hover {
    color: var(--listamos-primary);
    text-decoration: none;
}

.listing-meta {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    gap: 0.375rem;
    letter-spacing: 0;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
}

.listing-meta a {
    color: inherit;
}

.listing-meta a:hover {
    color: var(--listamos-primary);
}

.listing-sku {
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Where the listing is filed, said under the title instead of in two columns of its
   own. Off at every width but the one that cannot hold those columns — see the band
   below a small laptop. */
.listing-where {
    display: none;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A drawn separator rather than a typed one: a middle dot in the markup would be read
   aloud by a screen reader and copied out with the text. */
.listing-meta > * + *::before {
    background: var(--ink-250);
    border-radius: 50%;
    content: '';
    display: inline-block;
    height: 2px;
    margin-right: 0.375rem;
    vertical-align: middle;
    width: 2px;
}

/* A sentence in a cell — which fields are waiting, why Etsy refused the last attempt.
   Wraps to two lines and then clamps: the alternative is a fixed-layout column
   clipping a refusal mid-word, which is worse than not showing it. */
.row-note {
    color: var(--listamos-text-tertiary);
    display: -webkit-box;
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-height: 1.35;
    overflow: hidden;
}

.row-note.is-bad {
    color: var(--listamos-danger-text);
}

.listing-table.is-compact .row-note {
    display: none;
}

.listing-table td.numeric {
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* A listing priced per variation says so in two words on one extra line, not three.
   The cell is one of seven and every line it grows is a listing off the screen. */
.price-from {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    margin-right: 0.125rem;
}

.variant-note {
    color: var(--listamos-text-tertiary);
    display: block;
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    line-height: 1.3;
    text-align: right;
    white-space: nowrap;
}

.listing-dimension {
    color: var(--listamos-text-secondary);
    display: block;
    font-size: var(--text-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ——— Row actions ————————————————————————————————————————————————————————
   Four buttons on every row, showing, in the last column beside the health read.

   They used to be revealed: the four facts between the title and the score faded under
   the cursor and the actions opened across the space they left. It was an elegant trade
   and it was paying for the wrong thing. Every touch screen had to be sold a second
   layout, because a tap on something at zero opacity is a tap on nothing. The facts a
   seller was mid-way through reading blinked out as the pointer crossed the row, which
   is the one moment they were being read. And an action that is only there while you
   are pointing at it is an action most people never learn the product has.

   Showing them costs the width of the column they sit in, and the title column pays it.
   That is the whole bill, and it is charged once rather than on every pointer move. */

/* One row: the reading under the header that names it, then the buttons at the table's
   edge. The score keeps its place down the column — what follows it is the same width on
   every row — and it now sits where HEALTH points, which is what the header is for. The
   buttons are the last thing on the row in the order they are used: a seller reads the
   listing, reads what it scores, and only then does something about it. */
.lm-health-cell {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    /* The reading at the column's left edge, the buttons at the table's. Pushed apart
       rather than both hard right, so the score starts in the same place on every row —
       a number that moves with the width of what sits beside it cannot be scanned. */
    justify-content: space-between;
    min-width: 0;
}

.lm-health-cell > .lm-health {
    flex: 0 1 auto;
    min-width: 0;
}

/* What the marketplace still wants, in the column where a listing carries its score.
   No meter, because there is nothing to score: the number is about how a listing fares
   in a search, and this one is not in a search yet. */
.lm-needs {
    color: var(--listamos-text-secondary);
    cursor: default;
    display: inline-block;
    font-size: var(--text-2xs);
    line-height: 1.3;
    max-width: 100%;
    overflow: hidden;
    text-decoration: underline dotted var(--listamos-border-strong);
    text-overflow: ellipsis;
    text-underline-offset: 3px;
    white-space: nowrap;
}

/* Several shops on one product row — each chip opens that shop's listing. */
.lm-shop-pills {
    align-items: center;
    display: flex;
    flex: 0 1 auto;
    /* Wrap rather than shrink: a 11rem column with two Draft chips and an Add used
       to ellipsis "Draft" to "Dra" and paint the marks on top of each other. */
    flex-wrap: wrap;
    gap: 0.35rem;
    min-width: 0;
}

.lm-shop-chip {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    color: var(--listamos-text);
    display: inline-flex;
    /* Do not shrink below the mark and the state word — wrapping the pill row is
       cheaper than clipping "Draft" mid-word, which reads as a broken layout. */
    flex: 0 0 auto;
    gap: 0.35rem;
    max-width: 100%;
    padding: 0.2rem 0.5rem 0.2rem 0.35rem;
    text-decoration: none;
}

/* Status-only chips (pinned pins) are spans, not links — no hand cursor. */
span.lm-shop-chip {
    cursor: default;
}

.lm-shop-chip:hover {
    border-color: var(--listamos-border-strong);
    color: var(--listamos-text);
    text-decoration: none;
}

/* The shop name, when a chip carries one — not the state. State is short on purpose
   ("Draft", "Active") and ellipsising it was the whole complaint. */
.lm-shop-chip > span:not(.lm-shop-chip-state) {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-shop-chip-state {
    color: var(--listamos-text-tertiary);
    flex: 0 0 auto;
    font-size: var(--text-3xs);
    white-space: nowrap;
}

.lm-shop-chip-state::before {
    color: var(--ink-250);
    content: '·';
    margin-right: 0.35rem;
}

.lm-shop-chip.is-quiet {
    background: transparent;
    color: var(--listamos-text-secondary);
    cursor: default;
    pointer-events: none;
}

/* A shop this product can still go to. Dashed so it is not read as already there. */
.lm-shop-chip.is-add {
    border-style: dashed;
    color: var(--listamos-text-secondary);
}

/* Mark only: the logo is the shop. Padding matches the mark so the chip stays square. */
.lm-shop-chip.is-mark {
    padding: 0.2rem;
}

.lm-pin-empty {
    color: var(--listamos-text-tertiary);
}

/* Pin column — mark + word only, never a shop-chip button (border / fill).
   Planned pins stay links to finish setup; live pins are status spans. */
.lm-shop-chip.is-pin {
    background: transparent;
    border: none;
    color: var(--listamos-text-secondary);
    flex: 0 0 auto;
    gap: 0.25rem;
    overflow: visible;
    padding: 0;
}

.lm-shop-chip.is-pin:hover {
    background: transparent;
    border: none;
    color: var(--listamos-text-secondary);
}

.lm-shop-chip.is-pin.is-planned {
    color: var(--listamos-text-secondary);
}

.lm-shop-chip .destination-mark {
    display: block;
    flex: none;
    height: 14px;
    width: 14px;
}

/* Always on for products: the catalogue folds row actions into lm-row-more on narrow
   widths, but a published product's only control is this menu, so hiding it would leave
   a row with nowhere to go. */
.lm-product-actions {
    display: block;
    flex: 0 0 auto;
}

.lm-row-actions {
    align-items: center;
    display: flex;
    flex: 0 0 auto;
    gap: 0.125rem;
    white-space: nowrap;
}

/* The four of them behind one button, for a table with no room for four. Off wherever
   there is room — see the band below, which is the only place it is on. */
.lm-row-more {
    display: none;
    flex: 0 0 auto;
}

/* Named actions rather than icons repeated at a larger size: a menu is read, and a row
   of four icons in a list is four things to guess at. */
.lm-row-menu .dropdown-item {
    align-items: center;
    display: flex;
    gap: var(--space-2);
}

.lm-row-menu .dropdown-item.is-danger {
    color: var(--listamos-danger-text);
}

.lm-row-menu .dropdown-item.is-danger:hover,
.lm-row-menu .dropdown-item.is-danger:focus {
    background: var(--listamos-danger-soft);
    color: var(--listamos-danger-text);
}

/* ——— The columns ——————————————————————————————————————————————————————————
   The listing column has no width and takes what is left, which is what makes the
   title grow with the window rather than the price. */

.lm-col-category,
.lm-col-section {
    width: 12%;
}

.lm-col-price {
    width: 8%;
}

.lm-col-stock {
    width: 7%;
}

/* Products only — a short code, measured like price so compact density still shows it. */
.lm-col-sku {
    width: 9%;
}

/* The one column measured in text rather than in percent, because nothing in it grows
   with the window: four icon buttons, a three-digit score, a 48-pixel bar and a count
   beneath. A percentage was either too little of a narrow table to hold them or too
   much of a wide one, and the table is laid out fixed, so the number that fits is the
   number to write. What the column does not spend goes to the title. */
.lm-col-health {
    width: 17.5rem;
}

/* Products: two fixed trailing columns. Measured in rem like health — percent (or the
   old `1%` + nowrap trick) either starved Pin until the chip painted past the panel,
   or left Health's 17.5rem on Shops when the colgroup still had only one last column. */
.listing-table.is-ours .lm-col-shops {
    width: 13rem;
}

/* Room for a labelled pin chip beside edit/delete on one line. 7.25rem left only a
   sliver once cell padding and two icon buttons took their share — `overflow: hidden`
   then cut the Pinterest mark into a crescent. 10.5rem still wrapped the chip under
   the buttons; 12rem holds mark + "Pinned" + the two icons without clipping. */
.listing-table.is-ours .lm-col-pins {
    width: 12rem;
}

.listing-table.is-ours .lm-col-pins .lm-health-cell {
    flex-wrap: wrap;
    justify-content: flex-start;
    min-width: 0;
}

.listing-table.is-ours .lm-col-pins .lm-shop-pills,
.listing-table.is-ours .lm-col-pins .lm-pin-pills {
    min-width: 0;
}

/* ——— When the table is narrower than its columns ——————————————————————————
   Asked of the table and not of the window, which are two different measurements and
   the reason this was wrong: the rail takes 230 pixels and hands them back when it is
   folded, so a 1280 laptop with it out leaves the table exactly what a 1050 window
   leaves it — and a rule written against the window gave that table the wide layout and
   truncated the title to "Monopoly Man The World…". See `lm-table-scope` in the screen.

   Below this width the last column's four buttons are a fixed 280 pixels out of a table
   that no longer has them to give, and what pays is the title. Nothing else on the row
   identifies a listing, so it cannot be the column that gives way.

   What gives instead is the filing and the score's supporting cast:

   1. **Category and Section come off the table.** A percent of a narrow table is not a
      word — they were down to "Art & C…" and "Monop…", which names nothing and costs a
      fifth of the width. They go under the title instead, where the words fit, and the
      percent they were spending goes to Price and Stock, which are set in a monospace
      that does not truncate and so were running "$255.07" into "8989".
   2. **The bar and the count come off the score.** The bar says what the number says
      and the count is in the checklist behind it; the reading keeps the part nobody
      can infer.

   The two columns collapse rather than disappear. A fixed table takes its column count
   from the colgroup, so a cell removed with `display: none` does not remove a column —
   it shifts every cell after it one to the left and the price lands in the section's
   width. Zero and clipped looks the same and keeps the grid honest.

   Floored above a phone, where the row is not a table at all: the card has room for the
   whole reading and re-lays the cells itself further down. */
.lm-table-scope {
    container: listing-table / inline-size;
}

/* 70rem is where "Art & Collectibles" stops fitting in the twelve percent the column is
   given: below it the words are an ellipsis and the column is furniture. Products has
   neither column, so the nth-child rules that collapse them would hit price and stock
   instead — scoped to the shop catalogue only. */
@container listing-table (min-width: 30rem) and (max-width: 70rem) {
    .lm-col-category,
    .lm-col-section {
        width: 0;
    }

    .listing-table:not(.is-ours) > thead > tr > th:nth-child(3),
    .listing-table:not(.is-ours) > thead > tr > th:nth-child(4),
    .listing-table:not(.is-ours) > tbody > tr > td:nth-child(3),
    .listing-table:not(.is-ours) > tbody > tr > td:nth-child(4) {
        overflow: hidden;
        padding-left: 0;
        padding-right: 0;
    }

    .listing-table:not(.is-ours) > thead > tr > th:nth-child(3) > *,
    .listing-table:not(.is-ours) > thead > tr > th:nth-child(4) > *,
    .listing-table:not(.is-ours) > tbody > tr > td:nth-child(3) > *,
    .listing-table:not(.is-ours) > tbody > tr > td:nth-child(4) > * {
        display: none;
    }

    .listing-table:not(.is-ours) .listing-where {
        display: block;
    }

    /* Measured in text now, like the health column and for the same reason: what is in
       them is a price and a count, neither of which grows with the window. */
    .lm-col-price {
        width: 7rem;
    }

    .lm-col-sku {
        width: 7rem;
    }

    .lm-col-stock {
        width: 5rem;
    }

    /* And the four buttons fold into one. Four of them are 145 pixels of a table that is
       already short of them, and they are the part of the row that can be put behind a
       press: the score has to be read to be any use, an action is looked for only once
       the seller has decided to take it. */
    .lm-row-actions {
        display: none;
    }

    .lm-row-more {
        display: block;
    }

    /* Listings only need a score digit and the ⋯ — eight rem holds them. Products put
       a readiness sentence in the same column ("6 things to finish"), and eight rem
       left that sentence painting over the menu button. Keep the shrunk width for the
       shop catalogue; give Products room for the words plus the one control beside them. */
    .lm-col-health {
        width: 8rem;
    }

    .listing-table.is-ours .lm-col-shops {
        width: 11rem;
    }

    .listing-table.is-ours .lm-col-pins {
        width: 10.5rem;
    }

    .listing-table .lm-health-bar,
    .listing-table .lm-health-count {
        display: none;
    }
}

.lm-row-actions .lm-btn {
    --h: 2.125rem;
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    color: var(--listamos-text-secondary);
    /* Icon-only markup still carries newlines as anonymous flex items; without a clip
       those inflate the button's min-content and shove a fixed actions column past the
       panel — the grey strip on the right of Shops. */
    overflow: hidden;
    width: 2.125rem;
}

.lm-row-actions .lm-btn .bi {
    font-size: 1.0625rem;
    -webkit-text-stroke: 0.2px currentColor;
}

.lm-row-actions .lm-btn.is-danger {
    color: var(--listamos-danger-text);
}

/* Same reason as the row tint: tapping delete and then scrolling away used to leave
   the bin sitting in its red pressed state on a row nobody was looking at. */
@media (hover: hover) {
    .lm-row-actions .lm-btn:hover {
        background: var(--listamos-surface);
        border-color: var(--listamos-border-strong);
        box-shadow: var(--shadow-xs);
        color: var(--listamos-text);
    }

    .lm-row-actions .lm-btn.is-danger:hover {
        background: var(--listamos-danger-soft);
        border-color: var(--listamos-danger);
        color: var(--listamos-danger-text);
    }
}

/* ——— Density ————————————————————————————————————————————————————————————
   A real setting for a real difference in how people work: somebody auditing four
   hundred listings wants as many on screen as will fit, and somebody writing one
   wants to see the photograph. */

.listing-table:not(.is-compact) {
    font-size: var(--text-base);
}

/* The tick box column is excluded from both densities on purpose. It sets its own
   padding above, and a density rule written later in the file has the same weight —
   which is how the body boxes ended up inset by one gutter more than the header one. */
.listing-table:not(.is-compact) > tbody > tr > td:not(.lm-col-check) {
    padding: 0.75rem var(--space-3);
}

.listing-table:not(.is-compact) .listing-thumb {
    height: 3rem;
    width: 3rem;
}

.listing-table:not(.is-compact) .listing-title {
    font-size: var(--text-base);
}

.listing-table:not(.is-compact) .listing-meta {
    font-size: var(--text-2xs);
}

.listing-table:not(.is-compact) .badge-state,
.listing-table:not(.is-compact) .lm-status {
    font-size: var(--text-sm);
}

.listing-table.is-compact > tbody > tr > td:not(.lm-col-check) {
    padding: 0.3125rem var(--space-3);
}

.listing-table.is-compact .listing-thumb {
    height: 1.75rem;
    width: 1.75rem;
}

.listing-table.is-compact .lm-row-actions .lm-btn {
    --h: 1.75rem;
    width: 1.75rem;
}

.listing-table.is-compact .lm-row-actions .lm-btn .bi {
    font-size: 0.9375rem;
}

.listing-table.is-compact .listing-meta,
.listing-table.is-compact .lm-health-count,
.listing-table.is-compact .lm-health-bar,
.listing-table.is-compact .variant-note,
.listing-table.is-compact .price-from {
    display: none;
}

/* ——— On a phone ————————————————————————————————————————————————————————
   A seven-column table on a 390-pixel screen is not a table, it is four columns of
   overlapping text. So below a tablet each row becomes a card: the photograph and the
   title on the first line, the four facts in two columns under it, and the health
   reading with the actions beside it along the foot.

   Done by re-laying the cells rather than by rendering different markup, because two
   renderers for one row is two places for a column to go missing. The order is set
   explicitly so the reading order survives the reflow. */

@media (max-width: 47.9375em) {
    .listing-table,
    .listing-table > tbody,
    .listing-table > tbody > tr,
    .listing-table > tbody > tr > td {
        display: block;
        width: auto;
    }

    .listing-table > thead {
        display: none;
    }

    /* A grid and not a wrapping flex row. Wrapping puts the cells wherever they happen
       to fit, which on a real catalogue meant an empty category landing at the far
       right of the title line and the four facts breaking differently on every card in
       the list. Three tracks, and each cell told exactly which one it is in. */
    .listing-table > tbody > tr {
        align-items: center;
        column-gap: var(--space-3);
        display: grid;
        grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
        padding: var(--space-3) var(--space-4);
        row-gap: var(--space-1);
    }

    /* A hairline between the cards, which is the only thing saying where one listing
       ends and the next begins. On the desktop table the cells draw it and the eye has
       columns to follow anyway; a column of cards with nothing between them reads as
       one long card, and a price lands directly under the title it does not belong
       to. */
    .listing-table > tbody > tr + tr {
        border-top: 1px solid var(--listamos-border);
    }

    .listing-table > tbody > tr > td,
    .listing-table > tbody > tr > td:first-child,
    .listing-table > tbody > tr > td:last-child {
        border: 0;
        padding: 0;
    }

    /* The card does its own spacing, so the cells do none. Said at the weight of the
       density rules above — they are written with `:not(.is-compact)` and would
       otherwise outrank a plainer selector here, which is how a phone card ended up
       carrying a desktop row's padding four times over: twenty-four pixels on every
       line of it, a third of the card. */
    .listing-table:not(.is-compact) > tbody > tr > td:not(.lm-col-check),
    .listing-table.is-compact > tbody > tr > td:not(.lm-col-check) {
        padding: 0;
    }

    /* Category and Section are cells again here. The band above collapses them because a
       narrow table has no width for two more columns; a card has no columns at all, and
       it has a line for the two of them that reads better than a tail on the meta —
       which is where that band puts them, and which this puts back. Products has neither
       cell, so leave those alone on `.is-ours`. */
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(3) > *,
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(4) > * {
        display: block;
    }

    .listing-where {
        display: none;
    }

    /* The four buttons are back too. A card has a line for them along its foot, and a
       menu is the wrong trade on a touch screen: one press to open and a second to
       choose, where the card can simply show all four at a size worth tapping. */
    .lm-row-actions {
        display: flex;
    }

    .lm-row-more {
        display: none;
    }

    /* `min-width: 0` on the title cell, without which a flex item refuses to shrink
       below its content and a long title pushes the whole card off the screen instead
       of ellipsising inside it. */
    /* Four lines, not seven. Category and section had no business taking a line each on
       a phone: a listing with no section printed an em dash alone on its own row, and
       the card ran to most of a screen for facts that fit on two lines.

       Tick box and title on the first, the two dimensions on the second, price and
       stock on the third, health and the actions across the foot. `min-width: 0`
       wherever the content can be long, without which a grid item keeps its
       min-content width and a title pushes the card off the side of the phone instead
       of ellipsising. */
    /* Catalogue listings only. Shops, templates and the schedule borrow
       `listing-table` for the card shell but place their own cells later — and a
       `:not(.is-ours)` rule here used to outrank those, which is how a shop card
       put "Edits waiting" on the title line and left a hole in the middle. */
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(1) { grid-area: 1 / 1; }
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(2) { grid-area: 1 / 2 / 2 / -1; min-width: 0; }
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(3) { grid-area: 2 / 1 / 3 / 3; min-width: 0; }
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(4) { grid-area: 2 / 3; min-width: 0; }
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(5) { grid-area: 3 / 1 / 4 / 3; }
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(6) { grid-area: 3 / 3; }

    /* Products: check, title, sku, price, stock, shops, pin — two-fact lines for
       sku / price+stock, then shops, then pin with the row actions. */
    .listing-table.is-ours > tbody > tr > td:nth-child(1) { grid-area: 1 / 1; }
    .listing-table.is-ours > tbody > tr > td:nth-child(2) { grid-area: 1 / 2 / 2 / -1; min-width: 0; }
    .listing-table.is-ours > tbody > tr > td:nth-child(3) { grid-area: 2 / 1 / 3 / -1; min-width: 0; }
    .listing-table.is-ours > tbody > tr > td:nth-child(4) { grid-area: 3 / 1 / 4 / 3; }
    .listing-table.is-ours > tbody > tr > td:nth-child(5) { grid-area: 3 / 3; }
    .listing-table.is-ours > tbody > tr > td:nth-child(6) { grid-area: 4 / 1 / 5 / -1; min-width: 0; }

    /* The last cell carries pin chips and the row actions. */
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(7),
    .listing-table:not(.is-ours):not(.shops-table):not(.templates-table):not(.schedule-table) > tbody > tr > td:nth-child(7) > .lm-health-cell {
        display: contents;
    }

    .listing-table.is-ours > tbody > tr > td:nth-child(7),
    .listing-table.is-ours > tbody > tr > td:nth-child(7) > .lm-health-cell {
        display: contents;
    }

    .listing-table > tbody > tr .lm-health { grid-area: 4 / 1 / 5 / 3; }
    .listing-table.is-ours > tbody > tr .lm-pin-pills,
    .listing-table.is-ours > tbody > tr .lm-pin-empty { grid-area: 5 / 1 / 6 / 3; }
    .listing-table.is-ours > tbody > tr .lm-col-shops .lm-shop-pills { grid-area: auto; }
    .listing-table > tbody > tr .lm-row-actions { grid-area: 4 / 3; }
    .listing-table.is-ours > tbody > tr .lm-row-actions,
    .listing-table.is-ours > tbody > tr .lm-product-actions,
    .listing-table.is-ours > tbody > tr .lm-row-more { grid-area: 5 / 3; }

    /* Ranged left like everything else on the card. `!important` because the ranging is
       a Bootstrap utility in the markup and that is where its `!important` comes from —
       the class is right for the table and wrong for the card, and this is the cheaper
       of the two ways to say so.

       Ranged right, price and stock sat at two arbitrary points in the middle of the
       card. Left, the four facts line up in two columns that hold their place down the
       whole list, which is most of what a column was doing for them. */
    .listing-table > tbody > tr > td.numeric,
    .listing-table .variant-note {
        text-align: left !important;
    }

    /* No dots between the facts any more. They were there to separate cells that shared
       a wrapped line and could not be told apart; two columns that line up down the
       whole list do that better, and the dots were landing on their own line under the
       value they belonged to as often as beside it. */

    .lm-row-actions {
        justify-content: flex-end;
    }

    .listing-table > tbody > tr:has(.form-check-input:checked) > .lm-col-check {
        box-shadow: none;
    }

    .listing-table > tbody > tr:has(.form-check-input:checked) {
        box-shadow: inset 2px 0 0 var(--listamos-primary);
    }
}

/* ——— The templates table —————————————————————————————————————————————————
   The catalogue's table with five columns instead of seven, because it is read the same
   way: down a list of things, looking for the one to work on.

   It borrows `listing-table` for the grid, the head and the row hover, and says the rest
   itself. What it cannot borrow is anything written in column positions — the phone
   layout below is, and the third cell there is the section rather than the attribute
   count. Hence a class of its own rather than a sixth and seventh column of dashes. */

/* Wider than a filing column: this is the one summary of what the template covers, and
   a path plus "12 answers · postage" needs room. */
.lm-col-covers {
    width: 42%;
}

/* The catalogue's headings are buttons that sort, and they carry their own padding — so
   the cell under them is set to none. Nothing here sorts, so these are words in a `th`,
   and words in a cell with no padding run into the heading beside them. */
.templates-table > thead th {
    padding: 0.5rem var(--space-2);
}

/* Four icon buttons and nothing that grows with the window, so it is measured in text
   for the same reason the health column is. */
.lm-col-template-actions {
    width: 11rem;
}

/* The name opens the form rather than going anywhere, so it is a button — and a button
   that has to read as the title of its row rather than as a control. */
.templates-table .listing-title {
    background: none;
    border: 0;
    padding: 0;
    text-align: left;
}

.lm-template-name {
    align-items: center;
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-width: 0;
}

.lm-template-default {
    flex: 0 0 auto;
}

/* Whose template this is, in the form, where it is reported rather than asked. Set like
   a filled field so the row does not look like it is missing its input. */
.lm-shop-said {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    line-height: var(--control);
}

/* A name, not a title — short labels like "Prints" or "Made to order", not a 140-character
   marketplace headline. Full-width here makes the box look like it is waiting for a
   paragraph. */
[data-role="naming"] input[name="name"] {
    max-width: 28rem;
}

@media (max-width: 47.9375em) {
    /* Check beside the name, then what it covers, then the actions along the foot. */
    .templates-table > tbody > tr > td:nth-child(1) { grid-area: 1 / 1; }
    .templates-table > tbody > tr > td:nth-child(2) { grid-area: 1 / 2 / 2 / -1; min-width: 0; }
    .templates-table > tbody > tr > td:nth-child(3) { grid-area: 2 / 1 / 3 / -1; min-width: 0; }
    .templates-table > tbody > tr > td:nth-child(4) { grid-area: 3 / 1 / 4 / -1; }
}

/* ——— The release queue ——————————————————————————————————————————————————————

   The catalogue's table again, with two columns that only this screen has: the position,
   which is the queue, and the date, which is the whole reason to look at the screen. */

/* A number under a hundred and a hard right edge, so the column of them reads as a
   sequence rather than as a ragged left margin. Tabular figures because "9" and "10"
   sitting under each other in proportional digits look like a mistake. */
.lm-col-position {
    width: 3.5rem;
}

.lm-schedule-position {
    color: var(--listamos-muted);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* "3 Oct, 10:00" and its longer translations. Measured in text rather than in percent:
   the date is a fixed number of characters and the title beside it should take whatever
   is left. */
.lm-col-when {
    width: 11rem;
}

.lm-col-item-state {
    width: 8rem;
}

.lm-col-item-actions {
    width: 4rem;
}

.schedule-table > thead th {
    padding: 0.5rem var(--space-2);
}

/* Why a listing was refused, under its title.

   On the row rather than in a list of its own, because a seller reading a queue of two
   hundred has exactly one question about a refusal — which one — and the answer is the
   row they are already looking at. */
.lm-row-note {
    color: var(--red-700);
    font-size: var(--text-xs);
    line-height: 1.35;
    margin-top: 0.15rem;
}

/* The state of the agreement, above the table: what is left, when it ends, what it has
   cost. The only running total of the seller's money anywhere in this product, which is
   why it sits above the rows rather than behind a link. */
.lm-schedule-summary {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-5);
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
}

.lm-schedule-figures {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.lm-schedule-figure {
    font-size: var(--text-sm);
    font-weight: 600;
}

.lm-schedule-figure--quiet {
    color: var(--listamos-muted);
    font-weight: 400;
}

/* The rate, editable where it is read. A pace that could only be set when the queue was
   made would mean going faster costs a stop and a restart — which on this screen means
   agreeing to the money a second time. */
.lm-schedule-pace {
    align-items: flex-end;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-schedule-pace-field {
    display: flex;
    flex-direction: column;
    font-size: var(--text-xs);
    gap: 0.15rem;
}

.lm-schedule-pace-field > span {
    color: var(--listamos-muted);
}

.lm-schedule-pace-field input[name="per_day"] {
    width: 5rem;
}

.lm-schedule-pace-field input[name="at_time"] {
    width: 7rem;
}

.lm-schedule-when-fields {
    flex-wrap: nowrap;
}

@media (max-width: 29.99em) {
    .lm-schedule-when-fields {
        flex-wrap: wrap;
    }

    .lm-schedule-when-day {
        flex: 1 1 100%;
    }
}

/* Which clock the hour is on. Beside the time rather than under it, because it is not a
   field — it is what makes the field mean something. */
.lm-schedule-zone {
    color: var(--listamos-muted);
    font-size: var(--text-xs);
    line-height: var(--control);
}

@media (max-width: 47.9375em) {
    /* Three lines: the title with the position folded away, then the date beside the
       state, then the action. The position is the one cell that earns nothing on a phone
       — the rows are already in order and there is no column of numbers to scan. */
    .schedule-table > tbody > tr > td:nth-child(1) { display: none; }
    .schedule-table > tbody > tr > td:nth-child(2) { grid-area: 1 / 1 / 2 / -1; min-width: 0; }
    .schedule-table > tbody > tr > td:nth-child(3) { grid-area: 2 / 1 / 3 / 3; min-width: 0; }
    .schedule-table > tbody > tr > td:nth-child(4) { grid-area: 2 / 3; min-width: 0; }
    .schedule-table > tbody > tr > td:nth-child(5) { grid-area: 3 / 1 / 4 / -1; }
}

/* ——— The foot ———————————————————————————————————————————————————————————— */

.lm-table-foot {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: space-between;
}

.lm-pager {
    align-items: center;
    display: flex;
    gap: var(--space-2);
}

.lm-pager-page {
    color: var(--listamos-text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Settled checklist / advice — a status line, not an action. */
.advice-clear {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    gap: 0.4em;
    font-size: inherit;
    margin: 0;
}

.advice-clear .bi {
    color: var(--listamos-primary);
    flex: 0 0 auto;
}

/* ——— An icon ————————————————————————————————————————————————————————————
   Inline SVG at one optical weight, used where the icon carries meaning rather than
   decorating a label. Sized in `em` so it scales with whatever it sits in. */

.lm-ico {
    display: inline-block;
    flex: 0 0 auto;
    height: 1em;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    fill: none;
    vertical-align: -0.135em;
    width: 1em;
}


/* ———————————————————————————————— 9b. Shops, drafts and bulk history

   Drafts and bulk history are lists of objects rather than tables of rows, so they are
   built from cards: a mark and a name across the top, the facts in the middle set large
   enough to be read without leaning in, the actions along the foot.

   Shops was the third and is not any more. The rule that sorts them is whether the rows
   are worth comparing to each other — a bulk run is read on its own, a shop is read
   against the other shops, and two numbers in two boxes are two numbers that never line
   up. It is a table now, and what is left below it is the handful of things a table of
   shops needs that the catalogue's does not. */

/* ——— Shops ——————————————————————————————————————————————————————————————
   The catalogue's table with three columns: the shop, when it last synced, and the
   buttons. What a row has to say beyond that — access lost, no place on the plan — is
   a line under the name, where it is read with the name it is about. */

/* Measured in text rather than percentages, and kept tight. Everything that is not the
   first column is width taken off the shop's name and off the line under it that says
   why a shop stopped working — which is the one sentence on this screen somebody has to
   read to the end. */
.lm-col-shop-synced {
    width: 6rem;
}

.lm-col-shop-board {
    width: 14rem;
}

/* Sync and the menu: two 2.125rem icons and the gap between them, inside the cell's
   padding. */
.lm-col-shop-actions {
    width: 6.5rem;
}

/* The catalogue's headings are sort buttons carrying their own padding, so the cell
   under them is set to none. Nothing here sorts: three shops do not need ordering, and
   a heading that looks pressable and is not is worse than a plain one. */
.shops-table > thead th {
    padding: 0.5rem var(--space-2);
}

/* The name column is wide; a centred "Shop" / "Account" floats in the middle of the
   card. Left, over the mark and the name, is where the eye already is. */
.shops-table > thead th:first-child {
    padding-left: var(--space-4);
    text-align: left;
}

/* The name goes nowhere — a shop has no page of its own — so it must not light up
   under the pointer the way a listing's title does. */
.shops-table .listing-title:hover {
    color: var(--listamos-text);
}

/* Ranged right, so that the menu is in the same place on every row, the pin rows'
   included, which have no Sync beside it. */
.shops-table .lm-row-actions {
    justify-content: flex-end;
}

/* The catalogue only shows its ⋯ on narrow screens; on Shops it holds Disconnect, the
   one way to let a shop go, so it is shown at every width. */
.shops-table .lm-row-more {
    display: block;
}

/* Centred, under a heading that is: "3 d" left at the cell's edge sat visibly adrift
   of the word above it. */
.shops-table:not(.pins-table) > tbody > tr > td:nth-child(2) {
    text-align: center;
}

/* Pin accounts: Account · Actions. Synced sits under the name — a third column on a
   two-fact row left a blank stretch across the card that read as the layout sliding
   left. Mobile: name, then the buttons on the foot. */
.pins-table > tbody > tr > td:nth-child(2) {
    text-align: right;
}

/* The dot is the rail switcher's, and so is its size there — but the mark on a table row
   is smaller than the one in the menu, and at the menu's size the dot ate the corner of
   the logo it sits on. */
.shops-table .lm-shop-mark {
    height: 1.75rem;
    width: 1.75rem;
}

.shops-table .lm-shop-status {
    height: 0.4375rem;
    width: 0.4375rem;
}

@media (max-width: 47.9375em) {
    /* Two tracks: the catalogue's `auto 1fr 1fr` is for a tick beside a title. */
    .shops-table > tbody > tr {
        align-items: start;
        grid-template-columns: minmax(0, 1fr) auto;
        row-gap: var(--space-2);
    }

    /* Two lines: the shop, then when it synced with the buttons beside it. Written in
       column positions, like the catalogue's own phone layout, which this replaces
       rather than adds to. */
    .shops-table > tbody > tr > td:nth-child(1) { grid-area: 1 / 1 / 2 / -1; min-width: 0; }
    .shops-table > tbody > tr > td:nth-child(2) { grid-area: 2 / 1; }
    .shops-table > tbody > tr > td:nth-child(3) { grid-area: 2 / 2; }

    /* Name, then the buttons on the foot — not beside the name on the title line,
       which is where the catalogue's two-cell row used to leave them. */
    .pins-table > tbody > tr {
        grid-template-columns: minmax(0, 1fr);
    }

    .pins-table > tbody > tr > td:nth-child(1) { grid-area: 1 / 1 / 2 / -1; min-width: 0; }
    .pins-table > tbody > tr > td:nth-child(2) { grid-area: 2 / 1 / 3 / -1; }

    /* The column heading, on the cell, because at this width there is no heading row.
       Ranged left, like everything else on a card. */
    .shops-table > tbody > tr > td[data-label] {
        align-self: start;
        text-align: left;
    }

    .shops-table > tbody > tr > td[data-label]::before {
        color: var(--listamos-text-tertiary);
        content: attr(data-label);
        display: block;
        font-family: var(--font-mono);
        font-size: var(--text-3xs);
        letter-spacing: 0.07em;
        margin-bottom: 0.15rem;
        text-transform: uppercase;
    }

    /* A little air above the pin rows' buttons so they read as the foot of the card. */
    .pins-table > tbody > tr > td:nth-child(2) .lm-row-actions {
        margin-top: var(--space-2);
    }
}

/* One shop per row, with the answer to "why not" underneath rather than hidden
   behind a disabled button. */

.elsewhere-row {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    justify-content: space-between;
    padding: var(--space-2) 0;
}

.elsewhere-shop-name {
    align-items: center;
    display: inline-flex;
    gap: var(--space-2);
    min-width: 0;
}

.elsewhere-kicker {
    color: var(--listamos-text-secondary);
    flex: 0 0 auto;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.elsewhere-shop + .elsewhere-shop {
    border-top: 1px solid var(--listamos-border);
}

/* What the shop wants to be told before it will take the thing. Folded away until
   asked for, because most shops have been told already and a panel of open forms is
   a panel nobody reads. */

.elsewhere-asks {
    padding-bottom: var(--space-3);
}

.elsewhere-field + .elsewhere-field {
    margin-top: var(--space-2);
}

.elsewhere-asks .form-check {
    margin-top: var(--space-3);
}

.elsewhere-asks-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.elsewhere-row.is-there span:first-child {
    color: var(--listamos-text-secondary);
}

.elsewhere-why {
    margin: calc(var(--space-1) * -1) 0 var(--space-2);
}

/* ——— Bulk history ———————————————————————————————————————————————————————
   What was changed, to what, and whether it can be taken back. */

.run-head {
    align-items: flex-start;
    display: flex;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.run-mark {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    flex: 0 0 auto;
    font-size: 0.9375rem;
    height: 2rem;
    justify-content: center;
    width: 2rem;
}

.run-mark.is-undo {
    background: var(--listamos-warning-soft);
    color: var(--listamos-warning-text);
}

.run-what {
    flex: 1 1 auto;
    min-width: 0;
}

.run-actions {
    align-items: center;
    display: flex;
    flex: 0 0 auto;
    gap: var(--space-2);
}

.run-tally {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}


/* ———————————————————————————————————————————————— 10. Public pages

   The landing is the outside of the same product, so it is built from the same three
   planes and the same type. Nothing here is a second visual identity: a stranger who
   signs up should recognise the screen they land on, and the join between the two is
   exactly where somebody decides whether the screenshots were of this software.

   What is different is the budget for space. Inside the application a row is content
   and air around it is air a seller scrolls past; out here air *is* the product, and
   the sections are set on the large end of the same scale rather than a new one.

   The one compositional device is the band. Sections alternate between the shell white
   and the canvas tint, and the product still straddles the boundary between the first
   two — which is how a flat palette gets depth without a single blurred blob. */

.public-nav {
    background: var(--listamos-shell);
    border-bottom: 1px solid var(--listamos-border);
    padding: 0;
    z-index: 40;
}

.public-nav .container {
    align-items: center;
    display: flex;
    gap: var(--space-4);
    min-height: 4rem;
}

.public-nav .nav-link {
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    padding: 0.375rem 0.625rem;
    transition: background var(--fast), color var(--fast);
}

.public-nav .nav-link:hover {
    background: var(--ink-050);
    color: var(--listamos-text);
}

/* Bootstrap's toggler is a bordered box holding an SVG data-URI of three grey lines.
   The border goes, the icon is drawn in the product's ink, and it sizes to the same
   control square as every other icon button. */
.navbar-toggler {
    align-items: center;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    box-shadow: none;
    color: var(--listamos-text-secondary);
    display: flex;
    height: var(--control);
    justify-content: center;
    padding: 0;
    transition: background var(--fast), color var(--fast);
    width: var(--control);
}

.navbar-toggler:hover {
    background: var(--listamos-surface-subtle);
    color: var(--listamos-text);
}

.navbar-toggler:focus-visible {
    box-shadow: 0 0 0 3px var(--listamos-focus);
}

.navbar-toggler-icon {
    background: none;
    height: 1rem;
    position: relative;
    width: 1.0625rem;
}

.navbar-toggler-icon::before,
.navbar-toggler-icon::after {
    background: currentColor;
    border-radius: 1px;
    content: '';
    height: 1.5px;
    left: 0;
    position: absolute;
    right: 0;
}

.navbar-toggler-icon::before { top: 0.3125rem; }
.navbar-toggler-icon::after  { bottom: 0.3125rem; }

/* ——— The hero ——————————————————————————————————————————————————————————— */

.hero {
    background: var(--listamos-shell);
    overflow: hidden;
    padding: var(--space-11) 0 var(--space-10);
    position: relative;
    text-align: center;
}

.hero--stage,
.hero--workflow {
    padding-bottom: 0;
}

/* Soft atmosphere only — same brand wash the showcase uses, not a second design language. */
.hero::before {
    background:
        radial-gradient(ellipse 80% 55% at 50% -10%, rgba(37, 99, 235, 0.09), transparent 58%),
        radial-gradient(ellipse 50% 40% at 90% 80%, rgba(99, 102, 241, 0.05), transparent 55%);
    content: '';
    inset: 0;
    pointer-events: none;
    position: absolute;
}

.hero-content {
    position: relative;
    z-index: 1;
}

.hero-brand {
    align-items: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}

.hero-brand-lockup {
    font-size: 1.35rem;
}

.hero-brand-lockup .brand-mark {
    height: 2.5rem;
    width: 2.5rem;
}

.hero-brand-lockup .brand-word {
    font-size: 1.5rem;
    letter-spacing: -0.04em;
}

/* The product is the hero image — shell above, canvas below, window straddling both. */
.hero-stage {
    background: linear-gradient(
        var(--listamos-shell) 0 18%,
        var(--listamos-canvas) 18%
    );
    margin-top: var(--space-9);
    padding: 0 0 var(--space-11);
    position: relative;
    z-index: 1;
}

.hero-stage .container {
    max-width: 72rem;
}

.product-stage {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.8) inset,
        var(--shadow-lg);
    overflow: hidden;
    text-align: left;
    transform: perspective(1400px) rotateX(1.2deg);
    transform-origin: 50% 0;
}

.product-stage-chrome {
    align-items: center;
    background: var(--listamos-canvas);
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    padding: 0.65rem 1rem;
}

.product-stage-path {
    color: var(--listamos-text-secondary);
    flex: 1 1 auto;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.02em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.product-stage-channel {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: inline-flex;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    gap: 0.4rem;
    letter-spacing: 0.02em;
}

.product-stage-channel img {
    display: block;
    height: 1rem;
    width: 1rem;
}

.product-stage-body {
    display: grid;
    gap: 0;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr) minmax(14rem, 0.85fr);
    min-height: 22rem;
}

.product-stage-gallery {
    background: var(--ink-025);
    border-right: 1px solid var(--listamos-border);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
}

.product-stage-cover {
    border-radius: var(--radius-lg);
    flex: 1 1 auto;
    min-height: 14rem;
    overflow: hidden;
    position: relative;
}

.product-stage-cover img,
.product-stage-thumb img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.product-stage-cover img {
    animation: stage-ken 18s ease-in-out infinite alternate;
}

.product-stage-badge {
    background: rgba(15, 19, 32, 0.72);
    border-radius: var(--radius-sm);
    color: #fff;
    font-family: var(--font-mono);
    font-size: 0.625rem;
    left: 0.65rem;
    letter-spacing: 0.08em;
    padding: 0.28rem 0.45rem;
    position: absolute;
    text-transform: uppercase;
    top: 0.65rem;
}

.product-stage-thumbs {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.product-stage-thumb {
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-md);
    overflow: hidden;
}

.product-stage-thumb--add {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px dashed var(--listamos-border-strong);
    color: var(--listamos-text-tertiary);
    display: flex;
    font-size: 1.1rem;
    justify-content: center;
}

.product-stage-copy {
    border-right: 1px solid var(--listamos-border);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5);
}

.product-stage-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.08em;
    margin: var(--space-2) 0 0;
    text-transform: uppercase;
}

.product-stage-label:first-child {
    margin-top: 0;
}

.product-stage-title {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.025em;
    line-height: 1.25;
    margin: 0;
}

.product-stage-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.product-stage-desc {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
    margin: 0;
}

.product-stage-advice {
    background: linear-gradient(180deg, var(--listamos-surface) 0%, var(--ink-025) 100%);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
}

.product-stage-fix {
    pointer-events: none;
    width: 100%;
}

.product-stage-group {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.1em;
    margin: var(--space-1) 0 0;
    text-transform: uppercase;
}

.product-stage-advice .advice-list {
    margin: 0;
}

.product-stage-advice .advice-item {
    cursor: default;
}

@keyframes stage-ken {
    from { transform: scale(1); }
    to { transform: scale(1.04); }
}

@media (max-width: 62em) {
    .product-stage {
        transform: none;
    }

    .product-stage-body {
        grid-template-columns: 1fr 1fr;
    }

    .product-stage-advice {
        border-top: 1px solid var(--listamos-border);
        grid-column: 1 / -1;
    }
}

@media (max-width: 40em) {
    .product-stage-body {
        grid-template-columns: minmax(0, 1fr);
    }

    .product-stage-gallery,
    .product-stage-copy {
        border-right: 0;
    }

    .product-stage-copy {
        border-top: 1px solid var(--listamos-border);
    }

    .product-stage-cover {
        min-height: 12rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .product-stage-cover img {
        animation: none;
    }
}

.landing-trust {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.06em;
    line-height: 1.5;
    text-transform: uppercase;
}

.landing-reveal {
    animation: landing-rise 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.landing-reveal--2 { animation-delay: 0.06s; }
.landing-reveal--3 { animation-delay: 0.12s; }
.landing-reveal--4 { animation-delay: 0.18s; }
.landing-reveal--5 { animation-delay: 0.24s; }

@keyframes landing-rise {
    from {
        opacity: 0;
        transform: translateY(0.75rem);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .landing-reveal {
        animation: none;
    }
}

/* ——— Hero workflow demo ————————————————————————————————————————————————
   Photos land, copy appears, score rises, publish ready. Phase visibility is driven
   by `data-phase` on the root; reduced motion jumps straight to the final frame. */

.workflow-demo {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.8) inset,
        var(--shadow-lg);
    overflow: hidden;
    text-align: left;
    transform: perspective(1400px) rotateX(1.2deg);
    transform-origin: 50% 0;
}

.workflow-chrome {
    align-items: center;
    background: var(--listamos-canvas);
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    padding: 0.65rem 1rem;
}

.workflow-chrome-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.04em;
}

.workflow-body {
    display: grid;
    gap: 0;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1.15fr) minmax(12rem, 0.85fr);
    min-height: 22rem;
}

.workflow-drop {
    background: var(--ink-025);
    border-right: 1px solid var(--listamos-border);
    min-height: 18rem;
    overflow: hidden;
    padding: var(--space-5);
    position: relative;
}

.workflow-drop-zone {
    align-items: center;
    border: 1.5px dashed var(--listamos-border-strong);
    border-radius: var(--radius-lg);
    color: var(--listamos-text-tertiary);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    height: 100%;
    justify-content: center;
    min-height: 16rem;
    opacity: 1;
    transition: opacity 0.35s ease, transform 0.35s ease;
}

.workflow-drop-zone i {
    font-size: 1.75rem;
}

.workflow-drop-copy {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    margin: 0;
    max-width: 16ch;
    text-align: center;
}

.workflow-photos {
    inset: var(--space-5);
    pointer-events: none;
    position: absolute;
}

.workflow-photo {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    height: 42%;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    transform: translateY(-120%) rotate(-6deg) scale(0.92);
    transition: opacity 0.45s ease, transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
    width: 48%;
}

.workflow-photo img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.workflow-photo--1 { left: 6%; top: 10%; z-index: 3; }
.workflow-photo--2 { left: 28%; top: 28%; transform: translateY(-120%) rotate(4deg) scale(0.92); width: 44%; z-index: 2; }
.workflow-photo--3 { left: 48%; top: 48%; transform: translateY(-120%) rotate(-2deg) scale(0.92); width: 42%; z-index: 1; }

.workflow-analyzing {
    align-items: center;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-pill, 999px);
    bottom: var(--space-6);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    gap: 0.5rem;
    left: 50%;
    letter-spacing: 0.04em;
    opacity: 0;
    padding: 0.45rem 0.8rem;
    position: absolute;
    transform: translate(-50%, 0.5rem);
    transition: opacity 0.3s ease, transform 0.3s ease;
    white-space: nowrap;
    z-index: 4;
}

.workflow-analyzing-dot {
    animation: workflow-pulse 0.9s ease-in-out infinite;
    background: var(--listamos-primary);
    border-radius: 50%;
    display: block;
    height: 0.45rem;
    width: 0.45rem;
}

.workflow-listing {
    border-right: 1px solid var(--listamos-border);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-5);
}

.workflow-field {
    opacity: 0;
    transform: translateY(0.5rem);
    transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.workflow-field:nth-child(2) { transition-delay: 0.08s; }
.workflow-field:nth-child(3) { transition-delay: 0.16s; }

.workflow-label {
    color: var(--listamos-text-tertiary);
    display: block;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.08em;
    margin-bottom: 0.35rem;
    text-transform: uppercase;
}

.workflow-title {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.025em;
    line-height: 1.25;
    margin: 0;
}

.workflow-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.workflow-desc {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
    margin: 0;
}

.workflow-side {
    background: linear-gradient(180deg, var(--listamos-surface) 0%, var(--ink-025) 100%);
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-5);
}

.workflow-score {
    opacity: 0.35;
    transition: opacity 0.35s ease;
}

.workflow-score-read {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    margin-top: 0.35rem;
}

.workflow-score-num {
    font-family: var(--font-mono);
    font-size: var(--text-3xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    letter-spacing: -0.04em;
    line-height: 1;
}

.workflow-score-num--low { color: #c2410c; }
.workflow-score-num--high {
    color: #15803d;
    display: none;
}

.workflow-score-bar {
    background: var(--ink-100);
    border-radius: 999px;
    display: block;
    flex: 1 1 100%;
    height: 0.4rem;
    overflow: hidden;
}

.workflow-score-bar i {
    background: #c2410c;
    border-radius: inherit;
    display: block;
    height: 100%;
    transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1), background 0.4s ease;
    width: 61%;
}

.workflow-ready {
    opacity: 0;
    transform: translateY(0.4rem);
    transition: opacity 0.4s ease, transform 0.4s ease;
}

.workflow-channels {
    display: grid;
    gap: 0.45rem;
    list-style: none;
    margin: 0 0 var(--space-4);
    padding: 0;
}

.workflow-channels li {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    gap: 0.45rem;
}

.workflow-channels img {
    display: block;
    height: 1rem;
    width: 1rem;
}

.workflow-publish {
    pointer-events: none;
    width: 100%;
}

/* Phase machine */
.workflow-demo[data-phase="photos"] .workflow-drop-zone,
.workflow-demo[data-phase="analyze"] .workflow-drop-zone,
.workflow-demo[data-phase="build"] .workflow-drop-zone,
.workflow-demo[data-phase="score"] .workflow-drop-zone,
.workflow-demo[data-phase="ready"] .workflow-drop-zone {
    opacity: 0;
    transform: scale(0.98);
}

.workflow-demo[data-phase="photos"] .workflow-photo,
.workflow-demo[data-phase="analyze"] .workflow-photo,
.workflow-demo[data-phase="build"] .workflow-photo,
.workflow-demo[data-phase="score"] .workflow-photo,
.workflow-demo[data-phase="ready"] .workflow-photo {
    opacity: 1;
    transform: translateY(0) rotate(var(--photo-tilt, 0deg)) scale(1);
}

.workflow-demo[data-phase="photos"] .workflow-photo--1,
.workflow-demo[data-phase="analyze"] .workflow-photo--1,
.workflow-demo[data-phase="build"] .workflow-photo--1,
.workflow-demo[data-phase="score"] .workflow-photo--1,
.workflow-demo[data-phase="ready"] .workflow-photo--1 { --photo-tilt: -4deg; transition-delay: 0.05s; }

.workflow-demo[data-phase="photos"] .workflow-photo--2,
.workflow-demo[data-phase="analyze"] .workflow-photo--2,
.workflow-demo[data-phase="build"] .workflow-photo--2,
.workflow-demo[data-phase="score"] .workflow-photo--2,
.workflow-demo[data-phase="ready"] .workflow-photo--2 { --photo-tilt: 3deg; transition-delay: 0.18s; }

.workflow-demo[data-phase="photos"] .workflow-photo--3,
.workflow-demo[data-phase="analyze"] .workflow-photo--3,
.workflow-demo[data-phase="build"] .workflow-photo--3,
.workflow-demo[data-phase="score"] .workflow-photo--3,
.workflow-demo[data-phase="ready"] .workflow-photo--3 { --photo-tilt: -1deg; transition-delay: 0.3s; }

.workflow-demo[data-phase="analyze"] .workflow-analyzing {
    opacity: 1;
    transform: translate(-50%, 0);
}

.workflow-demo[data-phase="build"] .workflow-field,
.workflow-demo[data-phase="score"] .workflow-field,
.workflow-demo[data-phase="ready"] .workflow-field {
    opacity: 1;
    transform: none;
}

.workflow-demo[data-phase="score"] .workflow-score,
.workflow-demo[data-phase="ready"] .workflow-score {
    opacity: 1;
}

.workflow-demo[data-phase="score"] .workflow-score-num--low,
.workflow-demo[data-phase="ready"] .workflow-score-num--low {
    display: none;
}

.workflow-demo[data-phase="score"] .workflow-score-num--high,
.workflow-demo[data-phase="ready"] .workflow-score-num--high {
    display: inline;
}

.workflow-demo[data-phase="score"] .workflow-score-bar i,
.workflow-demo[data-phase="ready"] .workflow-score-bar i {
    background: #15803d;
    width: 94%;
}

.workflow-demo[data-phase="ready"] .workflow-ready {
    opacity: 1;
    transform: none;
}

@keyframes workflow-pulse {
    0%, 100% { opacity: 0.35; transform: scale(0.9); }
    50% { opacity: 1; transform: scale(1.1); }
}

@media (max-width: 62em) {
    .workflow-demo {
        transform: none;
    }

    .workflow-body {
        grid-template-columns: 1fr 1fr;
    }

    .workflow-side {
        border-top: 1px solid var(--listamos-border);
        grid-column: 1 / -1;
    }
}

@media (max-width: 40em) {
    .workflow-body {
        grid-template-columns: minmax(0, 1fr);
    }

    .workflow-drop,
    .workflow-listing {
        border-right: 0;
    }

    .workflow-listing {
        border-top: 1px solid var(--listamos-border);
    }
}

@media (prefers-reduced-motion: reduce) {
    .workflow-analyzing-dot {
        animation: none;
    }

    .workflow-photo,
    .workflow-field,
    .workflow-ready,
    .workflow-drop-zone,
    .workflow-analyzing,
    .workflow-score-bar i {
        transition: none;
    }
}

/* Channel strip under the hero */
.channel-bar {
    background: var(--listamos-shell);
    border-bottom: 1px solid var(--listamos-border);
    padding: var(--space-8) 0;
}

.channel-bar-inner {
    align-items: center;
    display: grid;
    gap: var(--space-6);
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
}

.channel-bar-title {
    font-size: var(--text-xl);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.025em;
    margin: 0 0 var(--space-2);
}

.channel-bar-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
    margin: 0;
    max-width: 36ch;
}

@media (max-width: 48em) {
    .channel-bar-inner {
        grid-template-columns: minmax(0, 1fr);
        text-align: center;
    }

    .channel-bar-lede {
        margin-inline: auto;
    }
}

/* Multi-channel map */
.everywhere-map {
    margin: 0 auto;
    max-width: 52rem;
}

.everywhere-hub {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    display: flex;
    gap: var(--space-4);
    margin: 0 auto var(--space-5);
    max-width: 28rem;
    padding: var(--space-4);
}

.everywhere-hub img {
    border-radius: var(--radius-md);
    display: block;
    height: 4.5rem;
    object-fit: cover;
    width: 4.5rem;
}

.everywhere-hub strong {
    display: block;
    font-size: var(--text-base);
    letter-spacing: -0.02em;
    margin-bottom: 0.15rem;
}

.everywhere-hub span {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.04em;
}

.everywhere-line {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.08em;
    margin: 0 0 var(--space-5);
    text-align: center;
    text-transform: uppercase;
}

.everywhere-cards {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(4, minmax(0, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.everywhere-card {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--space-4);
    transition: border-color var(--medium), transform var(--medium), box-shadow var(--medium);
}

.everywhere-card:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.everywhere-card img {
    display: block;
    height: 1.5rem;
    margin-bottom: 0.35rem;
    width: 1.5rem;
}

.everywhere-card strong {
    font-size: var(--text-sm);
    letter-spacing: -0.01em;
}

.everywhere-card span {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.4;
}

@media (max-width: 48em) {
    .everywhere-cards {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Trust checklist */
.trust-list {
    display: grid;
    gap: var(--space-3);
    list-style: none;
    margin: 0 auto;
    max-width: 36rem;
    padding: 0;
}

.trust-list li {
    align-items: flex-start;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    gap: var(--space-3);
    line-height: 1.5;
    padding: var(--space-4) var(--space-5);
}

.trust-list i {
    color: var(--listamos-primary);
    flex: 0 0 auto;
    font-size: 1.1rem;
    margin-top: 0.1rem;
}

/* Live-looking bulk preview — built from product classes, not a screenshot. */
.landing-proof {
    margin: 0 auto;
    max-width: 56rem;
}

.landing-proof-frame {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.landing-proof-chrome {
    align-items: center;
    background: var(--listamos-canvas);
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    padding: 0.7rem 1rem;
}

.landing-proof-dots {
    display: inline-flex;
    gap: 0.35rem;
}

.landing-proof-dots i {
    background: var(--ink-200);
    border-radius: 50%;
    display: block;
    height: 0.45rem;
    width: 0.45rem;
}

.landing-proof-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.04em;
}

.landing-proof .table {
    margin: 0;
}

.landing-proof .table thead th {
    background: var(--listamos-surface);
    border-bottom-color: var(--listamos-border);
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.06em;
    padding: 0.75rem 1rem;
    text-transform: uppercase;
}

.landing-proof .table tbody td {
    border-bottom-color: var(--listamos-border);
    padding: 0.85rem 1rem;
    vertical-align: middle;
}

.landing-proof .listing-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    letter-spacing: -0.01em;
}

.hero .display {
    font-size: clamp(2.25rem, 5.4vw, var(--text-6xl));
    letter-spacing: -0.042em;
    line-height: 1.02;
}

/* A drawn underline under the two words that carry the claim. It is one path, it
   inherits the ink, and it does the job a coloured highlight would do without turning
   a headline into a marker pen. */
.marked {
    display: inline-block;
    position: relative;
    white-space: nowrap;
}

/* Clear of the descenders. "listings" has a `g` in it, and a swash sitting on the
   baseline reads as a line struck through the word rather than under it. */
.marked svg {
    bottom: -0.26em;
    color: var(--brand-300);
    height: 0.3em;
    left: 0;
    position: absolute;
    width: 100%;
}

/* CREATE · BOOST · BULK, set as three facts rather than three badges. */
.hero-modes {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    flex-wrap: wrap;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    gap: var(--space-4);
    justify-content: center;
    letter-spacing: 0.12em;
    margin-top: var(--space-6);
    text-transform: uppercase;
}

.hero-modes span + span::before {
    background: var(--ink-250);
    border-radius: 50%;
    content: '';
    display: inline-block;
    height: 3px;
    margin-right: var(--space-4);
    vertical-align: middle;
    width: 3px;
}

/* The band the product still sits on. Two flat colours meeting on a hard stop, not a
   gradient: the still is half on the white the words are on and half on the tint the
   application uses, so it reads as a window opening onto the software. */
.hero-shot {
    background: linear-gradient(var(--listamos-shell) 0 46%, var(--listamos-canvas) 46%);
    margin-top: var(--space-9);
    padding-bottom: var(--space-10);
}

/* ——— Bands ——————————————————————————————————————————————————————————————— */

.section {
    padding: var(--space-11) 0;
}

.section-sunk {
    background: var(--listamos-canvas);
    border-block: 1px solid var(--listamos-border);
}

.section-head {
    margin: 0 auto var(--space-9);
    max-width: 46rem;
    text-align: center;
}

.section h2.h1,
.section .h1 {
    font-size: clamp(1.75rem, 3.4vw, var(--text-4xl));
    font-weight: var(--weight-bold);
    letter-spacing: -0.034em;
    line-height: 1.1;
}

/* ——— A product still ————————————————————————————————————————————————————
   Real markup inside a window, never a picture of one. It is the only image of this
   software that cannot go out of date, and the title bar is what tells a stranger
   they are looking at an application rather than at a table on a web page. */

.frame {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-2xl);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.frame-bar {
    align-items: center;
    background: var(--listamos-surface-subtle);
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    gap: 0.4375rem;
    padding: 0.625rem var(--space-4);
}

.frame-dot {
    background: var(--ink-200);
    border-radius: 50%;
    height: 9px;
    width: 9px;
}

/* ——— Cards on a page ————————————————————————————————————————————————————— */

.feature {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    height: 100%;
    padding: var(--space-7);
    transition: border-color var(--medium), box-shadow var(--medium), transform var(--medium);
}

.feature:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

/* The one place an icon is allowed a coloured field: three cards side by side, where
   the tile is what separates them at a glance before any word is read. */
.feature-icon {
    align-items: center;
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-primary);
    display: inline-flex;
    font-size: 1.125rem;
    height: 2.5rem;
    justify-content: center;
    width: 2.5rem;
}

#how .feature:nth-child(1) .feature-icon,
#how .col-md-6:nth-child(1) .feature-icon {
    background: rgba(37, 99, 235, 0.1);
    color: #2563eb;
}

#how .col-md-6:nth-child(2) .feature-icon {
    background: rgba(15, 23, 42, 0.06);
    color: #334155;
}

#how .col-md-6:nth-child(3) .feature-icon {
    background: rgba(99, 102, 241, 0.1);
    color: #4f46e5;
}

#how .col-md-6:nth-child(4) .feature-icon {
    background: rgba(37, 99, 235, 0.08);
    color: #1d4ed8;
}

.feature h3 {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.02em;
    margin-bottom: var(--space-2);
}

.feature p {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.6;
    margin-bottom: 0;
}

.product-pillar h3 {
    margin-bottom: var(--space-2);
}

.product-number {
    color: var(--ink-250);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}

.product-soon {
    border-top: 1px solid var(--listamos-border);
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.55;
    margin-top: var(--space-5);
    padding-top: var(--space-4);
}

/* Not yet, said plainly. A fact about the roadmap, not a promotion. */
.product-soon-badge {
    color: var(--listamos-warning-text);
    display: inline-block;
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.08em;
    margin-right: 0.375rem;
    text-transform: uppercase;
}

/* ——— How it works ———————————————————————————————————————————————————————— */

.step-number {
    align-items: center;
    background: var(--listamos-surface-inverse);
    border-radius: var(--radius-sm);
    box-shadow: var(--sheen-top);
    color: #fff;
    display: flex;
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    height: 1.5rem;
    justify-content: center;
    width: 1.5rem;
}

/* ——— Pricing ————————————————————————————————————————————————————————————— */

.plan {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    height: 100%;
    padding: var(--space-7);
}

.plan-featured {
    border-color: var(--listamos-primary-border);
    box-shadow: var(--shadow-md);
}

.plan-price {
    font-family: var(--font-mono);
    font-size: var(--text-4xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    letter-spacing: -0.038em;
    line-height: 1;
}

.check-list {
    display: grid;
    gap: var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.check-list li {
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    gap: 0.5rem;
    line-height: 1.5;
}

.check-list .bi {
    color: var(--listamos-success);
    flex: 0 0 auto;
    font-size: 0.9375rem;
    margin-top: 0.125rem;
}

/* ——— Questions ——————————————————————————————————————————————————————————
   Each one an object, with space between. Bootstrap's accordion draws a single
   bordered stack with a chevron in its own blue; this draws cards and a sign that
   turns, which is the difference between a control and a list. */

.accordion {
    display: grid;
    gap: var(--space-3);
}

.accordion-item {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-xs);
    overflow: hidden;
}

.accordion-button {
    background: transparent;
    border-radius: 0 !important;
    box-shadow: none !important;
    color: var(--listamos-text);
    font-size: var(--text-md);
    font-weight: var(--weight-medium);
    letter-spacing: -0.014em;
    padding: var(--space-4) var(--space-5);
}

.accordion-button:not(.collapsed) {
    background: transparent;
    color: var(--listamos-text);
}

.accordion-button::after {
    background: none;
    color: var(--listamos-text-tertiary);
    content: '+';
    font-size: 1.25rem;
    font-weight: var(--weight-normal);
    height: auto;
    line-height: 1;
    transition: transform var(--medium);
    width: auto;
}

.accordion-button:not(.collapsed)::after {
    transform: rotate(45deg);
}

.accordion-button:focus-visible {
    box-shadow: var(--listamos-ring) !important;
}

.accordion-body {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.65;
    padding: 0 var(--space-5) var(--space-5);
}

/* ——— The last word ——————————————————————————————————————————————————————
   One ink panel, the only inverted surface on the page, so the eye lands on it after
   scrolling past four light bands. Flat, with a hairline of light along the top inside
   edge — the same detail the dark button has, which is what stops a large dark
   rectangle reading as a hole in the page. */

.cta {
    background: var(--listamos-surface-inverse);
    border-radius: var(--radius-2xl);
    box-shadow: var(--sheen-top), var(--shadow-lg);
    color: #fff;
    padding: var(--space-10) var(--space-7);
}

.cta h2 {
    color: #fff;
    letter-spacing: -0.032em;
}

.cta .cta-secondary {
    background: transparent;
    border-color: rgba(255, 255, 255, 0.28);
    color: #fff;
}

.cta .cta-secondary:hover {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.45);
    color: #fff;
}

/* ——— The footer —————————————————————————————————————————————————————————— */

.site-footer {
    background: var(--listamos-canvas);
    border-top: 1px solid var(--listamos-border);
    font-size: var(--text-sm);
    padding: var(--space-9) 0 var(--space-8);
}

.site-footer .fw-semibold {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-weight: var(--weight-medium) !important;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.site-footer a {
    color: var(--listamos-text-secondary);
}

.site-footer a:hover {
    color: var(--listamos-text);
}

/* ——— Terms, privacy, what we do with your data ——————————————————————————
   One column at a reading measure. These are the two pages somebody actually reads
   before handing over access to their shop, so they get real typography rather than
   whatever the browser does with an unstyled `h2`. */

.legal-prose {
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    line-height: 1.75;
}

.legal-prose h2 {
    color: var(--listamos-text);
    font-size: var(--text-xl);
    letter-spacing: -0.024em;
    margin: var(--space-8) 0 var(--space-3);
}

.legal-prose h3,
.legal-prose .h5 {
    color: var(--listamos-text);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    margin: var(--space-6) 0 var(--space-2);
}

.legal-prose p,
.legal-prose ul,
.legal-prose ol {
    margin-bottom: var(--space-4);
}

/* Drawn markers rather than the browser's. A default `disc` at this text size sets a
   dot the width of a hairline, hanging in the gutter at the wrong height. */
.legal-prose ul {
    list-style: none;
    padding-left: 0;
}

.legal-prose li {
    margin-bottom: var(--space-2);
    padding-left: var(--space-5);
    position: relative;
}

.legal-prose ul > li::before {
    background: var(--ink-250);
    border-radius: 50%;
    content: '';
    height: 5px;
    left: 0.375rem;
    position: absolute;
    top: 0.72em;
    width: 5px;
}

/* A capsule that is genuinely a token: a status on a product card, a mode on a
   listing. Tinted at the signal's lightest step rather than filled. */
.pill {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    gap: 0.3125rem;
    line-height: 1;
    padding: 0.3125rem 0.5rem;
    white-space: nowrap;
}

.pill-dot {
    background: currentColor;
    border-radius: 50%;
    height: 5px;
    width: 5px;
}


/* ——— Sign in and sign up ————————————————————————————————————————————————
   A split, weighted towards the form. The right panel is the same three planes as the
   application — canvas with a white object on it — so the transition from this screen
   to the first real one is a continuation rather than an arrival somewhere else.

   The form half is *not* a card. A card floating on a canvas needs a reason, and one
   form alone on half a screen is not one: the white is the page, the type does the
   composing, and the result is calmer than a rectangle inside a rectangle. */

.auth-shell {
    background: var(--listamos-shell);
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
    min-height: 100vh;
}

.auth-main {
    align-items: center;
    display: flex;
    justify-content: center;
    min-height: 100vh;
    padding: var(--space-5) var(--space-6);
}

.auth-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 24rem;
    width: 100%;
}

.auth-top {
    align-items: center;
    display: flex;
    gap: var(--space-4);
    justify-content: space-between;
}

.auth-body {
    width: 100%;
}

.auth-card {
    width: 100%;
}

.auth-head {
    margin-bottom: var(--space-4);
}

.auth-head .lm-title,
.auth-head .lm-heading {
    margin-bottom: 0.35rem;
}

.auth-head .lm-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    margin-bottom: 0;
}

/* Who is approving and where the answer goes, boxed so they read as facts to check
   rather than as more of the lede. */
.oauth-facts {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    flex-direction: column;
    font-size: var(--text-sm);
    gap: var(--space-1);
    margin: var(--space-3) 0 var(--space-2);
    padding: var(--space-2) var(--space-3);
}

.oauth-facts strong {
    color: var(--listamos-text);
}

/* A button that sits inside a sentence and reads as part of it. */
.lm-inline-link {
    font-size: inherit;
    margin-left: var(--space-1);
    text-decoration: underline;
    vertical-align: baseline;
}

/* Tighter than the rest of the product: two fields and a button, not a catalogue. */
.auth-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.auth-field-pair {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 30rem) {
    .auth-field-pair {
        grid-template-columns: minmax(0, 1fr);
    }
}

.auth-form .alert {
    margin-bottom: 0;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.auth-field .form-label {
    margin-bottom: 0;
}

.auth-field .form-text {
    margin-top: 0;
}

.auth-remember {
    margin: 0.125rem 0 0.25rem;
}

/* Continue with Google, Apple, Facebook.
   Stacked rather than sat in a row of icons, because a row of three round marks asks
   the reader to recognise a logo, and a line of text asks them to read one. Full width
   and the same height as the form's own button, so the choice between them reads as a
   choice rather than as a main path and an afterthought. */
.auth-providers {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.auth-provider {
    align-items: center;
    display: flex;
    gap: 0.625rem;
    justify-content: center;
    width: 100%;
}

.auth-provider-mark {
    flex: none;
    height: 1.125rem;
    object-fit: contain;
    width: 1.125rem;
}

/* "or", with a rule through it. Above the form on the sign-in screen: the providers
   come first because most people arriving have one, and the password field below is
   for the rest. */
.auth-divide {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-xs);
    gap: var(--space-2);
    margin: var(--space-3) 0;
}

.auth-divide::before,
.auth-divide::after {
    background: var(--listamos-border);
    content: "";
    flex: 1 1 auto;
    height: 1px;
}

.auth-forgot {
    font-size: var(--text-xs);
    margin-left: auto;
}

.auth-submit {
    margin-top: 0.25rem;
}

.auth-agree {
    margin: 0.75rem 0 0;
    text-align: center;
}

.auth-agree a {
    white-space: nowrap;
}

.auth-code {
    font-size: 1.5rem;
    font-weight: 650;
    letter-spacing: 0.28em;
    text-align: center;
}

.auth-foot {
    border-top: 1px solid var(--listamos-border);
    padding-top: var(--space-3);
    text-align: center;
    width: 100%;
}

.auth-legal {
    font-size: var(--text-xs);
    text-align: center;
}

.auth-legal a {
    color: var(--listamos-text-tertiary);
    text-decoration: none;
}

.auth-legal a:hover {
    color: var(--listamos-text-secondary);
}

/* The language control: current flag on the button, the rest in the menu. */
.lang-toggle .lm-flag {
    flex: none;
    height: 0.75rem;
    width: 1.0625rem;
}

.lang-toggle-caret {
    color: var(--listamos-text-tertiary);
    font-size: 0.6875rem;
    margin-left: 0.125rem;
    opacity: 0.75;
}

.lm-flag {
    aspect-ratio: 4 / 3;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(15, 19, 32, 0.1);
    display: block;
    height: auto;
    object-fit: cover;
    width: 1.125rem;
}

.dropdown-item .lm-flag {
    flex: none;
    height: 0.75rem;
    width: 1.0625rem;
}

/* The marketplace mark on a connect button. Sized to sit beside a word, not above it. */
.lm-channel-logo {
    flex: none;
    height: 1.125rem;
    object-fit: contain;
    width: auto;
}

/* Marketing column — fills the height like competitor sign-in pages, not a small
   card floating in empty canvas. */
.auth-aside {
    background:
        radial-gradient(ellipse 90% 70% at 15% 0%, rgba(37, 99, 235, 0.07), transparent 58%),
        radial-gradient(ellipse 60% 50% at 85% 100%, rgba(15, 23, 42, 0.05), transparent 52%),
        var(--listamos-canvas);
    border-right: 1px solid var(--listamos-border);
    display: flex;
    min-height: 100vh;
    padding: clamp(var(--space-5), 3vw, var(--space-8));
}

.auth-aside-inner {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-5);
    justify-content: center;
    margin: 0 auto;
    max-width: 36rem;
    width: 100%;
}

.auth-aside-kicker {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.12em;
    margin: 0 0 var(--space-3);
    text-transform: uppercase;
}

.auth-aside-title {
    color: var(--listamos-text);
    font-size: clamp(2rem, 3.2vw, 2.75rem);
    font-weight: var(--weight-bold);
    letter-spacing: -0.038em;
    line-height: 1.02;
    margin: 0 0 var(--space-3);
}

.auth-aside-mark {
    color: var(--listamos-primary);
    display: block;
}

.auth-aside-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-base);
    line-height: 1.55;
    margin: 0;
    max-width: 36ch;
}

.auth-showcase {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.auth-listings {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.auth-listing {
    animation: auth-listing-in 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
    animation-delay: calc(var(--auth-listing-i, 0) * 70ms);
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
    transition: transform 180ms ease, box-shadow 180ms ease;
}

.auth-listing:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

@keyframes auth-listing-in {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.auth-listing-media {
    aspect-ratio: 1;
    background: var(--ink-050);
    overflow: hidden;
}

.auth-listing-media img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.auth-listing-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--space-3);
}

.auth-listing-title {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.015em;
    line-height: 1.25;
    margin: 0;
}

.auth-listing-meta {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    gap: 0.4rem;
    line-height: 1.3;
    margin: 0;
}

.auth-listing-channel {
    flex: none;
    height: 0.85rem;
    opacity: 0.85;
    width: auto;
}

.auth-showcase-caption {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.4;
    margin: 0;
    text-align: center;
}

.auth-highlights {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(3, minmax(0, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.auth-highlight {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: var(--space-3) var(--space-3) var(--space-4);
}

.auth-highlight-value {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.01em;
}

.auth-highlight-label {
    color: var(--listamos-text-secondary);
    font-size: var(--text-2xs);
    line-height: 1.4;
}

@media (prefers-reduced-motion: reduce) {
    .auth-listing {
        animation: none;
    }

    .auth-listing:hover {
        transform: none;
    }
}

/* Three tools — shared on landing, auth, and connect. Not a fake app screenshot. */
.lm-pillars {
    display: grid;
    gap: var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.hero-visual-panel .lm-pillars,
.onboard-preview-panel .lm-pillars {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.lm-pillar {
    align-items: flex-start;
    background: var(--listamos-surface-subtle);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    gap: var(--space-3);
    padding: var(--space-3);
}

.lm-pillars--icons .lm-pillar {
    flex-direction: column;
    padding: var(--space-4);
}

.lm-pillar-num {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xs);
    color: var(--listamos-text-tertiary);
    display: flex;
    flex: none;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    height: 1.625rem;
    justify-content: center;
    width: 1.625rem;
}

.lm-pillar-icon {
    align-items: center;
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-primary);
    display: flex;
    flex: none;
    font-size: 1.125rem;
    height: 2.5rem;
    justify-content: center;
    width: 2.5rem;
}

.lm-pillar-title {
    color: var(--listamos-text);
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    margin-bottom: 0.15rem;
}

.lm-pillar-copy {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
    line-height: 1.45;
    margin: 0;
}

.hero-visual-panel {
    background:
        radial-gradient(ellipse 120% 80% at 50% -20%, rgba(37, 99, 235, 0.08), transparent 55%),
        var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-2xl);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    padding: var(--space-5);
    position: relative;
}

.hero-visual-panel::before {
    background: linear-gradient(90deg, rgba(37, 99, 235, 0.55), rgba(99, 102, 241, 0.35), rgba(37, 99, 235, 0.15));
    content: '';
    height: 3px;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
}

.hero-visual-panel .lm-pillars--hero {
    gap: var(--space-4);
}

.hero-visual-panel .lm-pillars--hero .lm-pillar {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xs);
    flex-direction: column;
    gap: var(--space-4);
    min-height: 11.5rem;
    padding: var(--space-5);
    position: relative;
    transition: border-color var(--medium), box-shadow var(--medium), transform var(--medium);
}

.hero-visual-panel .lm-pillars--hero .lm-pillar:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-3px);
}

.hero-visual-panel .lm-pillars--hero .lm-pillar:nth-child(1) {
    --pillar-accent: rgba(37, 99, 235, 0.14);
    --pillar-icon: #2563eb;
}

.hero-visual-panel .lm-pillars--hero .lm-pillar:nth-child(2) {
    --pillar-accent: rgba(99, 102, 241, 0.14);
    --pillar-icon: #6366f1;
}

.hero-visual-panel .lm-pillars--hero .lm-pillar:nth-child(3) {
    --pillar-accent: rgba(15, 23, 42, 0.08);
    --pillar-icon: #334155;
}

.hero-visual-panel .lm-pillars--hero .lm-pillar::after {
    background: radial-gradient(circle at 100% 0%, var(--pillar-accent), transparent 68%);
    border-radius: inherit;
    content: '';
    inset: 0;
    pointer-events: none;
    position: absolute;
}

.hero-visual-panel .lm-pillar-step {
    color: var(--ink-250);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.12em;
    position: absolute;
    right: var(--space-4);
    top: var(--space-4);
}

.hero-visual-panel .lm-pillars--hero .lm-pillar-icon {
    background: linear-gradient(145deg, var(--listamos-primary-soft) 0%, rgba(255, 255, 255, 0.9) 100%);
    border: 1px solid rgba(37, 99, 235, 0.12);
    border-radius: var(--radius-lg);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
    color: var(--pillar-icon, var(--listamos-primary));
    font-size: 1.375rem;
    height: 3.25rem;
    position: relative;
    width: 3.25rem;
    z-index: 1;
}

.hero-visual-panel .lm-pillars--hero .lm-pillar-body {
    position: relative;
    z-index: 1;
}

.hero-visual-panel .lm-pillars--hero .lm-pillar-title {
    font-size: var(--text-md);
    letter-spacing: -0.018em;
    margin-bottom: var(--space-2);
}

.hero-visual-panel .lm-pillars--hero .lm-pillar-copy {
    font-size: var(--text-sm);
    line-height: 1.5;
    max-width: 22ch;
}

.onboard-preview-panel {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    max-width: 28rem;
    padding: var(--space-5);
}

.onboard-preview-assurance {
    border-top: 1px solid var(--listamos-border);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
}

/* Landing — three tools, full detail */
.what-showcase {
    overflow: hidden;
    position: relative;
}

.what-showcase::before {
    background: radial-gradient(ellipse 90% 70% at 50% -15%, rgba(37, 99, 235, 0.07), transparent 62%);
    content: '';
    inset: 0;
    pointer-events: none;
    position: absolute;
}

.what-showcase .container {
    position: relative;
    z-index: 1;
}

.what-showcase .lm-pillars--showcase {
    gap: var(--space-5);
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.lm-pillars--showcase .lm-pillar {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    flex-direction: column;
    gap: var(--space-5);
    min-height: 100%;
    padding: var(--space-6);
    position: relative;
    transition: border-color var(--medium), box-shadow var(--medium), transform var(--medium);
}

.lm-pillars--showcase .lm-pillar:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-4px);
}

.lm-pillars--showcase .lm-pillar:nth-child(1) {
    --pillar-accent: rgba(37, 99, 235, 0.13);
    --pillar-icon: #2563eb;
    --pillar-ring: rgba(37, 99, 235, 0.16);
}

.lm-pillars--showcase .lm-pillar:nth-child(2) {
    --pillar-accent: rgba(99, 102, 241, 0.13);
    --pillar-icon: #6366f1;
    --pillar-ring: rgba(99, 102, 241, 0.16);
}

.lm-pillars--showcase .lm-pillar:nth-child(3) {
    --pillar-accent: rgba(15, 23, 42, 0.07);
    --pillar-icon: #334155;
    --pillar-ring: rgba(15, 23, 42, 0.12);
}

.lm-pillars--showcase .lm-pillar::after {
    background: radial-gradient(circle at 100% 0%, var(--pillar-accent), transparent 68%);
    border-radius: inherit;
    content: '';
    inset: 0;
    pointer-events: none;
    position: absolute;
}

.lm-pillars--showcase .lm-pillar-step {
    color: var(--ink-250);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.12em;
    position: absolute;
    right: var(--space-5);
    top: var(--space-5);
    z-index: 2;
}

.lm-pillars--showcase .lm-pillar-icon {
    background: linear-gradient(145deg, var(--listamos-primary-soft) 0%, rgba(255, 255, 255, 0.92) 100%);
    border: 1px solid var(--pillar-ring, rgba(37, 99, 235, 0.12));
    border-radius: var(--radius-lg);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85);
    color: var(--pillar-icon, var(--listamos-primary));
    font-size: 1.5rem;
    height: 3.5rem;
    position: relative;
    width: 3.5rem;
    z-index: 1;
}

.lm-pillars--showcase .lm-pillar-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    position: relative;
    z-index: 1;
}

.lm-pillar-media {
    background: var(--ink-025);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    margin: 0 0 var(--space-5);
    min-height: 11.5rem;
    overflow: hidden;
}

/* Tool cards show product UI, not lifestyle photos of unrelated goods. */
.tool-preview {
    height: 100%;
    min-height: 11.5rem;
    padding: var(--space-4);
}

.tool-preview--create {
    align-items: stretch;
    display: grid;
    gap: var(--space-3);
    grid-template-columns: 5.5rem minmax(0, 1fr);
}

.tool-preview-photo {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.tool-preview-photo img {
    aspect-ratio: 1;
    border-radius: var(--radius-md);
    display: block;
    height: auto;
    object-fit: cover;
    width: 100%;
}

.tool-preview-photo span,
.tool-preview-badge {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: 0.625rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.tool-preview-draft {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    justify-content: center;
    min-width: 0;
}

.tool-preview-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.02em;
    line-height: 1.3;
    margin: 0;
}

.tool-preview-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.tool-preview--improve {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.tool-preview-score {
    align-items: center;
    display: flex;
    gap: var(--space-3);
}

.tool-preview-score-num {
    color: #c2410c;
    font-family: var(--font-mono);
    font-size: var(--text-2xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    letter-spacing: -0.04em;
    line-height: 1;
}

.tool-preview-score-bar {
    background: var(--ink-100);
    border-radius: 999px;
    flex: 1 1 auto;
    height: 0.35rem;
    overflow: hidden;
}

.tool-preview-score-bar i {
    background: #c2410c;
    display: block;
    height: 100%;
}

.tool-preview-checks {
    display: grid;
    gap: 0.4rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.tool-preview-checks li {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-left: 3px solid #c2410c;
    border-radius: var(--radius-sm);
    display: grid;
    gap: 0.1rem;
    padding: 0.4rem 0.55rem;
}

.tool-preview-checks strong {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    line-height: 1.2;
}

.tool-preview-checks span {
    color: var(--listamos-text-tertiary);
    font-size: 0.6875rem;
    line-height: 1.3;
}

.tool-preview-fix {
    pointer-events: none;
    width: 100%;
}

.tool-preview--bulk {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
}

.tool-preview--bulk table {
    border-collapse: collapse;
    font-size: 0.6875rem;
    width: 100%;
}

.tool-preview--bulk th {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: 0.5625rem;
    font-weight: var(--weight-medium);
    letter-spacing: 0.06em;
    padding: 0.25rem 0.35rem 0.45rem;
    text-align: left;
    text-transform: uppercase;
}

.tool-preview--bulk td {
    border-top: 1px solid var(--listamos-border);
    padding: 0.4rem 0.35rem;
    vertical-align: middle;
}

.tool-preview--bulk .diff {
    white-space: nowrap;
}

.tool-preview--bulk .badge-state {
    font-size: 0.5625rem;
}

.tool-preview-bulk-note {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: 0.625rem;
    letter-spacing: 0.04em;
    margin: 0;
    padding: 0 0.35rem;
}

.lm-pillars--showcase .lm-pillar-step {
    background: rgba(255, 255, 255, 0.88);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.35rem;
}

.lm-pillar-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.1em;
    margin-bottom: var(--space-2);
    text-transform: uppercase;
}

.lm-pillar-headline {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.022em;
    line-height: 1.25;
    margin-bottom: var(--space-3);
}

.lm-pillar-desc {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    line-height: 1.6;
    margin: 0;
}

.lm-pillars--showcase .product-soon {
    margin-top: var(--space-5);
}

@media (max-width: 62em) {
    .what-showcase .lm-pillars--showcase {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 48em) {
    .hero-visual-panel .lm-pillars,
    .onboard-preview-panel .lm-pillars {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Large marketplace wordmarks — the logos are the message. */
.marketplace-assurance {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    justify-content: center;
    margin: 0;
    width: 100%;
}

.marketplace-assurance-brand {
    align-items: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.marketplace-assurance-logo {
    display: block;
    height: 2rem;
    object-fit: contain;
    width: auto;
}

.marketplace-assurance-logo--etsy {
    height: 2.125rem;
}

.marketplace-assurance-logo--shopify {
    height: 2.25rem;
}

/* Smaller than its neighbours rather than equal to them, because eBay's artwork is
   cropped tight to the letters while Etsy's and Shopify's carry air inside the box.
   Matching the numbers would make eBay the loudest of the three. Optical, so the value
   is what looks level beside them and not something derived. */
.marketplace-assurance-logo--ebay {
    height: 1.75rem;
}
.auth-aside-assurance {
    margin-top: auto;
    padding-top: var(--space-3);
}

/* A notch smaller all round than these were when the column held three marks. Pinterest
   is the widest of the four by some way, and at the old sizes the row wanted about
   560px of a 544px column — so it dropped Pinterest onto a line of its own. The ratios
   between the four are the ones from the strip above; only the scale has come down. */
.auth-aside-assurance .marketplace-assurance-logo--etsy {
    height: 2.5rem;
}

.auth-aside-assurance .marketplace-assurance-logo--shopify {
    height: 2.625rem;
}

.auth-aside-assurance .marketplace-assurance-logo--ebay {
    height: 2rem;
}

.auth-aside-assurance .marketplace-assurance-logo--pinterest {
    height: 2.25rem;
}

.marketplace-assurance--center {
    justify-content: center;
    width: 100%;
}

@media (max-width: 67.5em) {
    .auth-shell {
        grid-template-columns: minmax(0, 1fr);
    }

    .auth-aside {
        display: none;
    }

    .auth-main {
        padding: var(--space-5) var(--space-4);
    }

    .auth-stack {
        max-width: 26rem;
    }
}

@media (max-width: 30rem) {
    .auth-highlights {
        grid-template-columns: minmax(0, 1fr);
    }

    .auth-listings {
        grid-template-columns: minmax(0, 1fr);
    }
}

/*
 * Bootstrap's spacing utilities stop at 3rem, which is fine inside an interface and
 * too small between sections of a page. These continue the same scale rather than
 * introducing a second one.
 */
.mt-6 { margin-top: var(--space-6) !important; }
.mt-7 { margin-top: var(--space-7) !important; }
.mb-6 { margin-bottom: var(--space-6) !important; }
.mb-7 { margin-bottom: var(--space-7) !important; }
.py-7 { padding-top: var(--space-7) !important; padding-bottom: var(--space-7) !important; }

.g-6 {
    --bs-gutter-x: var(--space-6);
    --bs-gutter-y: var(--space-6);
}

/* The surface everything sits on. One class rather than Bootstrap's card, because a
   card brings a header, a body and their paddings, and every screen here wants a plain
   sheet of paper with a hairline round it. */
.panel {
    background: var(--paper);
    border: var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xs);
    padding: var(--space-4);
}

/* ——— One listing ————————————————————————————————————————————————————————
   The screen that answers the grid's advice. The form is wide and the facts are
   narrow, because the work here is writing sentences: a description box the width of
   a sidebar is why sellers write their descriptions in Etsy's own editor instead. */
.detail {
    max-width: 1240px;
}

/* The bar the whole screen is steered from, and it follows: this is a long form and
   Save must never be somewhere you have to scroll back to. A white bar floating on
   the canvas rather than a rule across it, because it is the one element here that
   stays put while everything else moves — it should look like it is on top.

   `top` clears the shell's topbar when that bar is on (narrow windows). Measured into
   `--lm-topbar-h` rather than guessed — see the observer in forms.blade.php — because
   the strip's height depends on the language and on how long the shop name is. On a
   wide window the topbar is `display: none` and the variable is zero, so the bar parks
   under the window edge with only the small gap. */
.detail-head {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    display: flex;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    padding: var(--space-3);
    position: sticky;
    top: calc(var(--lm-topbar-h, 0px) + var(--space-3));
    z-index: 20;
}

.detail-headings {
    min-width: 0;
    flex: 1;
}

.detail-title {
    font-size: var(--text-lg);
    font-weight: 620;
    color: var(--ink-900);
    letter-spacing: -0.01em;
    /* The page's one h1, so the browser's own heading margins have to go. */
    margin: 0;
    /* One line with an ellipsis: a 140-character Etsy title would otherwise push the
       Save button off the fold on a laptop. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.detail-sub {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    margin-top: 0.15rem;
    font-size: var(--text-xs);
}

/* Whose listing this is, ahead of Draft. The catalogue already says a shop this way
   — mark and name — and repeating that here is what stops "Draft" being the first
   thing you read about a listing that belongs to a particular storefront. */
.detail-shop {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: inline-flex;
    gap: 0.35rem;
    max-width: 14rem;
    min-width: 0;
}

.detail-shop > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.detail-shop .destination-mark {
    height: 14px;
    width: 14px;
}

.detail-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* Create-screen send: one primary (draft) plus a caret for Schedule / live. The two
   halves share one border so they read as a single control, and the channel mark sits
   where the upload icon used to — so the shop name is not the only clue about where. */
.lm-send {
    display: inline-flex;
    flex: 0 0 auto;
    vertical-align: middle;
}

.lm-send > .lm-btn:first-child {
    border-bottom-right-radius: 0;
    border-top-right-radius: 0;
}

.lm-send > .lm-btn.dropdown-toggle {
    border-bottom-left-radius: 0;
    border-top-left-radius: 0;
    margin-left: -1px;
    padding-inline: 0.75rem;
}

.lm-send .destination-mark {
    flex: none;
    height: 16px;
    width: 16px;
}

.lm-send .dropdown-item .bi {
    margin-inline-end: 0.4rem;
    opacity: .75;
}

.detail-saved {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: var(--text-xs);
    font-weight: 540;
    color: var(--green-700);
}

/* On a phone the bar cannot be a row. A back arrow, a 140-character title, four status
   pills and up to three buttons need about 600 pixels and there are 390, and what the
   single row did with the shortfall was quietly wrong rather than merely cramped: the
   headings are the only flexible item, so they were squeezed to 43 pixels while their
   contents kept their own width and drew straight over the buttons. `min-width: 0`
   lets a flex item shrink; it does not make what is inside it shrink with it.

   So the actions drop to a line of their own and the pills get the width to wrap on.
   The bar stays sticky and Save stays on screen, which is the whole reason it is a bar
   and not a heading. */
@media (max-width: 47.9375em) {
    .detail-head {
        display: grid;
        gap: var(--space-2) var(--space-3);
        grid-template-columns: auto minmax(0, 1fr);
    }

    /* Level with the title rather than centred on the whole block, which put it beside
       the pills and made it look like it belonged to them. Back goes with the name of
       the thing you are going back from. */
    .detail-head > .lm-icon-btn {
        align-self: start;
    }

    .detail-headings {
        grid-area: 1 / 2;
    }

    .detail-actions {
        flex-wrap: wrap;
        grid-area: 2 / 1 / 3 / -1;
        justify-content: flex-end;
    }
}

.detail-notice {
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    font-size: var(--text-sm);
    font-weight: 500;
}

.detail-notice.is-success { color: var(--green-700); background: var(--green-100); }
.detail-notice.is-danger  { color: var(--red-700);   background: var(--red-100); }
.detail-notice.is-info    { color: var(--brand-700); background: var(--brand-100); }
.detail-notice.is-warning { color: var(--amber-800); background: var(--amber-100); }

/* Confirmations that need no answer — bottom corner, gone in a few seconds. */
.lm-toast-host {
    z-index: 1100;
    pointer-events: none;
}

.lm-toast-host .toast {
    pointer-events: auto;
}

.lm-toast {
    border: var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    max-width: 24rem;
    min-width: 16rem;
    /* Bootstrap hides a toast without `.show` by opacity; keep ours readable either way. */
    opacity: 1;
}

.lm-toast .toast-body {
    padding: var(--space-3) var(--space-4);
}

.lm-toast.is-success { color: var(--green-700); background: var(--green-100); }
.lm-toast.is-danger  { color: var(--red-700);   background: var(--red-100); }
.lm-toast.is-info    { color: var(--brand-700); background: var(--brand-050); }
.lm-toast.is-warning { color: var(--amber-800); background: var(--amber-100); }

/* A notice that carries the way back out of itself. The one on this screen is what a
   template just wrote into the form, and the button beside it undoes exactly that — so
   it belongs in the sentence rather than somewhere the eye has to go looking. */
.detail-notice.has-undo {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

/* The menu those fills are chosen from. Two lines an item, because a name is not enough
   to choose by: a shop with "Prints" and "Prints, framed" is choosing between what they
   hold, and the fields each one writes is the answer. */
.lm-template-menu {
    max-width: 22rem;
}

.lm-template-menu .dropdown-item {
    align-items: flex-start;
    flex-direction: column;
    gap: 0.125rem;
    white-space: normal;
}

.lm-template-note {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    font-weight: var(--weight-normal);
}

.detail-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--space-4);
    align-items: start;
}

/* The left column when it holds more than one panel — the words a seller writes, then
   the choices they pick from the shop's own lists. A wrapper rather than more children
   of the grid above, which would put the second panel where the aside goes. */
/* `minmax(0, 1fr)` and not `1fr`, which is `minmax(auto, 1fr)` and lets a track refuse
   to go below the widest thing in it. A panel holding a row of 120px thumbnails or a
   heading with a progress ring on the end has a min-content width somewhere near 330,
   so on a 320px phone the panels were drawing themselves wider than the column they
   are in and taking the page sideways with them. */
.detail-main {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    align-content: start;
    min-width: 0;
}

/* Late, because the advice belongs beside the field it is about. Below 940 the column
   would be too narrow to read and it goes under the form instead. */
@media (max-width: 940px) {
    .detail-body {
        grid-template-columns: minmax(0, 1fr);
    }

    .detail-side {
        position: static;
    }
}

/* One column, stated. An implicit grid track is `auto`, which sizes to the widest thing
   in it and never gives that width back — and the widest thing in a form is usually a
   `<select>`, whose min-content is its longest option. A section called "Christmas &
   Winter Holidays" was holding this form open at 380px on a 320px phone. */
.detail-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
}

/* While a conflict is waiting, the inputs hold the shop's version rather than the
   seller's. Editing them before the decision would produce a third version nobody
   asked for, so the form is dimmed and the eye goes to the panel above it. */
.detail-form.is-locked {
    opacity: .45;
    pointer-events: none;
}

/* Follows the form, and stops where the bar above it ends.
   Both this column and the bar stick to the top of the window, and the bar is there
   first: without the offset the column's first panel pins underneath it and stays
   there, which is a kind of hidden that scrolling does not fix. The height arrives
   measured, because the bar wraps on a narrow window — see layouts/forms. */
.detail-side {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    position: sticky;
    top: calc(var(--lm-detail-head, 0px) + var(--space-3));
}

.side-panel {
    padding: var(--space-4);
}

/* A section heading, not a form legend. It was set in small uppercase grey, which put
   the name of every section a rung below the labels of the fields inside it — the
   page read as a stack of anonymous boxes. Sentence case, ink, at the weight the
   thing deserves. */
.side-title {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.005em;
    margin-bottom: var(--space-3);
}

.facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-2) var(--space-3);
    margin: 0;
    font-size: var(--text-sm);
}

.facts dt {
    color: var(--ink-400);
    font-weight: 500;
}

.facts dd {
    margin: 0;
    color: var(--ink-800);
    text-align: right;
}

/* ——— The gallery ————————————————————————————————————————————————————————
   The first thing on the editing screen, because that is the order the work happens
   in. Still not a photo editor and it cannot be — Etsy takes images through their own
   upload endpoints and not through the PATCH that carries the words — so what the
   tiles are for is deciding: which photograph goes first, which one is soft, which
   three have no alt text.

   Sized by the room rather than by a count. This panel spent its life three across a
   three-hundred pixel column and now sits in the wide one, where three across would be
   three photographs the size of the description box. Clicking any of them opens the
   full-size original, which is the only way to answer "is it sharp". */

.media-grid {
    display: grid;
    gap: var(--space-2);
    grid-auto-flow: dense;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}

/* The photograph, its caption and its remove button. The slot owns the square and clips
   everything inside it — caption on top of the picture, not under it — so a 2×2 cover
   can pack beside the small tiles without a portrait image or two lines of alt blowing
   the cell past its neighbours.

   Not `.is-clip`: the video tile is sixteen-by-nine and lives outside the photo grid.
   Forcing a square on it cropped the poster into the top of an empty frame. */
.media-slot:not(.is-clip) {
    aspect-ratio: 1;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    min-width: 0;
    overflow: hidden;
    position: relative;
}

.media-slot.is-clip {
    position: relative;
}

/* The first photograph is what Etsy shows in search. A 2×2 cell with a badge, packed
   dense so the small tiles fill the space beside and under it. Still square — same as
   the small tiles, just twice as wide and tall — so a landscape file does not pull the
   cell into a rectangle. The image crops inside via `object-fit: cover`. */
.media-slot.is-cover {
    aspect-ratio: 1;
    grid-column: span 2;
    grid-row: span 2;
    min-height: 0;
}

.media-cover {
    background: rgba(10, 16, 36, .62);
    backdrop-filter: blur(4px);
    border-radius: var(--radius-pill);
    color: var(--paper, #fff);
    font-size: 0.65rem;
    font-weight: 620;
    left: var(--space-2);
    letter-spacing: .04em;
    padding: 0.05rem 0.5rem;
    position: absolute;
    text-transform: uppercase;
    top: var(--space-2);
    z-index: 1;
}

/* Only the photographs are draggable; the video tile and the add button are not, and
   the cursor is the only thing that says which is which before the attempt. */
.media-slot[draggable="true"] {
    cursor: grab;
}

.media-slot.is-dragging {
    cursor: grabbing;
    opacity: .4;
    outline: 2px dashed var(--brand-400);
}

/* A link is draggable in its own right and would drag its URL rather than the tile.
   The anchor carries draggable="false" for that; this stops the image doing the same. */
.media-slot[draggable="true"] .media-shot img {
    -webkit-user-drag: none;
    user-select: none;
}

.media-shot {
    display: block;
    height: 100%;
    inset: 0;
    overflow: hidden;
    position: absolute;
    transition: border-color var(--fast);
    width: 100%;
}

.media-slot:not(.is-clip):hover {
    border-color: var(--listamos-border-strong);
}

/* Hidden until the pointer is on the photograph, because this one takes the photograph
   off the shop rather than out of a draft, and six of them showing at rest is six
   chances to press the wrong thing while reading a gallery. Always there for a keyboard,
   which has no hover to reveal it with. */
.media-remove {
    align-items: center;
    background: rgb(0 0 0 / 0.6);
    border: 0;
    border-radius: var(--radius-pill);
    color: #fff;
    cursor: pointer;
    display: flex;
    font-size: 0.8125rem;
    height: 1.25rem;
    justify-content: center;
    /* Right rather than left: the cover badge sits on the left, and two pills fighting
       for the same corner is how one of them becomes unreadable. */
    opacity: 0;
    position: absolute;
    right: 4px;
    top: 4px;
    transition: opacity var(--fast), background var(--fast);
    width: 1.25rem;
    z-index: 2;
}

.media-slot:hover .media-remove,
.media-remove:focus-visible {
    opacity: 1;
}

.media-remove:hover {
    background: var(--listamos-danger);
}

/* One more of the same size as the photographs, so the grid does not change shape when
   the twentieth is added and it disappears. Label + remaining count sit under the plus
   the way Etsy's empty tile does — the note under the grid still says "N of M". */
.media-add {
    align-items: center;
    aspect-ratio: 1;
    background: none;
    border: 1px dashed var(--listamos-border-strong);
    border-radius: var(--radius-sm);
    color: var(--listamos-text-tertiary);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    font-size: var(--text-xs);
    gap: 0.125rem;
    justify-content: center;
    padding: var(--space-2);
    text-align: center;
    transition: border-color var(--fast), color var(--fast);
}

.media-add:hover {
    border-color: var(--listamos-primary);
    color: var(--listamos-primary);
}

.media-add .bi {
    font-size: 1.125rem;
}

.media-add span {
    color: var(--listamos-text-secondary);
    font-weight: 600;
    line-height: 1.2;
}

.media-add small {
    color: var(--listamos-text-tertiary);
    line-height: 1.2;
}

.media-video-add {
    align-items: center;
    aspect-ratio: 16 / 9;
    background: none;
    border: 1px dashed var(--listamos-border-strong);
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    font-size: var(--text-xs);
    gap: 0.125rem;
    justify-content: center;
    /* Same footprint as a video tile, so the panel does not jump when the first
       video lands beside it. */
    max-width: 22rem;
    min-height: 0;
    padding: var(--space-3);
    text-align: center;
    transition: border-color var(--fast), color var(--fast);
    width: 100%;
}

.media-video-add:hover {
    border-color: var(--listamos-primary);
    color: var(--listamos-primary);
}

.media-video-add .bi {
    font-size: 1.125rem;
}

.media-video-add small {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
}

/* An upload is a request to somebody else's server with a file on it, and pressing add
   twice while the first one is in flight is how a listing ends up with two of the same
   photograph. */
.panel.is-busy {
    opacity: .6;
    pointer-events: none;
}

.media-shot img,
.media-clip img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

/* The only finding on the panel, and it is the one the checklist counts. */
.media-slot:not(.is-clip):has(.media-shot.is-unlabelled) {
    border-color: var(--listamos-warning);
}

/* Advice "photos are small" — one ring, not border + outer shadow (that read as double). */
.media-slot.is-undersized {
    border-color: var(--listamos-warning);
    border-width: 2px;
}

/* ——— What the photograph shows ——————————————————————————————————————————
   On the tile rather than under it, clipped by the slot. Two lines at most — the point
   is to see at a glance which tiles are described and which are not, and the full text
   is in the editor.

   Amber where there are none. It is the one finding on this panel the checklist
   counts, and the only one a seller can fix in ten seconds. */
.media-alt {
    background: linear-gradient(transparent, rgb(10 16 36 / 0.78));
    border: 0;
    bottom: 0;
    color: #fff;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    font-size: var(--text-2xs);
    left: 0;
    line-height: 1.3;
    max-height: 2.6em;
    overflow: hidden;
    padding: var(--space-4) var(--space-2) var(--space-2);
    position: absolute;
    right: 0;
    text-align: left;
    word-break: break-word;
    z-index: 1;
}

.media-alt:hover {
    color: #fff;
    text-decoration: underline;
}

.media-alt.is-missing {
    color: var(--amber-100, #fde68a);
}

/* ——— The photograph, large, and the words for it ————————————————————————
   Two columns because they are one task. The alt text used to be written in a panel
   under the grid, which meant describing a photograph while looking at a tile the size
   of a postage stamp — and the description a person writes in that position is
   "handmade item". Here the picture is as big as the screen allows and the box is
   beside it.

   The picture gets the room and the box gets a working minimum. Below a tablet they
   stack, picture first, because a phone has one column and the picture is the thing
   being described. */
.photo-viewer {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
}

/* Sunken and centred, so a tall photograph and a wide one both sit in a frame of the
   same shape and the dialog does not jump size between them. Capped against the
   viewport rather than in pixels: the point of this is to be big. */
.photo-viewer-stage {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    justify-content: center;
    min-height: 18rem;
    overflow: hidden;
    padding: var(--space-2);
}

.photo-viewer-stage img {
    display: block;
    max-height: min(70vh, 40rem);
    max-width: 100%;
    object-fit: contain;
}

.photo-viewer-side {
    align-content: start;
    display: grid;
    gap: var(--space-3);
}

/* The suggest button sits under the box rather than beside the label. It is not part of
   writing the sentence, it is an offer to write it for you, and a control that changes
   the contents of a field belongs after the field and not before it. */
.photo-viewer-side .lm-btn,
.photo-viewer-side .btn-polish,
.photo-viewer-actions {
    justify-self: start;
}

.photo-viewer-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

@media (max-width: 62em) {
    .photo-viewer {
        grid-template-columns: minmax(0, 1fr);
    }

    .photo-viewer-stage img {
        max-height: 42vh;
    }
}

/* Black rather than sunken. A video frame with a light border around it reads as a
   picture of a video; this reads as a player, which is what it is. */
.video-viewer {
    background: var(--ink-900);
    padding: 0;
}

.video-viewer video {
    display: block;
    max-height: 70vh;
    width: 100%;
}

/* Capped rather than as wide as the panel. A sixteen-by-nine tile across a seven
   hundred pixel column is a four hundred pixel player for a fifteen second clip, which
   reads as the point of the page rather than as one of the things on it. Two sit side
   by side when Etsy has both; the add tile shares the same row. */
.media-clips {
    align-items: start;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-3);
}

.media-clips .media-slot.is-clip {
    aspect-ratio: 16 / 9;
    flex: 1 1 12rem;
    margin-top: 0;
    max-width: 22rem;
    position: relative;
}

.media-clips .media-video-add {
    flex: 1 1 12rem;
    margin-top: 0;
    max-width: 22rem;
}

.media-slot.is-clip {
    margin-top: var(--space-3);
    max-width: 22rem;
    position: relative;
}

.media-slot.is-clip .media-clip {
    height: 100%;
    width: 100%;
}

/* Same pill language as the photograph's featured badge; sits on the clip so it is
   readable over the poster. The "make featured" control is hidden until hover, like
   remove — two of them showing at rest on a short clip is noise. */
.media-feature {
    background: rgba(10, 16, 36, .62);
    backdrop-filter: blur(4px);
    border: 0;
    border-radius: var(--radius-pill);
    color: var(--paper, #fff);
    cursor: pointer;
    font-size: 0.65rem;
    font-weight: 620;
    left: var(--space-2);
    letter-spacing: .04em;
    opacity: 0;
    padding: 0.05rem 0.5rem;
    position: absolute;
    text-transform: uppercase;
    top: var(--space-2);
    transition: opacity var(--fast), background var(--fast);
    z-index: 2;
}

.media-slot.is-clip:hover .media-feature,
.media-feature:focus-visible {
    opacity: 1;
}

.media-feature:hover {
    background: var(--listamos-primary);
}

.media-clip {
    aspect-ratio: 16 / 9;
    background: var(--ink-900);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    display: block;
    overflow: hidden;
    position: relative;
}

.media-clip:hover {
    border-color: var(--listamos-border-strong);
}

.media-play {
    align-items: center;
    background: rgb(255 255 255 / 0.92);
    border-radius: var(--radius-pill);
    color: var(--ink-900);
    display: flex;
    font-size: 1.125rem;
    height: 2rem;
    justify-content: center;
    left: 50%;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 2rem;
}

.media-clip-label {
    background: rgb(0 0 0 / 0.55);
    border-radius: var(--radius-sm);
    bottom: var(--space-2);
    color: #fff;
    font-size: var(--text-2xs);
    left: var(--space-2);
    padding: 0.125rem 0.375rem;
    position: absolute;
}

.media-note {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    margin: var(--space-3) 0 0;
}

.media-note:first-child {
    margin-top: 0;
}

.media-note:last-child {
    margin-bottom: 0;
}

.advice-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-2);
    font-size: var(--text-sm);
}

.advice-group + .advice-group {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--ink-100);
}

.advice-group-label {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-400);
}

.advice-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

/* Same meter as the catalogue column, sized for the side panel rather than a table
   cell. Sits above Improve copy so the number moves when the list below does.

   Not interactive here: on the grid the dotted score opens the checklist; on this
   screen the checklist is already the list under the button, so a help cursor would
   promise a popover that never comes. */
.advice-score {
    margin-bottom: var(--space-3);
}

.advice-score .lm-health-read {
    cursor: default;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
}

.advice-score .lm-health-read:hover {
    background: transparent;
}

.advice-score .lm-health-score {
    border-bottom: 0;
    font-size: var(--text-lg);
}

.advice-score .lm-health-read:hover .lm-health-score {
    border-bottom-color: transparent;
}

.advice-score .lm-health-bar {
    flex: 1 1 auto;
    max-width: 8rem;
}

.advice-media-note {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.4;
}

/* One stripe per tier, and polish is grey on purpose: a search habit worth three
   points should not read as an alarm next to a listing with no price. */
.advice-list li {
    border-left: 2px solid var(--amber-500);
    padding-left: var(--space-2);
}

.advice-list li.is-blocking {
    border-left-color: var(--red-600);
}

.advice-list li.is-weak {
    border-left-color: var(--amber-500);
}

.advice-list li.is-polish {
    border-left-color: var(--ink-300);
}

.advice-item {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    text-align: left;
    cursor: pointer;
    color: inherit;
}

.advice-item:hover strong,
.advice-item:focus-visible strong {
    color: var(--listamos-primary);
}

.advice-item:focus-visible {
    outline: 2px solid var(--listamos-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

.advice-list strong {
    display: block;
    font-weight: 580;
    font-size: var(--text-sm);
    line-height: 1.3;
    color: var(--ink-900);
}

.advice-detail {
    display: block;
    margin-top: 0.15rem;
    font-size: var(--text-xs);
    line-height: 1.35;
    color: var(--ink-500);
}

/* ——— Fields ————————————————————————————————————————————————————————————— */
/* Lets a field shrink to whatever column it is in. Without it a grid item keeps its
   min-content width, and for anything holding a `<select>` that is the longest option
   the shop has ever named. */
.field {
    min-width: 0;
}

.field-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
}

/* An icon, a heading, a countdown that must not break mid-phrase and a 42px ring is
   more than a phone's width. Allowed to wrap rather than squeezed: the heading keeps
   the first line and the two readouts take the second. */
@media (max-width: 34em) {
    .field-head {
        flex-wrap: wrap;
        row-gap: var(--space-2);
    }
}

/* Shopify descriptionHtml — the toolbar is ours, the body is what the shop stores. */
.html-words {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.html-words-bar {
    align-items: center;
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    flex-wrap: wrap;
    gap: 0.125rem;
    padding: 0.3rem 0.35rem;
}

.html-words-bar .lm-icon-btn {
    --h: 2rem;
    width: 2rem;
}

.html-words-view {
    min-height: 16rem;
    outline: none;
    padding: 0.75rem 0.9rem;
}

.html-words-view:focus {
    box-shadow: inset 0 0 0 2px var(--listamos-ring);
}

.html-words-view p {
    margin: 0 0 0.75em;
}

.html-words-view p:last-child {
    margin-bottom: 0;
}

.html-words-view ul,
.html-words-view ol {
    margin: 0 0 0.75em;
    padding-left: 1.25rem;
}

.html-words-view a {
    color: inherit;
    text-decoration: underline;
}

.field-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
}

.field-row.is-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Price, quantity and SKU are a row because they are short and related. On a phone a
   third of the width is about ninety pixels, which is narrower than the label above
   the box, so the relation stops being worth the squeeze and they stack. */
@media (max-width: 34em) {
    .field-row,
    .field-row.is-2 {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-3);
    }
}

/* Counted down rather than up: "18 left" is a number a seller can act on, where
   "122/140" asks them to do the subtraction. */
.counter {
    font-size: var(--text-xs);
    color: var(--ink-400);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Amber at exactly nothing left, because "0 left" is now also the answer to "why has
   typing stopped working". A separate state from red: red is a listing the marketplace
   will refuse, and a full field is one it will accept. */
.counter.is-full {
    color: var(--amber-600);
    font-weight: 580;
}

/* Only reachable on an imported listing that arrived over the limit — the field will
   not let anybody type it there. Still red, because that one the save refuses. */
.counter.is-over {
    color: var(--red-700);
    font-weight: 580;
}

/* Why a field is locked, under the field it is about. Amber rather than the note's usual
   grey: it is the answer to "why can I not type in this box", which a seller goes looking
   for, and grey is the colour of everything here they can safely skip. */
.field-note.is-locked {
    color: var(--amber-600);
}

.field-note.is-locked a {
    color: inherit;
    font-weight: 580;
    text-decoration: underline;
    text-underline-offset: 0.12em;
}

.field-note.is-locked a:hover {
    color: var(--amber-700);
}

.field-error {
    display: none;
    font-size: var(--text-xs);
    color: var(--red-700);
    margin-top: 0.3rem;
}

.field-error.is-shown {
    display: block;
}

/* ——— What a category asks for ——————————————————————————————————————————
   However many fields this category happens to want, so a grid that reflows rather
   than the fixed three-column rows above: a ring wants nine attributes and a digital
   download wants none, and neither should leave a row of gaps. */
.attribute-fields {
    /* Rows, and each row holds one kind of control — see `inRows`. This was three CSS
       columns, balanced by height, which chopped Etsy's order at whatever point made the
       columns equal: read across, the panel opened with Materials, Sustainability and
       Aspect ratio, three unrelated questions that happened to be at the top of a column.

       Columns had a real argument behind them, and it is why the rows are sorted rather
       than laid out in order: a grid row is as tall as the tallest field in it, so one
       dropdown beside forty tick boxes leaves a hand's width of nothing. A row of one
       kind cannot do that. */
    display: grid;
    gap: var(--space-4);

    /* On its own width and not the window's: this panel sits in a column whose width
       depends on the screen around it, and three fields across is a question about the
       panel. 44rem is where three stop holding the 220px they need. */
    container: attribute-fields / inline-size;
}

.attribute-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
    /* A short field in a tall row sits at the top of it rather than stretching to the
       height of the tick list beside it. */
    align-items: start;
}

@container attribute-fields (max-width: 44rem) {
    .attribute-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@container attribute-fields (max-width: 28rem) {
    .attribute-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

.attribute-fields:empty {
    display: none;
}

/* ——— What the listing asks its buyer ————————————————————————————————————
   The fields hang under the tick box that switches them on, so they are spaced rather
   than boxed: a second border around four controls already inside a panel reads as a
   second panel. */
.personalization-fields {
    display: grid;
    gap: var(--space-4);
    margin-top: var(--space-4);
}

/* The read-only spelling, for a listing asking more than these controls can show. */
.personalization-read {
    margin: var(--space-3) 0 0;
    padding-left: var(--space-5);
    color: var(--ink-500);
}

.personalization-read li {
    margin-bottom: var(--space-1);
}

.field-flag {
    font-size: var(--text-xs);
    font-weight: 580;
    color: var(--ink-400);
    margin-left: 0.4rem;
}

/* ——— The matrix ———————————————————————————————————————————————————————
   One line per combination, with the same three boxes the listing itself has above.
   A grid rather than a table because the cells stack on a phone and a table cannot:
   four columns of ninety pixels is three boxes nobody can type a price into. */
.variation-rows {
    display: grid;
    gap: var(--space-2);
}

.variation-head,
.variation-row {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(7rem, 0.8fr) minmax(5rem, 0.6fr) minmax(0, 1fr);
    gap: var(--space-3);
    align-items: center;
}

/* The column names, said once. Repeating "Price" beside every box would be the label
   said ten times down a page where the boxes are already in line under it. */
.variation-head {
    font-size: var(--text-xs);
    font-weight: 580;
    color: var(--ink-400);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.variation-what {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    min-width: 0;
}

.variation-label {
    font-weight: 560;
    overflow-wrap: anywhere;
}

/* A combination the shop has switched off. Dimmed rather than hidden: it is still one
   of the things this listing is made of, and its price is worth fixing before it comes
   back on sale. */
.variation-row.is-off .variation-label {
    color: var(--ink-400);
}

.variation-off {
    font-size: var(--text-xs);
    font-weight: 580;
    color: var(--ink-400);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    padding: 0 var(--space-2);
}

/* A value typed here and not yet published. The same amber the rest of the screen uses
   for "waiting to go", so a seller can see at a glance which numbers are theirs and
   which are the shop's. */
.variation-row .form-control.is-waiting {
    border-color: var(--amber-200);
    background: var(--amber-050);
}

@media (max-width: 40em) {
    .variation-head {
        display: none;
    }

    .variation-row {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-2);
        padding-bottom: var(--space-3);
        border-bottom: 1px solid var(--listamos-border);
    }

    /* The heading row is gone, so each box says what it is. Out of the markup rather than
       another string: it is the same word the column above it used. The label hangs on
       the wrapper and not on the input, because an input is a replaced element and has no
       pseudo-elements to put it in. */
    .variation-field::before {
        content: attr(data-label);
        display: block;
        font-size: var(--text-xs);
        color: var(--ink-400);
        margin-bottom: var(--space-1);
    }
}

/* Said before the thing it warns about, and it has to look like a warning: moving a
   listing between categories clears its attributes on the marketplace, and there is
   no undo for that anywhere in this product or in Etsy's own. */
.notice-inline {
    font-size: var(--text-sm);
    color: var(--amber-700);
    background: var(--amber-050);
    border: 1px solid var(--amber-200);
    border-radius: var(--radius-md);
    padding: var(--space-3);
}

/* ——— Tags and materials ———————————————————————————————————————————————
   A chip each rather than a comma-separated line. Bulk work is mostly tag work, and
   in a single text field a duplicate or a trailing space stays invisible until Etsy
   refuses the whole listing for it. */
.chip-input {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-height: 42px;
    padding: 0.35rem 0.5rem;
    background: var(--paper);
    border: var(--border);
    border-radius: var(--radius);
    cursor: text;
    transition: border-color var(--fast), box-shadow var(--fast);
}

.chip-input:focus-within {
    border-color: var(--brand-400);
    box-shadow: var(--ring);
}

.chip-input > .picker-list {
    top: calc(100% + 0.2rem);
}

.chip-list {
    display: contents;
}

.chip-entry {
    flex: 1;
    min-width: 12ch;
    border: 0;
    outline: 0;
    background: transparent;
    font-size: var(--text-sm);
    color: var(--ink-900);
    padding: 0.2rem;
}

/* At the channel's ceiling the entry closes and its placeholder says so. Muted rather
   than hidden: a box that vanishes at thirteen tags leaves the seller looking for where
   the field went, and the box is also the only place there is room to explain. The
   whole frame loses its text cursor, because clicking it no longer does anything. */
.chip-input.is-full {
    cursor: default;
}

.chip-entry:disabled {
    color: var(--listamos-text-tertiary);
}

.chip-entry:disabled::placeholder {
    color: var(--listamos-text-tertiary);
    opacity: 1;
}

.chip-remove {
    border: 0;
    background: transparent;
    color: var(--brand-700);
    line-height: 1;
    padding: 0 0 0 0.15rem;
    opacity: .6;
}

.chip-remove:hover {
    opacity: 1;
}

.chip.is-static {
    color: var(--ink-700);
    background: var(--ink-050);
    border-color: var(--ink-100);
}

/* ——— Both versions of a listing ————————————————————————————————————————
   The moment this product is judged on. Every editor a seller has been burned by took
   the newest thing it saw; this asks, per field, because the person who fixed a title
   here and a price on the shop wants to keep both. */
.conflict {
    border-color: var(--amber-500);
    box-shadow: 0 0 0 3px var(--amber-100);
    margin-bottom: var(--space-4);
}

.conflict-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}

.conflict-title {
    font-size: var(--text-base);
    font-weight: 620;
    color: var(--ink-900);
    margin: 0 0 0.2rem;
}

.conflict-title i {
    color: var(--amber-600);
}

.conflict-lead {
    font-size: var(--text-sm);
    color: var(--ink-500);
    max-width: 62ch;
}

.conflict-rows {
    display: grid;
    gap: var(--space-3);
}

.conflict-row {
    display: grid;
    grid-template-columns: 120px minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-3);
    align-items: stretch;
    padding-top: var(--space-3);
    border-top: var(--border);
}

@media (max-width: 860px) {
    .conflict-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

.conflict-field {
    font-size: var(--text-sm);
    font-weight: 580;
    color: var(--ink-700);
    padding-top: 0.35rem;
    text-transform: capitalize;
}

.conflict-side {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto 1fr;
    gap: 0.35rem var(--space-2);
    padding: var(--space-3);
    border: var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color var(--fast), background-color var(--fast);
}

.conflict-side:hover {
    background: var(--paper-sunk);
}

/* The chosen side is filled in, not outlined: on a screen where two blocks of text sit
   side by side, a border alone does not survive a glance. */
.conflict-side:has(input:checked) {
    border-color: var(--brand-400);
    background: var(--brand-050);
}

/* Back to white here, and only here. Chips are filled so they stand off a white field;
   this is the one place they sit on a tinted one, and the two fills are close enough
   that a list of tags would go flat on the side the seller has just chosen. */
.conflict-side:has(input:checked) .chip {
    background: var(--listamos-surface);
}

.conflict-side input {
    /* Pinned to the top of the cell. Centred, it drifts halfway down a tall list of
       tags and stops looking like it belongs to the heading beside it. */
    grid-row: 1 / span 2;
    align-self: start;
    margin-top: 0.15rem;
}

.conflict-label {
    grid-column: 2;
    font-size: var(--text-xs);
    font-weight: 620;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-400);
}

.conflict-value {
    grid-column: 2;
    font-size: var(--text-sm);
    color: var(--ink-900);
    /* Long descriptions are clipped rather than allowed to make the panel taller than
       the window: the decision is the point, not the reading. */
    max-height: 8.5rem;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.conflict-foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: var(--border);
}

/* ——— Where a listing stands, in words ————————————————————————————————— */
/* Tinted at five per cent with the border carrying the signal, rather than a solid
   block of colour. On the listing screen three of these sit in a row under the
   title, and at full strength they were louder than the title. */
.pill.is-ok      { color: var(--green-700); background: var(--green-050); border-color: rgba(15, 138, 98, .24); }
.pill.is-info    { color: var(--brand-700); background: var(--brand-050); border-color: var(--brand-100); }
.pill.is-warning { color: var(--amber-700); background: var(--amber-050); border-color: rgba(217, 139, 31, .28); }
.pill.is-danger  { color: var(--red-700);   background: var(--red-050);   border-color: rgba(201, 52, 46, .24); }

.detail-sub .pill {
    padding: 0.1rem 0.6rem;
    font-size: var(--text-xs);
    box-shadow: none;
}

/* ——— The catalogue that is ours ————————————————————————————————————————
   Cards rather than rows. A grid of dense lines is right for four hundred mirrored
   listings and wrong for the six a person is making: here the photograph is the work,
   and the card is mostly photograph. */
.page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-5);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

.page-lead {
    max-width: 62ch;
    margin-top: var(--space-2);
    margin-bottom: 0;
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    line-height: 1.5;
}












/* The left column of the create screen holds several panels — photographs, words,
   Etsy's own questions — because they are answered at different times. */
.detail-column {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    min-width: 0;
}

/* ——— Photographs ———————————————————————————————————————————————————————
   Order is data, not presentation: the first one is what a marketplace shows in
   search, on the shop page and in its app. Dragging says that better than a menu
   item called "make this the cover". */
.shots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

.shots:empty {
    display: none;
}

/* Digital download files — a short list of names, not a photo grid. */
.file-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.file-list:empty {
    display: none;
}

.file-row {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    min-width: 0;
    padding: var(--space-2) var(--space-3);
    border: var(--border);
    border-radius: var(--radius);
    background: var(--listamos-surface);
}

.file-row .file-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.shot {
    position: relative;
    margin: 0;
    aspect-ratio: 1;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--ink-050);
    cursor: grab;
}

.shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
}

.shot.is-dragging {
    opacity: .4;
    outline: 2px dashed var(--brand-400);
}

.shot-cover {
    position: absolute;
    left: var(--space-2);
    top: var(--space-2);
    font-size: 0.65rem;
    font-weight: 620;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--paper);
    background: rgba(10, 16, 36, .62);
    border-radius: var(--radius-pill);
    padding: 0.05rem 0.5rem;
    backdrop-filter: blur(4px);
}

.shot-remove {
    position: absolute;
    right: var(--space-2);
    top: var(--space-2);
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-pill);
    font-size: 0.7rem;
    color: var(--ink-800);
    background: rgba(255, 255, 255, .88);
    box-shadow: var(--shadow-sm);
    /* Visible on touch, where there is no hover to reveal it. Hiding it until hover
       would make it unreachable on the device the photographs came from. */
    opacity: .85;
}

.shot-remove:hover {
    opacity: 1;
    color: var(--red-700);
}

.dropzone {
    display: grid;
    place-items: center;
    gap: 0.2rem;
    padding: var(--space-5);
    text-align: center;
    border: 1.5px dashed var(--ink-200);
    border-radius: var(--radius);
    background: var(--paper-sunk);
    cursor: pointer;
    transition: border-color var(--fast), background-color var(--fast);
}

.dropzone:hover,
.dropzone.is-over {
    border-color: var(--brand-400);
    background: var(--brand-050);
}

.dropzone.is-busy {
    opacity: .6;
    pointer-events: none;
}

.dropzone .bi {
    font-size: 1.4rem;
    color: var(--ink-300);
}

.dropzone-lead {
    font-size: var(--text-sm);
    font-weight: 560;
    color: var(--ink-800);
}

.dropzone-note {
    font-size: var(--text-xs);
    color: var(--ink-400);
}

/* ——— What is still missing ——————————————————————————————————————————————
   Not a percentage and not a progress bar. Each line is a thing to do, in the
   channel validator's own words, and the settled ones stay on the list — a checklist
   that hides what it has ticked is one nobody trusts. */
.checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-2);
    font-size: var(--text-sm);
}

.checklist li {
    display: grid;
    grid-template-columns: 1rem 1fr;
    gap: var(--space-2);
    color: var(--ink-800);
}

.checklist li .bi {
    color: var(--amber-600);
    font-size: 0.7rem;
    margin-top: 0.3rem;
}

.checklist li.is-done {
    color: var(--ink-400);
}

.checklist li.is-done .bi {
    color: var(--green-600);
}

/*
 * Worth knowing, not in the way — Etsy wanting a shipping profile before the listing
 * goes on sale, on a draft it will take without one. Amber is what the list uses for
 * work outstanding, and this is not that, so it reads in the quieter ink instead.
 */
.checklist li.is-aside {
    color: var(--ink-600);
}

.checklist li.is-aside .bi {
    color: var(--ink-400);
}

.field-note {
    font-size: var(--text-xs);
    color: var(--ink-400);
    margin-top: 0.3rem;
}

.field-note.is-setup {
    color: var(--amber-700);
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* ——— Choosing from a marketplace's own lists ——————————————————————————————
   Etsy has thousands of categories and refuses anything filed under a branch, so the
   field is a search rather than a dropdown, and what comes back is the whole line the
   category sits on. */
.picker {
    position: relative;
}

.picker-list {
    position: absolute;
    z-index: 20;
    left: 0;
    right: 0;
    margin-top: 0.25rem;
    max-height: 280px;
    overflow-y: auto;
    background: var(--paper);
    border: var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 0.25rem;
}

.picker-option {
    display: block;
    width: 100%;
    text-align: left;
    border: 0;
    background: none;
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.6rem;
    font-size: var(--text-sm);
    color: var(--ink-800);
}

.picker-option:hover {
    background: var(--brand-050);
    color: var(--brand-700);
}

.picker-option.is-branch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.picker-option.is-branch .picker-path {
    min-width: 0;
}

.picker-go {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    margin: -0.25rem -0.25rem -0.25rem 0;
    border-radius: var(--radius-sm);
    color: var(--ink-400);
}

.picker-option.is-branch:hover .picker-go {
    color: var(--brand-700);
}

.picker-list.is-browse {
    max-height: 22rem;
}

/* Suggestions from the listing's own words (title / tags), under the search box.
   Not absolute like the typeahead list — they stay visible until a category is
   chosen, so a seller can click without opening the search. */
.picker-suggest {
    display: grid;
    gap: 0.35rem;
    margin-top: 0.5rem;
}

.picker-suggest-label {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-3xs);
    margin: 0;
}

.picker-suggest-status {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-xs);
    gap: 0.4rem;
}

.picker-suggest-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.picker-suggest-chip {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-sm);
    color: var(--listamos-text);
    cursor: pointer;
    font-size: var(--text-xs);
    line-height: 1.35;
    max-width: 100%;
    padding: 0.35rem 0.6rem;
    text-align: left;
}

.picker-suggest-chip:hover {
    border-color: var(--listamos-border-strong);
    color: var(--listamos-primary);
}

.picker-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.3rem 0.4rem 0.45rem;
    margin-bottom: 0.15rem;
    border-bottom: var(--border);
}

.picker-back,
.picker-use {
    border: 0;
    background: none;
    padding: 0.2rem 0.35rem;
    font-size: var(--text-sm);
    color: var(--brand-700);
}

.picker-back {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    min-width: 0;
    color: var(--ink-700);
}

.picker-use {
    flex: none;
    font-weight: 600;
}

/* Shopify Price / Inventory / Shipping — three Admin cards in one form, so
   named controls stay on `shopifyForm` and the shop-form grid still spaces them. */
.shopify-facts {
    display: contents;
}

.shop-switch {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    user-select: none;
}

/* Own control, not `.form-check-input`. That class draws a tick in `::after`,
   and the switch knob is the same pseudo — so a shop toggle inherited a
   checkmark sitting on a square box. */
.shop-toggle {
    appearance: none;
    -webkit-appearance: none;
    background: var(--ink-300);
    background-image: none;
    border: 0;
    border-radius: var(--radius-pill);
    box-shadow: inset 0 0 0 1px rgba(15, 19, 32, .12);
    cursor: pointer;
    flex: none;
    height: 1.25rem;
    margin: 0;
    position: relative;
    transition: background-color var(--fast), box-shadow var(--fast);
    width: 2.125rem;
}

.shop-toggle::after {
    background: #fff;
    border-radius: 50%;
    box-shadow: var(--shadow-xs);
    content: '';
    height: 1rem;
    left: 0.125rem;
    position: absolute;
    top: 0.125rem;
    transition: transform var(--medium);
    width: 1rem;
}

.shop-toggle:checked {
    background: var(--listamos-primary);
    box-shadow: none;
}

.shop-toggle:checked::after {
    transform: translateX(0.875rem);
}

.shop-toggle:focus-visible {
    box-shadow: var(--listamos-ring);
    outline: none;
}

.shop-weight {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 5.5rem;
    gap: var(--space-2);
}

/* Barcode, stock policy, origin, HS — short values; do not stretch the panel. */
[data-role="shopify-inventory"] #field-barcode {
    max-width: 18rem;
}

[data-role="shopify-inventory"] #field-inventory-policy {
    max-width: 16rem;
}

[data-role="shopify-shipping"] #field-origin {
    max-width: 22rem;
}

[data-role="shopify-shipping"] #field-hs-code {
    max-width: 11rem;
}

.shopify-pack-row {
    grid-template-columns: minmax(0, 1fr) minmax(11rem, 15rem);
}

.package-field {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    color: var(--listamos-text);
    display: flex;
    font-size: var(--text-sm);
    gap: 0.55rem;
    min-height: 2.5rem;
    padding: 0 0.85rem;
}

.package-field .bi {
    color: var(--listamos-text-secondary);
    flex: none;
}

@media (max-width: 34em) {
    .shopify-pack-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

.shopify-shipping-body {
    display: grid;
    gap: var(--space-4);
}

/* Shopify Variants — option cards, then the combination table. */
.shopify-options {
    display: grid;
    gap: var(--space-3);
    margin-top: var(--space-3);
}

.shopify-option {
    display: grid;
    gap: var(--space-3);
    padding: 0.9rem 1rem;
    background: var(--listamos-surface-sunken);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
}

.shopify-option-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
}

.shopify-option-head .field {
    flex: 1;
    min-width: 0;
}

.shopify-option-drop {
    flex: none;
    margin-top: 1.55rem;
}

.shopify-add-option {
    justify-self: start;
    margin-top: var(--space-3);
}

/* Shopify Search engine listing — its own card, like Admin: a SERP preview,
   then the two counted fields, then the handle with the storefront URL under it. */
.search-listing {
    display: grid;
    gap: var(--space-4);
}

.serp {
    display: grid;
    gap: 0.2rem;
    padding: 1rem 1.1rem;
    background: var(--paper);
    border: var(--border);
    border-radius: var(--radius);
}

.serp-shop {
    font-size: var(--text-sm);
    color: var(--ink-800);
}

.serp-url {
    font-size: var(--text-xs);
    color: var(--ink-400);
    overflow-wrap: anywhere;
}

.serp-title {
    font-size: 1.15rem;
    font-weight: 400;
    line-height: 1.35;
    color: #1a0dab;
}

.serp-snippet,
.serp-price {
    font-size: var(--text-sm);
    color: var(--ink-700);
}

.serp-price {
    color: var(--ink-500);
}

.search-listing .counter {
    margin-top: 0.3rem;
}

.handle-field {
    display: flex;
    align-items: stretch;
    min-width: 0;
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    background: var(--listamos-surface);
    overflow: hidden;
}

.handle-field:focus-within {
    box-shadow: 0 0 0 2px var(--listamos-ring);
}

.handle-prefix {
    display: flex;
    align-items: center;
    flex: none;
    padding: 0 0.15rem 0 0.85rem;
    font-size: var(--text-sm);
    color: var(--ink-400);
    font-variant-numeric: tabular-nums;
}

.handle-field .lm-field,
.handle-field .lm-field:focus,
.handle-field .lm-field:hover:not(:focus) {
    border: 0;
    box-shadow: none;
    background: transparent;
    border-radius: 0;
    min-width: 0;
}

.picker-note {
    padding: 0.5rem 0.6rem;
    font-size: var(--text-sm);
    color: var(--ink-400);
}

/* What was chosen, as words rather than as the number Etsy actually wants. */
.chosen {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: 0.45rem 0.45rem 0.45rem 0.75rem;
    background: var(--brand-050);
    border: 1px solid var(--brand-100);
    border-radius: var(--radius);
    font-size: var(--text-sm);
    color: var(--brand-700);
}

/* ——— Sending to a shop ————————————————————————————————————————————————— */
.send-state:not(:empty) {
    margin-top: var(--space-3);
}

.send-progress,
.send-done,
.send-error {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-size: var(--text-sm);
    margin-bottom: var(--space-2);
}

.send-done {
    color: var(--green-700);
}

.send-error {
    color: var(--red-700);
}

/* ——— Words from a photograph ——————————————————————————————————————————————
   Nested in the words form on both create and listing: a quiet fork, same pattern
   as Studio under Photographs, so the seller sees title first, not a rewrite offer. */
.muse {
    display: grid;
    gap: var(--space-3);
    border-color: var(--brand-100);
    background: linear-gradient(180deg, var(--brand-050), var(--paper) 60%);
}

/* Nested in the words form: drop the card tint; hairline separates the offer
   from the fields below when open. */
.detail-form > .muse {
    gap: 0;
    margin: 0 0 var(--space-4);
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    box-shadow: none;
}

.muse-paths {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

.muse-paths > p {
    flex: 1;
    min-width: 0;
}

/* Same brand tint as Optimize, but a real control on the right of the words section —
   quiet was invisible next to the form fields. */
.muse-open {
    flex-shrink: 0;
    font-weight: var(--weight-semibold);
    gap: 0.35rem;
}

.muse-open .bi {
    color: var(--listamos-primary);
}

@media (max-width: 540px) {
    .muse-paths {
        align-items: stretch;
        flex-direction: column;
    }

    .muse-open {
        align-self: flex-end;
    }
}

.muse-make {
    display: grid;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4) var(--space-4);
    border: 1px solid var(--brand-100);
    border-radius: var(--radius);
    background: linear-gradient(165deg, var(--brand-050) 0%, var(--listamos-primary-soft) 40%, var(--paper) 100%);
}

.muse-make-head {
    align-items: start;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

.muse-make-titles {
    display: grid;
    gap: 0.2rem;
    min-width: 0;
}

.muse-half {
    align-items: center;
    display: inline-flex;
    gap: 0.35rem;
    margin: 0;
    color: var(--ink-800);
    font-size: var(--text-sm);
    font-weight: 600;
}

.muse-half .bi,
.muse-actions .btn-primary .bi {
    font-size: 0.95em;
}

.muse-actions .btn-primary {
    align-items: center;
    display: inline-flex;
    gap: 0.35rem;
}

.muse-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.muse-actions .form-select {
    width: auto;
    min-width: 8.5rem;
}

/* The one press in the shop-words panel, and its way back. Only ever one of the two is
   on screen, so this is a row of one — it exists to put space above it and to stop the
   button stretching to the panel's width. */
.words-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-3);
}

.words-actions + .detail-form {
    margin-top: var(--space-4);
}

.muse-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

/* Label, suggestion, and the button that takes it. The button keeps its column so
   four rows of different heights still read as a list. */
.muse-row {
    display: grid;
    grid-template-columns: 96px 1fr auto;
    align-items: start;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-top: var(--border);
}

.muse-label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-400);
    padding-top: 0.15rem;
}

.muse-body {
    font-size: var(--text-sm);
    color: var(--ink-800);
}

/* Tags and materials come back as chips, which want to wrap; the description wants
   paragraphs. Both live in this cell, so the layout is chosen per content. */
.muse-body:has(.chip) {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.muse-body {
    display: block;
}

.muse-para {
    margin: 0 0 var(--space-2);
}

.muse-para:last-child {
    margin-bottom: 0;
}

.chip.is-static {
    padding-right: 0.55rem;
}

@media (max-width: 700px) {
    .muse-row {
        grid-template-columns: 1fr auto;
    }

    .muse-label {
        grid-column: 1 / -1;
    }
}

/* The video panel ---------------------------------------------------------- */

/* Quieter than the suggestion panel above it on purpose: one costs money and is the
   reason somebody is here, the other is a free extra made out of what is already
   uploaded. Same grid, no gradient. */
.reel {
    display: grid;
    gap: var(--space-3);
}

/* What was made, under a hairline in the video panel: its progress, its preview and
   the press that adds it. The choosing is in the slideshow and AI windows. */
.reel-make {
    display: grid;
    gap: var(--space-4);
    padding-top: var(--space-4);
    border-top: var(--border);
}

.studio-make {
    display: grid;
    gap: var(--space-3);
    padding-top: var(--space-3);
    border-top: var(--border);
}

.studio-make-head {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

.studio-half {
    margin: 0;
    color: var(--ink-800);
    font-size: var(--text-sm);
    font-weight: 600;
}

.reel-make-head {
    align-items: start;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

/* Which half of the panel this is. The same type as the line inside the drop target
   below it, because the two are the same kind of thing — an answer to "where does the
   video come from" — and a heavier one would pick a winner for the seller. */
.reel-half {
    color: var(--ink-800);
    font-size: var(--text-sm);
    font-weight: 560;
    margin: 0;
}

.reel-sources {
    display: grid;
    gap: var(--space-1);
    min-width: 0;
}

.reel-sources .form-label {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink-500);
}

.reel-source-row {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-1);
    overflow-x: auto;
    padding: 2px 0;
    scrollbar-width: thin;
}

.reel-source-thumb {
    position: relative;
    flex: 0 0 2.75rem;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border-radius: var(--radius-sm);
    border: var(--border);
    overflow: hidden;
    background: var(--paper);
    cursor: pointer;
    transition: border-color var(--fast), opacity var(--fast), box-shadow var(--fast);
}

.reel-source-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.reel-source-thumb:hover {
    border-color: var(--listamos-border-strong);
}

.reel-source-thumb.is-chosen {
    border-color: var(--listamos-primary);
    box-shadow: inset 0 0 0 1px var(--listamos-primary);
}

.reel-source-check {
    position: absolute;
    right: 2px;
    bottom: 2px;
    display: grid;
    place-items: center;
    width: 1rem;
    height: 1rem;
    border-radius: 999px;
    background: var(--listamos-primary);
    color: #fff;
    font-size: 0.7rem;
    line-height: 1;
    pointer-events: none;
}

.reel-source-thumb:not(.is-chosen) {
    opacity: 0.45;
}

.reel-source-thumb.is-full:not(.is-chosen) {
    opacity: 0.28;
    cursor: not-allowed;
}

.reel-source-thumb:focus-visible {
    outline: 2px solid var(--listamos-primary);
    outline-offset: 2px;
}

.reel-body:empty {
    display: none;
}

/* The other answer to the same question, under a hairline: a seller who already has a
   video has no use for the row of choices above it. Second and quieter, because most
   sellers have no video — that is the whole reason the panel exists. */
.reel-own {
    border-top: var(--border);
    padding-top: var(--space-3);
}

.reel-ready {
    display: grid;
    gap: var(--space-4);
}

.reel-stage {
    display: grid;
    gap: var(--space-2);
    justify-items: start;
    max-width: 20rem;
}

.reel-stage video {
    background: var(--ink-900);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: block;
    max-width: 100%;
    width: 100%;
}

.reel-meta {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-xs);
    gap: var(--space-2) var(--space-3);
    width: 100%;
}

.reel-meta [data-action="reel-remove"] {
    margin-left: auto;
}

.reel-adopt {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
}

.reel-adopt .lm-btn-primary {
    gap: 0.4rem;
}

.reel-adopt-note {
    color: var(--listamos-text-tertiary);
    flex: 1 1 12rem;
    font-size: var(--text-xs);
    margin: 0;
    max-width: 22rem;
}

/* Said in amber rather than red: the video is fine, it is simply older than the
   photographs, and nothing is broken. */
.reel-stale {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--amber-600);
}


/* The mark on a video a model moved. */
.reel-opened {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--brand-700);
    font-size: var(--text-sm);
}

/* A video a model made, waiting for the seller's yes before it goes to a shop. */
.reel-review {
    display: grid;
    gap: 0.4rem;
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--amber-200);
    border-radius: var(--radius-md);
    background: var(--amber-050);
}

.reel-review p {
    margin: 0;
}

.reel-review-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.reel-waiting {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--amber-600);
}

/* A better photograph -------------------------------------------------------- */

.studio {
    display: grid;
    gap: var(--space-3);
}

/* One look picker: product ideas and classics share a horizontal strip. */
.studio-looks {
    display: grid;
    gap: var(--space-2);
    min-width: 0;
}

.studio-looks-title {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink-500);
}

.scenes {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-2);
    overflow-x: auto;
    padding: 2px;
    scrollbar-width: thin;
}

.studio-scenes-reading {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 4.5rem;
    color: var(--ink-500);
    font-size: var(--text-sm);
}

.scene {
    position: relative;
    display: grid;
    gap: 0.2rem;
    align-content: start;
    flex: 0 0 132px;
    width: 132px;
    min-height: 3.25rem;
    text-align: left;
    cursor: pointer;
    padding: 0.55rem 0.65rem 0.65rem;
    border: var(--border);
    border-radius: var(--radius-sm);
    background: var(--paper);
    transition: border-color var(--fast), background var(--fast);
}

/* Classics are a single short label — centre them so the card does not look empty. */
.scene:not(.scene-idea) {
    align-content: center;
    justify-items: center;
    text-align: center;
}

.scene:hover {
    border-color: var(--listamos-border-strong);
}

.scene.is-chosen {
    border-color: var(--listamos-primary);
    background: var(--listamos-primary-soft);
    box-shadow: inset 0 0 0 1px var(--listamos-primary);
}

.scene-badge {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--listamos-primary-text);
    line-height: 1.2;
}

.scene-label {
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--ink-900);
    line-height: 1.25;
}

.studio-look-note {
    margin: 0;
    font-size: var(--text-xs);
    line-height: 1.35;
    color: var(--ink-500);
}

.studio-source {
    flex: 1 1 12rem;
    min-width: 0;
}

.studio-source .form-label {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--ink-500);
    margin-bottom: 0.35rem;
}

/* Same order as the gallery above — see the source, do not count "Photograph 3". */
.studio-source-row {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-2);
    overflow-x: auto;
    padding: 2px;
    scrollbar-width: thin;
}

.studio-source-thumb {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--listamos-surface-sunken);
    cursor: pointer;
    transition: border-color var(--fast), box-shadow var(--fast);
}

.studio-source-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
}

.studio-source-thumb:hover {
    border-color: var(--listamos-border-strong);
}

.studio-source-thumb.is-chosen {
    border-color: var(--listamos-primary);
    box-shadow: 0 0 0 1px var(--listamos-primary);
}

.studio-source-thumb:focus-visible {
    outline: 2px solid var(--listamos-primary);
    outline-offset: 2px;
}

.studio-result {
    display: grid;
    gap: var(--space-3);
    min-width: 0;
}

.studio-preview:empty,
.studio-preview.d-none {
    display: none;
}

/* Deliberately not the same grid as the photographs above. These are suggestions, and
   a seller must never have to work out which set is going to Etsy. */
.studio-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: var(--space-2);
}

.studio-grid:empty,
.studio-grid.d-none {
    display: none;
}

.made {
    position: relative;
    margin: 0;
    display: grid;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius);
    background: var(--paper);
}

.made img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius-sm);
    display: block;
}

.made figcaption {
    display: grid;
    gap: 0.35rem;
    justify-items: start;
}

.made-feature {
    grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--listamos-border-strong);
    background: var(--listamos-surface-sunken);
}

.made-feature .made-shot {
    display: block;
    min-width: 0;
}

.made-feature img {
    aspect-ratio: 1;
    max-height: 220px;
}

.made-feature figcaption {
    gap: var(--space-2);
}

.made-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.made-scene {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ink-500);
}

.made-used {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: var(--text-sm);
    color: var(--green-700);
}

.made-error {
    font-size: var(--text-xs);
    line-height: 1.35;
    color: var(--red-700);
}

.made.is-waiting,
.made.is-failed {
    place-content: center;
    justify-items: center;
    gap: var(--space-2);
    min-height: 120px;
    text-align: center;
}

.made-feature.is-waiting,
.made-feature.is-failed {
    grid-template-columns: 1fr;
    min-height: 160px;
}

.made-drop {
    position: absolute;
    right: 0.55rem;
    top: 0.55rem;
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-pill);
    font-size: 0.7rem;
    color: var(--ink-800);
    background: rgba(255, 255, 255, .88);
    box-shadow: var(--shadow-sm);
    opacity: .75;
}

.made-drop:hover {
    opacity: 1;
    color: var(--red-700);
}

@media (max-width: 640px) {
    .made-feature {
        grid-template-columns: 1fr;
    }

    .made-feature img {
        max-height: none;
    }

    .scene {
        flex-basis: 118px;
        width: 118px;
    }
}

/* —————————————————————————————————————————————— 11. Onboarding

   The first hour, and the one screen in the application written for somebody who has
   never sold anything. Everything here is the opposite of the catalogue two pages
   away: that screen is for a seller with four hundred listings, this one is for a
   person deciding whether to hand us the keys to their shop.

   So it gets no rail, no toolbar and no empty table with a pagination control under
   it — a new seller landing in a grid of nothing has to work out what the product is
   *and* what went wrong, in that order. One card, on the canvas, with one decision in
   it, and a stepper saying how much of this there is. That last part matters more
   than it looks: the fear at this moment is not "which button", it is "how long". */

.onboard {
    background: var(--listamos-canvas);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.onboard-top {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    justify-content: space-between;
    padding: var(--space-5) var(--space-7);
}

.onboard-top-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.onboard-body {
    align-items: center;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-6);
    justify-content: center;
    padding: var(--space-6) var(--space-5) var(--space-10);
}

/* The one object on the screen, so it is allowed a real shadow. Wide enough for a
   sentence at a comfortable measure and no wider — a card that grows with the window
   makes the eye travel to read one line. */
/* Centred, and not as a style preference. Every stage here holds exactly one
   decision, and a single button ranged left on a 34rem card leaves a wide empty
   quarter beside it that reads as a column of something missing. A symmetrical card
   has nowhere for the eye to go except the middle, which is where the one thing to
   press is. The catalogue is ranged left because it is a document; this is a
   moment. */
.onboard-card {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-2xl);
    box-shadow: var(--shadow-md);
    max-width: 34rem;
    padding: var(--space-8);
    text-align: center;
    width: 100%;
}

.onboard-body .onboard-steps {
    justify-content: center;
}

.onboard-card--connect {
    max-width: 28rem;
}

/* ——— The stepper ————————————————————————————————————————————————————————
   Three stages, because there are three, and they are the real ones: a connection, an
   import, a catalogue. Not a checklist somebody has to tick — the product knows which
   stage it is in, and this only reports it. */

.onboard-steps {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.onboard-step {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    gap: 0.4375rem;
}

.onboard-step::before {
    background: var(--ink-200);
    border-radius: 50%;
    content: '';
    height: 6px;
    width: 6px;
}

.onboard-step.is-done::before {
    background: var(--listamos-success);
}

.onboard-step.is-now {
    color: var(--listamos-text);
}

.onboard-step.is-now::before {
    animation: lm-pulse 1.6s var(--ease) infinite;
    background: var(--listamos-primary);
}

.onboard-step + .onboard-step {
    margin-left: var(--space-2);
}

.onboard-step + .onboard-step::after {
    content: none;
}

/* ——— Inside the card ———————————————————————————————————————————————————— */

/* A word rather than a 64-pixel tinted tile with an icon in it. The tile was
   decoration standing in for hierarchy — this says the same thing in nine characters
   and leaves the eye for the sentence underneath. It had a rule trailing off to the
   right, which worked while the card was ranged left and became a line pointing at
   nothing once it was centred. */
.onboarding-kicker {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    gap: 0.5rem;
    justify-content: center;
    letter-spacing: 0.08em;
    margin-bottom: var(--space-4);
    text-transform: uppercase;
}

.onboarding-icon {
    display: none;
}

.onboarding-title {
    color: var(--listamos-text);
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.03em;
    line-height: 1.16;
    margin-bottom: var(--space-3);
}

.onboarding-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    line-height: 1.6;
    margin-bottom: var(--space-6);
    margin-inline: auto;
    max-width: 38ch;
}

/* One channel per row, full width, in the order they became real. The live one is the
   only filled button on the screen. */
.onboarding-channels {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.onboarding-channel-btn {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    box-shadow: var(--shadow-xs);
    color: var(--listamos-text);
    font-weight: var(--weight-medium);
    gap: var(--space-3);
    justify-content: flex-start;
    transition: border-color var(--medium), box-shadow var(--medium), transform var(--medium);
}

.onboarding-channel-btn.is-live:hover {
    border-color: var(--listamos-border-hover);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
}

.onboarding-channel-btn.is-live:active {
    transform: translateY(0);
}

.onboarding-channel-btn.is-live .bi-arrow-right {
    color: var(--listamos-primary);
    margin-left: auto;
}

.onboarding-channel-btn.is-soon {
    background: var(--listamos-surface-subtle);
    color: var(--listamos-text-secondary);
    cursor: not-allowed;
    opacity: 1;
}

.onboarding-channel-btn.is-soon .onboarding-soon {
    margin-left: auto;
}

.onboarding-channel-btn .lm-channel-logo--icon {
    display: block;
    flex: none;
    height: 1.5rem;
    object-fit: contain;
    width: 1.5rem;
}

.onboarding-assurance {
    border-top: 1px solid var(--listamos-border);
    margin-top: var(--space-6);
    padding-top: var(--space-5);
}

/* Not yet, said plainly. Mono capitals rather than a coloured pill: this is a fact
   about the roadmap, not a promotion. */
.onboarding-soon {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.06em;
    margin-left: auto;
    text-transform: uppercase;
}

.onboarding-note {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    font-size: var(--text-xs);
    gap: 0.4375rem;
    justify-content: center;
    margin: var(--space-5) 0 0;
}

.onboarding-note .bi {
    color: var(--listamos-success);
}

/* ——— The import ————————————————————————————————————————————————————————
   Counted, not spun. "84 of 187" is a fact a person can hold; a spinner is an
   apology. */

.onboarding-progress-copy {
    color: var(--listamos-text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    margin: 0 0 var(--space-3);
}

.onboarding-progress {
    background: var(--listamos-surface-sunken);
    height: 5px;
    width: 100%;
}

.onboarding-progress .progress-bar {
    background: var(--listamos-primary);
}

/* ——— What was found ————————————————————————————————————————————————————
   Two numbers set large in mono, rather than two sentences with digits in them. This
   is the moment the product proves it read the shop, and the counts are the proof. */

/* One panel divided by a hairline, not two tinted tiles floating apart. Two identical
   boxes claim the two numbers weigh the same, and they do not: the first is a receipt
   — we read your shop, here is what was in it — and the second is the reason the
   button below exists. So the second is the only one that carries the brand, and it
   carries it as a tint rather than as a louder number, because this screen is a
   welcome and not an alarm. When there is nothing to fix the tint is left off
   entirely: a shop in good order should not be coloured in as though it were not. */
.onboarding-figures {
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: var(--space-6) 0;
    overflow: hidden;
}

.onboarding-figure {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding: var(--space-5) var(--space-4);
}

.onboarding-figure + .onboarding-figure {
    border-left: 1px solid var(--listamos-border);
}

.onboarding-figure b {
    color: var(--listamos-text);
    font-family: var(--font-mono);
    font-size: var(--text-4xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    letter-spacing: -0.032em;
    line-height: 1;
}

.onboarding-figure span {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
}

.onboarding-figure--lead {
    background: var(--listamos-primary-soft);
    border-left-color: var(--listamos-primary-border);
}

.onboarding-figure--lead b {
    color: var(--listamos-primary);
}

.onboarding-figure--lead span {
    color: var(--listamos-primary-text);
}

@media (max-width: 47.9375em) {
    .onboard-top {
        padding: var(--space-4);
    }

    .onboard-card {
        padding: var(--space-6);
    }
}

/* ————————————————————————————————————————————— 12. Depth and motion

   The layer that decides whether this looks like a tool somebody pays for, and the
   one that was previously getting it wrong in the expensive direction: gradients on
   the buttons, a sheen crossing them under the cursor, cards lifting two pixels,
   a blue wash on every row the mouse passed over. Each of those is defensible on its
   own. Together, on a screen holding four hundred rows, they are noise — the page is
   never still, and nothing on it can be emphasised because everything already is.

   What replaced them:

   1. **Motion is orientation.** Something that was not there a moment ago arrives
      from where it came from, in under two tenths of a second. Nothing that was
      already on the page moves, ever.
   2. **Depth is meaning.** Three shadows exist and all three mean "in front of the
      page": a menu, a dialog, a message. Page content has none.
   3. **Colour points once.** The blue is the primary action, the current view, and
      the selected rows. Everything else is ink on paper. */

/* There is deliberately no page-entrance animation.

   An earlier version faded `main` in on every navigation. Two problems, one of them
   fatal. It cost a fifth of a second of waiting on every screen, forty times a day,
   forever. And an element with an opacity animation gets its own stacking context, so
   every dialog written inside `main` — which is all of them — was painted *underneath*
   Bootstrap's backdrop and became unclickable. A decoration that swallows clicks is
   not a decoration worth keeping. Things that were not on the screen a moment ago
   still animate; see below.

   `to { transform: none }` rather than `translateY(0)`, because the fill mode holds
   the last frame and a held `translateY(0)` is still a transform — which would make
   every animated element a containing block for anything fixed inside it. */
@keyframes rise {
    from { opacity: 0; transform: translateY(0.375rem); }
    to   { opacity: 1; transform: none; }
}

/* The same entrance for something whose `transform` is not ours to write.

   A dropdown is placed by Popper, which puts the distance from its button into an
   inline `transform` — and an animation beats an inline style, so the held last frame
   above was quietly cancelling it. Every menu in the product opened on top of its own
   button: the shop switcher, the row menus, the language picker.

   `translate` is a property of its own and composes with `transform` rather than
   replacing it, so the rise is ours and the placement stays Popper's. */
@keyframes rise-placed {
    from { opacity: 0; translate: 0 0.375rem; }
    to   { opacity: 1; translate: none; }
}

/* ——— Surfaces ——————————————————————————————————————————————————————————
   Held down by their hairline. A surface that is genuinely raised asks for it by
   name with `.lm-raised`. */
.panel,
.card,
.side-panel {
    box-shadow: none;
}

/* Only the ones that go somewhere answer the cursor, and they answer it by warming
   their border rather than by levitating. A card that rises when the mouse passes
   over it is a promise that clicking does something; a card that rises and does
   nothing is worse than a card that sits still. */
.start-card,
a.panel,
.panel-linked {
    transition: border-color var(--fast), background-color var(--fast);
}

.start-card:hover,
a.panel:hover,
.panel-linked:hover {
    border-color: var(--listamos-border-strong);
}

/* ——— Tiles ——————————————————————————————————————————————————————————————
   A small square with an icon in it, in front of a section's name, so a heading is
   found by the eye in a column of five panels where five identical grey capitals
   are read one after another. Flat, and tinted rather than filled: the previous
   version was five gradient chips of five different hues, which is the "random
   purple AI product" look arriving through the side door. */
/* The mark on a section heading. One neutral treatment for all of them, and this is
   the whole point: a page whose five sections each carry a differently coloured icon
   chip is a page that has spent its colour on decoration and has none left for the
   two things that actually differ — what needs attention and what is safe. Colour
   here belongs to status, so the variants below are for status, not for sections. */
.tile {
    align-items: center;
    background: var(--listamos-surface-subtle);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-xs);
    color: var(--listamos-text-secondary);
    display: flex;
    flex: none;
    font-size: 0.75rem;
    height: 24px;
    justify-content: center;
    width: 24px;
}

/* The four variants are kept as names so the markup does not have to be swept, but
   they resolve to the same mark. Every one of them was a section heading. */
.tile-mint,
.tile-amber,
.tile-violet,
.tile-ink { background: var(--listamos-surface-subtle); color: var(--listamos-text-secondary); }

/* The heading a tile lives in: the tile, the name, and whatever the panel says about
   itself on the right. */
.panel-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.panel-head .side-title,
.panel-head .panel-title {
    margin: 0;
}

/* A heading that has gained a tile stops being two things pushed apart: the name belongs
   beside its mark, and whatever the panel says about itself keeps the right edge. */
.field-head:has(> .tile) {
    align-items: center;
}

.field-head > .tile + * {
    margin-right: auto;
}

.panel-head .panel-aside {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--ink-400);
}

/* ——— A count as a ring ——————————————————————————————————————————————————
   Vela puts a letter grade in a ring on every row, and the grade is the part we refuse:
   F to A+ cannot be argued with and cannot be acted on. The ring itself is worth having
   for the thing a grade cannot say — how much of a limit is used, exactly — so ours holds
   a number over a total, drawn with one conic gradient and no SVG.

   `--ring-share` is a fraction between 0 and 1, set inline where the number is known. */
.ring {
    --ring-share: 0;
    --ring-color: var(--brand-500);
    width: 42px;
    height: 42px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: var(--radius-pill);
    background:
        conic-gradient(var(--ring-color) calc(var(--ring-share) * 360deg), var(--ink-100) 0),
        var(--paper);
    /* The hole in the middle, without a second element to draw it. */
    -webkit-mask: radial-gradient(closest-side, transparent 68%, #000 69%);
            mask: radial-gradient(closest-side, transparent 68%, #000 69%);
    transition: background var(--slow);
}

/* The number sits in a sibling, because the mask above would eat it. */
.ring-wrap {
    position: relative;
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    flex: none;
}

.ring-wrap .ring {
    position: absolute;
    inset: 0;
}

.ring-value {
    position: relative;
    font-size: var(--text-xs);
    font-weight: 660;
    font-variant-numeric: tabular-nums;
    color: var(--ink-700);
}

.ring-full { --ring-color: var(--green-600); }
.ring-over { --ring-color: var(--red-600); }

/* ——— Waiting ————————————————————————————————————————————————————————————
   Rows of shimmer in the shape of the answer, so nothing moves when the answer
   arrives. The widths vary because a stack of identical grey bars reads as a
   pattern rather than as content on its way. */
.skeleton-row + .skeleton-row { margin-top: var(--space-4); }
.skeleton-row:nth-child(2n) { width: 82%; }
.skeleton-row:nth-child(3n) { width: 64%; }

.listing-table td .skeleton-row:nth-child(2n),
.listing-table td .skeleton-row:nth-child(3n) {
    width: 100%;
}

/* ——— Things that arrive ——————————————————————————————————————————————————
   The only three animations left in the product, on the only three things that were
   not on the screen a moment ago. */
.modal.show .modal-dialog {
    animation: rise 0.2s var(--ease-spring) both;
}

.dropdown-menu.show {
    animation: rise-placed 0.12s var(--ease) both;
}

.toast,
.flash {
    animation: rise 0.2s var(--ease-spring) both;
}

/* ——— Everything above, off ————————————————————————————————————————————————
   Under `prefers-reduced-motion` the entrances are not shortened, they are removed:
   an animation somebody asked not to see is not improved by being quick. Hover and
   focus states stay, because they are information rather than decoration. */
@media (prefers-reduced-motion: reduce) {
    .lm-selection,
    .modal.show .modal-dialog,
    .dropdown-menu.show,
    .toast,
    .flash {
        animation: none;
    }

    .lm-skeleton,
    .skeleton-row,
    .rail-skeleton,
    .status-running::before {
        animation: none;
    }
}

/* ——————————————————————————————————— 12b. Destinations

   The seam between the thing and the marketplaces. Above it the form is about an object
   — photographs, words, a price — and below it every field belongs to whichever shop is
   asking, so the shops themselves have to be visible at the seam rather than implied by
   a heading that says "Etsy" because one was picked in a rail somewhere. */
/* A row per shop rather than a row of pills. The pills were right while the only thing
   a shop could be was ticked or not; each one carries a second control now — which
   template fills its questions in — and a chooser cannot live inside a label without
   every press on it also toggling the tick. */
.destination-picks {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2);
    margin-top: var(--space-3);
    /* Two shop rows: the list is filled after /channels returns, and without a
       reservation the actions jump up then down. */
    min-height: 6.75rem;
}

.wizard-pin {
    margin-top: var(--space-4);
}

.wizard-pin .destination-picks {
    min-height: 0;
    margin-top: var(--space-2);
}

.pin-link {
    display: grid;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.pin-link .destination-row {
    cursor: pointer;
}

.destination-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 12.5rem;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    transition: border-color var(--fast), background-color var(--fast);
}

.destination-row:hover {
    border-color: var(--listamos-border-hover);
}

.destination-row.is-on {
    border-color: var(--listamos-primary-border);
    background: var(--listamos-primary-soft);
}

.destination-row.is-shut {
    background: var(--ink-050, #f8f8f7);
}

.destination-row.is-shut:hover {
    border-color: var(--listamos-border);
}

.destination-note {
    color: var(--ink-600);
    font-size: var(--text-sm);
}

.destination-pick {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    cursor: pointer;
}

/* A shop it was begun for cannot stop being one, so the tick says so by not inviting a
   press rather than by moving and refusing. */
.destination-pick:has(input:disabled) {
    cursor: default;
    opacity: 0.75;
}

/* One width for every shop. Native selects size to their longest option, which
   made four "No template" boxes four different widths. The tick stays the main
   thing — the chooser is a column, not the whole row. */
.destination-fill {
    min-width: 0;
}

.destination-fill .form-select {
    width: 100%;
    background-color: var(--listamos-surface);
}

@media (max-width: 29.99em) {
    .destination-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

.destination-mark {
    flex: none;
    width: 18px;
    height: 18px;
    object-fit: contain;
}

/* The tabs sit against the panels they label rather than floating between them, so the
   column's own gap below is cancelled and a smaller one put back. Nothing is cancelled
   above: the gap there is the separation from the shop picker, which is a different
   question and should read as one. */
.destination-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: calc(var(--space-2) - var(--space-4));
}

/* The panels for whichever destination is open, and the reason this is a grid at all.
   They used to be plain children of a bare wrapper, which made the eight of them one
   item of the column above and left them touching each other with no gap — the column's
   spacing reaches its own children and stops there. Repeating the column's rule rather
   than borrowing its class, because this is a column in its own right: one of these
   panels is hidden whenever a seller is on a marketplace it does not belong to, and
   `display: none` costs a grid track and therefore costs its gap too, which is exactly
   the wanted behaviour and not true of margins. */
.destination-questions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    min-width: 0;
}

/* The shop panels live in one Blade component now. Without its own grid gap they meet
   edge to edge — the parent column's spacing only reaches direct children. */
.shop-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    min-width: 0;
}

.destination-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: none;
    color: var(--listamos-text-secondary);
    font: inherit;
    cursor: pointer;
    transition: background-color var(--fast), color var(--fast), border-color var(--fast);
}

.destination-tab:hover {
    background: var(--listamos-surface-sunken);
}

.destination-tab.is-on {
    border-color: var(--listamos-border);
    background: var(--listamos-surface);
    color: var(--listamos-text);
    font-weight: 500;
}

/* ——————————————————————————————————— 13. Folded panels, and the variation grid

   Etsy's own listing form is ten tabs and Vela's editor is ten more. Tabs solve a real
   problem — the form is enormous — and buy it with a worse one: everything that is not
   the open tab is invisible, and a seller cannot tell whether they filled it in without
   clicking through all ten. A folded panel keeps the whole listing in one scroll, says on
   its own summary line what is inside it, and costs one click to open. */
.panel-fold {
    padding: 0;
}

/* Bottom pad on the open panel itself, not on `:last-child`. The last child is often
   the empty `.field-error` (`display: none`), and a hidden last child does not hold
   margin — so the grid sat flush against the panel's bottom edge. */
.panel-fold[open] {
    padding-bottom: var(--space-4);
}

.panel-fold > summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-4);
    cursor: pointer;
    list-style: none;
    border-radius: var(--radius-lg);
    transition: background-color var(--fast);
}

/* The default triangle, replaced with a chevron that turns. */
.panel-fold > summary::-webkit-details-marker { display: none; }
.panel-fold > summary::marker { content: ''; }

.panel-fold > summary::after {
    content: '\f282';
    font-family: bootstrap-icons;
    margin-left: var(--space-2);
    color: var(--ink-300);
    font-size: 0.8rem;
    transition: transform var(--medium);
}

.panel-fold[open] > summary::after {
    transform: rotate(180deg);
}

.panel-fold > summary:hover {
    background: var(--paper-sunk);
}

.panel-fold > summary .panel-aside {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--ink-400);
    text-align: right;
}

.panel-fold > *:not(summary) {
    margin-left: var(--space-4);
    margin-right: var(--space-4);
}

.panel-fold[open] > summary {
    border-bottom: var(--border);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    margin-bottom: var(--space-4);
}

/* What is inside arrives rather than appearing, which is what makes the fold read as one
   panel opening instead of the page jumping. */
.panel-fold[open] > *:not(summary) {
    animation: rise 0.22s var(--ease) both;
}

/* ——— The axes ————————————————————————————————————————————————————————————
   A name and its options side by side, because that is one sentence: "Size: small,
   medium, large". Stacked on a phone, where two inputs in a row are two unusable ones. */
.vary-axes {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.vary-axis {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    gap: var(--space-3);
}

/* The margin is for the block contexts these also appear in — loose inside a fold, or
   inside a `.field`, where nothing above supplies spacing and without it the first
   checkbox sits against the control before it. */
.vary-flags,
.checks {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
    margin-top: var(--space-4);
}

/* Except where the form already spaces its children, which is most of them. A margin on
   a grid item is added to the gap rather than collapsed into it, so the same figure was
   being applied twice and a row of checkboxes sat twice as far from the field above it
   as every other pair of rows in the same form. */
.detail-form > .vary-flags,
.detail-form > .checks {
    margin-top: 0;
}

.checks {
    flex-direction: column;
    gap: var(--space-2);
}

.check {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--ink-700);
    cursor: pointer;
}

.check input {
    margin: 0;
    flex: none;
}

/* ——— A row that is on ————————————————————————————————————————————————————
   Only inside a list or a radio group, where the question is "which of these did I
   pick" and the answer was a fourteen-pixel tick somewhere down a scrolling column of
   sixty. The row itself carries it now.

   Brand-tinted here and neutral on the chips above, and the difference is not
   decoration: a chip is one of the seller's own words, and a ticked row is a switch
   this screen is holding on. Colour is for the second kind. */
/* The band bleeds sideways into the list's own padding and takes back vertically what
   it adds, so a highlighted row is wider than the text but no taller than it was: the
   list is capped at eleven rems and scrolled, and rows that grew when ticked would
   change how many the seller can see at once. The vertical give is under the half-rem
   gap, so two ticked neighbours stay two bands. */
.tick-list .check,
.radios .check {
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    margin: -0.15rem -0.4rem;
    padding: 0.15rem 0.4rem;
    transition: background var(--fast), border-color var(--fast), color var(--fast);
}

.tick-list .check:hover,
.radios .check:hover {
    background: var(--listamos-surface-sunken);
}

.tick-list .check:has(input:checked),
.radios .check:has(input:checked) {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary-soft-hover);
    color: var(--listamos-primary-text);
    font-weight: var(--weight-medium);
}

/* Greyed because the attribute is full, not because the value is unavailable. It has
   to stay legible: the seller reads these to decide which of the ones already ticked
   to give up. */
.tick-list .check.is-shut {
    color: var(--listamos-text-tertiary);
    cursor: default;
}

.tick-list .check.is-shut:hover {
    background: transparent;
}

/* "No answer" is the absence of a choice, not a choice. Tinting it the same as the
   real options would make an untouched attribute look like a decided one. */
.radios .check:has(input:checked) .subtle {
    color: inherit;
}

.radios .check:has(input[value=""]:checked) {
    background: var(--listamos-surface-sunken);
    border-color: var(--listamos-border);
    color: var(--listamos-text-secondary);
    font-weight: inherit;
}

/* A list of tick boxes fetched from the channel, rather than written into the page.
   Capped and scrolled because the length is the shop's business: most name no
   production partners and a few name several, and a field that grows down the page
   would push everything below it somewhere different in each shop. */
.tick-list {
    display: grid;
    gap: var(--space-2);
    max-height: 11rem;
    overflow-y: auto;
    border: var(--border);
    border-radius: var(--radius);
    padding: var(--space-3);
}

.tick-list:empty {
    display: none;
}

/* Two options are not a list to scroll, and a frame around them says otherwise: it
   reads as a window onto something longer and leaves the seller looking for the rest.
   Below the glance the boxes stand on the page like the radios beside them. */
.tick-list.is-few {
    border: 0;
    max-height: none;
    overflow: visible;
    padding: 0;
}

/* ——— Picking several out of many ————————————————————————————————————————
   A column of sixty tick boxes answers "what can I choose" and none of the other
   three questions a seller has. What did I choose — invisible once the list scrolls.
   Where is the one I want — scrolling. How do I take that one back off — scrolling
   again. So the chosen values are lifted out and shown as chips inside a box that
   filters the list as you type — one control that both holds the answer and finds the
   next one, and the same control a seller has already used on tags and materials.

   The boxes underneath are still what gets read. This is a way of seeing them. */
.pick {
    display: grid;
    gap: var(--space-2);
}

/* The chips live in the search box, so this holds nothing of its own: `display: contents`
   makes each chip a child of the frame around it and lets them wrap in among it. The
   frame itself is `chip-input`, the same one the tags and materials fields are made of —
   see "Tags and materials" above. */
.pick-chosen {
    display: contents;
}

.pick-empty {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-sm);
    margin: 0;
}

/* One answer out of a handful, drawn in full rather than folded into a dropdown.
   Framing is Framed or Unframed: a control you have to open to read two words hides
   how short the question was. */
/* Each answer as wide as its words, because the band behind a chosen one is the mark
   that it was chosen. Stretched to the panel, a two-word answer is a two-word answer
   under a foot of colour, and the kind of listing is asked in a form the width of the
   screen. */
.radios {
    display: grid;
    gap: var(--space-2);
    justify-items: start;
}

/* Two short answers side by side — renewal, and anything else Etsy draws as cards. */
.radios.radios-pair {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-items: stretch;
    width: 100%;
}

.radios.radios-pair .check {
    display: grid;
    gap: var(--space-1);
    margin: 0;
    padding: var(--space-3);
    width: 100%;
}

@media (max-width: 34em) {
    .radios.radios-pair {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ——— The grid ————————————————————————————————————————————————————————————
   One row per combination, and only the columns that vary. The value cells are the
   heading of their row, so they are the weight of one. */
.vary-grid-wrap {
    margin-top: var(--space-4);
    border: var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

.vary-grid {
    margin: 0;
}

.vary-grid td.vary-value {
    font-weight: 560;
    color: var(--ink-900);
}

/* An input group shares its width out evenly, so "from – to days" and "L × W × H in"
   leave each box too narrow to read its own placeholder. These are the two rows on this
   screen that put several boxes on one line. */
/* The opening line and the first control below it both bring their own space, and two
   spaces stacked read as a missing section. */
.panel-fold .detail-form > p:first-child {
    margin-bottom: 0;
}

/* A field that is waiting on the switch above it, still readable, plainly not in play. */
.is-off {
    opacity: .5;
}

.panel-fold .input-group {
    flex-wrap: nowrap;
}

.panel-fold .input-group > .form-control {
    min-width: 5rem;
}

.panel-fold .input-group > .form-select {
    min-width: 4.75rem;
}

/* Length × width × height and a unit is four controls on one line, and five rems each
   is wider than a phone. The floors come off and the row is allowed to wrap: three
   boxes stacked read worse than three in a row, but not as badly as a form that scrolls
   sideways. */
@media (max-width: 34em) {
    .panel-fold .input-group {
        flex-wrap: wrap;
    }

    .panel-fold .input-group > .form-control,
    .panel-fold .input-group > .form-select {
        min-width: 0;
    }
}

.vary-grid input.form-control {
    min-width: 5rem;
    max-width: 8rem;
}

@media (max-width: 640px) {
    .vary-axis {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ——— 14. Improving one field ——————————————————————————————————————————————
   The small model button. It sits in the heading of the field it belongs to, and it is
   deliberately quiet: this is a suggestion the seller may want, not the main action on
   the screen. The strip it opens under the field is the loud part, because that is what
   has to be read before Apply.
   ———————————————————————————————————————————————————————————————————————— */

/* ——— Ask a model for this one ————————————————————————————————————————————
   A tinted pill with a star on it, and all three of those are the fix for the same
   complaint. It began as a transparent word in tertiary grey with the icon set to
   `display: none` — which is to say it looked exactly like the character counter it
   sits next to, and the one control on the heading row that does something was the one
   that read as a label. Giving it an edge helped and was not enough: white on white is
   an outline, and an outline at this size is a hairline the eye files under "rule".

   Tinted rather than solid. There are nine of these across the two editing screens —
   one per field, plus the one in the photograph dialog — and nine blue buttons down a
   form is a form that has forgotten which action is the main one. That is Save, and
   Save is the only thing here wearing filled brand.

   The same class on all nine, including the one in the dialog, which used to be a
   `lm-btn-quiet`. It is the same offer about a different field, and a seller who has
   learned what the star means should not have to learn it twice. */
.btn-polish {
    align-items: center;
    background: var(--listamos-primary-soft);
    border: 1px solid var(--listamos-primary-soft-hover);
    border-radius: var(--radius-pill);
    color: var(--listamos-primary-text);
    display: inline-flex;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    gap: 0.25rem;
    letter-spacing: 0.01em;
    padding: 0.15rem 0.5rem 0.15rem 0.4rem;
    transition: background var(--fast), border-color var(--fast), color var(--fast);
}

/* The star carries the meaning; it is the one mark that says "a model wrote this"
   everywhere else in the product, and the word beside it is only there because an icon
   alone would be a guess. Slightly larger than the text so it does not read as a
   speck. */
.btn-polish > .bi {
    color: var(--listamos-primary);
    font-size: 0.8125rem;
    line-height: 1;
}

/* One step further along the same ramp, because the resting state has taken the tint
   that used to be the hover. */
.btn-polish:hover:not(:disabled) {
    background: var(--listamos-primary-soft-hover);
    border-color: var(--brand-200);
    color: var(--listamos-primary-text);
}

.btn-polish:disabled {
    opacity: .55;
}

/* The counter and the button share the right-hand end of the heading. */
.field-head > .counter + .btn-polish {
    margin-left: calc(var(--space-3) * -0.5);
}

.polish {
    margin-top: var(--space-2);
    padding: var(--space-3);
    border: var(--border);
    border-radius: var(--radius);
    background: var(--grad-paper);
    box-shadow: var(--shadow-sm), var(--lit);
    animation: rise .24s var(--ease-spring) both;
}

.polish-body {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    font-size: var(--text-sm);
    color: var(--ink-900);
}

.polish-body .muse-para {
    margin-bottom: var(--space-2);
}

/* Why it was changed, which is the part that teaches. */
.polish-why {
    margin: var(--space-2) 0 0;
    font-size: var(--text-xs);
    color: var(--ink-500);
}

.polish-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.form-control.is-polishing,
.chip-input.is-polishing {
    opacity: 0.55;
}

.polish-wait,
.polish-error {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: var(--text-sm);
    color: var(--ink-500);
}

.polish-error {
    color: var(--red-700);
}

/* ——————————————————————————————————————————————— 22. Fingers

   Everything above is drawn for a cursor, which is one pixel wide and lands where it
   is aimed. A fingertip is about nine millimetres across and lands near where it is
   aimed, so a control that is comfortable with a mouse can be a coin toss with a
   thumb — and the miss is not neutral, because the thing beside it usually does
   something too.

   Keyed on the pointer rather than on the width. A phone in landscape is 844 pixels
   wide and still a phone; a 1280 pixel window on a laptop still has a trackpad. Width
   answers "how much room", which is a different question.

   The rule here is to grow the target without growing the drawing. Where a control is
   already its own box the box grows; where the size is the design — a 17px tick box,
   a cross inside a tag — an invisible pad is thrown around it instead, so the layout
   is untouched and the miss stops happening.
   ———————————————————————————————————————————————————————————————————————— */

@media (hover: none) {
    /* The tick box that puts a listing into a bulk edit, and the one that answers an
       attribute. Drawn at seventeen pixels on purpose: it sits beside a photograph and
       a title, and a checkbox that competes with those is a checkbox shouting. The pad
       takes the hit area to forty and leaves the drawing alone. */
    .form-check-input::before {
        content: '';
        inset: -0.71875rem;
        position: absolute;
    }

    /* The cross on a tag. Thirteen pixels of glyph, and the whole chip is only twenty
       high, so the pad goes sideways more than down — a taller one would reach the
       chip on the row above. */
    .chip-remove {
        padding: 0.35rem 0.15rem 0.35rem 0.35rem;
        position: relative;
    }

    .chip-remove::before {
        content: '';
        inset: -0.25rem -0.35rem;
        position: absolute;
    }

    /* These three are their own box, so the box grows. */
    .shot-remove {
        height: 2.25rem;
        width: 2.25rem;
    }

    .btn-polish {
        padding: 0.35rem 0.7rem 0.35rem 0.6rem;
    }

    .lm-view {
        height: calc(var(--control) - 2px);
    }

    .lm-page-head--catalogue .lm-view {
        height: calc(var(--control) - 4px);
    }

    /* A row of icon buttons with nothing but air between them: on a phone the gap is
       the only thing standing between Duplicate and Delete. */
    .lm-row-actions {
        gap: var(--space-2);
    }

    /* The way into a listing, and one line of nineteen pixels to hit it with. Padded to
       thirty-three and pulled most of the way back, so the row grows by two pixels
       rather than fourteen.

       The lift matters as much as the padding. The line underneath is a later sibling,
       so in the strip the two now share it was winning the hit test and the padding
       bought nothing — it is the listing's id, text and not a target, and there is
       nothing there for the title to take. An overlay pseudo-element cannot do this
       job: the title clips its own overflow to keep the ellipsis, and that clips the
       overlay with it. */
    .listing-title {
        margin-block: -0.375rem;
        padding-block: 0.4375rem;
        position: relative;
        z-index: 1;
    }
}

/* ——————————————————————————————————— Crosspost wizard

   A short road for a product headed to several shops. Same cards and destination
   chrome as the editor; the stepper is the onboard one so progress reads the same
   way as first-run connect. */
.wizard-steps {
    margin: 0 0 var(--space-4);
}

/* An existing product resumes on whichever step it can support. Photographs is
   the markup default, so without this it paints the dropzone and then swaps it
   for Shops — the jump from a dashed chip on Products. Hold every panel until
   `show()` names the step, and put a spinner in their place: an empty shops
   panel while the product is still fetching reads as a blank page. */
.wizard.is-booting .wizard-panel:not(.wizard-boot) {
    display: none !important;
}

.wizard-boot {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: none;
    font-size: var(--text-sm);
    gap: var(--space-3);
    justify-content: center;
    min-height: 14rem;
    padding: var(--space-6) var(--space-4);
}

.wizard.is-booting .wizard-boot {
    display: flex;
}

.wizard-boot .spinner-border {
    border-width: 2px;
    color: var(--listamos-primary);
    flex: none;
    height: 1.5rem;
    width: 1.5rem;
}

.wizard-panel .wizard-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    justify-content: flex-end;
    margin-top: var(--space-4);
    padding-top: var(--space-3);
    border-top: var(--border);
}

.wizard-panel.is-busy .wizard-kind,
.wizard-panel.is-busy .wizard-files,
.wizard-panel.is-busy .destination-picks,
.wizard-panel.is-busy .wizard-pin {
    opacity: 0.65;
    pointer-events: none;
}

.wizard-shots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
    gap: var(--space-2);
    margin: var(--space-3) 0;
}

.wizard-shots:empty {
    display: none;
}

.wizard-shots .shot.is-video {
    position: relative;
}

.wizard-shots .shot-play {
    align-items: center;
    background: rgba(15, 23, 42, 0.55);
    border-radius: 999px;
    color: #fff;
    display: flex;
    font-size: 0.85rem;
    height: 1.5rem;
    justify-content: center;
    left: 50%;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 1.5rem;
}

.wizard-shots .shot-video-empty {
    background: var(--ink-100);
    display: block;
    inset: 0;
    position: absolute;
}

.wizard-sub {
    color: var(--ink-800);
    font-size: var(--text-sm);
    font-weight: 560;
    margin: 0 0 var(--space-2);
}

.wizard-words,
.wizard-shops {
    margin-top: var(--space-4);
}

.wizard-kind {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    margin: var(--space-4) 0 0;
}

.wizard-kind-card {
    align-items: start;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    display: grid;
    gap: var(--space-3);
    grid-template-columns: auto minmax(0, 1fr);
    margin: 0;
    min-width: 0;
    padding: var(--space-3);
    transition: border-color var(--fast), background-color var(--fast);
}

.wizard-kind-card:hover {
    border-color: var(--listamos-border-hover);
}

.wizard-kind-card:has(:checked) {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary-border);
}

.wizard-kind-mark {
    align-items: center;
    background: var(--ink-050, #f8f8f7);
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    display: flex;
    flex: none;
    font-size: 1.15rem;
    height: 2.25rem;
    justify-content: center;
    width: 2.25rem;
}

.wizard-kind-card:has(:checked) .wizard-kind-mark {
    background: var(--listamos-surface);
    color: var(--listamos-primary-text);
}

.wizard-kind-copy {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.wizard-kind-copy strong {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: 560;
    line-height: 1.3;
}

.wizard-kind-copy > span {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
    line-height: 1.4;
}

@media (max-width: 29.99em) {
    .wizard-kind {
        grid-template-columns: minmax(0, 1fr);
    }
}

.wizard-files {
    margin: var(--space-4) 0 0;
}

/* Review send refused: list sits above the buttons so the press is not a silent no-op,
   and the cards / fields that still block are painted. */
.wizard-block {
    margin: var(--space-4) 0 0;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--red-200);
    border-radius: var(--radius);
    background: var(--red-050, #fef2f2);
    color: var(--red-800, #991b1b);
    font-size: var(--text-sm);
}

.wizard-block-lead {
    align-items: center;
    display: flex;
    gap: 0.4rem;
    font-weight: 600;
    margin: 0;
}

.wizard-block ul {
    margin: var(--space-2) 0 0;
    padding-left: 1.25rem;
}

.wizard-block li + li {
    margin-top: 0.2rem;
}

.wizard-words.is-gap,
.elsewhere-shop.is-gap {
    border-radius: var(--radius);
    box-shadow: inset 0 0 0 1px var(--red-200);
    background: color-mix(in srgb, var(--red-050, #fef2f2) 55%, transparent);
}

.wizard-words.is-gap {
    margin-left: calc(-1 * var(--space-3));
    margin-right: calc(-1 * var(--space-3));
    padding: var(--space-3);
}

.elsewhere-shop.is-gap {
    margin: 0 calc(-1 * var(--space-2));
    padding: var(--space-3) var(--space-2);
}

.wizard .elsewhere-asks {
    margin-top: var(--space-2);
    padding: var(--space-3);
    border: var(--border);
    border-radius: var(--radius-md);
    background: var(--ink-025);
}

/* Seller Hub / board setup sits above the pickers it is about. Without the gap the
   amber banner ran into the eBay category label. */
.wizard .elsewhere-asks > .notice-inline {
    margin-bottom: var(--space-3);
}

/* Category on its own row; the thing's fields and the shop's pickers each take a
   row underneath. Without the wrap, a wide Finish card put eBay's shipping profile
   beside Brand instead of with payment, returns and the warehouse. */
.wizard .elsewhere-asks-fields {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
    margin-bottom: var(--space-3);
}

.wizard .elsewhere-asks-fields [data-gap-category],
.wizard .elsewhere-asks-fields .elsewhere-channel-fields,
.wizard .elsewhere-asks-fields .elsewhere-shop-fields {
    grid-column: 1 / -1;
}

/* Search, not a full-width essay. Half the card is enough to type "glass teapot"
   and read the suggestions under it. */
.wizard .elsewhere-asks-fields [data-gap-category] {
    max-width: 28rem;
}

.wizard .elsewhere-asks-fields .elsewhere-channel-fields,
.wizard .elsewhere-asks-fields .elsewhere-shop-fields {
    display: grid;
    gap: inherit;
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}

/* Condition alone must not stretch like a full row. Same four tracks as the
   policy pickers below, so one select sits a quarter wide. */
.wizard .elsewhere-asks-fields .elsewhere-channel-fields {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* Four shop answers on eBay fill the card. One Pinterest board must not:
   auto-fit with 1fr stretched a single Choose… across the row. Fixed tracks
   keep that picker a quarter of the width, same as postage next to payment. */
.wizard .elsewhere-asks-fields .elsewhere-shop-fields {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 48em) {
    .wizard .elsewhere-asks-fields .elsewhere-channel-fields,
    .wizard .elsewhere-asks-fields .elsewhere-shop-fields {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .wizard .elsewhere-asks-fields [data-gap-category] {
        max-width: none;
    }
}

@media (max-width: 34em) {
    .wizard .elsewhere-asks-fields .elsewhere-channel-fields,
    .wizard .elsewhere-asks-fields .elsewhere-shop-fields {
        grid-template-columns: minmax(0, 1fr);
    }
}

.wizard .elsewhere-asks-fields .field,
.wizard .elsewhere-asks-fields .elsewhere-field {
    margin: 0;
    min-width: 0;
}

.wizard .elsewhere-asks-fields .elsewhere-field + .elsewhere-field {
    margin-top: 0;
}

/* ——— Platform admin ————————————————————————————————————————————————————— */

.admin-body {
    background: var(--listamos-canvas);
    margin: 0;
    min-height: 100vh;
}

.admin-shell {
    display: flex;
    min-height: 100vh;
}

.admin-nav-toggle {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.admin-sidebar {
    align-self: flex-start;
    background: var(--listamos-shell, var(--paper));
    border-right: 1px solid var(--listamos-border);
    display: flex;
    flex-direction: column;
    flex: 0 0 15.5rem;
    height: 100vh;
    padding: var(--space-5) var(--space-4);
    position: sticky;
    top: 0;
    z-index: 40;
}

.admin-sidebar-brand {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    justify-content: space-between;
    margin-bottom: var(--space-6);
    padding: 0 var(--space-2);
}

.admin-brand,
.admin-topbar-brand {
    align-items: center;
    color: inherit;
    display: inline-flex;
    gap: 0.35rem;
    text-decoration: none;
}

.admin-brand:hover,
.admin-topbar-brand:hover {
    color: inherit;
    text-decoration: none;
}

.admin-mark {
    background: var(--ink-900);
    border-radius: var(--radius-sm);
    color: #fff;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.08em;
    margin-left: 0.15rem;
    padding: 0.15rem 0.4rem;
    text-transform: uppercase;
    vertical-align: middle;
}

.admin-sidebar-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.08em;
    margin: 0 0 var(--space-2);
    padding: 0 var(--space-3);
    text-transform: uppercase;
}

.admin-sidebar-list {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.admin-sidebar-link {
    align-items: center;
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.65rem;
    padding: 0.65rem 0.75rem;
    text-decoration: none;
    transition: background-color 120ms ease, color 120ms ease;
}

.admin-sidebar-link i {
    font-size: 1.05rem;
    line-height: 1;
    opacity: 0.85;
    width: 1.1rem;
}

.admin-sidebar-link:hover {
    background: var(--ink-050);
    color: var(--listamos-text);
    text-decoration: none;
}

.admin-sidebar-link.is-active {
    background: var(--brand-050);
    color: var(--brand-800);
}

.admin-sidebar-link.is-active i {
    opacity: 1;
}

.admin-sidebar-nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

.admin-sidebar-foot {
    border-top: 1px solid var(--listamos-border);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-top: auto;
    padding-top: var(--space-4);
}

.admin-sidebar-email {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    margin: 0;
    overflow: hidden;
    padding: 0 var(--space-2);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.admin-sidebar-back {
    align-items: center;
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.5rem;
    padding: 0.55rem 0.75rem;
    text-decoration: none;
}

.admin-sidebar-back:hover {
    background: var(--ink-050);
    color: var(--listamos-text);
    text-decoration: none;
}

.admin-content {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
}

.admin-topbar {
    align-items: center;
    background: var(--listamos-shell, var(--paper));
    border-bottom: 1px solid var(--listamos-border);
    display: none;
    gap: var(--space-3);
    padding: 0.75rem 1rem;
    position: sticky;
    top: 0;
    z-index: 30;
}

.admin-nav-open,
.admin-nav-close {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius-md);
    color: var(--listamos-text);
    cursor: pointer;
    display: inline-flex;
    font-size: 1.25rem;
    height: 2.25rem;
    justify-content: center;
    margin: 0;
    padding: 0;
    width: 2.25rem;
}

.admin-nav-open:hover,
.admin-nav-close:hover {
    background: var(--ink-050);
}

.admin-nav-close {
    display: none;
}

.admin-nav-scrim {
    background: rgba(15, 19, 32, 0.42);
    display: none;
    inset: 0;
    position: fixed;
    z-index: 35;
}

.admin-main {
    background: var(--listamos-canvas);
    flex: 1 1 auto;
    min-height: 0;
}

.admin-main-inner {
    margin: 0 auto;
    max-width: 72rem;
    padding: var(--space-7) var(--space-6);
}

@media (max-width: 62em) {
    .admin-sidebar {
        bottom: 0;
        box-shadow: var(--shadow-lg);
        left: 0;
        position: fixed;
        top: 0;
        transform: translateX(-105%);
        transition: transform 160ms ease, visibility 0s linear 160ms;
        visibility: hidden;
        width: min(18rem, 86vw);
    }

    .admin-nav-toggle:checked ~ .admin-shell .admin-sidebar {
        transform: translateX(0);
        transition: transform 160ms ease;
        visibility: visible;
    }

    .admin-nav-toggle:checked ~ .admin-shell .admin-nav-scrim {
        display: block;
    }

    .admin-nav-close {
        display: inline-flex;
    }

    .admin-topbar {
        display: flex;
    }

    .admin-main-inner {
        padding: var(--space-5) var(--space-4);
    }
}

.settings-plan-grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}

.settings-plan-card {
    background: var(--ink-025);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    padding: var(--space-4);
}

.settings-plan-card.is-current {
    background: var(--listamos-surface, #fff);
    border-color: var(--ink-900);
    box-shadow: 0 0 0 1px var(--ink-900) inset;
}

.settings-plan-head {
    align-items: center;
    display: flex;
    gap: var(--space-2);
    justify-content: space-between;
    margin-bottom: 0.25rem;
}

.settings-plan-name {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    margin: 0;
}

.settings-plan-badge {
    background: var(--ink-900);
    border-radius: 999px;
    color: #fff;
    font-size: var(--text-xs, 0.75rem);
    font-weight: var(--weight-semibold);
    padding: 0.125rem 0.5rem;
    white-space: nowrap;
}

.settings-plan-price {
    font-family: var(--font-mono);
    font-size: var(--text-xl);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    margin: 0 0 var(--space-2);
}

.settings-plan-note {
    margin: calc(var(--space-2) * -1) 0 var(--space-2);
}

.settings-subtitle {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    margin: 0 0 var(--space-1);
}

.settings-plan-points {
    display: grid;
    flex: 1;
    gap: 0.375rem;
    list-style: none;
    margin: 0 0 var(--space-3);
    padding: 0;
}

.settings-plan-points li {
    color: var(--ink-700);
    font-size: var(--text-sm);
    padding-left: 1.25rem;
    position: relative;
}

.settings-plan-points li::before {
    color: var(--ink-900);
    content: "✓";
    font-weight: var(--weight-semibold);
    left: 0;
    position: absolute;
}

.settings-interval {
    background: var(--ink-025);
    border: 1px solid var(--listamos-border);
    border-radius: 999px;
    display: inline-flex;
    gap: 0.25rem;
    padding: 0.25rem;
}

.settings-interval button {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: 999px;
    color: var(--ink-700);
    display: inline-flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium, 500);
    gap: 0.5rem;
    padding: 0.375rem 0.875rem;
}

.settings-interval button[aria-checked="true"] {
    background: var(--listamos-surface, #fff);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
    color: var(--ink-900);
}

.settings-interval-save {
    background: var(--green-050);
    border-radius: 999px;
    color: var(--green-700);
    font-size: var(--text-xs, 0.75rem);
    padding: 0.0625rem 0.5rem;
}

.settings-billing-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.settings-payment-element {
    min-height: 12rem;
    padding: 0.25rem 0;
}

.admin-identity {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    justify-content: flex-end;
}

.admin-facts {
    display: grid;
    gap: 1rem 1.5rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 0;
}

.admin-facts div {
    min-width: 0;
}

.admin-facts dt {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    margin-bottom: 0.2rem;
}

.admin-facts dd {
    font-size: var(--text-sm);
    margin: 0;
}

@media (max-width: 36em) {
    .admin-facts {
        grid-template-columns: 1fr;
    }
}

.admin-mono {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    overflow-wrap: anywhere;
}

.admin-grant {
    border-top: 1px solid var(--listamos-border);
    margin-top: 1.25rem;
    padding-top: 1rem;
}

.admin-grant summary {
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.admin-search {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    max-width: 36rem;
}

.admin-search .lm-field {
    flex: 1 1 12rem;
}

.admin-table-card {
    overflow: hidden;
    padding: 0;
}

.admin-table-card .table {
    margin: 0;
}

.admin-table-card thead th {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.06em;
    padding: 0.85rem 1rem;
    text-transform: uppercase;
}

.admin-table-card tbody td {
    padding: 0.85rem 1rem;
}

.admin-impersonation-banner {
    align-items: center;
    background: var(--ink-900);
    color: #fff;
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-sm);
    gap: var(--space-3);
    justify-content: center;
    padding: 0.65rem 1rem;
    position: sticky;
    top: 0;
    z-index: 50;
}

.admin-impersonation-banner a {
    color: #fff;
    font-weight: var(--weight-semibold);
    text-decoration: underline;
}

.admin-stat-grid {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.admin-stat {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--space-5);
}

.admin-stat-label {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.admin-stat-value {
    font-family: var(--font-mono);
    font-size: var(--text-3xl);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-medium);
    letter-spacing: -0.04em;
    line-height: 1;
}

.admin-ledger {
    margin: 0;
}

.admin-ledger > div {
    align-items: baseline;
    border-bottom: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    padding: var(--space-2) 0;
}

.admin-ledger > div:last-child {
    border-bottom: 0;
}

.admin-ledger dt {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-regular, 400);
}

.admin-ledger dd {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    margin: 0;
}

.admin-money-table td.numeric {
    white-space: nowrap;
}

.admin-margin-good { color: var(--listamos-success-text); }

.admin-margin-bad { color: var(--listamos-danger-text); }

.admin-month {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.admin-exception {
    background: var(--ink-025);
    border-radius: var(--radius-md);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.45;
    margin: 0;
    max-height: 28rem;
    overflow: auto;
    padding: var(--space-4);
    white-space: pre-wrap;
    word-break: break-word;
}

@media (max-width: 62em) {
    .admin-stat-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 40em) {
    .admin-stat-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ——— Platform admin: page furniture ——————————————————————————————————————
   One header, one row, one empty state for every operator page, so a screen reads
   the same way whether it lists accounts or failed jobs. */

.admin-skip {
    background: var(--listamos-surface);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    left: var(--space-3);
    padding: var(--space-2) var(--space-3);
    position: fixed;
    top: var(--space-3);
    transform: translateY(-200%);
    z-index: 60;
}

.admin-skip:focus {
    transform: none;
}

.admin-main:focus {
    outline: none;
}

.admin-sidebar-label + .admin-sidebar-list {
    margin-bottom: var(--space-5);
}

.admin-sidebar-count {
    background: var(--listamos-danger-soft);
    border-radius: var(--radius-pill);
    color: var(--listamos-danger-text);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    margin-left: auto;
    padding: 0.25rem 0.45rem;
}

.admin-sidebar-who {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0 var(--space-2);
}

.admin-sidebar-who .admin-sidebar-email {
    padding: 0;
}

.admin-sidebar-role {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}

.admin-lang .lang-picker,
.admin-lang .lang-toggle {
    width: 100%;
}

.admin-lang .lang-toggle {
    justify-content: flex-start;
}

.admin-lang .dropdown-menu {
    width: 100%;
}

.admin-flash {
    align-items: flex-start;
    border-radius: var(--radius-md);
    display: flex;
    font-size: var(--text-sm);
    gap: var(--space-3);
    line-height: 1.45;
    margin-bottom: var(--space-5);
    padding: var(--space-3) var(--space-4);
}

.admin-flash > .bi {
    flex: 0 0 auto;
    margin-top: 0.1rem;
}

.admin-flash ul {
    margin: var(--space-1) 0 0;
    padding-left: 1.1rem;
}

.admin-flash.is-success {
    background: var(--listamos-success-soft);
    color: var(--listamos-success-text);
}

.admin-flash.is-danger {
    background: var(--listamos-danger-soft);
    color: var(--listamos-danger-text);
}

.admin-flash-close {
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: inherit;
    cursor: pointer;
    margin: -0.15rem -0.35rem -0.15rem auto;
    opacity: 0.7;
    padding: 0.15rem 0.35rem;
}

.admin-flash-close:hover {
    opacity: 1;
}

.admin-page-head {
    margin-bottom: var(--space-6);
}

.admin-back {
    align-items: center;
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: 0.4rem;
    margin-bottom: var(--space-3);
}

.admin-back:hover {
    color: var(--listamos-text);
    text-decoration: none;
}

.admin-page-head-row {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    justify-content: space-between;
}

.admin-page-head-text {
    flex: 1 1 28rem;
    min-width: 0;
}

.admin-page-title {
    font-size: var(--text-3xl);
    font-weight: var(--weight-bold);
    letter-spacing: -0.03em;
    line-height: 1.15;
    margin: 0;
    overflow-wrap: anywhere;
}

.admin-page-meta {
    align-items: baseline;
    color: var(--listamos-text-secondary);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-sm);
    gap: 0.25rem 0.5rem;
    margin-top: var(--space-2);
}

.admin-page-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-md);
    line-height: 1.55;
    margin: var(--space-2) 0 0;
    max-width: 64ch;
}

.admin-page-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
}

.admin-section-title {
    color: var(--listamos-text);
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.015em;
    margin: 0 0 var(--space-3);
}

.admin-help {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.5;
}

.admin-subtitle {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    margin: 0 0 var(--space-1);
}

.admin-narrow {
    max-width: 45rem;
}

.admin-callout {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-sm);
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4);
}

.admin-callout.is-danger {
    background: var(--listamos-danger-soft);
    color: var(--listamos-danger-text);
}

/* Needs attention: the first thing on the overview, and the only coloured thing
   on it when something is wrong. */
.admin-attention-list {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.admin-attention-item {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-left-width: 3px;
    border-radius: var(--radius-md);
    color: var(--listamos-text);
    display: flex;
    font-size: var(--text-sm);
    gap: var(--space-3);
    height: 100%;
    padding: var(--space-3) var(--space-4);
    transition: border-color var(--fast), box-shadow var(--fast);
}

.admin-attention-item strong {
    font-size: var(--text-xl);
    font-weight: var(--weight-semibold);
    line-height: 1;
}

.admin-attention-item .bi {
    color: var(--listamos-text-tertiary);
    margin-left: auto;
}

.admin-attention-item:hover {
    box-shadow: var(--shadow-sm);
    color: var(--listamos-text);
    text-decoration: none;
}

.admin-attention-item.is-danger { border-left-color: var(--listamos-danger); }
.admin-attention-item.is-danger strong { color: var(--listamos-danger-text); }
.admin-attention-item.is-warning { border-left-color: var(--listamos-warning); }
.admin-attention-item.is-warning strong { color: var(--listamos-warning-text); }

.admin-attention-clear {
    align-items: center;
    background: var(--listamos-success-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-success-text);
    display: flex;
    font-size: var(--text-sm);
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
}

.admin-stat-note {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
}

.admin-card-head {
    align-items: baseline;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
}

.admin-card-head .lm-card-title {
    margin-bottom: var(--space-3);
}

.admin-card-link {
    font-size: var(--text-sm);
    white-space: nowrap;
}

/* A row in a card list: the thing on the left, its numbers on the right. */
.admin-row {
    align-items: flex-start;
    border-top: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    padding: var(--space-3) 0;
}

.lm-card-title + .admin-row,
.admin-card-head + .admin-row {
    border-top: 0;
    padding-top: 0;
}

.admin-row:last-child {
    padding-bottom: 0;
}

.admin-row.is-wrap {
    align-items: center;
    flex-wrap: wrap;
}

.admin-row-main {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.admin-row-title {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    overflow-wrap: anywhere;
}

a.admin-row-title {
    color: var(--listamos-primary);
}

.admin-row-sub {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.45;
}

.admin-row-end {
    align-items: flex-end;
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    gap: 0.15rem;
    max-width: 45%;
    text-align: right;
}

.admin-row-end .admin-mono {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

.admin-row-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.admin-clip {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    overflow-wrap: anywhere;
}

.admin-clip-2 {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow-wrap: anywhere;
}

.admin-exception-cell {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    max-width: 32rem;
    min-width: 14rem;
}

.admin-bad {
    color: var(--listamos-danger-text);
}

.admin-time {
    white-space: nowrap;
}

.admin-tag {
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-xs);
    color: var(--listamos-primary-text);
    display: inline-block;
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    line-height: 1.4;
    margin-left: 0.35rem;
    padding: 0.05rem 0.4rem;
    vertical-align: 0.1em;
}

.admin-tag.is-quiet {
    background: var(--listamos-surface-sunken);
    color: var(--listamos-text-secondary);
}

.admin-tag.is-danger {
    background: var(--listamos-danger-soft);
    color: var(--listamos-danger-text);
}

.admin-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: 0.3rem;
}

.admin-tags:empty {
    display: none;
}

.admin-tags .admin-tag {
    margin-left: 0;
}

.admin-empty {
    align-items: center;
    color: var(--listamos-text-tertiary);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5) var(--space-3);
    text-align: center;
}

.admin-empty .bi {
    font-size: 1.5rem;
    opacity: 0.7;
}

.admin-empty p {
    font-size: var(--text-sm);
    margin: 0;
}

.admin-empty.is-roomy {
    padding: var(--space-9) var(--space-4);
}

.admin-access {
    align-items: center;
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.85rem;
}

.admin-access.is-stacked {
    align-items: flex-start;
    flex-direction: column;
}

/* Lists */
.admin-toolbar {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: space-between;
}

.admin-toolbar .admin-search {
    flex: 1 1 22rem;
    justify-content: flex-end;
    max-width: 32rem;
}

.admin-search-field {
    flex: 1 1 12rem;
    position: relative;
}

.admin-search-field .bi {
    color: var(--listamos-text-tertiary);
    left: 0.75rem;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.admin-search-field .lm-field {
    padding-left: 2.15rem;
    width: 100%;
}

.admin-table-card tbody tr:last-child td {
    border-bottom: 0;
}

.admin-table-card tbody tr:hover td {
    background: var(--listamos-surface-subtle);
}

.admin-pager {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    margin-top: var(--space-4);
}

.admin-pager [aria-disabled="true"] {
    opacity: 0.45;
    pointer-events: none;
}

.admin-pager-where {
    color: var(--listamos-text-tertiary);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
}

/* Forms */
.lm-field.is-invalid,
.form-select.is-invalid {
    border-color: var(--listamos-danger);
}

.lm-field.is-invalid:focus,
.form-select.is-invalid:focus {
    box-shadow: 0 0 0 3px var(--listamos-danger-soft);
}

.admin-field-error {
    color: var(--listamos-danger-text);
    font-size: var(--text-xs);
    margin: 0.3rem 0 0;
}

.admin-fieldset {
    border: 0;
    margin: 0;
    min-width: 0;
    padding: 0;
}

.admin-fieldset legend {
    float: none;
    width: auto;
}

.admin-form-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.admin-mono-input {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
}

.admin-weights {
    display: flex;
    flex-direction: column;
}

.admin-weight {
    align-items: center;
    border-top: 1px solid var(--listamos-border);
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    padding: var(--space-3) 0;
}

.admin-weight:first-child {
    border-top: 0;
    padding-top: 0;
}

.admin-weight label {
    cursor: pointer;
    min-width: 0;
}

.admin-weight-input {
    flex: 0 0 6.5rem;
    width: 6.5rem;
}

.admin-month-label {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-sm);
}

.admin-month .form-select {
    min-width: 11rem;
}

.admin-facts.is-three {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 48em) {
    .admin-facts.is-three {
        grid-template-columns: 1fr;
    }
}

.admin-meter-line {
    align-items: baseline;
    display: flex;
    font-size: var(--text-sm);
    gap: var(--space-3);
    justify-content: space-between;
}

.admin-meter-bar {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-pill);
    height: 6px;
    margin-top: var(--space-2);
    overflow: hidden;
}

.admin-meter-bar span {
    background: var(--listamos-primary);
    border-radius: inherit;
    display: block;
    height: 100%;
}

.admin-meter-bar span.is-high {
    background: var(--listamos-warning);
}

.admin-danger {
    border-color: var(--red-200);
}

.admin-danger .lm-card-title {
    color: var(--listamos-danger-text);
}

/* A pressed submit button waits for the next page instead of sending twice. */
.lm-btn.is-busy {
    cursor: progress;
    opacity: 0.7;
    pointer-events: none;
}

.lm-btn.is-busy::after {
    animation: admin-spin 0.7s linear infinite;
    border: 2px solid currentColor;
    border-radius: 50%;
    border-right-color: transparent;
    content: '';
    display: inline-block;
    height: 0.8em;
    margin-left: 0.4rem;
    width: 0.8em;
}

@keyframes admin-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .lm-btn.is-busy::after {
        animation: none;
    }

    .admin-sidebar {
        transition: none;
    }
}

@media (max-width: 40em) {
    .admin-page-title {
        font-size: var(--text-2xl);
    }

    .admin-toolbar .admin-search {
        max-width: none;
    }

    .admin-row {
        flex-wrap: wrap;
    }

    .admin-row-end {
        align-items: flex-start;
        max-width: none;
        text-align: left;
    }

    .admin-pager-where {
        display: none;
    }
}

/* Admin forms fill their card; the 22rem measure is for the seller's settings. */
.admin-main .settings-form {
    max-width: none;
}

.admin-table-card td:first-child {
    min-width: 13rem;
}

.admin-tag,
.admin-row-sub .admin-mono {
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------------
   Copy to stores: each shop is a card to tick, with the marketplace's mark, so which
   shop is which is read at a glance rather than from a line of small grey words.
   --------------------------------------------------------------------------------- */
.lm-copy-for {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
    margin: var(--space-1) 0 0;
    overflow-wrap: anywhere;
}

.lm-copy-step {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    margin: 0 0 var(--space-2);
}

.lm-copy-shops {
    display: grid;
    gap: var(--space-2);
}

.lm-copy-shop {
    align-items: center;
    background: var(--listamos-surface);
    border: var(--border);
    border-radius: var(--radius-md);
    cursor: pointer;
    display: flex;
    gap: var(--space-3);
    margin: 0;
    padding: var(--space-3) var(--space-4);
    transition: border-color var(--fast), background-color var(--fast), box-shadow var(--fast);
}

.lm-copy-shop:hover {
    border-color: var(--listamos-border-hover);
}

.lm-copy-shop .form-check-input {
    flex: none;
    margin: 0;
}

.lm-copy-shop:has(.form-check-input:checked) {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary-border);
}

.lm-copy-shop:has(.form-check-input:focus-visible) {
    box-shadow: var(--listamos-ring);
}

.lm-copy-shop.is-disabled {
    background: var(--listamos-surface-subtle);
    cursor: not-allowed;
}

.lm-copy-shop.is-disabled:hover {
    border-color: var(--listamos-border);
}

.lm-copy-shop.is-disabled .lm-copy-shop-logo,
.lm-copy-shop.is-disabled .lm-copy-shop-name {
    opacity: 0.55;
}

.lm-copy-shop-logo {
    align-items: center;
    background: var(--listamos-surface);
    border: var(--border);
    border-radius: var(--radius-sm);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    flex: none;
    height: 2.25rem;
    justify-content: center;
    width: 2.25rem;
}

.lm-copy-shop-logo img {
    height: 1.25rem;
    object-fit: contain;
    width: 1.25rem;
}

.lm-copy-shop-text {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    line-height: 1.3;
    min-width: 0;
}

.lm-copy-shop-name {
    color: var(--listamos-text);
    font-size: var(--text-base);
    font-weight: var(--weight-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lm-copy-shop-channel {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
}

.lm-copy-shop-reason {
    align-items: baseline;
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-xs);
    gap: var(--space-1);
    margin-top: var(--space-1);
}

.lm-copy-shop-badge {
    border-color: var(--listamos-primary-border);
    flex: none;
    white-space: nowrap;
}

.lm-copy-shop:has(.form-check-input:checked) .lm-copy-shop-badge {
    background: var(--listamos-surface);
}

.lm-copy-target {
    align-items: center;
    display: flex;
    gap: var(--space-3);
    min-width: 0;
}

.lm-copy-review {
    border-top: var(--border);
    margin: var(--space-4) 0 0;
    padding-top: var(--space-4);
}

.lm-copy-hint {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
    margin-top: var(--space-1);
}

/* ---------------------------------------------------------------------------------
   Background work. A dock in the corner, present only while something is running,
   finished a moment ago, or waiting on the seller — see layouts/activity. Under the
   rail drawer and every dialog, because it is a status and never the thing in front.
   --------------------------------------------------------------------------------- */
.lm-activity {
    align-items: flex-end;
    bottom: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    position: fixed;
    right: var(--space-4);
    z-index: 40;
}

.lm-activity.d-none {
    display: none;
}

.lm-activity-toggle {
    align-items: center;
    background: var(--listamos-surface);
    border: var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-md);
    color: var(--listamos-text);
    display: inline-flex;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    gap: var(--space-2);
    min-height: var(--control);
    padding: 0 var(--space-4);
}

.lm-activity-toggle:focus-visible {
    box-shadow: var(--listamos-ring);
    outline: 0;
}

.lm-activity.is-running .lm-activity-toggle .bi {
    animation: lm-sync-spin 0.9s linear infinite;
    color: var(--listamos-primary);
}

.lm-activity.is-waiting .lm-activity-toggle {
    background: var(--listamos-warning-soft);
    border-color: var(--amber-200);
    color: var(--listamos-warning-text);
}

.lm-activity-panel {
    background: var(--listamos-surface);
    border: var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    max-height: min(26rem, 70dvh);
    overflow: auto;
    width: min(22rem, calc(100vw - 2rem));
}

.lm-activity-panel.d-none {
    display: none;
}

.lm-activity-head {
    align-items: baseline;
    border-bottom: var(--border);
    display: flex;
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
}

.lm-activity-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.lm-activity-item {
    align-items: flex-start;
    border-bottom: var(--border);
    display: flex;
    gap: var(--space-2);
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
}

.lm-activity-item:last-child {
    border-bottom: 0;
}

.lm-activity-item.is-warning {
    background: var(--listamos-warning-soft);
}

.lm-activity-text {
    font-size: var(--text-sm);
    min-width: 0;
    overflow-wrap: anywhere;
}

.lm-activity-bar {
    height: 4px;
    margin-top: var(--space-2);
}

/* Toasts rise above the dock rather than landing on it. */
body:has(.lm-activity:not(.d-none)) .lm-toast-host {
    bottom: calc(var(--control) + var(--space-4)) !important;
}

@media (prefers-reduced-motion: reduce) {
    .lm-activity.is-running .lm-activity-toggle .bi {
        animation: none;
    }
}


/* ——— Adding media: the tiles, the chooser, the AI video ——————————————————————
   The add tile names its ways in a second line, so a seller sees that AI is one of
   them before pressing anything. Pressing it asks "how?" in a short window of cards,
   each with its price; the AI video then has a window of its own, with the seller's
   photograph moving the way the camera will. */

.media-ways-line {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-2xs);
    line-height: 1.25;
}

.media-ways-line .bi-stars,
.media-ways-icons .bi-stars {
    color: var(--listamos-primary);
}

.media-ways-icons {
    align-items: center;
    display: inline-flex;
    gap: 0.375rem;
}

.media-ways-icons .bi + .bi {
    font-size: 0.9375rem;
}

.media-add:hover .media-ways-line,
.media-video-add:hover .media-ways-line {
    color: var(--listamos-text-secondary);
}

/* The chooser. */
/* Held from the top, so that a page growing or shrinking moves only its bottom edge. */
.lm-sheet .modal-dialog {
    margin-top: min(8vh, 4.5rem);
    max-width: 32rem;
}

/* The parts keep their own heights while the page's height moves, so measuring them
   gives what the page needs rather than what it is being stretched to. A stretched
   body fed its stretched height back and the motion never settled. */
.lm-sheet .modal-content > * {
    flex: none;
}

.lm-sheet .modal-content > .modal-footer {
    margin-top: auto;
}

.lm-ways-list {
    display: grid;
    gap: var(--space-2);
}

.lm-way {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-lg);
    color: var(--listamos-text);
    cursor: pointer;
    display: grid;
    font-family: inherit;
    gap: var(--space-3);
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    padding: var(--space-3) var(--space-4);
    text-align: left;
    transition: border-color var(--fast), box-shadow var(--fast), transform var(--fast);
    width: 100%;
}

.lm-way:hover:not(:disabled) {
    border-color: var(--listamos-primary-border);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
}

.lm-way:focus-visible {
    box-shadow: var(--ring);
    outline: 0;
}

.lm-way:disabled {
    cursor: not-allowed;
    opacity: .55;
}

.lm-way.is-ai {
    background: linear-gradient(180deg, var(--brand-050), var(--listamos-surface) 70%);
    border-color: var(--brand-200);
}

.lm-way-icon {
    align-items: center;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: inline-flex;
    font-size: 1.125rem;
    height: 2.5rem;
    justify-content: center;
    width: 2.5rem;
}

.lm-way.is-ai .lm-way-icon {
    background: var(--listamos-primary);
    box-shadow: var(--shadow-brand);
    color: var(--listamos-text-on-primary);
}

.lm-way-words {
    display: grid;
    gap: 0.125rem;
    min-width: 0;
}

.lm-way-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: -0.01em;
}

.lm-way-body {
    color: var(--listamos-text-secondary);
    font-size: var(--text-xs);
    line-height: 1.4;
}

.lm-way-price {
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-pill);
    color: var(--listamos-text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    padding: 0.1875rem 0.5rem;
    white-space: nowrap;
}

.lm-way-price.is-paid {
    background: var(--listamos-primary-soft);
    color: var(--listamos-primary-text);
}

.lm-way-go {
    color: var(--listamos-text-tertiary);
    transition: transform var(--fast);
}

.lm-way:hover:not(:disabled) .lm-way-go {
    color: var(--listamos-primary);
    transform: translateX(2px);
}

/* The AI video. */
.lm-sheet .modal-dialog.is-wide {
    max-width: 54rem;
}

.lm-ai .modal-header {
    align-items: flex-start;
}

.lm-ai-titles {
    display: grid;
    gap: 0.125rem;
}

.lm-ai-titles .bi-stars {
    color: var(--listamos-primary);
}

.lm-ai-lede {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
}

.lm-ai-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
}

.lm-ai-look,
.lm-ai-choose {
    align-content: start;
    display: grid;
    gap: var(--space-4);
    min-width: 0;
}

.lm-ai-group {
    align-content: start;
    display: grid;
    gap: var(--space-2);
    min-width: 0;
}

.lm-ai-label {
    color: var(--listamos-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    letter-spacing: -0.008em;
    margin: 0;
}

.lm-ai-hint {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    margin: 0;
}

/* The seller's photograph as it stands, framed to the shape the video will have. */
.lm-ai-stage {
    aspect-ratio: 1;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    position: relative;
    transition: aspect-ratio var(--medium);
}

.lm-ai-stage[data-shape='portrait'] {
    aspect-ratio: 4 / 5;
}

.lm-ai-stage[data-shape='landscape'] {
    aspect-ratio: 16 / 9;
}

.lm-ai-stage img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.lm-ai-stage-badge {
    backdrop-filter: blur(4px);
    background: rgba(10, 16, 36, .62);
    border-radius: var(--radius-pill);
    color: #fff;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    left: var(--space-2);
    letter-spacing: .04em;
    padding: 0.1875rem 0.5rem;
    position: absolute;
    top: var(--space-2);
}

.lm-ai-photos {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-ai-photo {
    background: var(--listamos-surface-sunken);
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    height: 3rem;
    opacity: .72;
    overflow: hidden;
    padding: 0;
    transition: border-color var(--fast), opacity var(--fast);
    width: 3rem;
}

.lm-ai-photo img {
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.lm-ai-photo:hover {
    opacity: 1;
}

.lm-ai-photo.is-on {
    border-color: var(--listamos-primary);
    opacity: 1;
}

.lm-ai-photo:focus-visible {
    box-shadow: var(--ring);
    outline: 0;
}

/* Why this costs and the slideshow does not, said once, above the choice it explains. */
.lm-ai-versus {
    align-items: flex-start;
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    font-size: var(--text-xs);
    gap: var(--space-2);
    line-height: 1.45;
    margin-bottom: var(--space-3);
    padding: var(--space-2) var(--space-3);
}

.lm-ai-versus .bi {
    color: var(--listamos-primary);
}

.lm-ai-scenes {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.lm-ai-scene {
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-md);
    color: var(--listamos-text);
    cursor: pointer;
    display: grid;
    font-family: inherit;
    gap: 0.25rem;
    align-content: start;
    padding: var(--space-3);
    text-align: left;
    transition: border-color var(--fast), box-shadow var(--fast), background var(--fast);
}

.lm-ai-scene:hover {
    border-color: var(--listamos-border-hover);
}

.lm-ai-scene.is-on {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary);
    box-shadow: 0 0 0 3px var(--listamos-primary-soft);
}

.lm-ai-scene:focus-visible {
    box-shadow: var(--ring);
    outline: 0;
}

.lm-ai-scene-name {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.lm-ai-scene-name .bi {
    color: var(--listamos-primary);
}

.lm-ai-scene-body {
    color: var(--listamos-text-tertiary);
    font-size: var(--text-xs);
    line-height: 1.35;
}

.lm-ai-scene.is-loading {
    cursor: default;
    min-height: 4.25rem;
}

.lm-ai-scene-bar {
    animation: lm-ai-shimmer 1.2s ease-in-out infinite alternate;
    background: var(--listamos-surface-sunken);
    border-radius: var(--radius-pill);
    display: block;
    height: 0.75rem;
    width: 70%;
}

.lm-ai-scene-bar.is-short {
    width: 45%;
}

@keyframes lm-ai-shimmer {
    from { opacity: .5; }
    to { opacity: 1; }
}

.lm-ai-own {
    font-size: var(--text-sm);
    margin-top: var(--space-2);
    resize: vertical;
}

/* The chosen moment, written across the bottom of the photograph it happens around. */
.lm-ai-stage-caption {
    backdrop-filter: blur(4px);
    background: rgba(10, 16, 36, .62);
    border-radius: var(--radius-md);
    bottom: var(--space-2);
    color: #fff;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    left: var(--space-2);
    line-height: 1.3;
    padding: 0.25rem 0.5rem;
    position: absolute;
    right: var(--space-2);
    text-align: center;
}

.lm-ai-pair {
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

/* The kinds of thing that can happen, one row of chips above the ideas of that kind. */
.lm-ai-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.lm-ai-chip {
    align-items: center;
    background: var(--listamos-surface);
    border: 1px solid var(--listamos-border);
    border-radius: var(--radius-pill);
    color: var(--listamos-text-secondary);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    gap: 0.375rem;
    padding: 0.3125rem 0.75rem;
    transition: border-color var(--fast), background var(--fast), color var(--fast);
}

.lm-ai-chip:hover {
    border-color: var(--listamos-border-hover);
    color: var(--listamos-text);
}

.lm-ai-chip.is-on {
    background: var(--listamos-primary-soft);
    border-color: var(--listamos-primary);
    color: var(--listamos-primary);
}

.lm-ai-chip:focus-visible {
    box-shadow: var(--ring);
    outline: 0;
}

.lm-ai-chip.is-loading {
    animation: lm-ai-shimmer 1.2s ease-in-out infinite alternate;
    background: var(--listamos-surface-sunken);
    border-color: transparent;
    color: transparent;
    cursor: default;
}

/* On a person: a photograph of it worn comes first, then the video is made from that. */
.lm-ai-worn {
    background: var(--listamos-primary-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: grid;
    font-size: var(--text-xs);
    gap: var(--space-2);
    justify-items: start;
    line-height: 1.45;
    padding: var(--space-3);
}

.lm-ai-worn strong {
    color: var(--listamos-text);
    font-size: var(--text-sm);
}

.lm-ai-worn-how {
    color: var(--listamos-text-tertiary);
}

/* Nothing on the backdrop to bring to life: a room for the thing comes first. */
.lm-ai-plain {
    align-items: center;
    background: var(--listamos-warning-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--text-xs);
    gap: var(--space-2) var(--space-3);
    justify-content: space-between;
    line-height: 1.45;
    margin-bottom: var(--space-3);
    padding: var(--space-2) var(--space-3);
}

.lm-ai-plain strong {
    color: var(--listamos-text);
}

/* The model, for the seller who cares; the default is right for everyone else. */
.lm-ai-more {
    margin-bottom: var(--space-3);
}

.lm-ai-more > summary {
    color: var(--listamos-text-tertiary);
    cursor: pointer;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    margin-bottom: var(--space-2);
    width: fit-content;
}

.lm-ai-more > summary:hover {
    color: var(--listamos-text);
}

.lm-ai-segmented {
    display: flex;
    width: 100%;
}

.lm-ai-segmented > .lm-segment {
    flex: 1;
}

.lm-ai-shape-glyph {
    border: 1.5px solid currentColor;
    border-radius: 2px;
    display: inline-block;
    height: 0.75rem;
    width: 0.75rem;
}

.lm-ai-shape-glyph[data-shape='portrait'] {
    height: 0.8125rem;
    width: 0.625rem;
}

.lm-ai-shape-glyph[data-shape='landscape'] {
    height: 0.5625rem;
    width: 1rem;
}

.lm-ai-promise {
    background: var(--listamos-surface-subtle);
    border-radius: var(--radius-md);
    color: var(--listamos-text-secondary);
    display: grid;
    font-size: var(--text-xs);
    gap: 0.375rem;
    line-height: 1.4;
    list-style: none;
    margin: 0;
    padding: var(--space-3) var(--space-4);
}

.lm-ai-promise li {
    display: flex;
    gap: 0.5rem;
}

.lm-ai-promise .bi {
    color: var(--listamos-text-tertiary);
    flex: none;
}

.lm-ai-error {
    background: var(--listamos-danger-soft);
    border-radius: var(--radius-md);
    color: var(--listamos-danger-text);
    font-size: var(--text-sm);
    margin: 0;
    padding: var(--space-2) var(--space-3);
}

.lm-ai-footer {
    flex-wrap: wrap;
    justify-content: space-between;
}

.lm-ai-left {
    color: var(--listamos-text-secondary);
    font-size: var(--text-sm);
}

.lm-ai-short {
    color: var(--listamos-warning-text);
    margin-right: var(--space-2);
}

.lm-ai-buttons {
    display: flex;
    gap: var(--space-2);
    margin-left: auto !important;
}

.lm-ai-make {
    min-width: 11rem;
}

/*
 * The slideshow plays on two layers so the change between photographs can be seen as
 * it will be in the video: the next one fades, dissolves or slides over the one showing.
 * Half a second, as the slideshow planner has it.
 */
.lm-maker-reel img {
    inset: 0;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    opacity: 0;
    position: absolute;
}

.lm-maker-reel img.is-current,
.lm-maker-reel img.is-coming {
    opacity: 1;
}

.lm-maker-reel img.is-coming {
    z-index: 1;
}

.lm-maker-reel .lm-ai-stage-badge {
    z-index: 2;
}

.lm-maker-reel[data-passing='fade'] img.is-coming {
    animation: lm-maker-in .5s linear;
}

.lm-maker-reel[data-passing='slide'] img.is-coming {
    animation: lm-maker-slide-in .5s ease-in-out;
}

.lm-maker-reel[data-passing='slide'] img.is-current {
    animation: lm-maker-slide-out .5s ease-in-out forwards;
}

@keyframes lm-maker-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes lm-maker-slide-in {
    from { transform: translateX(100%); }
    to { transform: none; }
}

@keyframes lm-maker-slide-out {
    from { transform: none; }
    to { transform: translateX(-100%); }
}

/* Every scene at once, in rows, rather than a strip that hides the rest off the edge. */
.lm-maker .scenes {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
    overflow: visible;
    padding: 0;
}

.lm-maker .scene {
    flex: none;
    width: auto;
}

.lm-maker .studio-looks-title {
    margin-top: var(--space-2);
}

.lm-maker .studio-source-row,
.lm-maker .reel-source-row {
    flex-wrap: wrap;
}

.media-maker-open {
    margin-left: auto;
}

@media (max-width: 767.98px) {
    .lm-ai-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .lm-ai-look {
        grid-template-columns: 7.5rem minmax(0, 1fr);
    }

    .lm-ai-stage {
        align-self: start;
    }
}

/* On a phone the window comes up from the bottom, where the thumb already is. */
@media (max-width: 575.98px) {
    .lm-sheet .modal-dialog,
    .lm-sheet .modal-dialog.is-wide {
        align-items: flex-end;
        display: flex;
        margin: 0;
        max-width: none;
        min-height: 100%;
    }

    .lm-sheet .modal-content {
        border-bottom: 0;
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    }

    .lm-sheet.fade .modal-dialog {
        transform: translateY(2rem);
    }

    .lm-sheet.show .modal-dialog {
        transform: none;
    }

    .lm-ai .modal-footer {
        border-radius: 0;
    }

    .lm-ai-scenes {
        grid-template-columns: minmax(0, 1fr);
    }

    .lm-ai-make {
        flex: 1;
    }

    .lm-ai-buttons {
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .lm-ai-scene-bar,
    .lm-ai-chip.is-loading,
    .lm-maker-reel img {
        animation: none !important;
    }

    .lm-way,
    .lm-way-go {
        transition: none;
    }
}
