/* ─────────────────────────────────────────────────────────────────────────────
   Offra — "Cockpit" design system (chosen 2026-07-29 as RequestJapan.com;
   rebranded to Offra 2026-08-06, accent moved from vermillion to the mark's blue;
   re-skinned 2026-08-14 to quentin's design_en/design_jp comps — warm paper
   canvas, terracotta accent, Playfair wordmark and display type).
   Dense B2B ops console: dark sidebar, warm paper canvas, white bordered
   panels, compact data tables, terracotta accent. Light theme only.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  color-scheme: light;

  /* The comps put everything on warm paper rather than the old cool gray — a
     hair off-white, never pure. Note --wash is now the DEEPER paper the
     alternating bands are painted in, so it is warmer than --bg rather than
     paler: on the old cool ramp it went the other way, and any rule written
     assuming "wash is the lighter one" wants re-reading. */
  --bg:      #fcfaf8;
  --panel:   #ffffff;
  --wash:    #fbf6f1;
  --line:    #ece5dd;
  --line-2:  #ddd3c8;

  --ink:     #14181e;
  --sub:     #5b6472;
  --faint:   #99938c;

  --side:      #191614;
  --side-ink:  #d2ccc5;
  --side-mut:  #8a827a;
  --side-hov:  #262220;

  /* One accent. It was vermillion, then the mark's electric blue, and the
     comps take it to terracotta — the ink of a hanko rather than a
     highlighter, which is the point on a page made of watercolour. The token
     is named for its job rather than its hue: it was called --red for two
     years and held vermillion, which is how a rebrand turns into a search for
     every place someone wrote a colour name. */
  --accent:      #dd6337;
  --accent-deep: #bc4c25;
  --accent-soft: #fceee7;
  --accent-line: #f0c5ae;
  /* The illustrations are inked in this indigo and the step badges take it,
     so it stays a live token rather than a leftover of the blue era. */
  --brand-navy:  #2c5580;

  --ok-ink:  #177245; --ok-soft:  #e6f2ea; --ok-line: #c2dccd;
  --info-ink:#33527a; --info-soft:#eaeff6; --info-line:#c8d5e4;

  --r: 9px;
  /* The landing's measure. It is a token because the hero backdrop has to
     line its right edge up with the same column the words sit in — an
     art element anchored to the viewport instead drifts further and
     further from the text as the screen gets wider. */
  --shell: 1180px;
  /* ...and its gutter. Nav, hero, sections and footer each carried their own
     (1rem, 1.8rem, 1.1rem), which reads as three different left edges on a
     phone. One value, overridden once at the phone breakpoint. */
  --gutter: 1.8rem;
  --font: "Figtree", -apple-system, BlinkMacSystemFont, "Hiragino Sans", "Noto Sans JP", sans-serif;
  /* Display face: the comps set the wordmark and every landing heading in a
     high-contrast serif. Japanese has no Playfair cut — the stack falls
     through to the gothic, which is what the JP comp shows anyway. */
  --font-display: "Playfair Display", "Hiragino Mincho ProN", Georgia, serif;
  /* The wordmark is a mark, not body copy. Surfaces may change --font; this
     one is pinned so the logo is the same object on every page. */
  --font-brand: "Playfair Display", Georgia, "Times New Roman", serif;
  --brand-ink:  #14181e;
}

* { box-sizing: border-box; }
body { margin: 0; font-family: var(--font); color: var(--ink); background: var(--bg);
       font-size: 13.5px; line-height: 1.55; -webkit-font-smoothing: antialiased;
       /* 禁則処理. The Japanese tagline in the footer wrapped as "B2Bマ /
          ーケットプレイス" — a line opening on a prolonged sound mark, which
          Japanese typesetting forbids along with small kana and closing
          punctuation. A Japanese reader sees it the way an English reader sees
          a word broken with no hyphen. Affects CJK only, so the French and
          English pages pay nothing for it. */
       line-break: strict; }
a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }
h1 { font-weight: 800; font-size: 1.35rem; line-height: 1.25; letter-spacing: -.01em; margin: 0; }
h2 { font-weight: 800; font-size: 1.1rem; letter-spacing: -.01em; }
h3 { font-size: .7rem; font-weight: 700; margin: 0 0 .9rem; text-transform: uppercase;
     letter-spacing: .12em; color: var(--sub); }
code { font-size: .82em; background: var(--bg); padding: .12em .4em; border-radius: 5px; color: var(--sub); }

.kicker { color: var(--accent); font-size: .68rem; font-weight: 700; letter-spacing: .16em;
          text-transform: uppercase; margin-bottom: .5rem; }

/* ── Brand ─────────────────────────────────────────────────────────────── */
/* Pinned, not inherited: face, weight, tracking and ink are all fixed so the
   mark is the same object on the Mercari-flat JP surface and the collector
   one — including the face, which is why it does NOT ride var(--font) and does
   not translate. .sidebar .brandmark is more specific and still turns it
   white. */
.brandmark { display: inline-flex; align-items: baseline; gap: .5rem;
             font-family: var(--font-brand); font-weight: 700; font-size: 1.28rem;
             letter-spacing: -.005em; color: var(--brand-ink) !important; }
.brandmark .brand-word { font-family: var(--font-brand); font-weight: 700;
                         letter-spacing: -.005em; line-height: 1; }
/* The full stop is the only coloured part of the mark and it is a full stop,
   not a bullet: sized with the word, nudged tight against the a. */
.brandmark .brand-dot { color: var(--accent); margin-left: -.04em; }
/* On the dark sidebar and app bar the word inverts; the stop keeps its
   terracotta, which is the part of the mark that is saying something. */
.sidebar .brandmark, .appbar .brandmark { color: #fff !important; }
.sidebar .brandmark .brand-dot, .appbar .brandmark .brand-dot { color: var(--accent); }
.brandmark .dot { width: 21px; height: 21px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.brandmark svg { width: 26px; height: 26px; flex-shrink: 0; display: block; }
.brandmark .brand-icon { width: 28px; height: auto; flex-shrink: 0; display: block; }
.brandmark.brand-lg .brand-icon { width: 42px; }
.brandmark.brand-lg svg { width: 40px; height: 40px; }
.brandmark.brand-lg { font-size: 2.1rem; }
.brandmark.brand-lg .dot { width: 33px; height: 33px; }
/* The env tag rides the baseline of a serif word now — nudge it back to the
   middle so "BUYER" does not hang off the bottom of the O. */
.brandmark .env-tag { align-self: center; }
.env-tag { font-size: .58rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
           color: #fff; background: var(--accent); padding: .14rem .5rem;
           border-radius: 4px; margin-left: .6rem; }

/* ── App shell: dark sidebar + main column ─────────────────────────────── */
.shell { display: grid; grid-template-columns: 216px minmax(0, 1fr); min-height: 100vh;
         /* The sidebar is sticky + 100vh, so on a page taller than the viewport its
            column would show bare canvas below it. Paint the column on the grid. */
         background: linear-gradient(to right, var(--side) 0 216px, transparent 216px); }
.sidebar { background: var(--side); color: var(--side-ink); padding: 1rem .8rem;
           display: flex; flex-direction: column; position: sticky; top: 0;
           height: 100vh; overflow-y: auto; }
.sidebar .brandmark { color: #fff !important; padding: .3rem .5rem 1rem; flex-wrap: wrap;
                      row-gap: .35rem; font-size: .95rem; }
/* In the chrome the badge is a label, not a call to action: it names the app
   without competing with the wordmark beside it. Solid accent stays on the
   sign-in doors, where the mark is 1.5rem and the badge has room to be loud. */
.sidebar .env-tag, .appbar .env-tag {
  margin-left: .4rem; font-size: .54rem; padding: .12rem .42rem; letter-spacing: .1em; }
.sidebar .env-tag { background: var(--accent-soft); color: var(--accent-deep); }
/* The phone app bar is dark, where a pale ground is the loudest thing on it. */
.appbar .env-tag { background: rgba(255,255,255,.16); color: #fff; }
.sidebar h5 { font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
              color: var(--side-mut); padding: .9rem .55rem .35rem; margin: 0; font-weight: 700; }
.nv { display: flex; align-items: center; gap: .55rem; padding: .45rem .6rem; border-radius: 7px;
      font-weight: 600; font-size: .84rem; margin-bottom: 2px; color: var(--side-ink); }
.nv:hover { background: var(--side-hov); color: #fff; }
.nv.on { background: var(--accent); color: #fff; }
.nv .n { margin-left: auto; background: #2c2f36; border-radius: 99px; padding: .02rem .5rem;
         font-size: .68rem; font-weight: 700; }
.nv.on .n { background: rgba(255,255,255,.25); }

/* The navigation icon set (IconHelper). Sized by the helper, positioned here.
   `flex: none` because a flex child with an intrinsic width still shrinks when
   the label is long, and a squashed 18px icon reads as a rendering fault; the
   Japanese labels are the ones that reach the edge. */
.ico { flex: none; display: block; }
/* Optically centred against the cap height of the label rather than its line
   box: the labels are 600-weight at .84rem and the icon is 18px, so a strict
   baseline alignment sits it a hair high. */
.nv .ico, .appmenu-link .ico { margin-top: -1px; }
.sidebar .foot { margin-top: auto; font-size: .72rem; color: var(--side-mut);
                 padding: .8rem .55rem .2rem; border-top: 1px solid #24272d; }
.sidebar .foot b { color: var(--side-ink); display: block; font-size: .76rem; }
.sidebar .foot form { margin-top: .6rem; }
.sidebar .foot .lang-switch { margin-top: .7rem; }
.sidebar .foot .lang-switch form { margin-top: 0; }
.sidebar select { width: 100%; font: inherit; font-size: .76rem; color: var(--side-ink);
                  background: var(--side-hov); border: 1px solid #2c2f36; border-radius: 6px;
                  padding: .3rem .5rem; margin-bottom: .5rem; }

main.container { max-width: 1240px; margin: 0 auto; padding: 1.4rem 1.8rem 4rem; width: 100%; }
body.public main.container { max-width: 1080px; padding-top: 3rem; }

/* ── Page header (toolbar row) ─────────────────────────────────────────── */
.page-head { display: flex; align-items: center; justify-content: space-between;
             gap: 1.5rem; margin-bottom: 1.2rem; }
.page-head .sub { color: var(--sub); font-size: .85rem; margin: .3rem 0 0; }
/* Each button_to is its own <form> — a nested form is dropped by the browser,
   so they have to be siblings. Left to themselves they are block elements of
   different widths stacking unevenly; the row is what makes "publish" and
   "cancel" sit level. */
.page-head .actions { flex-shrink: 0; display: flex; align-items: stretch; gap: .5rem; }
.page-head .actions form { margin: 0; display: flex; }
.page-head .actions .btn { white-space: nowrap; }
.page-head .kicker { margin-bottom: .2rem; }

/* ── Panels ────────────────────────────────────────────────────────────── */
.card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
        padding: 1.4rem 1.6rem; margin-bottom: 1.1rem; }
.card h2 { margin-top: 0; }
.card-narrow { max-width: 640px; }
/* Detail pages that stack narrow cards: keep every block on the same measure,
   including wide children like the claim thread. */
.col-narrow { max-width: 640px; }
.col-narrow .card, .col-narrow .card-narrow { max-width: none; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; align-items: start; }
@media (max-width: 780px) { .grid-2 { grid-template-columns: 1fr; } }

/* ── Data tables: boxed dense panels ───────────────────────────────────── */
table.list { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--panel);
             border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
table.list th { text-align: left; font-size: .62rem; font-weight: 700; text-transform: uppercase;
                letter-spacing: .09em; color: var(--sub); padding: .55rem 1rem;
                background: var(--wash); border-bottom: 1px solid var(--line);
                white-space: nowrap; }
table.list td { padding: .7rem 1rem; border-bottom: 1px solid #eef0f3;
                vertical-align: middle; font-size: .84rem; }
table.list td:not(:first-child) { white-space: nowrap; }
table.list tr:last-child td { border-bottom: none; }
table.list tr:hover td { background: var(--wash); }
table.list td a { font-weight: 700; }
table.list th.num, table.list td.num { text-align: right; font-variant-numeric: tabular-nums; }
.cell-sub { display: block; color: var(--faint); font-size: .72rem; font-weight: 400; margin-top: .1rem; }
.card table.list { border: none; border-radius: 0; }
.card table.list td { padding: .6rem 1rem .6rem 0; }

/* thumbnails in lists */
.thumb { width: 38px; height: 38px; border-radius: 6px; flex-shrink: 0;
         display: flex; align-items: center; justify-content: center; }
.thumb svg { width: 17px; height: 17px; }
.rowflex { display: flex; align-items: center; gap: .8rem; }
.tint-0 { background: #efe9dc; color: #a08a5f; }
.tint-1 { background: #e6e0f0; color: #7a68a6; }
.tint-2 { background: #e0ebe4; color: #5d8a72; }
.tint-3 { background: #f2e0e0; color: #a66868; }
.thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 6px; }

/* ── Badges: compact status chips ──────────────────────────────────────── */
.badge { display: inline-block; padding: .13rem .5rem; border-radius: 5px;
         font-size: .66rem; font-weight: 700; letter-spacing: .03em; white-space: nowrap;
         background: #eef0f3; color: var(--sub); }
.badge-open, .badge-active, .badge-approved, .badge-escrowed, .badge-paid,
.badge-succeeded, .badge-fulfilled { background: var(--ok-soft); color: var(--ok-ink); }
.badge-exclusive, .badge-shortlisted, .badge-pending, .badge-awaiting_payment,
.badge-applied, .badge-draft, .badge-under_review, .badge-executing, .badge-batched,
.badge-requires_payment { background: var(--accent-soft); color: var(--accent-deep); }
.badge-shipped, .badge-delivered, .badge-awarded, .badge-accepted, .badge-closed,
.badge-processing, .badge-resolved_partial { background: var(--info-soft); color: var(--info-ink); }
.badge-cancelled, .badge-expired, .badge-rejected, .badge-suspended, .badge-declined,
.badge-lost, .badge-withdrawn, .badge-in_claim, .badge-held, .badge-failed,
.badge-refunded, .badge-resolved_refund { background: var(--accent-soft); color: var(--accent-deep);
                                          box-shadow: inset 0 0 0 1px var(--accent-line); }

/* ── Buttons ───────────────────────────────────────────────────────────── */
/* A transparent border rather than none, so a filled button and an outlined
   one are the same height. Without it, "publish" (no border) and "cancel" (a
   1px one) sat a pixel apart wherever they appeared side by side, which is
   exactly the kind of thing that reads as sloppy without being nameable. */
.btn { display: inline-block; border: 1px solid transparent; cursor: pointer;
       font: inherit; font-weight: 700;
       font-size: .84rem; line-height: 1.25; padding: .55rem 1.1rem; border-radius: 7px;
       background: var(--accent); color: #fff; transition: background .15s ease; }
.btn:hover { background: var(--accent-deep); color: #fff; }
.btn-brand { background: var(--accent); }
.btn-secondary { background: var(--panel); color: var(--ink); border: 1px solid var(--line-2); }
.btn-secondary:hover { background: var(--wash); color: var(--ink); border-color: var(--faint); }
.btn-danger { background: var(--panel); color: var(--accent-deep); border: 1px solid var(--accent-line); }
.btn-danger:hover { background: var(--accent-soft); color: var(--accent-deep); }
.btn-sm { padding: .35rem .8rem; font-size: .76rem; }
/* The landing's call to action: bigger, rounder, and carrying an arrow, per
   the comps. Landing-only on purpose — inside the app a "save" button that
   points somewhere is a lie. The arrow is a pseudo-element so the label stays
   one translatable string with no markup in it. */
.btn-cta { padding: .82rem 1.5rem; font-size: .92rem; border-radius: 9px;
           box-shadow: 0 1px 2px rgba(20, 24, 30, .1); }
.btn-cta::after { content: ""; display: inline-block; vertical-align: -1px;
  width: 15px; height: 12px; margin-left: .85rem;
  background: no-repeat center / 15px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15m-6-6 6 6-6 6'/%3E%3C/svg%3E"); }

/* ── Action rows ───────────────────────────────────────────────────────── */
/* Every button_to is its own <form>, and a form is a block: two of them stack
   at different widths instead of sitting level. `.page-head .actions` fixed
   that for the toolbar and nowhere else, so the same two buttons inside a
   panel went back to being misaligned. This is that fix, usable anywhere.
   Size matters as much as alignment — a .btn next to a .btn-sm reads as a
   mistake even when their left edges line up, so rows keep one size. */
.actions-row { display: flex; align-items: stretch; flex-wrap: wrap; gap: .5rem;
               margin-top: 1rem; }
.actions-row form { margin: 0; display: flex; }
.actions-row .btn { white-space: nowrap; }

/* The persistent notices — .flash-info and .flash-todo — used to be declared
   here, six hundred lines above `.flash` itself. Same specificity, earlier in
   the file, so `.flash` won and both rendered in the success green: the
   "pending validation" banner has been green since it was written. They live
   with `.flash` now. See the Flash section below. */

/* A seller who cannot quote yet gets more than a notice: there is one thing to
   do about it, so the action is part of the component. Text and button are a
   row, not one run of inline content — inline, the button landed wherever the
   paragraph happened to stop, which in Japanese is mid-block because the copy
   is longest there. Now the sentence wraps under itself and the button holds
   the end of the row. */
.setup-gate { display: flex; align-items: center; justify-content: space-between;
              gap: .9rem 1.4rem; flex-wrap: wrap;
              background: var(--panel); border: 1px solid var(--line);
              border-left: 3px solid var(--accent); border-radius: var(--r);
              padding: .95rem 1.2rem; margin-bottom: 1.1rem; }
.setup-gate-text { flex: 1 1 24rem; min-width: 0; }
.setup-gate-text b { display: block; font-size: .88rem; margin-bottom: .2rem; }
.setup-gate-text span { display: block; color: var(--sub); font-size: .8rem;
                        line-height: 1.55; }
.setup-gate .btn { flex: 0 0 auto; }

/* A panel heading that carries a status: the badge belongs beside the title,
   not orphaned in a paragraph of its own underneath it. */
.panel-head { display: flex; align-items: center; gap: .55rem; flex-wrap: wrap; }
.panel-head .badge { text-transform: none; letter-spacing: 0; }
.setup-body { color: var(--sub); font-size: .84rem; line-height: 1.55; margin: 0; }
.setup-points { margin: .45rem 0 0; padding-left: 1.1rem; color: var(--sub);
                font-size: .82rem; line-height: 1.65; }

/* Where the Stripe popup writes back. `.hint` is only styled inside
   form.stacked, so out here it rendered at full body size — the one line on
   the page that is genuinely transient looked like the most important. */
.setup-status { color: var(--sub); font-size: .76rem; line-height: 1.45;
                margin: .6rem 0 0; }
.setup-status[hidden] { display: none; }

/* ── Forms ─────────────────────────────────────────────────────────────── */
form.stacked label { display: block; font-weight: 600; margin: 1rem 0 .3rem; font-size: .8rem; }
/* Every input except the ones that style themselves. This was a list of the
   types the forms happened to use, so the day a field changed from
   datetime-local to date it fell out of the list and rendered as raw browser
   chrome — a native box, a different height, a different font, beside eight
   fields that matched. Opting out is a short, closed list; opting in is not. */
form.stacked input:not([type=file]):not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=submit]):not([type=button]),
form.stacked textarea, form.stacked select {
  width: 100%; font: inherit; font-size: .86rem; color: var(--ink); background: var(--panel);
  border: 1px solid var(--line-2); border-radius: 7px; padding: .5rem .7rem; }
form.stacked input:focus, form.stacked textarea:focus, form.stacked select:focus {
  outline: 2px solid var(--accent-line); border-color: var(--accent); }
form.stacked input::placeholder, form.stacked textarea::placeholder { color: var(--faint); }
/* Block, not the inline-block a textarea defaults to. Inline-block sits on a
   text baseline and leaves descender space under it — a gap whose size is the
   browser's business, not ours. Anything placed under a textarea then needs a
   negative margin to look right, and that margin is correct in exactly one
   browser: on the others the thing below it lands on top of the field. */
form.stacked textarea { display: block; min-height: 90px; resize: vertical; }
/* A date input carries native chrome the rest of the form does not: its own
   picker button, and on some browsers its own idea of line height. Match the
   field height to the others and tint the button so it reads as ours. */
form.stacked input[type=date]::-webkit-calendar-picker-indicator {
  cursor: pointer; opacity: .55; transition: opacity .15s; }
form.stacked input[type=date]:hover::-webkit-calendar-picker-indicator { opacity: 1; }
form.stacked input[type=file] { width: 100%; font: inherit; font-size: .82rem; color: var(--sub);
  background: var(--panel); border: 1px dashed var(--line-2); border-radius: 7px; padding: .5rem .7rem; }
/* The whole .upload-zone is a <label>: clicking anywhere in it opens the
   picker, so the native control only adds untranslatable English chrome. */
form.stacked .upload-zone input[type=file] { position: absolute; width: 1px; height: 1px;
                                             opacity: 0; pointer-events: none; }
form.stacked input[type=file]::file-selector-button {
  font: inherit; font-size: .78rem; font-weight: 600; color: var(--ink); background: var(--bg);
  border: 1px solid var(--line-2); border-radius: 5px; padding: .28rem .7rem; margin-right: .7rem;
  cursor: pointer; }
form.stacked input[type=file]:hover::file-selector-button { border-color: var(--accent); color: var(--accent); }
form.stacked hr { border: none; border-top: 1px solid var(--line); margin: 1.5rem 0 .4rem; }
form.stacked .hint { color: var(--faint); font-size: .74rem; margin-top: .3rem; }
form.stacked .grid-2 { gap: 0 1.2rem; }
form.stacked .grid-2 > div > label:first-child { margin-top: 1rem; }
.grid-2 .span-2 { grid-column: 1 / -1; }

/* ── Flash ─────────────────────────────────────────────────────────────── */
.flash { padding: .7rem 1.1rem; border-radius: 7px; margin-bottom: 1rem; font-size: .84rem;
         font-weight: 600; background: var(--ok-soft); color: var(--ok-ink);
         border: 1px solid var(--ok-line); }
.flash-alert { background: var(--accent-soft); color: var(--accent-deep); border-color: var(--accent-line); }

/* ── Persistent notices ────────────────────────────────────────────────── */
/* `.flash` is for something that just happened and will be gone next page.
   Several screens needed the opposite — a standing condition — and each
   reached for .flash-alert with the same three colours overridden inline to
   stop it looking like an error. That is a component, so here it is. It has to
   sit AFTER `.flash`: one class each, so the cascade is decided by which line
   is lower, and for a long time this one was not. */
.flash-info { background: var(--info-soft); color: var(--info-ink);
              border-color: var(--info-line); }

/* The other standing notice, and the reason there are two: `.flash-info` is
   "we are working on it", and this is "you are". An unfinished company profile
   is a to-do with an owner, so it takes the accent.

   The action is part of it for the same reason the seller's `.setup-gate` has
   one: a notice that names a problem and offers no route to it is how "later"
   becomes never. A row on a desktop, stacked once the sentence needs the
   width; the button never shrinks below its own text. */
.flash-todo { background: var(--accent-soft); color: var(--accent-deep);
              border-color: var(--accent-line);
              display: flex; align-items: center; justify-content: space-between;
              gap: .7rem 1.2rem; flex-wrap: wrap; }
.flash-todo > span { flex: 1 1 22rem; min-width: 0; }
.flash-todo-action { flex: 0 0 auto; margin: 0; }

/* ── KPI cards (stats) ─────────────────────────────────────────────────── */
.stats-bar { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
             gap: .9rem; margin-bottom: 1.2rem; }
.stats-bar .stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
                   padding: .8rem 1rem; }
.stats-bar .num { font-size: 1.5rem; font-weight: 800; line-height: 1.2;
                  font-variant-numeric: tabular-nums; }
.stats-bar .label { font-size: .64rem; font-weight: 700; text-transform: uppercase;
                    letter-spacing: .08em; color: var(--sub); margin-top: .05rem; }
.stats-bar .sub-metric { font-size: .72rem; color: var(--faint); margin-top: .3rem; }

/* ── Content + right rail ──────────────────────────────────────────────── */
.rail-layout { display: grid; grid-template-columns: minmax(0, 1fr) 272px; gap: 1.2rem;
               align-items: start; }
@media (max-width: 1080px) { .rail-layout { grid-template-columns: 1fr; } }
.rail .panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
               margin-bottom: 1rem; overflow: hidden; }
.rail .panel-head { display: flex; align-items: center; justify-content: space-between;
                    padding: .65rem 1rem; border-bottom: 1px solid var(--line); }
.rail .panel-head b { font-size: .8rem; }
.rail .panel-head span { font-size: .68rem; color: var(--sub); }
.rail .row { display: flex; gap: .6rem; padding: .6rem 1rem; border-bottom: 1px solid #eef0f3;
             font-size: .74rem; align-items: flex-start; }
.rail .row:last-child { border-bottom: none; }
.rail .row .dot2 { width: 8px; height: 8px; border-radius: 50%; margin-top: .35rem;
                   flex-shrink: 0; background: var(--accent); }
.rail .row .dot2.g { background: var(--ok-ink); } .rail .row .dot2.b { background: var(--info-ink); }
.rail .row b { font-size: .76rem; display: block; }
.rail .row .tm { color: var(--faint); font-size: .66rem; margin-left: auto; white-space: nowrap; }
.rail .mrow { display: flex; justify-content: space-between; padding: .5rem 1rem;
              border-bottom: 1px solid #eef0f3; font-size: .78rem; }
.rail .mrow:last-child { border-bottom: none; }
.rail .mrow b { font-variant-numeric: tabular-nums; }

/* ── Auth pages ────────────────────────────────────────────────────────── */
/* Centred with auto margins rather than justify-content: a form taller than
   the phone (registration is) would otherwise have its top pushed above the
   viewport, where scrolling cannot reach it. dvh because iOS counts the URL
   bar in vh, which cuts the last field off until you scroll the bar away. */
body.auth { display: flex; flex-direction: column; align-items: center;
            justify-content: flex-start; min-height: 100vh; min-height: 100dvh;
            padding: 2rem 1rem; box-sizing: border-box; gap: 1.1rem; }
.auth-card { margin-top: auto; }
.auth-langs { display: flex; justify-content: center; margin-bottom: auto; }

/* The way out. Full width of the column and left-aligned, so it reads as page
   chrome rather than as part of the form — and it sits ABOVE the auto margin
   that centres the card, so a tall form pushes the card down past it instead
   of pushing it off the top of the screen. 44px tall: it is the one control on
   these pages a thumb reaches for by accident. */
/* It has to start on the same line as the card under it, and the card comes
   in three widths — 384 for a sign-in, 580+310 for the registration split.
   :has() reads which one is on the page rather than the layout being told,
   the same trick main.container already uses for the landing. */
.auth-top { width: min(384px, 100%); display: flex; }
body.auth:has(.auth-card-wide) .auth-top { width: min(580px, 100%); }
body.auth:has(.auth-split) .auth-top { width: min(914px, 100%); }  /* 580 + 310 + the 1.5rem gap */
.auth-back { display: inline-flex; align-items: center; gap: .5rem;
             min-height: 44px; padding: .3rem .1rem;
             font-size: .84rem; font-weight: 600; color: var(--sub); }
.auth-back:hover { color: var(--accent); }
.auth-back-arrow { width: 15px; height: 12px; flex: 0 0 auto;
  background: no-repeat center / 15px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6472' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12H5m6 6-6-6 6-6'/%3E%3C/svg%3E");
  transition: transform .15s ease; }
.auth-back:hover .auth-back-arrow { transform: translateX(-2px); }
/* min(), not a fixed width. At 384px flat the card was wider than the space
   left by the body's padding on a 390px phone and wider than the phone itself
   below that — and because the column is centred, the overflow went to both
   sides, putting the left edge of every label at a negative x where no amount
   of scrolling reaches it. The register form, at an inline 520px, lost its
   whole left column that way. */
.auth-card { width: min(384px, 100%); box-sizing: border-box;
             background: var(--panel); border: 1px solid var(--line);
             border-radius: 12px; padding: 2.2rem 2.2rem 2rem;
             box-shadow: 0 8px 28px rgba(20,22,26,.07); }
.auth-card-wide { width: min(520px, 100%); }
.auth-card .brandmark { margin-bottom: 1.4rem; }
.auth-card h2 { font-size: 1.05rem; margin: 0 0 1rem; }
.auth-card-wide h2 { font-size: 1.35rem; letter-spacing: -.01em; margin-bottom: .5rem; }
.auth-card .btn { width: 100%; margin-top: 1.3rem; text-align: center; }
.auth-card .aux { margin-top: 1rem; font-size: .78rem; color: var(--sub); text-align: center; }
.auth-card .aux a { color: var(--accent); font-weight: 600; }
.auth-card .card { border: none; padding: 0; margin: 0; background: transparent; }

/* ── Empty states ──────────────────────────────────────────────────────── */
.empty { text-align: center; color: var(--faint); padding: 3.2rem 1rem; font-size: .88rem;
         background: var(--panel); border: 1px dashed var(--line-2); border-radius: var(--r); }
.empty .dot-mark { display: inline-block; width: 12px; height: 12px; border-radius: 50%;
                   background: var(--accent-line); margin-bottom: .6rem; }

code.wrap, .wrap-any { word-break: break-all; overflow-wrap: anywhere; }

/* Click-to-select fields (invitation links, TOTP URI). Styled like an input so
   they don't fall back to raw browser chrome inside a Cockpit panel. */
input.copy { width: 100%; font: inherit; font-size: .76rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
             color: var(--sub); background: var(--wash); border: 1px solid var(--line-2);
             border-radius: 6px; padding: .34rem .55rem; cursor: pointer; }
input.copy:focus { outline: 2px solid var(--accent-line); border-color: var(--accent); color: var(--ink); }

.rail .row-link { text-decoration: none; color: inherit; }
.rail .row-link:hover { background: var(--wash); }
.rail .dot2.r { background: var(--accent); }

/* Segmented control: one compact affordance where two full buttons would
   overflow. Shares the language switcher's shape so the nav reads as a set. */
.seg { display: inline-flex; align-items: center; gap: 2px; padding: 2px;
       border-radius: 999px; background: var(--wash); border: 1px solid var(--line);
       white-space: nowrap; }
.seg-label { font-size: .68rem; font-weight: 700; text-transform: uppercase;
             letter-spacing: .06em; color: var(--faint); padding: 0 .5rem 0 .6rem; }
.l-nav .seg a, .legal-head .seg a {
  font-size: .78rem; font-weight: 700; color: var(--sub); background: var(--panel);
  border: 1px solid var(--line-2); border-radius: 999px; padding: .22rem .7rem;
  line-height: 1.5; }
.l-nav .seg a:hover, .legal-head .seg a:hover { color: var(--ink); border-color: var(--ink); }

/* ── Language switcher (every page) ────────────────────────────────────── */
.lang-switch { display: inline-flex; flex-wrap: nowrap; white-space: nowrap;
               gap: 2px; padding: 2px; border-radius: 999px;
               background: var(--wash); border: 1px solid var(--line); }
.lang-switch-form { display: inline; margin: 0; }
.lang-switch a, .lang-switch button { font: inherit; font-size: .7rem; font-weight: 700; letter-spacing: .02em;
                      color: var(--sub); background: transparent; border: none; cursor: pointer;
                      padding: .22rem .6rem; border-radius: 999px; line-height: 1.5; }
.lang-switch a:hover, .lang-switch button:hover { color: var(--ink); }
.lang-switch a.on, .lang-switch button.on { background: var(--panel); color: var(--ink);
                                             box-shadow: 0 1px 2px rgba(16, 18, 22, .08); }
.lang-switch a { display: inline-block; text-decoration: none; }

/* Inside the dark sidebar the same control inverts. */
.lang-switch-dark { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .1);
                    width: 100%; justify-content: center; }
.lang-switch-dark a, .lang-switch-dark button { color: var(--side-mut); }
.lang-switch-dark a:hover, .lang-switch-dark button:hover { color: #fff; }
.lang-switch-dark a.on, .lang-switch-dark button.on { background: rgba(255, 255, 255, .14);
                                                      color: #fff; box-shadow: none; }

/* ── Filters, misc ─────────────────────────────────────────────────────── */
.filter-row { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: 1.2rem; }
.filter-row a { font-size: .78rem; font-weight: 600; color: var(--sub); background: var(--panel);
                border: 1px solid var(--line); padding: .3rem .85rem; border-radius: 999px; }
.filter-row a.on, .filter-row a:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.muted { color: var(--faint); }
.hero { text-align: center; padding: 11vh 1rem 2.5rem; }
.hero .brandmark { margin-bottom: 1.4rem; }
.hero p.tagline { max-width: 460px; margin: 1rem auto 2rem; color: var(--sub); font-size: 1rem; }
.hero h1, h1.display { font-size: 2rem; font-weight: 800; }

/* ── Negotiation thread ────────────────────────────────────────────────── */
/* A <ul> that is a flex column is still a list: without this the negotiation
   bubbles each carried a marker floating beside them. */
.thread { display: flex; flex-direction: column; gap: .6rem; list-style: none; margin: 0; padding: 0; }
.thread .msg { max-width: 78%; padding: .65rem .9rem; border-radius: 9px; font-size: .84rem;
               background: var(--bg); border: 1px solid var(--line); }
.thread .msg.mine { align-self: flex-end; background: var(--accent-soft); border-color: var(--accent-line); }
.thread .msg .price { font-weight: 800; }
.thread .muted { margin-top: .12rem; font-size: .68rem; }

/* ── Deal showcase cards ───────────────────────────────────────────────── */
.deal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 1rem; }
.deal-grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .deal-grid-4 { grid-template-columns: repeat(2, 1fr); } }
.deal-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
             overflow: hidden; font-size: .82rem; }
.deal-card-img { position: relative; aspect-ratio: 4 / 3; display: flex; align-items: center;
                 justify-content: center; }
.deal-card-img img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.deal-card-img svg { width: 30px; height: 30px; opacity: .55; }
.deal-card-img .sold { position: absolute; top: .5rem; left: .5rem; background: var(--accent);
                       color: #fff; font-size: .62rem; font-weight: 800; letter-spacing: .08em;
                       padding: .18rem .5rem; border-radius: 4px; }
.deal-card-title { font-weight: 700; padding: .7rem .9rem .1rem; line-height: 1.35; }
.deal-card-price { padding: .25rem .9rem 0; font-weight: 800; font-size: .95rem; }
.deal-card-price .muted { font-weight: 500; font-size: .74rem; }
.deal-card-meta { padding: .15rem .9rem .8rem; color: var(--faint); font-size: .7rem; }

/* ── Market prices (price-transparency pages) ──────────────────────────── */
.market-hero { text-align: center; padding: 2.8rem 1rem 2.2rem; background: var(--panel);
               border: 1px solid var(--line); border-radius: var(--r); }
.market-hero .big-price { font-size: 2.7rem; font-weight: 800; line-height: 1.05;
                          letter-spacing: -.02em; }
.market-hero .approx { color: var(--sub); font-size: 1rem; margin-top: .3rem; }
.market-hero .basis { color: var(--faint); font-size: .76rem; margin-top: .6rem; }

.price-band { max-width: 460px; margin: 1.6rem auto 0; }
.price-band .band-track { position: relative; height: 5px; background: #eef0f3; border-radius: 3px; }
.price-band .band-fill { position: absolute; top: 0; bottom: 0; background: var(--accent-line);
  border-radius: 3px; }
.price-band .band-dot { position: absolute; top: 50%; width: 11px; height: 11px;
  background: var(--accent); border-radius: 50%; transform: translate(-50%, -50%); }
.price-band .band-labels { display: flex; justify-content: space-between; margin-top: .5rem;
  font-size: .76rem; color: var(--sub); }
.price-band .band-labels b { color: var(--ink); font-weight: 700; }
.price-band .band-caption { margin: .5rem 0 0; font-size: .72rem; color: var(--faint); }

.market-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem; }
@media (min-width: 900px) { .market-cards { grid-template-columns: repeat(3, 1fr); } }
.market-card { display: block; color: inherit; text-decoration: none; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r); padding: 1.2rem 1.3rem;
  transition: border-color .15s; }
.market-card:hover { border-color: var(--faint); }
.market-card .cat { font-size: .68rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent); margin-bottom: .6rem; }
.market-card .avg { font-size: 1.55rem; font-weight: 800; line-height: 1.1; }
.market-card .approx { color: var(--sub); font-size: .82rem; margin-top: .1rem; }
.market-card .range { margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--line);
  font-size: .76rem; color: var(--sub); display: flex; justify-content: space-between; }
.market-card .count { margin-top: .4rem; font-size: .7rem; color: var(--faint); }

.market-context { background: var(--wash); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1rem 1.2rem; font-size: .82rem; margin: 1.4rem 0 .4rem; }
.market-context .mc-title { font-size: .64rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent); margin-bottom: .4rem; }
.market-context .mc-price { font-size: 1.15rem; font-weight: 800; }
.market-context .mc-range { color: var(--sub); margin-top: .15rem; }
.market-context a { color: var(--accent); text-decoration: none; font-weight: 600; }

/* ── Two-column form pages ─────────────────────────────────────────────── */
.form-layout { display: grid; grid-template-columns: minmax(0, 640px) minmax(260px, 1fr);
  gap: 1.4rem; align-items: start; }
@media (max-width: 980px) { .form-layout { grid-template-columns: 1fr; } }
.form-aside { position: sticky; top: 1.2rem; }
/* The four "how it works" boxes — new request, new offer, payment, new
   category. A stepper, not a bordered list: numbered chips joined by a rail,
   so the eye follows the sequence down the column instead of reading four
   equal-weight rows. The title was an inline <b> sitting flush against the
   body text; it is a block now, which is what the run-together line was. */
.how-it-works { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.2rem 1.4rem 1.3rem; }
.how-it-works ol { list-style: none; margin: 1rem 0 0; padding: 0; counter-reset: step; }
.how-it-works li { position: relative; counter-increment: step;
  padding: 0 0 1.15rem 2.4rem; }
.how-it-works li:last-child { padding-bottom: 0; }

/* The rail runs from under one chip to the next, and stops at the last. */
.how-it-works li::after { content: ""; position: absolute; left: 11px; top: 26px;
  bottom: 0; width: 1px; background: var(--line-2); }
.how-it-works li:last-child::after { display: none; }

.how-it-works li::before { content: counter(step); position: absolute; left: 0; top: 0;
  width: 23px; height: 23px; border-radius: 50%; box-sizing: border-box;
  background: var(--accent-soft); border: 1px solid var(--accent-line); color: var(--accent-deep);
  font-size: .72rem; font-weight: 800; line-height: 1;
  display: flex; align-items: center; justify-content: center; }

.how-it-works li b { display: block; font-size: .85rem; font-weight: 700;
  line-height: 1.35; margin-bottom: .2rem; }
.how-it-works li span { display: block; color: var(--sub); font-size: .78rem; line-height: 1.55; }
body.jp .how-it-works li::before { border-radius: 4px; }

/* legacy helpers kept for public pages */
nav.topbar { display: flex; align-items: center; gap: 1.5rem; padding: 0 2rem; height: 58px;
             background: var(--panel); border-bottom: 1px solid var(--line);
             position: sticky; top: 0; z-index: 50; }
nav.topbar .brand-wrap { display: flex; align-items: center; margin-right: .6rem; }
nav.topbar a.nav-link { color: var(--sub); font-weight: 600; font-size: .86rem; padding: 1.15rem 0;
                        border-bottom: 2px solid transparent; }
nav.topbar a.nav-link:hover { color: var(--ink); }
nav.topbar a.nav-link.active { color: var(--ink); font-weight: 700; border-bottom-color: var(--accent); }
nav.topbar .spacer { flex: 1; }
nav.topbar .who { font-size: .8rem; color: var(--sub); }
nav.topbar form { margin: 0; }
nav.topbar select { font: inherit; font-size: .8rem; color: var(--ink); background: var(--panel);
                    border: 1px solid var(--line-2); border-radius: 7px; padding: .3rem .7rem; }
.change-link { color: var(--accent); font-size: .8rem; font-weight: 600; }

/* ── Company avatars (logo or tinted initial) ──────────────────────────── */
.avatar { border-radius: 6px; object-fit: cover; flex-shrink: 0; display: inline-flex;
          align-items: center; justify-content: center; vertical-align: middle; }
.avatar-fallback { font-weight: 800; }
.sidebar .avatar { border-radius: 5px; }

/* ── Key-value definition lists ────────────────────────────────────────── */
dl.kv { display: grid; grid-template-columns: minmax(120px, auto) 1fr; margin: 0; }
dl.kv dt { color: var(--sub); font-size: .8rem; padding: .42rem 1.2rem .42rem 0; }
dl.kv dd { margin: 0; padding: .42rem 0; font-size: .84rem; border-bottom: 1px solid #eef0f3; }
dl.kv dt { border-bottom: 1px solid #eef0f3; }
dl.kv dt:last-of-type, dl.kv dd:last-of-type { border-bottom: none; }
dl.kv dd.num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
dl.kv dd > p:first-child { margin-top: 0; }
dl.kv dd > p:last-child { margin-bottom: 0; }
dl.kv .kv-total { font-weight: 800; font-size: .92rem; }

/* ── Photo grids on offers ─────────────────────────────────────────────── */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
              gap: .6rem; margin: .7rem 0; }
.photo-grid img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 7px;
                  border: 1px solid var(--line); }
.photo-grid-sm { grid-template-columns: repeat(auto-fill, minmax(74px, 1fr)); }

/* ── Misc components restored ──────────────────────────────────────────── */
.pill-row { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin: .3rem 0 .8rem; }

/* ── Card lookup ───────────────────────────────────────────────────────────
   Results under the card-name field, then the chosen card as a preview. The
   whole thing is an enhancement: it never covers the field it sits under and
   never blocks the form, so a slow catalogue costs a picture and nothing
   more. */
/* A remark, never an error. No red, no icon, no border — the form is fine
   whatever the catalogue says, and the styling should not suggest otherwise. */
/* ── "Improve with AI" ─────────────────────────────────────────────────────
   Sits under the description as a quiet secondary action, not a call to
   action: the buyer's own words are the default and this is an offer to
   tidy them. The rewritten fields flash once so it is obvious what moved. */
.polish-row { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem .7rem;
              margin: .55rem 0 .2rem; }
.btn-polish { flex: 0 0 auto; padding: .4rem .8rem; font: inherit; font-size: .78rem;
              font-weight: 600; color: var(--ink); background: #fff; cursor: pointer;
              border: 1px solid var(--line); border-radius: 999px;
              transition: border-color .15s, color .15s, background .15s; }
.btn-polish::before { content: "\2726"; margin-right: .4rem; color: var(--accent); }
.btn-polish:hover:not(:disabled) { border-color: var(--accent-line); color: var(--accent); }
.btn-polish:disabled { opacity: .6; cursor: progress; }
/* Two different disabled states, and the cursor is the only thing that tells
   them apart: `progress` while the model is working, plain while the button is
   simply waiting for the buyer to write something for it to work on. */
.polish-waiting .btn-polish:disabled { cursor: default; }
.polish-note { flex: 1 1 15rem; min-width: 0; font-size: .72rem; line-height: 1.45;
               color: var(--sub); }
.polish-status { flex: 1 1 100%; font-size: .74rem; color: var(--accent); }
.polish-status[hidden] { display: none; }
/* The flash was rgba(0, 59, 242, .10) — the electric blue of two rebrands ago,
   surviving in an animation because a colour written as rgb() is invisible to
   a hex grep. Terracotta, at the same weight. */
@keyframes polish-flash {
  from { background: rgba(221, 99, 55, .12); border-color: var(--accent-line); }
  to   { background: #fff; }
}
.is-polished { animation: polish-flash 1.1s ease-out; }
@media (prefers-reduced-motion: reduce) { .is-polished { animation: none; } }

/* The catalogue is Pokémon-only, and a lot is not one card. When the lookup
   cannot run, the sentence telling you to "start typing and pick the exact
   card" is promising a picker that will never appear. */
.lookup-off .card-lookup-hint { display: none; }

/* A box the catalogue fills in. Not greyed out like something broken —
   readonly, and quietly so, because the answer is on its way rather than
   forbidden. It is readonly and not disabled on purpose: a disabled input is
   not submitted, and this one carries what the lookup wrote into it. */
form.stacked input.is-filled-for-you {
  background: var(--wash); color: var(--sub); cursor: default; }
form.stacked input.is-filled-for-you:focus { outline-color: var(--line-2); border-color: var(--line-2); }

.card-status { margin: .5rem 0 0; font-size: .76rem; color: var(--sub); line-height: 1.45; }
.card-status[hidden] { display: none; }

.card-count { margin: .5rem 0 0; font-size: .7rem; font-weight: 700; color: var(--faint);
              text-transform: uppercase; letter-spacing: .05em; }
.card-count[hidden] { display: none; }

/* "Pikachu" is two hundred cards. They are all here, in a panel that scrolls
   rather than one that pushes the rest of the form off the screen. */
.card-results { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
                gap: .5rem; margin-top: .5rem; max-height: 340px; overflow-y: auto;
                overscroll-behavior: contain; padding-right: .2rem; }
.card-results:empty { display: none; }
.card-hit { display: flex; flex-direction: column; gap: .2rem; padding: .4rem;
            background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
            cursor: pointer; text-align: left; font: inherit; color: var(--ink);
            transition: border-color .14s ease, transform .14s ease; }
.card-hit:hover { border-color: var(--accent-line); transform: translateY(-2px); }
.card-hit img, .card-hit-blank { width: 100%; aspect-ratio: 63 / 88; border-radius: 5px;
                                 object-fit: cover; background: var(--wash); display: block; }
.card-hit-name { font-size: .72rem; font-weight: 700; line-height: 1.25;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-hit-meta { font-size: .62rem; color: var(--faint); text-transform: uppercase;
                 letter-spacing: .04em; }

/* Twelve at a time, so the results do not bury the form. The button carries
   its own state: more to reveal, or a plain statement that the catalogue has
   more than we fetched — never a list that just stops. */
.card-more { display: block; width: 100%; margin-top: .5rem; padding: .55rem;
             background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
             font: inherit; font-size: .78rem; font-weight: 700; color: var(--accent);
             cursor: pointer; transition: border-color .14s ease; }
.card-more:hover { border-color: var(--accent-line); }
.card-more[hidden] { display: none; }
.card-more-done, .card-more-done:hover { color: var(--faint); font-weight: 600;
                                         cursor: default; border-color: var(--line); }

/* Twelve fits without scrolling; the cap only matters once you ask for more. */
.card-results { max-height: 420px; }

/* Results on the left, the enlargement in a column of its own on the right.
   Reserved rather than floating: a panel that appears over the list covers
   the cards you are trying to compare, and jumps as you move between them. */
.card-browse { display: flex; gap: .7rem; align-items: flex-start; margin-top: .5rem; }
.card-browse[hidden] { display: none; }
.card-browse .card-results { flex: 1 1 auto; min-width: 0; margin-top: 0; }

.card-zoom { flex: 0 0 168px; width: 168px; position: sticky; top: .25rem;
             background: var(--wash); border: 1px solid var(--line); border-radius: 10px;
             padding: .5rem; }
.card-zoom-hint { margin: 0; padding: 1.4rem .4rem; font-size: .7rem; line-height: 1.5;
                  color: var(--faint); text-align: center; }

/* The frame holds its shape before the scan arrives, so nothing jumps when
   it does — a card is 63×88mm and the panel is that ratio from the start. */
.card-zoom-frame { position: relative; display: block; width: 100%; aspect-ratio: 63 / 88;
                   border-radius: 6px; overflow: hidden; background: var(--panel);
                   border: 1px solid var(--line); }
.card-zoom-frame img { display: block; width: 100%; height: 100%; object-fit: cover;
                       opacity: 1; transition: opacity .18s ease; }
.card-zoom-frame img.is-loading { opacity: 0; }
.card-zoom-frame.is-ready .card-zoom-spinner { display: none; }

.card-zoom-spinner { position: absolute; inset: 0; margin: auto; width: 22px; height: 22px;
                     border: 2px solid var(--line-2); border-top-color: var(--accent);
                     border-radius: 50%; animation: card-zoom-spin .7s linear infinite; }
@keyframes card-zoom-spin { to { transform: rotate(360deg); } }

/* It never arrived — a dead link, a slow CDN, an office firewall. Say so in
   the space rather than leaving a grey rectangle or a spinner that never
   stops. */
.card-zoom-frame.is-missing::after {
  content: "—"; position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: var(--faint); font-size: 1.4rem; }
.card-zoom-frame.is-missing .card-zoom-spinner { display: none; }

.card-zoom-name { display: block; margin-top: .4rem; font-size: .72rem; font-weight: 700;
                  text-align: center; color: var(--ink); line-height: 1.3;
                  overflow: hidden; text-overflow: ellipsis; }

.card-preview { display: flex; gap: .9rem; margin-top: .7rem; padding: .8rem;
                background: var(--wash); border: 1px solid var(--line); border-radius: var(--r); }
.card-preview[hidden] { display: none; }
.card-preview img { width: 84px; border-radius: 6px; align-self: flex-start;
                    box-shadow: 0 6px 16px -8px rgba(20, 22, 26, .5); }
.card-preview-body { display: flex; flex-direction: column; gap: .25rem; align-items: flex-start; }
.card-preview-body > b { font-size: .92rem; }
.card-preview-body > span { font-size: .76rem; color: var(--sub); }
.card-preview-price { font-size: .8rem; font-weight: 700; color: var(--ink); }
/* Somebody else's number, and it says so. */
.card-preview-price span { font-weight: 600; font-size: .66rem; color: var(--faint);
                           text-transform: uppercase; letter-spacing: .05em; margin-left: .3rem; }
.card-preview-body .btn { margin-top: .3rem; }

/* A field the schema does not apply to this request type is gone, not greyed. */
[data-spec-fields-target="field"][hidden] { display: none; }

/* ── The deadline, as three answers ─────────────────────────────────────────
   There was a date input here with a track drawn under it, because a calendar
   enforces min/max by greying out days you can only find by bumping into them,
   and the window had to be shown some other way. Three chips show it outright:
   the choosable periods ARE the options. The picker and its track went with
   the question they were answering.

   A radio group wearing chips, so the selected state is real state rather than
   a class somebody has to keep in sync. input:checked + span rather than
   :has(), because the selected state is the whole control and should not
   depend on a selector; the input is present and focusable, just not painted. */
.dw-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.dw-choice { display: inline-flex; position: relative; }
.dw-choice input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
.dw-chip { font: inherit; font-size: .74rem; padding: .3rem .8rem; border-radius: 999px;
           border: 1px solid var(--line-2); background: var(--panel); color: var(--sub);
           cursor: pointer; transition: border-color .15s, color .15s, background .15s; }
.dw-chip:hover { border-color: var(--accent); color: var(--accent-deep); }
.dw-choice input:checked + .dw-chip { border-color: var(--accent); color: var(--accent-deep);
                                      font-weight: 700; }
.dw-choice input:focus-visible + .dw-chip { outline: 2px solid var(--accent);
                                            outline-offset: 2px; }

/* ── Category picker ───────────────────────────────────────────────────────
   The first decision in the request flow. It used to be three grey buttons on
   an otherwise empty page, which read as a placeholder — so it is the page
   now: what each category is, drawn, and what it is closing at. The artwork
   is SVG on purpose (see CategoryArtHelper): a photograph of a card is
   somebody else's property, and these tiles sit in front of the shops that
   trade in it. */
.picker-lead { color: var(--sub); font-size: .95rem; max-width: 56ch; margin: -.4rem 0 1.4rem; }

/* The second picker keeps the breadcrumb on the same line as the question,
   so the step you are on and the step you came from read together. */
.picker-back { display: block; margin-top: .45rem; font-size: .82rem; color: var(--faint); }
.picker-back strong { color: var(--sub); font-weight: 700; }

.cat-picker { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
              gap: 1.1rem; align-items: stretch; }
@media (max-width: 1000px) { .cat-picker { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .cat-picker { grid-template-columns: minmax(0, 1fr); } }

.cat-card { display: flex; flex-direction: column; overflow: hidden;
            background: var(--panel); border: 1px solid var(--line);
            border-radius: var(--r); text-decoration: none; color: var(--ink);
            transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
.cat-card:hover { transform: translateY(-3px); border-color: var(--accent-line);
                  box-shadow: 0 14px 30px -14px rgba(20, 22, 26, .28); color: var(--ink); }
.cat-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.cat-art { display: block; position: relative; overflow: hidden; border-bottom: 1px solid var(--line); }
.cat-art img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
/* The foil sweep the collector layer uses elsewhere, on hover only. */
.cat-art::after { content: ""; position: absolute; inset: 0; opacity: 0;
                  background: linear-gradient(105deg, transparent 35%, rgba(255,255,255,.55) 50%, transparent 65%);
                  transition: opacity .2s ease, transform .5s ease; transform: translateX(-30%); }
.cat-card:hover .cat-art::after { opacity: 1; transform: translateX(30%); }

.cat-body { display: flex; flex-direction: column; gap: .3rem; padding: 1rem 1.1rem 1.1rem; flex: 1; }
.cat-body > b { font-size: 1.02rem; font-weight: 800; letter-spacing: -.01em; }
.cat-blurb { color: var(--sub); font-size: .8rem; line-height: 1.5; }
.cat-go { margin-top: auto; padding-top: .9rem; border-top: 1px solid var(--line);
          font-size: .78rem; font-weight: 700; color: var(--accent); }
.cat-go::after { content: " →"; transition: margin-left .16s ease; }
.cat-card:hover .cat-go::after { margin-left: .2rem; }

/* Flat surface, no lift, no sweep. */
body.jp .cat-card { border-radius: 4px; }
body.jp .cat-card:hover { transform: none; box-shadow: none; }
body.jp .cat-art::after { display: none; }

/* A one-line form inside a table cell: evidence note + button on one row. */
.inline-declare { display: flex; gap: .4rem; justify-content: flex-end; margin: 0; }
.inline-declare input { width: min(220px, 100%); }
.inline-declare .btn { white-space: nowrap; }
/* ── The market screen ──────────────────────────────────────────────────── */
/* Four sections down one column, each with a heading and a line of context.
   They are h3s rather than page-heads because there is one page here, not
   four; the rule above each is what does the separating. */
.market-head { margin: 2.2rem 0 .3rem; font-size: 1.05rem; padding-top: 1.4rem;
               border-top: 1px solid var(--line); }
.market-head:first-of-type { border-top: 0; padding-top: 0; margin-top: 1.6rem; }
.market-sub { max-width: 46rem; font-size: .82rem; margin: 0 0 1.1rem; }

/* Demand as counts, never as rows — a list of open requests is somebody's
   order book (see MarketPulseService). The card is a link to the form with
   that category chosen: the honest thing to do with "eleven shops are asking
   for cards" is to let the reader add the twelfth. */
.demand-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
               gap: .8rem; margin-bottom: .4rem; }
.demand-card { display: flex; flex-direction: column; gap: .15rem;
               background: var(--panel); border: 1px solid var(--line);
               border-radius: var(--r); padding: .9rem 1rem; text-decoration: none;
               transition: border-color .15s, transform .15s; }
.demand-card:hover { border-color: var(--accent-line); transform: translateY(-1px); }
.demand-cat { font-weight: 700; font-size: .88rem; color: var(--ink); }
.demand-open { font-size: 1.15rem; font-weight: 800; color: var(--accent-deep);
               letter-spacing: -.01em; }
.demand-offers { font-size: .76rem; color: var(--sub); }

/* Does anyone answer: three ratios, deliberately not styled like the pulse bar
   above them. That one counts things; this one is the platform reporting on
   its own behaviour, which is a different claim and earns a different box. */
.figure-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
              gap: 1px; margin-bottom: .4rem; background: var(--line);
              border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.figure { background: var(--wash); padding: 1rem 1.2rem; }
.fig-num { font-size: 1.5rem; font-weight: 800; line-height: 1.1; letter-spacing: -.02em;
           color: var(--ink); }
.fig-label { margin-top: .2rem; font-size: .74rem; color: var(--sub); }

.showcase-strip { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
                  padding: 1.1rem 1.3rem 1.3rem; margin-bottom: 1.2rem; }
.showcase-strip h3 { margin-bottom: .9rem; }
.accent-bar { height: 3px; background: var(--accent); border-radius: 2px; width: 34px;
              margin-bottom: 1rem; }
.auth-head-center { text-align: left; }
.upload-zone { border: 1px dashed var(--line-2); border-radius: 7px; padding: .8rem 1rem;
               background: var(--wash); font-size: .8rem; color: var(--sub); }

/* ── The curtain ─────────────────────────────────────────────────────────
   Holds the public page until window.load, so the landing does not assemble
   itself in front of the reader — Playfair arriving late and reflowing three
   lines of headline, a hero that is blank and then is a city.

   Everything here hangs off `html.is-loading`, which is added by an inline
   script in the head and removed by one at the end of the body. If neither
   runs — no JavaScript, a 404 on the bundle — the class is never added and
   none of this applies, so the page is simply visible. That is the right way
   round: a curtain that depends on script to LIFT must also depend on script
   to FALL, or a reader without it waits forever.

   Ripples on water rather than a spinner: the hero is a city on a river and
   every drawing on the page is watercolour. Three discs on one delay ladder,
   expanding and fading the way a dropped stone reads.
   ───────────────────────────────────────────────────────────────────────── */
.curtain { display: none; }
.is-loading .curtain {
  display: flex; position: fixed; inset: 0; z-index: 999;
  align-items: center; justify-content: center; background: var(--bg);
}
/* Hidden, not undisplayed: the layout is still computed and the browser still
   fetches what it needs, so `load` means what we want it to mean. */
.is-loading main.container { visibility: hidden; }
.is-loaded .curtain { opacity: 0; pointer-events: none; transition: opacity .45s ease; }

.curtain-mark { display: flex; flex-direction: column; align-items: center; gap: 1.9rem; }
.curtain-mark .brandmark { opacity: 0; animation: curtain-word .7s .15s ease-out forwards; }

.curtain-sun { position: relative; display: block; width: 96px; height: 96px; }
.curtain-sun svg { position: absolute; inset: 0; width: 100%; height: 100%;
                   transform: rotate(-125deg); }
/* r=40 -> circumference 251.3. The dash is 232 of it, so ~19 units never get
   painted and the ring closes with a gap where the brush left the paper. The
   offset runs the full circumference, which is what draws it. */
.curtain-enso {
  stroke: var(--accent); stroke-width: 4.5;
  stroke-dasharray: 232 251.3; stroke-dashoffset: 251.3;
  animation: curtain-draw 2.6s cubic-bezier(.5, .05, .3, 1) infinite;
}
/* Two ripples spreading past the ensō, as if it were drawn on water. */
.curtain-sun i {
  position: absolute; inset: 6%; border-radius: 50%;
  border: 1.5px solid var(--accent-line); opacity: 0;
  animation: curtain-ripple 2.6s cubic-bezier(.22, .61, .36, 1) infinite;
}
.curtain-sun i:nth-of-type(2) { animation-delay: 1.3s; }
/* The hinomaru at the centre, breathing. */
.curtain-sun::after {
  content: ""; position: absolute; inset: 37%; border-radius: 50%;
  background: var(--accent); animation: curtain-breathe 2.6s ease-in-out infinite;
}
@keyframes curtain-draw {
  0%   { stroke-dashoffset: 251.3; opacity: .25; }
  55%  { stroke-dashoffset: 19.3;  opacity: 1; }
  80%  { stroke-dashoffset: 19.3;  opacity: 1; }
  100% { stroke-dashoffset: 19.3;  opacity: 0; }
}
@keyframes curtain-ripple {
  0%   { transform: scale(.9); opacity: 0; }
  25%  { opacity: .7; }
  100% { transform: scale(1.5); opacity: 0; }
}
@keyframes curtain-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(.8); opacity: .7; }
}
@keyframes curtain-word { to { opacity: 1; } }

/* Reduced motion keeps the curtain — it is doing a real job — and stops the
   animation rather than the reveal. */
@media (prefers-reduced-motion: reduce) {
  .curtain-sun i { display: none; }
  .curtain-enso { animation: none; stroke-dashoffset: 19.3; opacity: .55; }
  .curtain-sun::after, .curtain-mark .brandmark { animation: none; opacity: 1; }
  .is-loaded .curtain { transition: none; }
}

/* ── Public landing ────────────────────────────────────────────────────── */
/* The public pages are edge-to-edge: the coloured bands (nav, hero, alternate
   sections, footer) must reach the viewport edge, not stop at the app shell's
   1240px column. The negative margins below only cancel the container's own
   padding — on a screen wider than the container they would still leave a
   white gutter, so the container itself is unconstrained on these pages. */
main.container:has(> .landing),
main.container:has(> .legal-page) { max-width: none; padding: 0; }
main.container:has(> .landing) > .landing { margin: 0; }
main.container:has(> .legal-page) > .legal-page { margin: 0; min-height: 100vh; }

.landing { margin: -1.4rem -1.8rem 0; background: var(--bg); }

/* ── The swash ───────────────────────────────────────────────────────────
   Every heading on the comps is underscored by a short brush stroke in the
   accent — drawn, tapered, sitting slightly off-square. One SVG, inlined so
   it costs no request, tinted by re-drawing rather than by filter (the
   accent is a token; a hue-rotate would not follow it). It is decoration,
   so it lives in a pseudo-element and never reaches the accessibility tree. */
.swash::after, .l-hero h1::after, .l-sec-head h2::after {
  content: ""; display: block; height: 9px; width: 74px; margin-top: .55rem;
  background: no-repeat center / 100% 100%
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 74 9'%3E%3Cpath d='M2 6.4C14 3.1 30 1.6 45 2.1c9 .3 18 1.4 27 3.2' fill='none' stroke='%23dd6337' stroke-width='3.2' stroke-linecap='round'/%3E%3C/svg%3E"); }
.l-sec-head h2::after { margin-left: auto; margin-right: auto; }

.l-nav { display: flex; align-items: center; gap: 1.4rem; padding: 1rem 2.4rem;
         background: color-mix(in srgb, var(--bg) 88%, transparent);
         -webkit-backdrop-filter: saturate(180%) blur(10px);
         backdrop-filter: saturate(180%) blur(10px);
         border-bottom: 1px solid transparent;
         position: sticky; top: 0; z-index: 40; }
/* Section anchors are the first thing to give when the labels are English or
   French rather than Japanese — the same links live in the footer. */
@media (max-width: 1240px) { .l-nav > a[href^="#"] { display: none; } }
@media (max-width: 1000px) { .l-nav .seg-label { display: none; } }
.l-nav a { font-weight: 500; font-size: .92rem; color: var(--ink); white-space: nowrap; }
.l-nav a:hover { color: var(--accent); }
.l-nav .btn { color: #fff; } .l-nav .btn-secondary { color: var(--ink); }
.l-spacer { flex: 1; }
.l-langs { font-size: .78rem; color: var(--faint); }
.l-langs a { color: var(--sub); font-weight: 600; }

/* ── Hero: the pitch ON the picture ──────────────────────────────────────
   hero.png (2026-08-14) puts the drawing BEHIND the words rather than in a
   column beside them, running off the top and right edges of the viewport —
   so this is a full-bleed backdrop with the sentence laid over its quiet end,
   not the two-column grid it replaced.

   One image for every surface. The art carries no language beyond "TOKYO",
   so the English, French and Japanese landings open on the same picture; the
   two per-side drawings it replaces were saying the same thing twice, in two
   different hands. */
.l-hero { position: relative; overflow: hidden; max-width: none; margin: 0;
          padding: 3.6rem 0 3.4rem; text-align: left;
          background: none; border-bottom: none; }
.l-hero-say { position: relative; z-index: 1;
              max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
/* Not a column — a measure. The backdrop runs on underneath and is simply
   pale where the words are. */
.l-hero-say > * { max-width: 32rem; }
.l-hero h1 { font-family: var(--font-display); font-size: 3.15rem; font-weight: 700;
             letter-spacing: -.012em; line-height: 1.1; margin: 0; max-width: 16ch; }
.l-sub { color: var(--sub); font-size: 1.02rem; line-height: 1.72; max-width: 40ch;
         margin: 1.5rem 0 1.7rem; }

/* Anchored to the COLUMN, not the viewport. Pinning it to the right edge of
   the window was wrong the moment the window grew wider than the measure: the
   words stop at the --shell column while the picture keeps walking right, and
   past about 1600px the two stop being one composition — a headline on the
   left and a skyline stranded across a widening empty stripe. Both the offset
   and the width are struck from --shell, so the hero holds its proportions at
   every width and simply stops growing when the column does.

   No z-index here, deliberately. A positioned element WITH one is a stacking
   context, and mix-blend-mode blends against the backdrop of its
   stacking-context ancestor — so `z-index: 0` on this figure isolated the
   image, which then painted its own near-white over the warm page instead of
   multiplying into it. Invisible at the top of the tall English hero, a
   plainly visible pale rectangle on the shorter Japanese one. Position alone
   is enough to sit it behind .l-hero-say, which carries the z-index. */
.l-hero-art { position: absolute; margin: 0; pointer-events: none;
              top: 0; bottom: 0;
              right: calc(max(0px, (100% - var(--shell)) / 2) + var(--gutter));
              width: min(72%, calc(var(--shell) * .66)); }
.l-hero-art img { display: block; width: 100%; height: 100%;
                  object-fit: contain; object-position: right center;
                  mix-blend-mode: multiply; }
/* The scrim. Watercolour is low-contrast but it is not nothing, and body copy
   over the map's coastline is the one place laying type on a picture could
   go wrong — so the art's near end is faded into the page colour, rather than
   the type being given a shadow it would have to wear everywhere. Same idea
   turned ninety degrees on phones, where the words sit under the picture
   instead of beside it.
   Kept SHORT on purpose: the text column ends around a fifth of the way into
   the art, so the fade is done by a third. Reaching further washed the map
   out and left "TOKYO" a grey smudge, which is the opposite of the job. */
.l-hero-art::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--bg) 0%, var(--bg) 7%,
                              color-mix(in srgb, var(--bg) 55%, transparent) 19%,
                              transparent 33%); }
/* A phone gets the same idea as a desktop — words ON the picture — turned
   ninety degrees: the art is a full-bleed band across the TOP of the hero and
   the type sits over it, rather than a column beside it.

   It briefly shipped as a stacked layout (art in normal flow, words beneath),
   which was legible but wrong: it made the phone a different page from the
   desktop instead of the same page narrower. Back to a backdrop, with the
   scrim doing the work it does on desktop — turned from horizontal to
   vertical, and pitched harder, because here the type crosses the drawing
   rather than skirting its pale end. */
@media (max-width: 940px) {
  .l-hero { padding: 1.4rem 0 2.6rem; }
  /* Smaller, and one line tighter: every line of headline is a line the
     backdrop has to stay quiet under. */
  .l-hero h1 { font-size: 2.05rem; line-height: 1.14; max-width: none; }
  .l-sub { margin-top: 1.1rem; }
  .l-hero-say > * { max-width: none; }
  /* Full-bleed against 50vw rather than a hard-coded inset, so it reaches the
     screen edges whatever gutter an ancestor is carrying — the phone rules
     further down have changed that number twice. Height auto: the band is as
     tall as the drawing wants to be, and the hero grows past it. */
  .l-hero-art { position: absolute; top: 0; bottom: auto; right: auto; left: 0;
                width: 100vw; margin-left: calc(50% - 50vw); }
  .l-hero-art img { height: auto; object-position: center top; }

  /* The scrim moves off the art and onto the HERO here, and that is the whole
     trick. The art is a fixed ratio at full viewport width, so its height is
     whatever the screen makes it, while the hero's height is however tall the
     words happen to be — and the two do not match. Scrimmed against the art,
     the fade finished at the art's own bottom edge: on a 760 tablet, where the
     drawing is taller than the copy, `overflow: hidden` then sliced it off
     mid-building in a dead straight line. Anchored to the hero, the fade
     always lands in paper exactly where the section ends, whichever of the two
     is taller.

     The stops are set by the BODY COPY, not by the headline. --sub on paper is
     only 5.74:1 to begin with, so it has almost no contrast to spend: a scrim
     gentle enough to leave the drawing showing under the paragraph measured
     3.7:1, which fails AA. Reaching near-paper by the time the paragraph
     starts brings it to 4.95:1 — measured as the 2nd percentile of the actual
     rendered backdrop, worst case across en/fr/ja at 320–940. The headline,
     which has 17:1 to spend, keeps the drawing behind it at ~9.8:1.

     So the shape is: the art reads behind the big serif, and is paper by the
     time anything small is set on it. Re-measure if these stops change —
     nothing here is safe by eye. */
  .l-hero-art::after { display: none; }
  .l-hero::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(180deg,
                color-mix(in srgb, var(--bg) 44%, transparent) 0%,
                color-mix(in srgb, var(--bg) 55%, transparent) 13%,
                color-mix(in srgb, var(--bg) 94%, transparent) 29%,
                var(--bg) 45%); }
}

/* The kicker is the line that says what this place is before the headline
   lands. The comps drop it above the hero — it survives as the small print
   under the buttons and in the manifest — but the app still sets kickers on
   section heads, so the class keeps its landing sizing. */
.l-hero .kicker { font-size: .95rem; letter-spacing: .11em; margin-bottom: .7rem; }
body.jp .l-hero .kicker { font-size: 1rem; letter-spacing: .04em; }

/* Trade-only, said once, above the first button. A private collector who
   registers costs a manual review and gets a rejection either way; better to
   be turned away by a sentence than by an email three days later.

   A 999px radius only reads as a pill while the text is on ONE line. On two
   it becomes a lozenge with 28px end-caps and an orphan word hard against the
   left padding — which is what the seller hero showed at every desktop width,
   because its sentence was fourteen characters longer than the buyer's. Two
   changes, because both cases are real: the copy is cut to sit on one line at
   the measure (landing.seller.hero_trade), AND the box is made to survive
   wrapping, since every phone wraps it whatever the copy says. Centred and
   balanced, two lines read as a shape somebody chose.

   `inline-block`, not the `inline-flex` it was: the gap was there for an icon
   none of the three usages passes, and a flex container gives the anonymous
   text item nothing to balance against. */
.l-trade { display: inline-block; text-align: center; text-wrap: balance;
           margin: 0 0 1.4rem; padding: .45rem 1.1rem; border-radius: 999px;
           border: 1px solid var(--accent-line); background: var(--accent-soft);
           color: var(--accent-deep); font-size: .8rem; font-weight: 600;
           line-height: 1.5; }
.l-ctas { margin: 0; }
.l-ctas .btn { margin: 0 .5rem 0 0; }

/* The three assurances under the button: the objections a shop owner has
   before they will type an email address, answered in six words each. Drawn
   as outlined glyphs on the comps — line icons here, one token deep, so they
   follow the accent instead of being three more files to re-export. */
/* Three across, and they have to STAY three across: the hero text now sits on
   the picture, so a row that wraps does not just look untidy, it pushes the
   third item out over the map.

   A grid of three equal tracks, not a flex row with a max-width per item.
   The max-width was in `ch` — the width of a "0" — which is a guess in Latin
   and meaningless in Japanese, and it guessed short: "Secure payment until
   shipment" came out over four lines. Equal tracks give each label the same
   real measure, and the row is slightly wider than the 32rem the rest of the
   column takes so two lines is enough for the longest string in all three
   languages. Two lines is the contract; if a translation ever needs three,
   shorten the translation. */
.l-assure { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
            gap: 1.1rem 1.1rem; margin: 2.4rem 0 0; padding: 0; list-style: none;
            max-width: 35rem; }
.l-assure li { display: flex; align-items: center; gap: .55rem;
               font-size: .8rem; line-height: 1.35; color: var(--sub);
               text-wrap: balance; }
.l-assure .ico { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
                 border: 1px solid var(--line-2); background: var(--panel) no-repeat center / 17px;
                 display: block; }
/* Below ~560 three tracks are too narrow to hold the longest label in two
   lines, so the row stacks. One column rather than two: two leaves the third
   item alone on its own row, which reads as a wrap rather than a choice. */
@media (max-width: 560px) { .l-assure { grid-template-columns: minmax(0, 1fr); } }
.l-assure li:nth-child(1) .ico { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2.6 4.5 5.6v5.7c0 4.6 3.1 8.4 7.5 9.9 4.4-1.5 7.5-5.3 7.5-9.9V5.6z'/%3E%3Cpath d='m8.9 12 2.1 2.1 4.1-4.4'/%3E%3C/svg%3E"); }
.l-assure li:nth-child(2) .ico { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10.4' width='16' height='10.4' rx='2'/%3E%3Cpath d='M7.8 10.4V7.6a4.2 4.2 0 0 1 8.4 0v2.8'/%3E%3Cpath d='M12 14.6v2'/%3E%3C/svg%3E"); }
.l-assure li:nth-child(3) .ico { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 7.6 12 3.2l8.8 4.4v8.8L12 20.8l-8.8-4.4z'/%3E%3Cpath d='m3.2 7.6 8.8 4.4 8.8-4.4M12 12v8.8'/%3E%3C/svg%3E"); }

.l-stats { display: flex; justify-content: center; gap: 3.2rem; margin-top: 2.6rem; }
.l-stats b { display: block; font-family: var(--font-display); font-size: 1.9rem; font-weight: 700; }
.l-stats span { font-size: .7rem; font-weight: 600; text-transform: uppercase;
                letter-spacing: .09em; color: var(--sub); }

.l-sec { max-width: var(--shell); margin: 0 auto; padding: 4rem var(--gutter); }
/* The alternating band is WARMER paper, not whiter — see the --wash note at
   the top of the file. It is full-bleed, so it cancels the section's own
   max-width and re-imposes it on the children. */
.l-sec-alt { max-width: none; background: var(--wash);
             border-top: 1px solid transparent; border-bottom: 1px solid transparent; }
.l-sec-alt > * { max-width: var(--shell); margin-left: auto; margin-right: auto; }
.l-sec-head { text-align: center; margin-bottom: 2.6rem; }
.l-sec-head h2 { font-family: var(--font-display); font-size: 2rem; font-weight: 700;
                 letter-spacing: -.008em; margin: .2rem 0; }
.l-sec-head p { color: var(--sub); font-size: .95rem; max-width: 62ch; margin: .9rem auto 0; }
@media (max-width: 700px) { .l-sec-head h2 { font-size: 1.6rem; } }

/* ── The journey, drawn ──────────────────────────────────────────────────
   The four step cards carry quentin's illustrations. The images number
   themselves, so the pip row and the connecting rail hide on this variant;
   the frame keeps the file's own 4:3 ratio so nothing is trimmed. */
.l-steps.with-art::before { display: none; }
.l-steps.with-art .l-step { padding: 0; overflow: hidden; }
.l-step-art { display: block; border-bottom: 1px solid var(--line); position: relative; }
/* At rest the scene is calm; the manga exclamations live on a second copy
   of the drawing that fades in when the card is hovered. */
.l-step-art .art-live { position: absolute; inset: 0; opacity: 0; transition: opacity .28s ease; }
.l-step:hover .art-live, .l-step:focus-within .art-live { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .l-step-art .art-live { transition: none; } }
.l-step-art img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.l-step-body { display: block; padding: .95rem 1.15rem 1.15rem; }
.l-step-body b { font-size: .95rem; }
.l-step-body p { color: var(--sub); font-size: .84rem; margin: .4rem 0 0; }

/* One arrow per gap, riding the image band. The container is the grid, so
   the three arrows sit at the column boundaries regardless of card height. */
.l-steps.with-art { position: relative; }
/* These four wear quentin's illustrations — the holo cap on top read as one
   effect too many ("pokemon style"). A plain accent line does the job. */
.l-steps.with-art .l-step::before { display: none; }
.l-steps.with-art .l-step { border-top: 2px solid var(--accent); }
/* ...and the foil hover sweep goes with it — no shine pass over the
   illustrations, no lift. These four cards are pictures, not cards. */
.l-steps.with-art .l-step::after { display: none; }
.l-steps.with-art .l-step:hover { transform: none; box-shadow: none; }
.l-step-next { position: absolute; top: 19%; width: 30px; height: 30px;
  border-radius: 50%; transform: translate(-50%, -50%); z-index: 2;
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 12h13m-6-6 6 6-6 6' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 15px no-repeat;
  box-shadow: 0 0 0 3px var(--bg), 0 4px 10px -4px rgba(221, 99, 55, .5); }
.l-step-next:nth-of-type(1) { left: calc(25% - .3rem); }
.l-step-next:nth-of-type(2) { left: 50%; }
.l-step-next:nth-of-type(3) { left: calc(75% + .3rem); }
@media (max-width: 900px) { .l-step-next { display: none; } }

/* ── The categories, drawn ───────────────────────────────────────────────
   Three tiles of invented objects — the public-safe stand-in for the retired
   deal showcase. Same card grammar as everything else on the page. */
.cats-strip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
@media (max-width: 900px) { .cats-strip { grid-template-columns: 1fr; } }
.cats-tile { margin: 0; background: var(--panel); border: 1px solid var(--line);
  border-radius: 14px; overflow: hidden; box-shadow: 0 1px 2px rgba(20, 24, 30, .04); }
/* The tile art is a drawing on paper, not a photograph: it sits ON the card
   rather than bleeding to its edges, and multiplies so its own paper takes
   the card's white instead of stacking a second off-white rectangle on it. */
.cats-art { display: block; padding: 1.2rem 1.2rem 0; }
.cats-art img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 10;
  object-fit: contain; mix-blend-mode: multiply; }
.cats-tile figcaption { display: block; padding: 1rem 1.3rem 1.4rem; }
.cats-tile figcaption b { display: block; font-size: 1rem; letter-spacing: -.005em; }
.cats-tile figcaption span { display: block; color: var(--sub); font-size: .84rem;
  line-height: 1.6; margin-top: .35rem; }

.l-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; }
@media (max-width: 900px) { .l-steps { grid-template-columns: repeat(2, 1fr); } }
.l-step { background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
          padding: 1.4rem 1.5rem; box-shadow: 0 1px 2px rgba(20, 24, 30, .04); }
.l-sec-alt .l-step { background: var(--panel); }
/* The step numeral takes the illustrations' indigo, not the accent: on the
   comps the accent is reserved for what you can click. */
.l-num { width: 30px; height: 30px; border-radius: 50%; background: var(--brand-navy); color: #fff;
         font-weight: 700; font-size: .84rem; display: flex; align-items: center;
         justify-content: center; margin-bottom: .9rem; }
.l-step b { font-size: .98rem; }
.l-step p, .l-col p, .l-trust-card p { color: var(--sub); font-size: .86rem;
                                       line-height: 1.62; margin: .45rem 0 0; }
.l-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
@media (max-width: 900px) { .l-cols { grid-template-columns: 1fr; } }
.l-col { background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
         padding: 1.5rem 1.6rem; box-shadow: 0 1px 2px rgba(20, 24, 30, .04); }

/* ── Trust: four claims, each with its glyph ─────────────────────────────
   Centred cards on the comps, each opening with an outlined drawing on a
   soft disc. The glyphs are inline SVG on the ::before rather than five more
   image files — they are line art at one weight, which is exactly what a
   data-URI is good for, and they stay crisp at any density. */
.l-trust { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.4rem; }
@media (max-width: 900px) { .l-trust { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .l-trust { grid-template-columns: 1fr; } }
.l-trust-card { background: var(--panel); border: 1px solid var(--line);
                border-radius: 14px; padding: 1.9rem 1.4rem 1.7rem; text-align: center;
                box-shadow: 0 1px 2px rgba(20, 24, 30, .04); }
.l-trust-card b { display: block; font-size: .98rem; }
.l-trust-card::before { content: ""; display: block; width: 62px; height: 62px;
  margin: 0 auto 1.1rem; border-radius: 50%;
  background: var(--wash) no-repeat center / 32px; }
.l-trust-card:nth-child(1)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2.4 4.4 5.5v5.9c0 4.7 3.1 8.6 7.6 10.2 4.5-1.6 7.6-5.5 7.6-10.2V5.5z'/%3E%3Cpath d='M12 8.4c-1.2 1.6-2 2.7-2 3.8a2 2 0 0 0 4 0c0-1.1-.8-2.2-2-3.8z'/%3E%3C/svg%3E"); }
.l-trust-card:nth-child(2)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.4 8.6h17.2L18.9 4H5.1z'/%3E%3Cpath d='M4.6 8.6V20h14.8V8.6'/%3E%3Cpath d='M9.4 20v-5.4h5.2V20'/%3E%3Cpath d='M3.4 8.6c0 1.4 1 2.4 2.2 2.4s2.2-1 2.2-2.4c0 1.4 1 2.4 2.2 2.4s2.2-1 2.2-2.4c0 1.4 1 2.4 2.2 2.4s2.2-1 2.2-2.4c0 1.4 1 2.4 2.2 2.4s2.2-1 2.2-2.4'/%3E%3C/svg%3E"); }
.l-trust-card:nth-child(3)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8a3 3 0 0 1 3-3h6.4a3 3 0 0 1 3 3v2.6a3 3 0 0 1-3 3H8l-3.4 2.6v-2.6H6a3 3 0 0 1-3-3z'/%3E%3Cpath d='M17.6 9.2h.4a3 3 0 0 1 3 3v2.6a3 3 0 0 1-3 3h-.6L14 20.4v-2.6'/%3E%3C/svg%3E"); }
.l-trust-card:nth-child(4)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232c5580' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.6 2.8h8L19 8.2v13H5.6z'/%3E%3Cpath d='M13.6 2.8v5.4H19'/%3E%3Cpath d='M8.4 12.2h1.8m2.4 0h3.6M8.4 16.2h1.8m2.4 0h3.6'/%3E%3C/svg%3E"); }

/* Testimonials: the quote, then the verdict, then who said it. */
.l-quotes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
@media (max-width: 1000px) { .l-quotes { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px) { .l-quotes { grid-template-columns: 1fr; } }
.l-quote { background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
           padding: 1.5rem 1.5rem 1.4rem; margin: 0; display: flex; flex-direction: column;
           gap: .8rem; box-shadow: 0 1px 2px rgba(20, 24, 30, .04); }
.l-quote::before { content: "\201C"; font-family: var(--font-display); font-size: 2.6rem;
                   line-height: .7; color: var(--accent); height: .5em; }
.l-quote .stars { color: var(--accent); font-size: .92rem; letter-spacing: .1em; }
.l-quote .stars .off { color: var(--line-2); }
.l-quote blockquote { margin: 0; font-size: .9rem; line-height: 1.62; flex: 1; }
.l-quote figcaption { display: flex; align-items: center; gap: .6rem;
                      font-size: .78rem; color: var(--sub); }
.l-quote figcaption b { display: block; color: var(--ink); font-size: .84rem; }
/* The initials disc stands in for a logo we do not have and would not be
   allowed to publish anyway. Indigo, so it is not a fourth accent. */
.l-quote .who { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
                background: var(--brand-navy); color: #fff; font-size: .74rem; font-weight: 700;
                display: flex; align-items: center; justify-content: center; letter-spacing: .02em; }

/* ── FAQ: the list, and on the buyer page a drawing beside it ──────────── */
.l-faq-wrap { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
              align-items: center; gap: 2.4rem; }
.l-faq-art { margin: 0; }
.l-faq-art img { display: block; width: 100%; height: auto; mix-blend-mode: multiply; }
@media (max-width: 900px) { .l-faq-wrap { grid-template-columns: 1fr; } .l-faq-art { display: none; } }
.l-faq { margin: 0; }
.l-faq details { background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
                 padding: .95rem 1.3rem; margin-bottom: .55rem; }
.l-faq summary { font-weight: 600; cursor: pointer; font-size: .92rem;
                 list-style: none; display: flex; align-items: center; gap: 1rem; }
.l-faq summary::-webkit-details-marker { display: none; }
/* The marker is the affordance, so it is drawn rather than left to the UA
   triangle: a plus that becomes a minus, pushed to the right edge. */
.l-faq summary::after { content: ""; flex: 0 0 auto; margin-left: auto; width: 13px; height: 13px;
  background: no-repeat center / 13px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314181e' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E"); }
.l-faq details[open] summary::after { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314181e' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E"); }
.l-faq p { color: var(--sub); font-size: .87rem; line-height: 1.65; margin: .7rem 0 0; }

/* ── Closing band: a panel, not a billboard ──────────────────────────────
   It used to be a full-bleed slab of accent with white type. The comps make
   it an inset card in a pale wash with the shop drawn at its right end — the
   accent kept for the button, which is the only thing being asked for. */
.l-cta-band { max-width: var(--shell); margin: 1rem auto 3.4rem; padding: 0 var(--gutter);
              background: none; color: inherit; text-align: left; }
.l-cta-inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .78fr);
               align-items: center; gap: 1.5rem;
               background: #fbf4ea; border-radius: 16px; padding: 2.4rem 2.6rem; overflow: hidden; }
.l-cta-band h2 { font-family: var(--font-display); color: var(--ink); font-size: 1.85rem;
                 font-weight: 700; margin: 0 0 .5rem; }
.l-cta-band p { opacity: 1; color: var(--sub); font-size: .95rem; margin: 0 0 1.5rem; }
/* The skyline is drawn on its waterline, so it hangs off the bottom and the
   right of the panel rather than sitting inside it as a picture would. */
.l-cta-art { margin: -2.4rem -2.6rem -2.4rem 0; align-self: stretch;
             display: flex; align-items: center; }
.l-cta-art img { display: block; width: 100%; height: auto; mix-blend-mode: multiply; }
@media (max-width: 820px) {
  .l-cta-inner { grid-template-columns: 1fr; padding: 1.8rem; }
  .l-cta-art { display: none; }
}

/* ── Footer: the mark and its address book ────────────────────────────────
   Three blocks — brand, links, base rule — laid on a two-column grid that
   collapses to one. It was a single flex row of eight children, which held at
   1440 and came apart under it: `.l-spacer` stranded the first link on its own
   line beside the wordmark, the remaining links wrapped three-then-one (and
   two-then-one-then-one in Japanese, where they are longer), and the language
   switch stretched to the full width of the phone. Giving each block its own
   stacking is what fixes that, not more breakpoints. */
/* Two columns with a stated job each.
   This used to be `minmax(0, 1fr) auto`, and `auto` means max-content: the
   link row claimed the width of all six labels on one line, and the brand
   column — minmax(0, 1fr) — took what was left. In English that was enough.
   In French, where the same six labels run a thousand pixels, what was left
   was ninety, and the tagline came out as nine lines of one word.
   A fixed measure for the brand and the remainder for the links puts the
   pressure on the row that is built to wrap. */
.l-foot { display: grid; grid-template-columns: minmax(15rem, 23rem) minmax(0, 1fr);
          align-items: start; gap: 2rem 3rem;
          max-width: var(--shell); margin: 0 auto; padding: 3rem var(--gutter) 2.6rem;
          border-top: 1px solid var(--line); font-size: .86rem; }
.l-foot a { color: var(--sub); font-weight: 500; }
.l-foot a:hover { color: var(--accent); }
.l-foot .brandmark { font-size: 1.6rem; }
.l-foot-brand p { color: var(--sub); font-size: .82rem; line-height: 1.65;
                  margin: .75rem 0 0; max-width: 26em; text-wrap: pretty; }
.l-foot-links { display: flex; flex-wrap: wrap; justify-content: flex-end;
                gap: .7rem 1.7rem; }
/* The base rule: small print on one side, language on the other. */
.l-foot-base { grid-column: 1 / -1; display: flex; align-items: center;
               justify-content: space-between; gap: 1rem 1.5rem; flex-wrap: wrap;
               border-top: 1px solid var(--line); padding-top: 1.4rem; }
.l-foot-base small { color: var(--faint); font-size: .78rem; }
/* Never full-width: the switcher is a control, not a banner. It is a flex item
   here and a grid item on the phone, and both will stretch it given the
   chance. */
.l-foot .lang-switch { flex: 0 0 auto; width: auto; justify-self: start; }

/* 900, not 700. Six links right-aligned over three ragged lines is not better
   than a column, and French gets there a good deal earlier than English —
   which is why this used to look fine on the page it was built on. */
@media (max-width: 900px) {
  .l-foot { grid-template-columns: minmax(0, 1fr); gap: 1.8rem; padding-top: 2.4rem; }
  /* A column, not a wrapped row — six links of uneven length wrapping is what
     made the old one look broken, and it is worse in Japanese. */
  .l-foot-links { justify-content: flex-start; flex-direction: column; gap: .95rem; }
  .l-foot-base { flex-direction: column; align-items: flex-start; gap: 1.1rem; }
}
/* When a page brings its own auth-card (e.g. the wider register form), the
   layout's wrapper collapses to avoid a double frame. */
.auth-card:has(> .auth-card), .auth-card:has(> .auth-split) {
  background: transparent; border: none; box-shadow: none; padding: 0; width: auto; }

/* ── Registration: form beside the pitch ───────────────────────────────────
   The form alone was a 520px box in the middle of a 1440px screen, which
   reads as a page that failed to load rather than a company worth handing a
   VAT number to. The aside carries the same three claims the landing leads
   with, next to the form instead of behind it, and collapses under it on a
   narrow screen — never beside a form that would then be too cramped to
   fill in. */
.auth-split { display: grid; grid-template-columns: minmax(0, 580px) minmax(0, 310px);
              gap: 1.5rem; align-items: start; }
.auth-card-wide { width: min(580px, 100%); }

.auth-aside { display: flex; flex-direction: column; gap: 1rem; }
.auth-aside .l-trade { margin: 0; align-self: flex-start; font-size: .76rem; padding: .4rem .85rem; }
.auth-aside .how-it-works { background: var(--panel); }

.auth-facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem;
              padding: .1rem .2rem; }
.auth-facts b { display: block; font-size: 1.05rem; font-weight: 800;
                background: var(--holo-ink); -webkit-background-clip: text;
                background-clip: text; color: transparent; }
.auth-facts span { font-size: .6rem; font-weight: 700; text-transform: uppercase;
                   letter-spacing: .07em; color: var(--sub); line-height: 1.3;
                   display: block; margin-top: .15rem; }
/* Flat surface: the foil is the one thing the JP layer removes everywhere. */
body.jp .auth-facts b { background: none; color: var(--accent); }

@media (max-width: 980px) {
  .auth-split { grid-template-columns: minmax(0, 580px); }
  .auth-aside { order: 2; }
  .auth-facts { padding-bottom: .4rem; }
  /* The split drops its second column here, so the back link above it has to
     drop the same 310px or it starts 74px to the left of the form. */
  body.auth:has(.auth-split) .auth-top { width: min(580px, 100%); }
}

/* ── Legal pages ───────────────────────────────────────────────────────── */
.legal-page { margin: -1.4rem -1.8rem 0; display: flex; flex-direction: column;
              min-height: calc(100vh - 1.4rem); }
.legal-page > .l-foot { margin-top: auto; }
.legal-head { display: flex; align-items: center; gap: 1.2rem; padding: .8rem 2.4rem;
              background: var(--panel); border-bottom: 1px solid var(--line); }
/* Declared after the three rows so it overrides their padding shorthand: the
   band spans the viewport, its contents stay inside the column.

   The same column as the page below it. This ran on its own 1240px measure,
   which put the wordmark ~60px to the left of the headline it sits above at
   1440 and further adrift as the screen widened — the sort of misalignment
   that is invisible until you look at the two together and unmissable after.
   --shell plus the sections' own 1.8rem, so every band starts on one line.
   .l-foot is NOT in this list — it centres itself with max-width and auto
   margins already, and adding the band padding on top offset it twice. */
.l-nav, .legal-head {
  padding-inline: calc(max(0px, (100% - var(--shell)) / 2) + var(--gutter)); }
/* .legal-page is a flex column, so an auto-margin child shrinks to its content
   instead of filling its max-width — width:100% is what makes the column real. */
/* ── Price bands ─────────────────────────────────────────────────────────
   One row per category, one card per kind of item inside it. The empty cells
   are drawn on purpose and kept quiet: they say what is coming without
   competing with the buckets that have a number in them. */
/* The demand cards stopped being links — see buyer/prices/index. A pointer
   cursor on something that does not go anywhere is a promise the page does not
   keep. */
.demand-card { cursor: default; }

.band-row { margin: 1.4rem 0 0; }
.band-cat { font-size: .82rem; font-weight: 700; letter-spacing: .02em;
            text-transform: uppercase; color: var(--sub); margin: 0 0 .6rem; }
.band-cat a:hover { color: var(--accent); }
.band-grid { display: grid; gap: .9rem;
             grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.band-card { border: 1px solid var(--line); border-radius: 12px; padding: 1rem 1.1rem;
             background: var(--panel); }
.band-card.is-empty { background: transparent; border-style: dashed; }
.band-type { font-size: .78rem; font-weight: 600; color: var(--sub); }
.band-num { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em;
            margin-top: .35rem; }
.band-approx { font-size: .78rem; color: var(--faint); }
.band-range { display: flex; justify-content: space-between; gap: .8rem;
              margin-top: .7rem; padding-top: .7rem; border-top: 1px solid var(--line);
              font-size: .78rem; color: var(--sub); }
.band-meta { display: flex; align-items: center; justify-content: space-between;
             gap: .6rem; margin-top: .5rem; font-size: .76rem; color: var(--faint); }
.band-none { margin-top: .6rem; font-size: .8rem; color: var(--faint); }
.band-foot { margin-top: 1.6rem; }
/* An arrow is a claim. It is only drawn when the bucket can support one — see
   PriceBandService::TREND_MINIMUM — so it is allowed to be loud. */
.trend { font-weight: 700; padding: .1rem .45rem; border-radius: 999px;
         font-size: .72rem; }
.trend-up   { color: #0a7d4b; background: #0a7d4b14; }
.trend-down { color: #b3261e; background: #b3261e14; }
.trend-flat { color: var(--sub); background: var(--line); }

.legal-body { width: 100%; max-width: 780px; margin: 0 auto; padding: 2.6rem 1.8rem 3rem; }

/* 特定商取引法に基づく表記 is read as a table of named facts, not as prose: a
   reader is looking for one row — the phone number, the returns rule — and
   should find it without reading the others. Two columns where there is room,
   stacked where there is not, and a hairline between rows so the eye can walk
   down the labels. */
.legal-facts { display: grid; grid-template-columns: 12.5rem 1fr; gap: 0;
               margin: 1.6rem 0 2.2rem; font-size: .9rem; }
.legal-facts dt { padding: .8rem 1.2rem .8rem 0; font-weight: 700; color: var(--ink);
                  border-top: 1px solid var(--line); }
.legal-facts dd { margin: 0; padding: .8rem 0; color: var(--sub); line-height: 1.7;
                  border-top: 1px solid var(--line); }
@media (max-width: 620px) {
  .legal-facts { grid-template-columns: 1fr; }
  .legal-facts dt { padding-bottom: .1rem; border-bottom: 0; }
  .legal-facts dd { padding-top: .1rem; border-top: 0; padding-bottom: 1rem; }
}
.public-body { width: 100%; max-width: 1080px; margin: 0 auto; padding: 2.5rem 1.4rem 4rem; }
.legal-body h1 { font-size: 1.8rem; margin: .2rem 0 .5rem; }
.legal-body h2 { font-size: 1rem; margin: 2rem 0 .5rem; }
.legal-body p, .legal-body li { color: var(--sub); font-size: .88rem; line-height: 1.7; }
.legal-body ul { padding-left: 1.1rem; }
.legal-body li { margin-bottom: .3rem; }
.legal-body b { color: var(--ink); }
.legal-body a { color: var(--accent); font-weight: 600; }
.legal-meta { font-size: .78rem !important; color: var(--faint) !important; margin: 0 0 1.6rem; }
.legal-operator { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
                  padding: 1rem 1.2rem; font-size: .82rem; color: var(--sub); line-height: 1.7;
                  margin-bottom: 1.8rem; }
.legal-operator b { display: block; font-size: .66rem; text-transform: uppercase;
                    letter-spacing: .12em; color: var(--faint); margin-bottom: .3rem; }
.legal-note { background: var(--accent-soft); border: 1px solid var(--accent-line); border-radius: var(--r);
              padding: .9rem 1.1rem; font-size: .8rem; color: var(--accent-deep); margin-bottom: 1.6rem; }
.legal-table { width: 100%; border-collapse: collapse; margin: .8rem 0 1rem; font-size: .82rem; }
.legal-table th { text-align: left; font-size: .64rem; text-transform: uppercase; letter-spacing: .09em;
                  color: var(--sub); padding: .45rem .8rem .45rem 0; border-bottom: 1px solid var(--line-2); }
.legal-table td { padding: .5rem .8rem .5rem 0; border-bottom: 1px solid #eef0f3; color: var(--sub); }

/* ── Claim conversation ────────────────────────────────────────────────── */
.claim-thread { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
                padding: 1.2rem 1.4rem; margin-bottom: 1.1rem; }
.claim-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
.claim-thread .msg { max-width: 82%; }
.claim-msg-buyer { align-self: flex-start; }
.claim-msg-seller { align-self: flex-end; background: var(--accent-soft); border-color: var(--accent-line); }
.claim-msg-platform { align-self: center; max-width: 92%; background: var(--info-soft);
                      border-color: var(--info-line); }
.claim-msg-internal { background: #fff8e5; border-color: #e8d9a8; }
.claim-thread .msg p { margin: .2rem 0 0; }

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE
   The shop floor is the primary context: a phone, one hand, standing up.
   Below 860px the dark sidebar is replaced by a top app bar and a fixed
   bottom tab bar, both rendered from the same nav_items list.
   ══════════════════════════════════════════════════════════════════════════ */

.appbar, .tabbar { display: none; }

@media (max-width: 860px) {
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr;
           align-content: start; background: none; }
  .sidebar { display: none; }

  .appbar {
    display: flex; align-items: center; gap: .6rem;
    position: sticky; top: 0; z-index: 40;
    padding: .55rem 1rem calc(.55rem);
    padding-top: calc(.55rem + env(safe-area-inset-top));
    background: var(--side); color: #fff;
  }
  .appbar .brandmark { color: #fff !important; font-size: .9rem; margin: 0; }

  /* Secondary destinations + language + logout, behind one tap. <details> so
     it works with Turbo without any JS to re-bind. */
  .appmenu { position: relative; }
  .appmenu > summary {
    list-style: none; cursor: pointer; font-size: 1.15rem; line-height: 1;
    padding: .45rem .6rem; border-radius: 8px; color: #fff;
    background: rgba(255, 255, 255, .1);
    min-width: 44px; min-height: 44px;
    display: flex; align-items: center; justify-content: center;
  }
  .appmenu > summary::-webkit-details-marker { display: none; }
  .appmenu[open] > summary { background: rgba(255, 255, 255, .2); }
  .appmenu-sheet {
    position: absolute; right: 0; top: calc(100% + .5rem); z-index: 50;
    min-width: 15rem; padding: .7rem; border-radius: 12px;
    background: var(--panel); border: 1px solid var(--line);
    box-shadow: 0 12px 32px rgba(16, 18, 22, .18); color: var(--ink);
  }
  .appmenu-who { font-size: .74rem; color: var(--faint); padding: .1rem .4rem .5rem;
                 border-bottom: 1px solid var(--line); margin-bottom: .4rem; }
  .appmenu-link { display: flex; align-items: center; gap: .6rem; min-height: 44px;
                  padding: .4rem; border-radius: 8px; color: var(--ink);
                  text-decoration: none; font-size: .88rem; font-weight: 600; }
  .appmenu-link:hover { background: var(--wash); }
  .appmenu-sheet .lang-switch { width: 100%; justify-content: center; margin-top: .5rem; }
  .appmenu-sheet form { margin: 0; }
  .appmenu-sheet .btn { width: 100%; }

  .tabbar {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    background: var(--panel); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .tab { display: flex; flex-direction: column; align-items: center; justify-content: center;
         gap: .15rem; min-height: 56px; padding: .4rem .2rem;
         color: var(--faint); text-decoration: none; }
  .tab-icon { position: relative; line-height: 0; }
  .tab-label { font-size: .62rem; font-weight: 700; letter-spacing: .01em;
               max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tab.on { color: var(--accent); }
  .tab-dot { position: absolute; top: -2px; right: -6px; width: 7px; height: 7px;
             border-radius: 50%; background: var(--accent); }

  /* Room for the fixed bar, plus a thumb's worth of slack. */
  main.container { padding: 1rem 1rem calc(5.5rem + env(safe-area-inset-bottom)); }

  /* Three KPI tiles should fit one row on a phone, not 2 + an orphan. */
  .stats-bar { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: .55rem; }
  .stats-bar .stat { padding: .6rem .7rem; }
  .stats-bar .num { font-size: 1.15rem; }

  .page-head { flex-direction: column; align-items: flex-start; gap: .7rem; }
  .page-head h1 { font-size: 1.25rem; }
  .page-head .actions { width: 100%; display: flex; flex-wrap: wrap; gap: .5rem; }
  .page-head .actions form { margin: 0; }

  .grid-2, .form-layout, .rail-layout { grid-template-columns: 1fr; }
  .card { padding: 1rem; }
  .card-narrow, .col-narrow { max-width: none; }
  .deal-grid { grid-template-columns: repeat(2, 1fr); }
  .l-steps, .l-trust, .l-cols, .market-cards { grid-template-columns: 1fr; }
  .l-stats { display: grid; grid-template-columns: repeat(3, 1fr);
             gap: 1.2rem .6rem; text-align: center; }
  .l-stats b { font-size: 1.35rem; }
  /* The phone gutter, set once — every band reads --gutter. */
  .landing, .legal-page { --gutter: 1.1rem; }
  .l-nav { flex-wrap: wrap; row-gap: .6rem; padding-block: .8rem; }
  /* No side padding on the band: .l-hero-say carries the gutter, and the
     backdrop is meant to reach the screen edges here. */
  .l-hero { padding: 1rem 0 2rem; }
  .display, .hero h1 { font-size: 1.7rem; }
  .legal-page { margin: 0; }
  .landing { margin: -1rem -1rem 0; }
  .legal-head { flex-wrap: wrap; row-gap: .6rem; padding-block: .8rem; }
  .legal-body { padding: 1.8rem var(--gutter) 2.4rem; }
  .l-foot { padding-bottom: calc(2rem + env(safe-area-inset-bottom)); }

  /* On a phone the language switch lives at the bottom of public pages:
     the header copy hides, the footer copy gets its own centered row. */
  .l-nav > .lang-switch, .legal-head > .lang-switch { display: none; }

  /* The register button leaves the phone header too — the hero CTA, the CTA
     band and the footer all still lead there; the cramped top bar need not. */
  .l-nav > .btn-brand, .legal-head > .btn-brand { display: none; }
  /* Tables become cards: a 6-column table on a 390px screen is unreadable
     however you scroll it. Each cell carries its header via data-label. */
  table.list, table.list tbody, table.list tr, table.list td { display: block; width: 100%; }
  table.list thead, table.list tr:first-child th { display: none; }
  table.list tr {
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
    margin-bottom: .7rem; padding: .3rem .2rem; }
  table.list td {
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
    border: none; border-bottom: 1px solid #f1f2f4; padding: .5rem .8rem;
    text-align: right; }
  table.list td:last-child { border-bottom: none; }

  /* The first cell is the card's subject — give it the weight of a heading
     instead of squeezing it into a right-aligned label/value row. */
  table.list td:first-child {
    display: block; text-align: left; font-size: .95rem; font-weight: 700;
    padding: .7rem .8rem .55rem; }
  table.list td:first-child::before { display: none; }
  table.list td:first-child a { color: inherit; text-decoration: none; }

  /* Zebra striping belongs to a table, not to a list of cards. */
  table.list tr:nth-child(even), table.list tr:nth-child(odd) { background: var(--panel); }

  /* Action cells have no header, and render nothing when the action does not
     apply — an empty row with a divider. */
  table.list td:not([data-label]):not(:has(*)) { display: none; }
  table.list td::before {
    content: attr(data-label); flex: 0 0 auto; text-align: left;
    font-size: .62rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .08em; color: var(--sub); }
  table.list td:empty { display: none; }
  table.list td.num { text-align: right; }

  /* iOS zooms the whole page when a focused input is under 16px, and does not
     zoom back when you leave the field — you finish the form at 130%, panning.
     This is one of the two things that made every form here feel broken on a
     phone, and the rule meant to prevent it never applied: it was written as
     `form.stacked input`, while the base size is set per input[type=…], which
     outranks it. Hence !important — the guard has to beat a typed selector it
     cannot out-specify without listing every type twice. */
  input:not([type=hidden]), select, textarea { font-size: 16px !important; }

  .btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .btn-sm { min-height: 38px; }

  /* Thumb-sized: the language segments were 24px tall and the landing's own
     links 21px, well under the 44px a fingertip actually covers. The segment
     wrapper has to become a flex item along with them, or the pill keeps its
     old height and the two segments overlap inside it. */
  .lang-switch { align-items: stretch; }
  .lang-switch .lang-switch-form { display: inline-flex; }
  .lang-switch a, .lang-switch button, .l-nav > a, .l-foot > a, .auth-card .brandmark {
    min-height: 40px; display: inline-flex; align-items: center; justify-content: center; }
  .lang-switch a, .lang-switch button { padding-inline: 1.1rem; font-size: .78rem; }
  .thread li { max-width: 100%; }
}

@media (max-width: 420px) {
  .deal-grid { grid-template-columns: 1fr; }
  .l-stats { grid-template-columns: 1fr; }
  .tab-label { font-size: .58rem; }
}

/* Installed as a home-screen app: no browser chrome, so pull the app bar down
   past the status bar and keep the tab bar clear of the home indicator. */
@media (display-mode: standalone) {
  .appbar { padding-top: calc(.55rem + env(safe-area-inset-top)); }
  body { overscroll-behavior-y: none; }
}

/* Respect the OS setting rather than animating regardless. */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ── Mobile refinements ─────────────────────────────────────────────────── */
@media (max-width: 860px) {
  /* The header row of a stacked table would otherwise render as an empty card. */
  table.list tr:has(> th) { display: none; }

  /* Secondary content belongs after the work on a phone. */
  .rail-layout > div { display: flex; flex-direction: column; }
  .order-last-on-mobile { order: 99; margin-top: 1.4rem; }

  /* A dl with a long value reads better stacked than squeezed into 2 columns. */
  dl.kv { grid-template-columns: 1fr; }
  dl.kv dt { padding: .5rem 0 .1rem; border-bottom: none; font-size: .72rem;
             text-transform: uppercase; letter-spacing: .06em; }
  dl.kv dd { padding: 0 0 .55rem; }
  dl.kv dd.num { text-align: left; }
  dl.kv dt:last-of-type { border-bottom: none; }

  /* There is no hover on a phone, and no room for a floating panel either. */
  .card-zoom { display: none !important; }

  /* Tap targets in the negotiation thread and pill rows. */
  .pill-row { row-gap: .4rem; }
  .filter-row a { min-height: 38px; display: inline-flex; align-items: center; }
}

@media (max-width: 860px) {
  .appback { display: flex; align-items: center; justify-content: center;
             min-width: 44px; min-height: 44px; margin-left: -.4rem;
             font-size: 1.6rem; line-height: 1; color: #fff; text-decoration: none; }
  .apptitle { font-size: .92rem; font-weight: 700; color: #fff;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* At 22px tall the questions were the smallest thing on the page a visitor
     is actually meant to tap. The row layout and the drawn +/− marker now
     live in the main FAQ block; all that is left for phones is the height. */
  .l-faq summary { min-height: 44px; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   Collector layer — the market-price pages.

   The buyers are card and hobby shops; the goods are collectibles, and this
   layer borrowed the one visual cue everyone in that trade reads instantly:
   the holo foil of a rare card. It used to run the whole public side — an
   aurora behind the hero, a rainbow cap and a shine sweep on every card.

   The 2026-08-14 comps replaced that with drawn illustration on warm paper,
   and the two do not mix: a rainbow hairline over a watercolour reads as a
   rendering bug. Everything the landing touched has been removed rather than
   overridden, so there is nothing left to fight with. What survives is the
   price pages, which are members-only and still trade-facing.

   Deliberately generic foil, not any publisher's palette or wordmark — the
   platform sells to shops that trade Pokémon, it is not licensed by them.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  --holo: linear-gradient(115deg, #ffcf5c 0%, #ff6f9c 22%, #a06bff 45%,
                                  #3fc9ff 66%, #48e39b 84%, #ffcf5c 100%);
  --holo-soft: linear-gradient(115deg, rgba(255,207,92,.16), rgba(255,111,156,.16),
                               rgba(160,107,255,.16), rgba(63,201,255,.16), rgba(72,227,155,.16));
  /* Foil for text. Anchored on the brand colour so a headline number reads as
     ours rather than as a generic rainbow — it has now been three colours
     (vermillion, then the brand blue, now the terracotta), which is the cost
     of writing a hex where a token belongs. */
  --holo-ink: linear-gradient(100deg, var(--accent-deep) 0%, var(--accent) 55%, #e08a4a 100%);
}

/* ── Cards that behave like cards ──────────────────────────────────────── */
.market-card {
  position: relative; overflow: hidden; border-radius: 12px;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.market-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--holo); opacity: .85;
}

/* The shine sweep: a diagonal highlight that crosses the card once on hover,
   the way a foil catches the light when you tilt it. It is an animation, not
   a transition, so it passes and clears instead of parking a tinted half-card
   under the cursor for as long as the pointer rests there. */
@keyframes foil-sweep {
  0%   { opacity: 0;   transform: translateX(-75%); }
  22%  { opacity: .95; }
  100% { opacity: 0;   transform: translateX(75%); }
}
.market-card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(115deg, transparent 38%, rgba(255,255,255,.9) 50%,
                              transparent 62%), var(--holo-soft);
}
.market-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px -12px rgba(20, 24, 30, .24);
  border-color: var(--line-2);
}
.market-card:hover::after { animation: foil-sweep .8s ease-out; }
.market-card > * { position: relative; z-index: 1; }

/* The market price hero is the page a collector lands on from search. */
.market-hero { position: relative; overflow: hidden; border-radius: 12px; }
.market-hero::before { content: ""; position: absolute; top: 0; left: 0; right: 0;
                       height: 3px; background: var(--holo); opacity: .85; }
.market-hero .big-price { background: var(--holo-ink); -webkit-background-clip: text;
                          background-clip: text; color: transparent; }

/* Reduced motion: keep the foil, drop the movement. */
@media (prefers-reduced-motion: reduce) {
  .l-step, .l-trust-card, .market-card, .l-col { transition: none; }
  .l-step:hover, .l-trust-card:hover, .market-card:hover, .l-col:hover { transform: none; }
  .l-step::after, .l-trust-card::after, .market-card::after, .l-col::after { display: none; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   JP surface — the seller side (body.jp, set by the public layout).

   This block used to rebuild the whole page in Mercari's image: white field,
   4px corners, ink #333, Hiragino, no ornament, on the argument that a
   Japanese shop's reference for "a marketplace safe to sign up to" is Mercari
   and that the collector layer's foil would read as a consumer gimmick to the
   audience we most need to look boring to.

   design_jp.png retires that argument. quentin's Japanese comp is the SAME
   page as the English one — same warm paper, same rounded cards, same
   watercolour, same terracotta — with Japanese in it. Two surfaces that look
   like two companies was the thing to avoid; the illustration is what carries
   the trust now, on both sides. So what is left here is only what is actually
   about setting Japanese: the face, a size up (Japanese needs more x-height
   than 13.5px gives it), and tracking that does not squeeze kana.
   ───────────────────────────────────────────────────────────────────────── */
body.jp {
  --font: "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", Meiryo,
          "Figtree", "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
}

/* Playfair has no kana, so a Japanese heading set in var(--font-display) falls
   through glyph by glyph and arrives as the system serif at Playfair's tight
   tracking. Set it deliberately instead: the gothic, at normal tracking, one
   step down — which is what the comp shows. */
body.jp .l-hero h1, body.jp .l-sec-head h2, body.jp .l-cta-band h2,
body.jp .display, body.jp .brandmark:not(:has(.brand-word)) {
  font-family: var(--font); letter-spacing: normal; font-weight: 700; }
/* Japanese sets no wider than about 13 full-width characters here, so the
   16ch cap the Latin headline needs would strand "います。" on a line of its
   own; and the assurances, which are six English words, are six Japanese
   words in twice the space. Both measures are counted in em, not ch — ch is
   the width of a "0", which tells you nothing about kana. */
body.jp .l-hero h1 { font-size: 2.4rem; line-height: 1.3; max-width: 13.5em; }
body.jp .l-sec-head h2 { font-size: 1.7rem; }
body.jp .l-cta-band h2 { font-size: 1.6rem; }
body.jp .kicker { letter-spacing: .08em; }
body.jp .l-sub { font-size: .98rem; }
body.jp .l-assure li { font-size: .76rem; line-height: 1.45; }
/* The mark itself does NOT translate — see brand_logo. Latin on both sides,
   Playfair on both sides, which is what design_jp.png shows. */
body.jp .brandmark, body.jp .brandmark .brand-word { font-family: var(--font-brand); }

/* The closing band used to be peach here and cool grey on the English side,
   because each carried its own drawn shop cropped on its own ground. Both
   sides now take the same skyline on the same peach — see .l-cta-inner. */

/* The progress bar is ornament: a drifting rainbow across the top of the
   page. Same terracotta, no foil. */
body.jp .turbo-progress-bar { background: var(--accent); animation: none; box-shadow: none; }

/* A submit button held back until the form is complete has to look
   unavailable, or it reads as a broken button rather than a pending one.
   Neutral rather than a faded red: a washed-out brand colour reads as a
   rendering fault on the one screen where a stranger is judging whether this
   platform is a real company.

   aria-disabled as well as disabled, because form_gate_controller no longer
   sets the real attribute: a browser autofilling the whole form on page load
   fires no events, so the button stayed genuinely disabled over a form that
   was already complete. The look is the same; the click now lands and the
   browser names the missing field. */
.btn[disabled], .btn[disabled]:hover,
.btn[aria-disabled="true"], .btn[aria-disabled="true"]:hover {
                                       opacity: 1; cursor: not-allowed; box-shadow: none;
                                       background: var(--wash); color: var(--faint);
                                       border: 1px solid var(--line); }
.btn-secondary[disabled], .btn-secondary[disabled]:hover,
.btn-secondary[aria-disabled="true"], .btn-secondary[aria-disabled="true"]:hover { background: var(--wash); color: var(--faint); }

/* A long address — "prenom.nom@boutique-de-cartes-dijon.example" — is wider
   than a 216px sidebar or a 15rem menu sheet, and was pushing the panel out
   of shape rather than wrapping. Truncate it and keep the full value in the
   title attribute, so hovering (or a screen reader) still gets all of it. */
.sidebar .foot b, .sidebar .foot .who, .appmenu-who {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 100%; min-width: 0;
}
/* The avatar must not shrink to make room for a long name — the name gives. */
.sidebar .foot .rowflex { min-width: 0; }
.sidebar .foot .rowflex > b { min-width: 0; }

/* ── Modal prompt ──────────────────────────────────────────────────────── */
/* Used for data the account cannot operate without — the setup wizard's legal
   identity and shipping address. Deliberately not a dialog element: this must
   render identically with JavaScript off, where "later" is simply
   unavailable. */
.modal-scrim { position: fixed; inset: 0; z-index: 90; display: flex;
               align-items: center; justify-content: center; padding: 1.2rem;
               background: rgba(16, 18, 22, .55); }
.modal-card { background: var(--panel); border-radius: 12px; width: 100%; max-width: 440px;
              padding: 1.6rem 1.7rem; box-shadow: 0 18px 48px rgba(16, 18, 22, .3);
              max-height: 92vh; overflow-y: auto; }
.modal-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: 1.3rem; }
.modal-actions .btn { width: auto; margin: 0; }
@media (max-width: 560px) { .modal-card { padding: 1.2rem 1.1rem; } }

/* The wizard's count and pips. A modal with no visible end is why people close
   the tab: "Step 1 of 2" is a promise that this stops. */
.wizard-head { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.wizard-count { font-size: .72rem; font-weight: 600; color: var(--faint);
                letter-spacing: .02em; white-space: nowrap; }
.wizard-pips { display: flex; gap: .3rem; margin: .5rem 0 .9rem; }
.wizard-pips span { height: 3px; flex: 1; border-radius: 2px; background: var(--line); }
.wizard-pips span.on { background: var(--accent); }

/* Phone: a narrow country-code select welded to the number input, so the two
   read as one field rather than two unrelated controls. */
.phone-field { display: flex; gap: 0; }
/* Just wide enough for a flag and the longest code (+81) plus the native
   select arrow — it is a prefix, not a field in its own right. */
.phone-field select { flex: 0 0 6.3rem; padding-left: .55rem; padding-right: .2rem;
                      border-radius: 7px 0 0 7px; border-right: none; }
.phone-field input { flex: 1 1 auto; min-width: 0; border-radius: 0 7px 7px 0; }

/* The 1240px cap is right for a page that owns the whole viewport, but inside
   the shell the sidebar has already taken 216px, so the column ends up
   centred in what is left with a wide dead gutter on either side — on a
   2200px screen, ~370px of empty canvas each side of a dense table. Give the
   app column a cap of its own: wide enough to use a large monitor, bounded so
   a table row does not become a 3000px line nobody can scan. */
.shell main.container { max-width: 1680px; padding-left: 2rem; padding-right: 2rem; }
@media (min-width: 1900px) {
  /* Past this width the two-column dashboards have room for a real split
     rather than a narrow rail squeezed against the edge. */
  .shell main.container { padding-left: 2.6rem; padding-right: 2.6rem; }
}

/* The prompt has no "later", so it must never be a dead end: signing out is
   always one click away — on the same row as the action, pushed to the far
   side, so the eye reads "leave … or finish" rather than finding the escape
   stranded below the button it is an alternative to. */
.modal-actions { align-items: center; }
.modal-escape { margin: 0; margin-right: auto; }
.linklike { background: none; border: none; padding: 0; font: inherit;
            font-size: .78rem; color: var(--faint); cursor: pointer;
            text-decoration: underline; }
.linklike:hover { color: var(--sub); }

/* TOTP enrolment: the QR is the primary path, the typed secret the fallback. */
.totp-enrol { display: flex; gap: 1.4rem; align-items: flex-start; margin: 1.2rem 0 1.6rem; }
.totp-qr { flex: 0 0 auto; background: #fff; padding: .6rem; border: 1px solid var(--line);
           border-radius: 8px; line-height: 0; }
.totp-qr svg { width: 100%; height: auto; display: block; }
@media (max-width: 560px) { .totp-enrol { flex-direction: column; } }

/* ── Navigation progress ───────────────────────────────────────────────── */
/* Turbo's own bar is a flat blue that belongs to no design system in this
   app. Since it is the one piece of chrome a member sees on every single
   navigation, it may as well be the foil — the same cue the public pages use,
   here reduced to a 3px line. Turbo only inserts the element after 500ms, so
   this is never seen on a fast page. */
.turbo-progress-bar {
  height: 3px;
  background: var(--holo);
  background-size: 200% 100%;
  animation: holo-drift 1.6s linear infinite;
  box-shadow: 0 0 8px -1px rgba(221, 99, 55, .45);
}

@keyframes holo-drift {
  from { background-position:   0% 0; }
  to   { background-position: 200% 0; }
}

/* A bar that hurries is worse than no bar for anyone who finds motion
   uncomfortable; the colour still says "working". */
@media (prefers-reduced-motion: reduce) {
  .turbo-progress-bar { animation: none; }
}

/* The Stripe onboarding popup — our own two pages, on screen for about a
   second each. Centred rather than laid out, because there is never more here
   than a spinner, a sentence and a way out. */
.popup-body {
  display: grid; place-items: center; min-height: 100vh; margin: 0;
  padding: 1.5rem; background: var(--bg); color: var(--ink);
  font-family: var(--font);
}
.popup-card {
  max-width: 30rem; text-align: center;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 14px; padding: 2rem 1.6rem;
}
.popup-h { font-size: 1.15rem; margin: .9rem 0 .4rem; }
.popup-actions { display: flex; gap: .6rem; justify-content: center; margin-top: 1.2rem; flex-wrap: wrap; }

.popup-spinner {
  width: 2rem; height: 2rem; margin: 0 auto; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--accent);
  animation: popup-spin .8s linear infinite;
}
@keyframes popup-spin { to { transform: rotate(360deg); } }

/* A window the browser refused to close. Until then the button is noise —
   the window is already going. */
.popup-actions { visibility: hidden; }
.popup-stuck .popup-actions, [data-controller~="stripe-launch"] .popup-actions { visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .popup-spinner { animation-duration: 3s; }
}

/* Visible to a screen reader, not to the eye. Used where a table cell's
   column header is the label — a sighted user reads the header, a screen
   reader user needs it on the input itself. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* A form element that only exists to be targeted by form= attributes. */
.contents { display: contents; }

/* ══════════════════════════════════════════════════════════════════════════
   PAPIER — the authenticated consoles
   Chosen 2026-08-06 from three directions rendered on the running app with
   real data, because judging a console on an empty screen has already cost
   this project a design direction.

   The claim: an operator does not read a dashboard, they read a page. So the
   chrome goes: no dark rail, no grey canvas, no cards floating on it. What is
   left is a white sheet, hairlines, and a wider type scale — the sidebar is a
   light margin with an accent rule against the current section, panels are
   sections separated by rules, and a table is set as a table rather than
   boxed. Colour is spent only where it means something: the accent on the
   current page and the primary action, and nowhere else.

   Scope. Everything here is (a) inside .shell, so the public site and the
   auth/popup surfaces are untouched, and (b) inside min-width: 861px, so the
   phone chrome below — dark app bar, bottom tab bar, tables as cards — is
   untouched too. Both matter: several of these rules are more specific than
   their mobile counterparts and would otherwise win at every width.
   ══════════════════════════════════════════════════════════════════════════ */

@media (min-width: 861px) {

  /* The tokens, re-pointed for a white sheet. --side is deliberately NOT
     touched: the mobile app bar reads it and must stay dark. */
  .shell {
    --bg:     #ffffff;
    --panel:  #ffffff;
    --wash:   #ffffff;
    --line:   #e7e9ec;
    --r:      0px;

    grid-template-columns: 244px minmax(0, 1fr);
    /* The gradient existed to paint the sidebar's column under a sticky
       100vh rail. On a white sheet one colour does it. */
    background: #ffffff;
  }
  /* Everything inside the sheet reads a notch larger. Most rules are in rem
     and unaffected; this is for the text that has no size of its own. */
  .shell { font-size: 14.5px; }

  /* ── The margin (ex-sidebar) ───────────────────────────────────────── */
  .shell .sidebar { background: #ffffff; color: var(--sub);
                    border-right: 1px solid var(--line); padding: 1.5rem 1rem; }
  /* The mark carries the whole margin — there is no dark rail behind it any
     more — so it is set a size up from the public header's. Ink, not navy:
     the wordmark is one object everywhere, and the only colour in it is the
     full stop. */
  .shell .sidebar .brandmark { color: var(--brand-ink) !important;
                               font-size: 1.6rem; gap: .55rem;
                               padding: .2rem .5rem 1.7rem; }
  .shell .sidebar .brandmark svg { width: 35px; height: 35px; }
  .shell .sidebar h5 { color: var(--faint); padding: 1.2rem .6rem .4rem; }

  /* The current section is marked by a rule in the margin, not a filled
     block — the same device the page uses for its own headings. */
  .shell .nv { border-radius: 0; border-left: 2px solid transparent;
               padding: .5rem .7rem; font-size: .88rem; color: var(--sub); }
  .shell .nv:hover { background: #f5f6f8; color: var(--ink); }
  .shell .nv.on { background: transparent; color: var(--brand-navy);
                  border-left-color: var(--accent); font-weight: 800; }
  .shell .nv .n { background: #eceef2; color: var(--sub); }
  .shell .nv.on .n { background: var(--accent-soft); color: var(--accent-deep); }

  .shell .sidebar .foot { border-top: 1px solid var(--line); color: var(--sub); }
  .shell .sidebar .foot b { color: var(--ink); }
  .shell .sidebar select { color: var(--ink); background: #fff;
                           border: 1px solid var(--line); }

  /* The language switch was built to invert on a dark rail: on white its
     "current" state came out white-on-white, which is worse than having no
     state at all. Put it back on the light treatment. */
  .shell .sidebar .lang-switch-dark { background: #f1f3f6; border-color: var(--line); }
  .shell .sidebar .lang-switch-dark a,
  .shell .sidebar .lang-switch-dark button { color: var(--sub); }
  .shell .sidebar .lang-switch-dark a:hover,
  .shell .sidebar .lang-switch-dark button:hover { color: var(--ink); }
  .shell .sidebar .lang-switch-dark a.on,
  .shell .sidebar .lang-switch-dark button.on { background: #fff; color: var(--ink);
                                                box-shadow: 0 1px 2px rgba(16, 18, 22, .10); }

  /* ── The sheet ─────────────────────────────────────────────────────── */
  .shell main.container { padding-top: 2.6rem; padding-bottom: 5rem; }
  .shell h1 { font-size: 2rem; font-weight: 800; letter-spacing: -.025em; }
  .shell h3 { letter-spacing: .16em; font-size: .66rem; color: var(--faint); }
  .shell .page-head { margin-bottom: 2.2rem; padding-bottom: 1.1rem;
                      border-bottom: 1px solid var(--line); }

  /* Panels become sections: the rule between them says the same thing a
     border and a drop shadow were saying, with two fewer edges. */
  .shell .card { border: none; border-radius: 0; padding: 0 0 2rem;
                 margin-bottom: 2rem; border-bottom: 1px solid var(--line); }
  .shell .card:last-child { border-bottom: none; }
  .shell .grid-2 { gap: 3rem; }
  .shell .grid-2 > .card { border-bottom: none; padding-bottom: 0; }

  /* ── Tables, set rather than boxed ─────────────────────────────────── */
  .shell table.list { border: none; border-radius: 0; }
  .shell table.list th { background: transparent; color: var(--ink);
                         border-bottom: 1px solid var(--ink);
                         padding: 0 1rem .5rem 0; font-size: .6rem; }
  .shell table.list td { padding: .95rem 1rem .95rem 0;
                         border-bottom: 1px solid var(--line); font-size: .9rem; }
  .shell table.list tr:last-child td { border-bottom: 1px solid var(--line); }
  .shell table.list tr:hover td { background: #fbfcfd; }
  .shell .cell-sub { font-size: .74rem; }

  /* ── KPI row: bare figures between two rules ───────────────────────── */
  .shell .stats-bar { gap: 0; margin-bottom: 2.2rem;
                      border-top: 1px solid var(--line);
                      border-bottom: 1px solid var(--line); }
  .shell .stats-bar .stat { border: none; border-right: 1px solid var(--line);
                            border-radius: 0; padding: 1.1rem 1.4rem; }
  .shell .stats-bar .stat:last-child { border-right: none; }
  .shell .stats-bar .num { font-size: 2rem; letter-spacing: -.03em; }
  .shell .stats-bar .label { color: var(--faint); font-size: .62rem; }

  /* ── Right rail: marginalia, not a stack of boxes ──────────────────── */
  .shell .rail-layout { grid-template-columns: minmax(0, 1fr) 300px; gap: 3rem; }
  .shell .rail .panel { border: none; border-radius: 0; margin-bottom: 2rem; }
  .shell .rail .panel-head { padding: 0 0 .5rem; border-bottom: 1px solid var(--ink); }
  .shell .rail .row, .shell .rail .mrow { padding: .7rem 0; }

  /* Corners: square everywhere the sheet is, soft only on the controls a
     finger aims at. */
  .shell .thumb { border-radius: 2px; }
  .shell .badge { border-radius: 3px; }
  .shell .btn { border-radius: 4px; }
}
