﻿/* Walupi app styles, redesign 2026: dense storefront + escrow portal.
   Palette and rules in tokens.css. */
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html {
-webkit-text-size-adjust: 100%;
scroll-padding-top: 80px;
}
body {
font-family: var(--font);
font-size: 14px;
line-height: 1.55;
color: var(--ink);
background: var(--paper);
min-height: 100dvh;
display: flex;
flex-direction: column;
overflow-x: clip;
}
main {
flex: 1;
}
img,
svg {
display: block;
max-width: 100%;
}
h1,
h2,
h3 {
font-family: var(--font-display);
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.02em;
}
h1 {
font-size: 24px;
}
h2 {
font-size: 19px;
}
h3 {
font-size: 15px;
}
a {
color: var(--royal-700);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
border-radius: 4px;
}
button {
font: inherit;
}
@media (min-width: 980px) {
body {
font-size: 15px;
}
h1 {
font-size: 32px;
}
h2 {
font-size: 22px;
}
h3 {
font-size: 16px;
}
}
.wrap {
width: 100%;
max-width: 720px;
margin: 0 auto;
padding: 0 16px;
}
.wrap-wide {
max-width: 1440px;
}
@media (min-width: 980px) {
.wrap,
.wrap-wide {
padding: 0 32px;
}
}
main.wrap {
padding-top: 20px;
padding-bottom: 120px;
}
main.full {
width: 100%;
padding: 0 0 48px;
}
@media (min-width: 980px) {
main.wrap {
padding-top: 32px;
padding-bottom: 64px;
}
}
/* Vertical rhythm. There were only two steps here (16px and 8px), so every
   gap between them got hand-written at the call site: 850 inline style
   attributes across 99 templates, 46 of them margin-top:6px. These five steps
   cover the range, and they read from the spacing tokens. */
.stack > * + * {
margin-top: var(--s-4);
}
.stack-xs > * + * {
margin-top: var(--s-1);
}
.stack-sm > * + * {
margin-top: var(--s-2);
}
.stack-md > * + * {
margin-top: var(--s-3);
}
.stack-lg > * + * {
margin-top: var(--s-6);
}
.section {
margin-top: 32px;
}
@media (min-width: 980px) {
.section {
margin-top: 56px;
}
}
.row {
display: flex;
align-items: center;
gap: 8px;
}
.row-between {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.muted {
color: var(--ink-60);
}
.small {
font-size: 13px;
}
.tiny {
font-size: 12px;
}
.site-header {
background: var(--indigo-950);
color: #fff;
position: sticky;
top: 0;
z-index: 30;
}
.site-header .bar {
display: flex;
align-items: center;
gap: 16px;
min-height: 60px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
color: #fff;
font-family: var(--font-display);
font-weight: 700;
font-size: 18px;
letter-spacing: -0.02em;
flex: none;
}
.brand:hover {
text-decoration: none;
}
.brand svg {
width: 32px;
height: 22px;
color: var(--indigo-950-soft);
}
.header-search {
flex: 1;
max-width: 640px;
display: none;
}
.header-search input {
min-height: 44px;
border-radius: 999px;
border-color: transparent;
padding: 8px 22px;
background: #fff;
}
.header-nav {
display: flex;
align-items: center;
gap: 14px;
font-size: 13px;
margin-left: auto;
min-width: 0;
}
@media (min-width: 980px) {
.header-nav {
gap: 20px;
}
}
.header-nav a {
color: var(--indigo-950-muted);
font-weight: 500;
white-space: nowrap;
}
.header-nav a.active,
.header-nav a:hover {
color: #fff;
text-decoration: none;
}
@media (min-width: 980px) {
.site-header .bar {
min-height: 72px;
}
.header-search {
display: block;
}
.header-nav {
font-size: 14px;
}
}
.tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 50;
display: flex;
background: var(--surface);
border-top: 1px solid var(--line);
padding: 8px 4px calc(8px + env(safe-area-inset-bottom));
}
.tabbar a {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
font-size: 11px;
font-weight: 500;
color: var(--ink-60);
min-height: 44px;
justify-content: center;
}
.tabbar a:hover {
text-decoration: none;
}
.tabbar a svg {
width: 21px;
height: 21px;
}
.tabbar a.active {
color: var(--royal-700);
font-weight: 700;
}
main.wrap,
main.full {
padding-bottom: 140px;
}
@media (min-width: 980px) {
.tabbar {
display: none;
}
main.wrap {
padding-bottom: 64px;
}
main.full {
padding-bottom: 48px;
}
}
.hero-grid {
display: grid;
gap: 24px;
padding: 32px 0;
}
.hero-copy h1 {
font-size: 28px;
color: var(--royal-900);
max-width: 15ch;
}
.hero-copy .lede {
margin-top: 12px;
color: var(--ink-60);
max-width: 44ch;
}
.hero-actions {
display: flex;
gap: 12px;
margin-top: 20px;
flex-wrap: wrap;
}
.hero-aside {
min-width: 0;
}
.hero-aside .card {
border-color: var(--royal-500);
}
@media (min-width: 980px) {
.hero-grid {
grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
align-items: center;
padding: 56px 0;
gap: 56px;
}
.hero-copy h1 {
font-size: 42px;
}
.hero-copy .lede {
font-size: 17px;
}
}
.stat-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin-top: 28px;
}
.stat {
border-left: 2px solid var(--line);
padding-left: 14px;
}
.stat .num {
font-size: 22px;
font-weight: 500;
font-variant-numeric: tabular-nums;
color: var(--royal-900);
white-space: nowrap;
}
.stat .lbl {
font-size: 12px;
color: var(--ink-60);
margin-top: 2px;
}
@media (min-width: 980px) {
.stat-row {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 24px;
}
.stat .num {
font-size: 26px;
}
}
.stat-tiles {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 12px;
}
.stat-tiles .card {
padding: 14px 16px;
}
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-card);
padding: 16px;
/* Cards are always ink on white, even inside dark sections. */
color: var(--ink);
}
@media (min-width: 980px) {
.card {
padding: 20px;
}
}
.card-link {
display: block;
color: var(--ink);
}
.card-link:hover {
text-decoration: none;
border-color: var(--royal-500);
background: #fdfdff;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
min-height: 44px;
padding: 0 22px;
border-radius: var(--r-control);
border: 1px solid transparent;
font-size: 14px;
font-weight: 700;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
}
.btn-light {
background: #fff;
color: var(--indigo-950);
}
.btn-light:hover {
background: var(--royal-100);
}
.btn:hover {
text-decoration: none;
}
.btn-primary {
background: var(--royal-700);
color: #fff;
}
.btn-primary:hover {
background: var(--royal-900);
}
.btn-quiet {
background: var(--surface);
color: var(--royal-700);
border-color: var(--line);
}
.btn-quiet:hover {
border-color: var(--royal-500);
}
.btn-block {
width: 100%;
}
.btn-lg {
min-height: 50px;
padding: 0 28px;
font-size: 15px;
}
.btn[disabled] {
opacity: 0.55;
cursor: default;
}
label {
display: block;
font-size: 13px;
color: var(--ink-60);
margin-bottom: 4px;
}
input,
select,
textarea {
font: inherit;
width: 100%;
min-height: 44px;
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: var(--r-control);
background: var(--surface);
color: var(--ink);
}
textarea {
min-height: 88px;
resize: vertical;
}
input::placeholder,
textarea::placeholder {
color: var(--ink-40);
}
input[type="checkbox"] {
width: 20px;
min-height: 20px;
accent-color: var(--royal-700);
}
.field {
margin-bottom: 16px;
}
.field-error {
color: var(--danger-700);
font-size: 13px;
margin-top: 4px;
}
.chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 11px;
border-radius: 999px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
.chip-sky {
background: var(--sky-100);
color: var(--sky-700);
}
.chip-royal {
background: var(--royal-100);
color: var(--royal-900);
}
.chip-danger {
background: var(--danger-100);
color: var(--danger-700);
}
.chip-plain {
background: var(--paper);
color: var(--ink-60);
border: 1px solid var(--line);
}
.chip-active {
background: var(--indigo-950);
color: #fff;
}
.chip-accent {
background: var(--accent-100);
color: var(--accent);
}
a.chip {
text-decoration: none;
flex: none;
}
.chip-row {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 4px;
scrollbar-width: none;
}
.chip-row::-webkit-scrollbar {
display: none;
}
.badge-chip {
display: inline-block;
position: relative;
}
.badge-chip > summary {
list-style: none;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 11px;
border-radius: 999px;
background: var(--royal-100);
color: var(--royal-900);
font-size: 12px;
font-weight: 500;
}
.badge-chip > summary::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--royal-500);
}
.badge-chip > summary::-webkit-details-marker {
display: none;
}
.badge-chip[open] > summary {
background: var(--royal-700);
color: #fff;
}
.badge-popover {
position: absolute;
z-index: 40;
top: calc(100% + 6px);
left: 0;
min-width: 240px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-card);
padding: 14px;
font-size: 13px;
}
.trail {
position: relative;
padding: 30px 0 8px;
}
.trail-track {
display: flex;
align-items: center;
list-style: none;
padding: 0;
}
.trail-step {
display: flex;
align-items: center;
flex: 1;
}
.trail-step:first-child {
flex: 0;
}
.trail-seg {
flex: 1;
height: 2px;
background: var(--line);
}
.trail-step.done .trail-seg {
background: var(--sky-500);
}
.trail-dot {
flex: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--surface);
border: 2px solid var(--line);
display: block;
padding: 0;
}
button.trail-dot {
cursor: pointer;
}
.trail-step.done .trail-dot {
background: var(--sky-500);
border-color: var(--sky-500);
}
.trail-step.current .trail-dot {
border-color: var(--royal-500);
background: var(--surface);
}
a.trail-dot:hover,
button.trail-dot:hover {
border-color: var(--sky-700);
}
.trail-snail {
position: absolute;
top: 0;
width: 34px;
height: 24px;
margin-left: -17px;
color: var(--royal-500);
transition: left 400ms ease;
}
.trail-labels {
display: flex;
justify-content: space-between;
margin-top: 6px;
font-size: 12px;
color: var(--ink-40);
}
.trail-labels .done {
color: var(--sky-700);
}
.trail-labels .current {
color: var(--royal-900);
font-weight: 500;
}
.mini-trail .trail {
padding-top: 22px;
}
.mini-trail .trail-snail {
width: 26px;
height: 18px;
margin-left: -13px;
}
@media (prefers-reduced-motion: reduce) {
.trail-snail {
transition: none;
}
}
.milestone-row {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
padding: 14px 0;
border: 0;
border-bottom: 1px solid var(--line);
background: none;
text-align: left;
cursor: pointer;
color: inherit;
font: inherit;
}
.milestone-row:last-child {
border-bottom: 0;
}
.milestone-row:hover {
background: var(--paper);
}
.milestone-icon {
flex: none;
width: 30px;
height: 30px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--sky-100);
color: var(--sky-700);
font-size: 13px;
}
.milestone-row.pending .milestone-icon {
background: var(--paper);
color: var(--ink-40);
border: 1px solid var(--line);
}
.capacity-label {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
color: var(--ink-60);
margin-bottom: 6px;
}
.capacity-track {
height: 8px;
border-radius: 999px;
background: var(--royal-100);
overflow: hidden;
}
.capacity-fill {
height: 100%;
border-radius: 999px;
background: var(--royal-700);
}
.capacity.urgent .capacity-label {
color: var(--accent);
font-weight: 700;
}
.money {
font-family: var(--font-display);
font-weight: 700;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.price {
color: var(--price);
}
.price-old {
font-size: 12px;
color: var(--ink-40);
text-decoration: line-through;
font-weight: 400;
}
.money-lg {
font-size: 30px;
line-height: 1.2;
}
.money-md {
font-size: 19px;
}
.escrow-notice {
display: flex;
gap: 12px;
align-items: flex-start;
width: 100%;
text-align: left;
background: var(--royal-100);
color: var(--royal-900);
border: 0;
border-radius: var(--r-card);
padding: 14px 16px;
font-size: 13px;
cursor: pointer;
font-family: inherit;
line-height: 1.5;
}
.escrow-notice:hover {
background: #e4dff8;
}
.escrow-notice svg {
flex: none;
width: 18px;
height: 18px;
margin-top: 1px;
}
.runner-card {
display: flex;
gap: 12px;
align-items: center;
}
.avatar {
flex: none;
width: 46px;
height: 46px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--royal-100);
color: var(--royal-900);
font-weight: 500;
font-size: 16px;
}
.avatar-sm {
width: 32px;
height: 32px;
font-size: 12px;
}
.thumb-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.evidence-thumb {
width: 84px;
text-align: center;
font-size: 11px;
color: var(--ink-60);
}
.evidence-thumb .frame {
width: 84px;
height: 84px;
border-radius: var(--r-control);
border: 1px solid var(--line);
background: var(--paper);
overflow: hidden;
display: grid;
place-items: center;
}
.evidence-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.evidence-thumb .doc-badge {
font-weight: 700;
font-size: 15px;
letter-spacing: 0.06em;
color: var(--ink-60);
}
.evidence-thumb .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-top: 4px;
}
.grid-cards {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}
.grid-listings {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
@media (min-width: 700px) {
.grid-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-listings {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (min-width: 980px) {
.grid-cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (min-width: 1060px) {
.grid-listings {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
}
}
@media (min-width: 1200px) {
.grid-cards {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-listings {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}
@media (min-width: 1440px) {
.grid-cards {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-listings {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
}
.grid-cards > .card,
.grid-cards > .cell-stack > .card {
margin: 0;
display: flex;
flex-direction: column;
gap: 9px;
/* A grid item defaults to min-width:auto, so it refuses to shrink below its
   own min-content: the nowrap run title beside its chip then pushed the card
   wider than its cell and the whole document scrolled sideways on a phone.
   The title already clips with an ellipsis; this lets it. */
min-width: 0;
}
/* Run cards keep 16px padding at every width so the -16px cover inset
   always sits flush with the card edge (the desktop 20px bump on plain
   cards would leave a 4px seam around the cover). */
.grid-cards > .card,
.grid-cards > .cell-stack > .card,
.hero-aside .card {
padding: 16px;
}
.layout-2col {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 16px;
}
/* On the mobile-first single column, lead with the side column (runner
   identity, badges, escrow reassurance) so the trust-first hierarchy the
   buyer run page mandates is not buried under the shelf and milestones. */
.layout-2col > .col-side {
order: -1;
}
@media (min-width: 980px) {
.layout-2col {
grid-template-columns: minmax(0, 1fr) 380px;
gap: 32px;
align-items: start;
}
.layout-2col > .col-side {
order: 0;
position: sticky;
top: 88px;
}
}
.layout-sidebar {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 16px;
}
@media (min-width: 980px) {
.layout-sidebar {
grid-template-columns: 240px minmax(0, 1fr);
gap: 32px;
align-items: start;
}
.layout-sidebar > .col-nav {
position: sticky;
top: 88px;
}
.layout-sidebar .chip-row {
display: none;
}
}
.side-nav {
display: none;
}
@media (min-width: 980px) {
.side-nav {
display: block;
}
}
.side-nav a {
display: flex;
justify-content: space-between;
align-items: center;
padding: 9px 12px;
border-radius: var(--r-control);
color: var(--ink-60);
font-size: 14px;
}
.side-nav a:hover {
background: var(--paper);
text-decoration: none;
color: var(--royal-700);
}
.side-nav a.active {
background: var(--royal-100);
color: var(--royal-900);
font-weight: 500;
}
.side-nav .count {
font-size: 12px;
color: var(--ink-40);
}
/* Walmart-style flat tile: no card box. A rounded media tile, then
   price first, title, meta. The quick-add pill floats on the tile. */
.listing-card {
position: relative;
background: transparent;
border: 0;
display: flex;
flex-direction: column;
color: inherit;
/* Same reason as .grid-cards > .card: a grid item's default min-width:auto
   lets its min-content (a price line that must not wrap) push it past its
   cell, which scrolls the whole page sideways on a narrow phone. */
min-width: 0;
}
.listing-card .body {
display: flex;
flex-direction: column;
flex: 1;
}
/* Amazon-style fixed zones: photo (square), title (always two lines),
   price (one line), meta (always two lines), action. Every card in a row
   is structurally identical, so nothing floats up or leaves a hole. */
.listing-card .price-line {
margin-top: 4px;
font-size: 16px;
line-height: 1.3;
/* The line may wrap between the price and the shop price it beats; each
   amount stays whole (below). A nowrap line could not fit both on a 320px
   phone two cards wide, and pushed the page into sideways scroll. The zone
   reserves both lines at every width, so a card that wraps and one that
   does not still line up, the way the title and meta zones already do. */
white-space: normal;
min-height: 2.6em;
}
.listing-card .price-line > * {
white-space: nowrap;
}
.listing-card .meta-line {
font-size: 11.5px;
line-height: 1.45;
min-height: 2.9em;
margin-top: 1px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.listing-card .body > form,
.listing-card .body > .card-action {
margin-top: auto;
padding-top: 8px;
}
.listing-card:hover {
text-decoration: none;
}
.listing-card:hover .photo {
border-color: var(--royal-500);
}
/* Fixed square media box, Amazon-style: the whole product, contained and
   centered, whatever the file's own shape. The absolute img is what pins
   the box height even for portrait photos inside a flex column. */
.listing-card .photo {
position: relative;
flex: none;
display: block;
aspect-ratio: 1 / 1;
background: #fff;
border: 1px solid var(--line-soft);
border-radius: var(--r-card);
overflow: hidden;
}
.listing-card .photo img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.listing-card .body {
padding: 10px 2px 4px;
}
.quick-add {
position: absolute;
top: 8px;
right: 8px;
z-index: 2;
}
.quick-add .btn {
min-height: 34px;
padding: 0 14px;
border-radius: 999px;
font-size: 13px;
box-shadow: var(--shadow-pop);
}
/* Exactly two lines reserved: a one-line title occupies the same height
   as a clamped two-line one, so price rows align across a card row. */
.listing-card .title {
font-size: 13px;
line-height: 1.35;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
min-height: 2.7em;
}
.listing-card .money {
font-size: 15px;
color: var(--price);
}
.gallery-thumbs {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.gallery-thumb {
width: 64px;
height: 64px;
padding: 0;
border: 2px solid var(--line);
border-radius: 10px;
overflow: hidden;
background: #fff;
cursor: pointer;
}
.gallery-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.gallery-thumb.active {
border-color: var(--royal-700);
}
.listing-hero {
aspect-ratio: 4 / 3;
border-radius: var(--r-card);
border: 1px solid var(--line);
background: var(--paper);
overflow: hidden;
}
.listing-hero img {
width: 100%;
height: 100%;
object-fit: cover;
}
.attr-row {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 9px 0;
border-bottom: 1px solid var(--line);
font-size: 13px;
}
.attr-row:last-child {
border-bottom: 0;
}
.attr-row dt {
color: var(--ink-60);
}
.attr-row dd {
margin: 0;
text-align: right;
}
.page-head {
padding: 4px 0 8px;
}
.page-head .crumbs {
font-size: 12px;
color: var(--ink-40);
margin-bottom: 8px;
}
.page-head .crumbs a {
color: var(--ink-60);
}
.page-head .sub {
color: var(--ink-60);
margin-top: 6px;
max-width: 60ch;
}
.section-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
}
.section-head h2 {
color: var(--ink);
}
.tabs {
display: inline-flex;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
padding: 3px;
max-width: 100%;
overflow-x: auto;
scrollbar-width: none;
}
.tabs::-webkit-scrollbar {
display: none;
}
.tabs a {
padding: 7px 16px;
border-radius: 999px;
font-size: 13px;
font-weight: 500;
color: var(--ink-60);
white-space: nowrap;
}
.tabs a:hover {
text-decoration: none;
color: var(--royal-700);
}
.tabs a.active {
background: var(--royal-700);
color: #fff;
}
.steps {
display: grid;
gap: 16px;
}
@media (min-width: 720px) {
.steps {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.step-num {
width: 30px;
height: 30px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--royal-100);
color: var(--royal-900);
font-weight: 500;
font-size: 13px;
margin-bottom: 10px;
}
.steps .card h3 + p {
margin-top: 6px;
}
/* Buying-from-abroad: a slim indigo promise band above the standard light
   step cards. The band states the one idea; the cards carry the detail on
   paper, where three columns of body copy stay readable. */
.eyebrow-light {
color: var(--indigo-950-soft);
display: inline-flex;
align-items: center;
gap: 8px;
}
.abroad-band {
padding: 22px 22px 24px;
}
.abroad-promise {
margin: 10px 0 0;
color: #fff;
font-size: 22px;
line-height: 1.25;
letter-spacing: -0.01em;
max-width: 24ch;
}
.abroad-note {
margin: 8px 0 0;
color: var(--indigo-950-muted);
font-size: 14px;
max-width: 60ch;
}
@media (min-width: 980px) {
.abroad-band {
padding: 28px 32px 30px;
}
.abroad-promise {
font-size: 28px;
}
}
/* The department row: round photo tiles in one swipeable line, sitting
   right under the hero the way the big shops put departments first. The
   same component serves home and catalogue (active state on catalogue). */
.cat-circles {
display: flex;
gap: 14px;
overflow-x: auto;
/* The first label was clipping at the viewport edge ("lectronics"): a
   horizontal rail needs its own leading and trailing room, because the
   page gutter stops at the scroll container. */
padding: 4px 16px 8px;
margin-inline: -16px;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.cat-circles::-webkit-scrollbar {
display: none;
}
/* On the catalogue the row is the department nav between header and grid. */
.page-head + .cat-circles {
margin-bottom: 14px;
}
.cat-circle {
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 7px;
width: 76px;
color: var(--ink);
}
.cat-circle .disc {
width: 62px;
height: 62px;
border-radius: 50%;
overflow: hidden;
background: var(--royal-100);
border: 2px solid transparent;
display: flex;
align-items: center;
justify-content: center;
transition: box-shadow 150ms ease;
}
.cat-circle .disc img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.cat-circle .disc svg {
width: 28px;
height: 28px;
color: var(--royal-700);
}
.cat-circle .name {
font-size: 11.5px;
font-weight: 600;
line-height: 1.25;
text-align: center;
max-width: 76px;
}
.cat-circle:hover {
text-decoration: none;
}
.cat-circle:hover .disc {
box-shadow: var(--shadow-pop);
}
.cat-circle.active .disc {
border-color: var(--royal-700);
}
.cat-circle.active .name {
color: var(--royal-700);
}
.cat-circle:focus-visible {
outline: none;
}
.cat-circle:focus-visible .disc {
box-shadow: 0 0 0 3px var(--royal-500);
}
.cat-circle:active .disc {
transform: scale(0.94);
}
.cat-circle .disc {
transition: box-shadow 150ms ease, transform 100ms ease;
scroll-snap-align: start;
}
.cat-circles {
scroll-snap-type: x proximity;
scroll-padding-left: 16px;
}
@media (min-width: 980px) {
.cat-circles {
gap: 20px;
}
.cat-circle {
width: 92px;
}
.cat-circle .disc {
width: 76px;
height: 76px;
}
.cat-circle .name {
font-size: 12.5px;
max-width: 92px;
}
.cat-circle .disc svg {
width: 34px;
height: 34px;
}
}
.cat-tiles {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
@media (min-width: 720px) {
.cat-tiles {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (min-width: 1060px) {
.cat-tiles {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
}
.cat-tile {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-card);
padding: 14px;
color: inherit;
display: block;
}
.cat-tile:hover {
text-decoration: none;
border-color: var(--royal-500);
}
.cat-tile .glyph {
width: 36px;
height: 36px;
border-radius: 10px;
background: var(--royal-100);
color: var(--royal-700);
display: grid;
place-items: center;
margin-bottom: 10px;
}
.cat-tile .glyph svg {
width: 19px;
height: 19px;
}
.cat-tile .name {
font-size: 13px;
font-weight: 500;
}
.cat-tile .count {
font-size: 12px;
color: var(--ink-40);
}
.list-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--line);
}
.list-row:last-child {
border-bottom: 0;
}
.tap-row {
width: 100%;
background: none;
border: 0;
text-align: left;
cursor: pointer;
color: inherit;
font: inherit;
}
.table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.table th {
text-align: left;
font-weight: 500;
color: var(--ink-60);
font-size: 12px;
padding: 8px 12px;
border-bottom: 1px solid var(--line);
white-space: nowrap;
}
.table td {
padding: 12px;
border-bottom: 1px solid var(--line);
vertical-align: middle;
}
.table tr:last-child td {
border-bottom: 0;
}
.table tr:hover td {
background: var(--paper);
}
.table .num {
text-align: right;
}
.notice {
border-radius: var(--r-card);
padding: 13px 16px;
font-size: 13px;
}
.notice-info {
background: var(--sky-100);
color: var(--sky-700);
}
.notice-success {
background: var(--royal-100);
color: var(--royal-900);
}
.notice-error {
background: var(--danger-100);
color: var(--danger-700);
}
.empty-state {
text-align: center;
padding: 56px 24px;
color: var(--ink-60);
}
.empty-state svg {
width: 72px;
height: 48px;
margin: 0 auto 16px;
color: var(--royal-500);
}
#toast-region {
position: fixed;
bottom: 160px;
left: 16px;
right: 16px;
z-index: 60;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
pointer-events: none;
}
.toast {
background: var(--ink);
color: #fff;
border-radius: var(--r-control);
padding: 10px 18px;
font-size: 13px;
max-width: 440px;
}
#sheet-region:empty {
display: none;
}
.sheet-backdrop {
position: fixed;
inset: 0;
z-index: 70;
background: rgba(27, 27, 36, 0.4);
display: flex;
align-items: flex-end;
justify-content: center;
}
.sheet {
background: var(--surface);
border-radius: var(--r-sheet) var(--r-sheet) 0 0;
width: 100%;
max-width: 680px;
max-height: 85dvh;
overflow-y: auto;
padding: 18px 20px calc(28px + env(safe-area-inset-bottom));
}
@media (min-width: 980px) {
.sheet-backdrop {
align-items: center;
padding: 24px;
}
.sheet {
border-radius: var(--r-sheet);
max-width: 520px;
}
}
.sheet-handle {
width: 36px;
height: 4px;
border-radius: 999px;
background: var(--line);
margin: 0 auto 12px;
}
.cta-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 40;
background: var(--surface);
border-top: 1px solid var(--line);
box-shadow: var(--shadow-cta);
padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
}
/* On mobile the tab bar owns the bottom edge; the CTA bar sits above it. */
@media (max-width: 979.98px) {
.cta-bar {
bottom: calc(60px + env(safe-area-inset-bottom));
padding-bottom: 12px;
}
}
.cta-bar .inner {
max-width: 680px;
margin: 0 auto;
}
.cta-bar .micro {
text-align: center;
font-size: 12px;
color: var(--ink-60);
margin-top: 6px;
}
@media (min-width: 980px) {
.cta-bar.mobile-only {
display: none;
}
}
/* On mobile the sticky CTA bar is the single checkout action; the basket
   rail keeps its own button on desktop only. */
@media (max-width: 979.98px) {
.rail-checkout {
display: none;
}
}
.site-footer {
border-top: 1px solid var(--line);
margin-top: 64px;
background: var(--surface);
}
.footer-grid {
display: grid;
gap: 28px;
padding: 36px 0;
grid-template-columns: 1fr;
}
@media (min-width: 720px) {
.footer-grid {
grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
padding: 48px 0;
}
}
.footer-brand svg {
width: 40px;
height: 28px;
color: var(--royal-500);
margin-bottom: 10px;
}
.footer-brand p {
font-size: 13px;
color: var(--ink-60);
max-width: 32ch;
}
.footer-col h4 {
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--ink-40);
margin-bottom: 10px;
}
.footer-col a {
display: block;
padding: 4px 0;
font-size: 13px;
color: var(--ink-60);
}
.footer-col a:hover {
color: var(--royal-700);
}
.footer-base {
border-top: 1px solid var(--line);
padding: 16px 0;
font-size: 12px;
color: var(--ink-40);
display: flex;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.htmx-swapping {
opacity: 0;
transition: opacity 150ms ease-out;
}
.htmx-added {
opacity: 0;
}
.htmx-settling {
opacity: 1;
transition: opacity 150ms ease-in;
}
@media (prefers-reduced-motion: reduce) {
.htmx-swapping,
.htmx-settling,
.htmx-added {
transition: none;
opacity: 1;
}
}
.queue-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 0;
font-size: 13px;
border-bottom: 1px solid var(--line);
}
.queue-item:last-child {
border-bottom: 0;
}
.btn-ghost {
background: transparent;
color: #fff;
border-color: rgba(255, 255, 255, 0.5);
}
.btn-ghost:hover {
border-color: #fff;
}
.auth-shell {
max-width: 920px;
margin: 24px auto 0;
}
.auth-split {
display: grid;
grid-template-columns: 1fr;
border: 1px solid var(--line);
border-radius: var(--r-sheet);
overflow: hidden;
background: var(--surface);
}
.auth-photo {
display: none;
position: relative;
/* The photo itself arrives as a media-gated background set by the login
   template, so mobile never downloads it. */
}
.auth-photo figcaption {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 16px 20px;
background: rgba(30, 21, 74, 0.72);
color: #fff;
font-size: 12px;
}
.auth-form {
padding: 28px 24px;
}
/* Storefront-style brand panel beside the form: the same indigo trust
   surface as the home hero, so signing in feels like the shop, not a plain
   form. Hidden on mobile (the form leads on a phone). */
.auth-brand {
display: none;
position: relative;
padding: 44px 40px;
background: linear-gradient(150deg, var(--indigo-950), var(--royal-900));
color: #fff;
flex-direction: column;
gap: 22px;
}
.auth-brand h2 {
font-family: var(--font-display);
font-size: 26px;
line-height: 1.15;
margin: 0;
color: #fff;
}
.auth-brand .auth-brand-sub {
font-size: 14px;
color: var(--indigo-950-muted);
margin: 8px 0 0;
line-height: 1.5;
}
.auth-brand .held-badge {
align-self: flex-start;
}
.auth-brand ul {
list-style: none;
margin: auto 0 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 14px;
}
.auth-brand li {
display: flex;
gap: 11px;
align-items: flex-start;
font-size: 13.5px;
color: #fff;
}
.auth-brand li svg {
flex: none;
width: 20px;
height: 20px;
color: var(--indigo-950-soft);
margin-top: 1px;
}
.auth-form {
padding: 28px 24px;
}
@media (min-width: 980px) {
.auth-split {
grid-template-columns: 5fr 6fr;
min-height: 540px;
}
.auth-brand {
display: flex;
}
.auth-form {
padding: 48px 52px;
}
}
.photo-strip {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 720px) {
.photo-strip {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.photo-strip figure {
margin: 0;
}
.photo-strip img {
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: var(--r-card);
border: 1px solid var(--line);
}
.photo-strip figcaption {
font-size: 12px;
color: var(--ink-60);
margin-top: 8px;
}
.photo-banner {
width: 100%;
aspect-ratio: 21 / 8;
object-fit: cover;
border-radius: var(--r-card);
border: 1px solid var(--line);
}
/* Inline-SVG banners (the escrow-journey scene) share the frame but need
   block flow and a paint under the gradient while fonts load. */
svg.photo-banner {
display: block;
background: var(--indigo-950);
}
/* The hidden attribute must always win, even over classes that set their own
   display (flex rows, block images): without this, JS-toggled elements leak
   into view as empty boxes. */
[hidden] {
display: none !important;
}
/* Cover-photo dropzone on the run form: tap or drop, preview in place at the
   same 21/8 the run page paints, so the runner sees the real crop. */
.upload-zone {
position: relative;
display: block;
border: 2px dashed var(--line);
border-radius: var(--r-card);
background: var(--paper);
text-align: center;
cursor: pointer;
overflow: hidden;
transition: border-color 0.15s;
}
.upload-zone:hover,
.upload-zone.dragging,
.upload-zone:focus-within {
border-color: var(--royal-500);
}
.upload-zone input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
.upload-zone .uz-idle {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 26px 16px;
color: var(--ink-60);
}
.upload-zone .uz-idle svg {
width: 30px;
height: 30px;
color: var(--royal-700);
margin-bottom: 2px;
}
.upload-zone .uz-idle strong {
font-size: 14px;
color: var(--ink);
}
.upload-zone img {
display: block;
width: 100%;
aspect-ratio: 21 / 8;
object-fit: cover;
}
.upload-zone.has-photo {
border-style: solid;
}
.upload-zone-slim .uz-idle {
padding: 16px;
}
/* Listing photo grid: existing and freshly chosen photos share one strip at
   the 4:3 crop buyers see; the first standing photo wears the Cover chip. */
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 10px;
}
.photo-grid:empty {
display: none;
}
.pg-thumb {
position: relative;
border-radius: var(--r-control);
overflow: hidden;
border: 1px solid var(--line);
}
.pg-thumb img {
display: block;
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
}
.pg-thumb.marked {
opacity: 0.45;
border-color: var(--danger-700);
}
.pg-x {
position: absolute;
top: 6px;
right: 6px;
width: 24px;
height: 24px;
border: 0;
border-radius: 50%;
background: rgba(22, 21, 31, 0.72);
color: #fff;
font-size: 15px;
line-height: 1;
cursor: pointer;
}
/* Slim cover strip at the top of runner-hub run cards. */
.card-cover-strip {
display: block;
width: 100%;
aspect-ratio: 21 / 7;
object-fit: cover;
border-radius: var(--r-control);
margin-bottom: 10px;
border: 1px solid var(--line);
}
.pg-cover {
position: absolute;
left: 6px;
bottom: 6px;
background: var(--royal-700);
color: #fff;
font-size: 11px;
font-weight: 700;
padding: 2px 8px;
border-radius: 999px;
}

/* Runner value banner: the pitch band on the runner hub and invite page.
   Same trust surface as the portal hero; royal icon chips carry the list. */
.value-banner {
background: linear-gradient(135deg, var(--indigo-950), var(--royal-900));
color: #fff;
border-radius: var(--r-hero);
padding: 22px 22px 18px;
}
.value-banner h2 {
font-family: var(--font-display);
font-size: 22px;
margin: 4px 0 14px;
color: #fff;
}
.vb-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 14px 18px;
}
.vb-item {
display: flex;
gap: 10px;
align-items: flex-start;
}
.vb-item strong {
font-size: 14px;
}
.vb-item p {
font-size: 12.5px;
color: var(--indigo-950-muted);
margin: 3px 0 0;
line-height: 1.45;
}
.vb-icon {
flex: none;
width: 34px;
height: 34px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.1);
color: var(--indigo-950-soft);
display: inline-flex;
align-items: center;
justify-content: center;
}
.vb-icon svg {
width: 20px;
height: 20px;
}
.value-banner-compact {
padding: 16px;
border-radius: var(--r-card);
}
.value-banner-compact h2 {
font-size: 17px;
margin-bottom: 10px;
}
.value-banner-compact .vb-grid {
grid-template-columns: 1fr;
gap: 10px;
}
.value-banner-compact .vb-item p {
font-size: 12px;
}
/* Staging deployments label themselves on every page: terracotta is the
   urgency colour, and a tester never mistakes this for the real thing. */
.staging-strip {
background: var(--accent);
color: #fff;
text-align: center;
font-size: 12.5px;
font-weight: 600;
padding: 6px 12px;
}
.route {
display: flex;
align-items: center;
gap: 14px;
background: var(--sky-100);
border-radius: var(--r-control);
padding: 12px 16px;
color: var(--sky-700);
}
.route .end {
display: flex;
align-items: baseline;
gap: 6px;
min-width: 0;
flex: none;
white-space: nowrap;
}
.route .code {
font-size: 17px;
font-weight: 500;
letter-spacing: 0.03em;
line-height: 1.1;
}
.route .city {
font-size: 11px;
color: var(--ink-60);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 86px;
}
.route .path {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
min-width: 40px;
}
.route .path .line {
flex: 1;
border-top: 2px dashed var(--sky-500);
opacity: 0.7;
}
.route .path svg {
width: 20px;
height: 20px;
flex: none;
}
.route-royal {
background: var(--royal-100);
color: var(--royal-700);
}
.route-royal .path .line {
border-color: var(--royal-500);
}
.activity-row {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--line);
color: inherit;
}
.activity-row:last-child {
border-bottom: 0;
}
.activity-row:hover {
text-decoration: none;
background: var(--paper);
}
.activity-dot {
flex: none;
width: 30px;
height: 30px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--sky-100);
color: var(--sky-700);
font-size: 13px;
}
.activity-row .when {
font-size: 12px;
color: var(--ink-40);
white-space: nowrap;
}
.msg {
display: flex;
padding: 2px 0;
}
.msg.mine {
justify-content: flex-end;
}
.msg .bubble {
max-width: 78%;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 14px 14px 14px 4px;
padding: 8px 12px;
font-size: 13px;
}
.msg.mine .bubble {
background: var(--royal-100);
border-color: #e0daf6;
color: var(--royal-900);
border-radius: 14px 14px 4px 14px;
}
.msg .stamp {
display: block;
font-size: 11px;
color: var(--ink-40);
margin-top: 3px;
}
.run-cover {
display: block;
position: relative;
margin: -16px -16px 0;
border-radius: var(--r-card) var(--r-card) 0 0;
overflow: hidden;
}
/* Route subline on run cards: always the same two-line zone so the
   runner row starts at one height across a card row. */
.run-sub {
font-size: 12px;
line-height: 1.45;
color: var(--ink-60);
margin-top: -4px;
min-height: 2.9em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.cover-badge {
position: absolute;
top: 10px;
left: 10px;
background: var(--accent);
color: #fff;
font-size: 11px;
font-weight: 700;
padding: 4px 10px;
border-radius: 9px;
}
/* Terracotta stays urgency-only: a run closing further out wears a quiet
   white badge instead. */
.cover-badge-neutral {
background: rgba(255, 255, 255, 0.94);
color: var(--ink);
}
/* Scarcity chip: bottom corner so it never collides with the closes badge. */
.cover-badge.cover-slots {
top: auto;
left: auto;
bottom: 10px;
right: 10px;
}

.run-cover img {
width: 100%;
aspect-ratio: 21 / 9;
object-fit: cover;
}
/* Shop-window strip on open-run cards: what the shelf holds and the entry
   price, so a card sells the run before the tap. */
.run-shelf {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
}
.run-shelf-thumb {
width: 38px;
height: 38px;
flex: none;
border-radius: 9px;
border: 1px solid var(--line);
background: var(--paper);
overflow: hidden;
}
.run-shelf-thumb img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.run-shelf-more {
display: grid;
place-items: center;
font-size: 11px;
font-weight: 700;
color: var(--royal-700);
background: var(--royal-100);
border-color: var(--royal-200);
}
.run-shelf-from {
margin-left: auto;
display: flex;
flex-direction: column;
align-items: flex-end;
line-height: 1.25;
flex: none;
white-space: nowrap;
}
.run-shelf-from .money {
font-family: var(--font-display);
font-weight: 700;
font-size: 14px;
color: var(--price);
white-space: nowrap;
}
.account-menu {
position: relative;
}
.account-menu > summary {
list-style: none;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
}
.account-menu > summary::-webkit-details-marker {
display: none;
}
.account-menu[open] > summary .avatar {
outline: 2px solid var(--royal-500);
outline-offset: 2px;
}
.account-pop {
position: absolute;
right: 0;
top: calc(100% + 10px);
z-index: 60;
width: 272px;
max-width: calc(100vw - 24px);
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-card);
padding: 6px;
box-shadow: var(--shadow-pop);
color: var(--ink);
}
.ap-who {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 10px 12px;
border-bottom: 1px solid var(--line);
margin-bottom: 6px;
}
.ap-who-text {
min-width: 0;
}
.ap-name {
font-family: var(--font-display);
font-weight: 700;
font-size: 15px;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ap-phone {
font-size: 12px;
color: var(--ink-60);
font-variant-numeric: tabular-nums;
margin-top: 2px;
}
.ap-group {
padding: 4px 0;
}
.ap-group + .ap-group {
border-top: 1px solid var(--line-soft);
}
.ap-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-40);
padding: 6px 12px 2px;
margin: 0;
}
.ap-row {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
min-height: 40px;
padding: 8px 10px;
border-radius: var(--r-control);
font-size: 13.5px;
font-weight: 500;
color: var(--ink);
background: none;
border: 0;
text-align: left;
cursor: pointer;
font-family: inherit;
}
.ap-row svg {
width: 17px;
height: 17px;
flex: none;
color: var(--ink-40);
}
.ap-row:hover,
.ap-row:focus-visible {
background: var(--royal-100);
color: var(--royal-900);
text-decoration: none;
}
.ap-row:hover svg,
.ap-row:focus-visible svg {
color: var(--royal-700);
}
.ap-count {
margin-left: auto;
min-width: 20px;
height: 20px;
padding: 0 6px;
border-radius: 999px;
background: var(--accent);
color: #fff;
font-size: 11px;
font-weight: 700;
display: grid;
place-items: center;
}
.ap-state {
margin-left: auto;
font-size: 11px;
font-weight: 700;
padding: 2px 8px;
border-radius: 999px;
background: var(--paper);
color: var(--ink-60);
border: 1px solid var(--line);
}
.ap-state.is-on {
background: var(--royal-100);
color: var(--royal-900);
border-color: var(--royal-200);
}
.account-pop form {
margin: 0;
}
.ap-foot {
border-top: 1px solid var(--line);
margin-top: 6px;
padding-top: 6px;
}
.ap-logout,
.ap-logout svg {
color: var(--danger-700);
}
.ap-logout:hover,
.ap-logout:focus-visible {
background: var(--danger-100);
color: var(--danger-700);
}
.eyebrow {
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-40);
}
.eyebrow-sky {
color: var(--sky-700);
}
.eyebrow-royal {
color: var(--royal-700);
}
hr.divider {
height: 1px;
background: var(--line);
border: 0;
margin: 4px 0;
}
.icon {
width: 15px;
height: 15px;
flex: none;
}
.meta {
display: flex;
gap: 6px 16px;
flex-wrap: wrap;
font-size: 12px;
color: var(--ink-60);
}
.meta > span {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.meta .icon {
color: var(--ink-40);
}
.card-foot {
border-top: 1px solid var(--line);
margin-top: auto;
padding-top: 12px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
/* The foot is one line in every card, so the value on its right sits at the
   same height across a row instead of riding up when the label beside it
   happens to wrap. */
.card-foot > :first-child {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.band {
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
background: var(--surface);
padding: 36px 0;
}
.band-royal {
background: var(--royal-100);
border-color: #e0daf6;
}
.band-royal h2,
.band-royal h3 {
color: var(--royal-900);
}
@media (min-width: 980px) {
.band {
padding: 56px 0;
}
}
.skip-link {
position: absolute;
left: -9999px;
top: 0;
z-index: 100;
background: var(--royal-700);
color: #fff;
padding: 10px 18px;
border-radius: 0 0 var(--r-control) 0;
}
.skip-link:focus {
left: 0;
text-decoration: none;
}
.mobile-search {
padding: 0 16px 12px;
}
.mobile-search input {
min-height: 44px;
border-radius: 999px;
border-color: transparent;
padding: 8px 20px;
background: #fff;
}
@media (min-width: 980px) {
.mobile-search {
display: none;
}
}
.field:has(.field-error) input,
.field:has(.field-error) select,
.field:has(.field-error) textarea {
border-color: var(--danger-700);
}
.btn.is-loading {
opacity: 0.65;
pointer-events: none;
}
.btn.is-loading::after {
content: "\2026";
}
.search-hint {
display: none;
font-size: 12px;
color: var(--ink-40);
margin-top: 6px;
}
.htmx-request ~ .search-hint,
.htmx-request .search-hint {
display: block;
}
/* Mobile shelves: below 700px a .shelf grid becomes a swipeable row with
   the next card peeking, the way big-retailer mobile homepages read. */
@media (max-width: 699.98px) {
.grid-cards.shelf,
.grid-listings.shelf {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
padding-bottom: 6px;
scrollbar-width: none;
}
.grid-cards.shelf::-webkit-scrollbar,
.grid-listings.shelf::-webkit-scrollbar {
display: none;
}
.grid-cards.shelf > * {
flex: 0 0 auto;
width: 82vw;
max-width: 320px;
scroll-snap-align: start;
}
.grid-listings.shelf > * {
flex: 0 0 auto;
width: 42vw;
max-width: 185px;
scroll-snap-align: start;
}
}
/* ---- P0 commerce mechanics: stepper, badge, suggestions, loading ---- */
.stepper {
display: flex;
align-items: center;
gap: 2px;
background: var(--royal-700);
border-radius: 999px;
box-shadow: var(--shadow-pop);
}
.stepper button {
width: 34px;
height: 34px;
border: 0;
background: none;
color: #fff;
display: grid;
place-items: center;
cursor: pointer;
border-radius: 999px;
}
.stepper button svg {
width: 14px;
height: 14px;
}
.stepper .qty {
min-width: 20px;
text-align: center;
color: #fff;
font-weight: 700;
font-size: 13.5px;
font-variant-numeric: tabular-nums;
}
.header-basket {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
color: #fff;
flex: none;
}
.header-basket svg {
width: 21px;
height: 21px;
}
.header-basket .badge {
position: absolute;
top: 2px;
right: 0;
min-width: 17px;
height: 17px;
padding: 0 4px;
border-radius: 9px;
background: var(--accent);
color: #fff;
font-size: 11px;
font-weight: 700;
display: grid;
place-items: center;
font-variant-numeric: tabular-nums;
}
.header-basket .badge[hidden] {
display: none;
}
.header-search,
.mobile-search {
position: relative;
}
.suggest-box {
position: absolute;
top: calc(100% + 6px);
left: 0;
right: 0;
z-index: 70;
color: var(--ink);
}
.mobile-search .suggest-box {
left: 16px;
right: 16px;
}
.suggest-box:empty {
display: none;
}
.suggest-panel {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 14px;
box-shadow: var(--shadow-pop);
overflow: hidden;
}
.suggest-row {
display: flex;
align-items: center;
gap: 11px;
padding: 9px 14px;
color: var(--ink);
font-size: 13px;
}
.suggest-row:hover {
background: var(--paper);
text-decoration: none;
}
.suggest-row .thumb {
flex: none;
width: 34px;
height: 34px;
border-radius: 8px;
background: var(--paper);
overflow: hidden;
display: grid;
place-items: center;
}
.suggest-row .thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.suggest-row .thumb-icon {
color: var(--royal-700);
}
.suggest-row .thumb-icon svg {
width: 17px;
height: 17px;
}
.suggest-row .grow {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.suggest-row .t {
font-weight: 600;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.suggest-row .s {
font-size: 11.5px;
color: var(--ink-60);
}
.suggest-all {
display: block;
padding: 10px 14px;
font-size: 12.5px;
font-weight: 700;
border-top: 1px solid var(--line-soft);
}
.suggest-all:hover {
background: var(--paper);
text-decoration: none;
}
.suggest-empty {
padding: 12px 14px;
font-size: 13px;
color: var(--ink-60);
}
/* Verified ratings: input stars (reversed-row hover trick) and static
   display stars. Gold is reserved for stars alone. */
.star-input {
display: inline-flex;
flex-direction: row-reverse;
gap: 2px;
}
.star-input input {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
}
.star-input label {
cursor: pointer;
color: var(--line);
display: grid;
place-items: center;
width: 34px;
height: 34px;
}
.star-input label svg {
width: 27px;
height: 27px;
fill: currentColor;
}
.star-input input:checked ~ label,
.star-input label:hover,
.star-input label:hover ~ label {
color: var(--gold);
}
.star-input input:focus-visible + label {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
border-radius: 6px;
}
.stars-static {
display: inline-flex;
gap: 1px;
}
.stars-static svg {
width: 16px;
height: 16px;
fill: var(--line);
}
.stars-static svg.on {
fill: var(--gold);
}
.rating-inline {
display: inline-flex;
align-items: center;
gap: 3px;
color: var(--ink-60);
}
.rating-inline svg {
width: 12px;
height: 12px;
fill: var(--gold);
}
/* A tappable rating opens the written reviews it is built from. */
a.rating-inline {
text-decoration: none;
}
a.rating-inline:hover strong {
text-decoration: underline;
}
.review-row {
padding: 14px 0;
border-bottom: 1px solid var(--line);
}
.review-row:last-child {
border-bottom: 0;
padding-bottom: 4px;
}
.review-row .chip {
font-size: 11px;
padding: 2px 9px;
}
/* Receipt: a document the buyer can print or save as their record. */
.receipt {
max-width: 560px;
margin: 0 auto;
}
.receipt-snail svg {
width: 44px;
height: 30px;
color: var(--royal-700);
}
.receipt-meta {
display: flex;
flex-wrap: wrap;
gap: 12px 24px;
font-size: 14px;
}
.statement-row {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--line);
}
.statement-row:last-child {
border-bottom: 0;
}
/* Notification feed: unread rows carry the royal dot, read ones go quiet. */
.notif-row {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 13px 0;
border-bottom: 1px solid var(--line);
color: inherit;
text-decoration: none;
}
.notif-row:last-child {
border-bottom: 0;
}
.notif-row:hover {
background: var(--paper);
}
.notif-dot {
flex: none;
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 7px;
background: var(--line);
}
.notif-row.unread .notif-dot {
background: var(--royal-700);
}
/* Save heart: outline until saved, royal fill once watching (royal marks
   active states; gold and green are reserved). */
.btn-save {
width: 44px;
height: 44px;
flex: none;
display: grid;
place-items: center;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--surface);
color: var(--ink-60);
cursor: pointer;
}
.btn-save svg {
width: 20px;
height: 20px;
fill: none;
}
.btn-save:hover {
border-color: var(--royal-700);
color: var(--royal-700);
}
.btn-save.active {
border-color: var(--royal-700);
color: var(--royal-700);
}
.btn-save.active svg {
fill: var(--royal-100);
}
.btn-save:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.pagination {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 16px;
}
.filter-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-end;
}
.filter-field {
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
min-width: 96px;
}
.filter-field input,
.filter-field select {
min-height: 42px;
border: 1px solid var(--line);
border-radius: var(--r-control);
padding: 0 12px;
font: inherit;
background: var(--surface);
color: inherit;
}
.filter-field input:focus-visible,
.filter-field select:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
/* Price presets and deal toggles: chips that act like buttons. */
button.preset-chip,
.filter-toggle {
border: 0;
font-family: inherit;
cursor: pointer;
}
.filter-toggle {
position: relative;
}
button.preset-chip:focus-visible,
.filter-toggle:focus-within {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.chip-divider {
width: 1px;
align-self: stretch;
background: var(--line);
margin: 2px 4px;
flex: none;
}
/* The basket drawer: a right-hand slide-over. Functional motion only (it is
   how the panel arrives), 200ms, snapped away under reduced motion. */
.drawer-backdrop {
position: fixed;
inset: 0;
z-index: 80;
background: rgba(27, 27, 36, 0.4);
display: flex;
justify-content: flex-end;
animation: drawer-fade 150ms ease-out;
}
.drawer {
background: var(--surface);
width: min(420px, 94vw);
height: 100dvh;
overflow-y: auto;
padding: 16px 18px calc(24px + env(safe-area-inset-bottom));
animation: drawer-in 200ms ease-out;
}
@keyframes drawer-fade {
from { opacity: 0; }
}
@keyframes drawer-in {
from { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
.drawer-backdrop,
.drawer {
animation: none;
}
}
.drawer-head {
position: sticky;
top: -16px;
margin: -16px -18px 8px;
padding: 16px 18px 10px;
background: var(--surface);
border-bottom: 1px solid var(--line);
z-index: 1;
}
.drawer-close {
width: 44px;
height: 44px;
flex: none;
display: grid;
place-items: center;
border: 0;
border-radius: 50%;
background: none;
color: var(--ink-60);
cursor: pointer;
}
.drawer-close svg {
width: 18px;
height: 18px;
}
.drawer-close:hover {
background: var(--paper);
color: var(--ink);
}
.drawer-close:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.drawer-group {
padding: 14px 0;
border-bottom: 1px solid var(--line);
}
.drawer-group:last-of-type {
border-bottom: 0;
}
.drawer-row {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 0;
}
.drawer-thumb {
flex: none;
width: 52px;
height: 52px;
border-radius: 10px;
overflow: hidden;
border: 1px solid var(--line);
background: var(--paper);
}
.drawer-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.drawer-step {
width: 30px;
height: 30px;
flex: none;
display: grid;
place-items: center;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--surface);
color: var(--ink);
font: inherit;
cursor: pointer;
}
.drawer-step:hover {
border-color: var(--royal-500);
color: var(--royal-700);
}
.drawer-step:focus-visible,
.drawer-remove:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.drawer-remove {
border: 0;
background: none;
padding: 4px 0;
color: var(--ink-40);
text-decoration: underline;
cursor: pointer;
font-family: inherit;
}
.drawer-remove:hover {
color: var(--danger-700);
}
/* Avatar with an uploaded photo: the frame stays, the photo fills it. */
.avatar-photo {
padding: 0;
overflow: hidden;
}
.avatar-photo img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.avatar-anon::before {
content: "";
width: 58%;
height: 58%;
background: currentColor;
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'><circle cx='12' cy='8' r='4'/><path d='M4.5 20a7.5 7.5 0 0 1 15 0'/></svg>") center / contain no-repeat;
mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'><circle cx='12' cy='8' r='4'/><path d='M4.5 20a7.5 7.5 0 0 1 15 0'/></svg>") center / contain no-repeat;
}
.avatar-lg {
width: 72px;
height: 72px;
font-size: 26px;
}
/* Listing page: the buy box keeps itself in view on desktop. */
@media (min-width: 980px) {
.col-side-sticky {
position: sticky;
top: 84px;
align-self: start;
max-height: calc(100dvh - 100px);
overflow-y: auto;
}
}
.gallery-zoom {
display: block;
cursor: zoom-in;
}
/* Gallery carousel controls. */
.carousel {
position: relative;
}
.carousel-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 44px;
height: 44px;
border-radius: 50%;
border: 1px solid var(--line);
background: rgba(255, 255, 255, 0.92);
color: var(--ink);
display: grid;
place-items: center;
cursor: pointer;
z-index: 1;
}
.carousel-arrow svg {
width: 18px;
height: 18px;
}
.carousel-arrow.prev {
left: 10px;
}
.carousel-arrow.next {
right: 10px;
}
.carousel-arrow:hover {
border-color: var(--royal-500);
color: var(--royal-700);
}
.carousel-arrow:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.carousel-count {
position: absolute;
right: 12px;
bottom: 10px;
z-index: 1;
font-size: 11.5px;
font-weight: 700;
font-variant-numeric: tabular-nums;
padding: 3px 10px;
border-radius: 999px;
background: rgba(23, 16, 64, 0.72);
color: #fff;
}
/* Full-screen photo lightbox; shares the carousel's index and arrows. */
.lightbox-backdrop {
position: fixed;
inset: 0;
z-index: 90;
background: rgba(23, 16, 64, 0.94);
display: grid;
place-items: center;
animation: drawer-fade 150ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
.lightbox-backdrop {
animation: none;
}
}
.lightbox-backdrop img {
max-width: 94vw;
max-height: 86dvh;
object-fit: contain;
border-radius: 8px;
}
.lightbox-backdrop .carousel-arrow {
position: fixed;
top: 50%;
}
.lightbox-backdrop .carousel-arrow.prev {
left: 14px;
}
.lightbox-backdrop .carousel-arrow.next {
right: 14px;
}
.lightbox-close {
position: fixed;
top: 14px;
right: 14px;
width: 44px;
height: 44px;
border: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.14);
color: #fff;
display: grid;
place-items: center;
cursor: pointer;
}
.lightbox-close svg {
width: 18px;
height: 18px;
}
.lightbox-close:hover {
background: rgba(255, 255, 255, 0.26);
}
.lightbox-close:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.lightbox-count {
position: fixed;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
right: auto;
}
/* Buy-box quantity stepper. */
.qty-stepper {
display: flex;
align-items: stretch;
border: 1px solid var(--line);
border-radius: var(--r-control);
overflow: hidden;
flex: none;
}
.qty-stepper button {
width: 40px;
border: 0;
background: var(--paper);
color: var(--ink);
font-size: 17px;
cursor: pointer;
}
.qty-stepper button:hover {
color: var(--royal-700);
}
.qty-stepper button:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: -2px;
}
.qty-stepper input {
width: 44px;
border: 0;
text-align: center;
font: inherit;
font-weight: 700;
font-variant-numeric: tabular-nums;
background: var(--surface);
color: inherit;
}
.qty-stepper input:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: -2px;
}
/* At-a-glance spec grid. */
.glance-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px 16px;
margin-top: 10px;
}
.glance-cell {
background: var(--paper);
border-radius: var(--r-control);
padding: 9px 12px;
}
.glance-cell dd {
margin: 2px 0 0;
}
/* Star histogram beside review lists. */
.rating-bars {
display: flex;
flex-direction: column;
gap: 4px;
max-width: 260px;
margin: 8px 0 4px;
}
.rating-bar-row {
display: flex;
align-items: center;
gap: 8px;
}
.rating-bar-track {
flex: 1;
height: 8px;
border-radius: 4px;
background: var(--paper);
overflow: hidden;
}
.rating-bar-fill {
display: block;
height: 100%;
border-radius: 4px;
background: var(--gold);
}
/* Honest merchandising flags overlaid on card photos. */
.card-flag {
position: absolute;
top: 8px;
left: 8px;
z-index: 1;
font-size: 11px;
font-weight: 700;
padding: 3px 9px;
border-radius: 999px;
color: #fff;
}
.flag-accent {
background: var(--accent);
}
.flag-royal {
background: var(--royal-700);
}
/* The journey strip: how an item physically reaches the buyer. */
.journey-steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-top: 12px;
}
@media (max-width: 699px) {
.journey-steps {
grid-template-columns: 1fr 1fr;
}
}
.journey-step {
display: flex;
flex-direction: column;
gap: 6px;
}
.journey-num {
width: 26px;
height: 26px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--royal-100);
color: var(--royal-700);
font-weight: 700;
font-size: 12px;
font-variant-numeric: tabular-nums;
}
/* The On-the-move journey card: the trail sits in its own quiet panel,
   everything else stays on two calm rails above and below it. */
.journey-card .journey-trail {
background: var(--paper);
border-radius: var(--r-control);
padding: 2px 16px 10px;
margin-top: 14px;
}
.journey-card .journey-trail .trail-dot {
border-color: var(--ink-40);
}
.journey-card .journey-trail .trail-step.done .trail-dot {
border-color: var(--sky-500);
}
.journey-card .journey-trail .trail-step.current .trail-dot {
border-color: var(--royal-500);
}
.journey-latest {
display: flex;
align-items: flex-start;
gap: 7px;
margin-top: 10px;
}
.journey-foot {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 12px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid var(--line);
}
/* Micro progress on compact in-flight rows: the run's stage at a glance. */
.row-progress {
display: block;
height: 3px;
border-radius: 2px;
background: var(--line);
margin-top: 7px;
overflow: hidden;
max-width: 260px;
}
.row-progress span {
display: block;
height: 100%;
border-radius: 2px;
background: var(--sky-500);
}
/* Travelling run cards: the mini trail gets the same quiet panel. */
.card-trail {
background: var(--paper);
border-radius: 10px;
padding: 2px 12px 8px;
}
.travel-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--ink-60);
}
.travel-meta svg {
width: 13px;
height: 13px;
flex: none;
color: var(--sky-700);
}
/* Feel: press feedback and considered hovers, all functional and tiny. */
.btn:active,
.chip:active,
.quick-add .btn:active {
transform: translateY(1px);
}
a.chip.chip-plain:hover,
button.preset-chip.chip-plain:hover,
.filter-toggle.chip-plain:hover {
background: var(--line);
}
html {
scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.btn:active,
.chip:active,
.quick-add .btn:active {
transform: none;
}
}
/* Capacity bar as a tap-open trust element (run page only). */
.capacity-open {
display: block;
width: 100%;
border: 0;
background: none;
padding: 0;
text-align: left;
cursor: pointer;
color: inherit;
font: inherit;
}
.capacity-open:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
border-radius: 6px;
}
/* Data saver: the placeholder that stands where a photo would load. It fills
   whatever photo frame contains it so layouts hold. */
.ds-photo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
height: 100%;
min-height: 72px;
background: var(--paper);
color: var(--ink-40);
text-decoration: none;
text-align: center;
padding: 10px;
}
.ds-photo svg {
width: 22px;
height: 22px;
}
a.ds-photo:hover {
color: var(--royal-700);
}
a.ds-photo:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: -2px;
}
.footer-toggle {
border: 0;
background: none;
padding: 0;
color: inherit;
font: inherit;
text-decoration: underline;
cursor: pointer;
}
.footer-toggle:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
/* Card-level section heading: one size everywhere, still steps up on desktop. */
.h-card {
font-size: 17px;
}
@media (min-width: 980px) {
.h-card {
font-size: 18px;
}
}
/* Stat rows (hero figures side by side): wrap into a grid on narrow screens
   instead of clipping tabular money against the viewport edge. */
.stat-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 12px 10px;
}
/* Numeric stats that are counts, not money: same display face, no GHS. */
.stat-num {
font-family: var(--font-display);
font-weight: 700;
font-variant-numeric: tabular-nums;
font-size: 22px;
}
/* Compact button that still honors the 44px touch floor. */
.btn-sm {
min-height: 44px;
padding: 0 16px;
flex: none;
}
/* The trail's completed dots stay 12px visually but tap at 44px. */
button.trail-dot {
position: relative;
}
button.trail-dot::after {
content: "";
position: absolute;
inset: -16px;
}
/* Rows borrowing milestone-row's look without its tap behavior. */
.milestone-row.static {
cursor: default;
}
.milestone-row.static:hover {
background: none;
}
/* Disabled pagination ends share the standard disabled look. */
.btn[aria-disabled="true"] {
opacity: 0.55;
pointer-events: none;
}
/* The catalogue search block (was unstyled). */
.search-bar label {
display: block;
margin-bottom: 4px;
color: var(--ink-60);
}
.search-bar input {
width: 100%;
min-height: 46px;
border: 1px solid var(--line);
border-radius: var(--r-control);
padding: 0 14px;
font: inherit;
background: var(--surface);
color: inherit;
}
.search-bar input:focus-visible {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.search-bar .search-hint {
display: none;
font-size: 12px;
color: var(--ink-40);
}
.search-bar .search-hint.htmx-request {
display: inline;
}
/* Visually hidden form input behind a styled label (radio groups, capture
   zones); replaces six inline copies of the same style. */
.vh-input {
position: absolute;
opacity: 0;
width: 1px;
min-height: 1px;
pointer-events: none;
}
@media print {
.site-header,
.site-footer,
.tabbar,
.cta-bar,
.no-print,
#toast-region,
#sheet-region {
display: none !important;
}
body {
background: #fff;
}
.receipt {
border: 0;
box-shadow: none;
max-width: none;
}
}
/* Loading affordance on swapped regions: quiet dim, no shimmer. */
#listing-grid.htmx-request,
#basket-box.htmx-request {
opacity: 0.5;
transition: opacity 150ms ease;
}
.hide-mobile {
display: none;
}
@media (min-width: 980px) {
.hide-mobile {
display: revert;
}
#toast-region {
bottom: 92px;
}
}
.hero-dark {
background: var(--indigo-950);
color: #fff;
}
.hero-dark .hero-copy h1 {
color: #fff;
}
.hero-dark .hero-copy .lede {
color: var(--indigo-950-muted);
}
.hero-dark .eyebrow {
color: var(--indigo-950-soft);
}
.hero-dark .stat {
border-left-color: rgba(255, 255, 255, 0.25);
}
.hero-dark .stat .num {
color: #fff;
}
.hero-dark .stat .lbl {
color: var(--indigo-950-muted);
}
.held-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 999px;
padding: 7px 14px;
font-size: 12.5px;
font-weight: 600;
color: #fff;
}
.held-badge svg {
width: 15px;
height: 15px;
flex: none;
color: var(--indigo-950-soft);
}
.trust-strip {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
background: var(--line);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
@media (max-width: 480px) {
.trust-strip {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.trust-strip > div {
background: var(--surface);
padding: 14px 8px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 1.3;
}
.trust-strip svg {
width: 20px;
height: 20px;
color: var(--royal-700);
}
.portal-hero {
background: var(--indigo-950);
color: #fff;
border-radius: var(--r-hero);
padding: 24px 22px;
}
@media (min-width: 980px) {
.portal-hero {
padding: 32px;
}
}
.portal-hero .money-lg {
font-size: 36px;
letter-spacing: -0.02em;
}
.portal-hero a:hover {
text-decoration: none;
}
.ceiling-track {
height: 7px;
/* Track on the indigo hero: the muted indigo tint at low opacity. The
   solid fallback covers engines without color-mix. */
background: var(--indigo-950-muted);
background: color-mix(in srgb, var(--indigo-950-muted) 30%, transparent);
border-radius: 4px;
overflow: hidden;
}
.ceiling-track .fill {
height: 100%;
background: var(--indigo-950-soft);
border-radius: 4px;
}
@media (min-width: 980px) {
.portal-hero .money-lg {
font-size: 44px;
}
}
.pay-method {
position: relative;
display: flex;
align-items: center;
gap: 12px;
width: 100%;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-card);
padding: 14px 16px;
cursor: pointer;
font: inherit;
text-align: left;
color: inherit;
}
.pay-method:focus-within {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.pay-method.selected {
border: 2px solid var(--royal-700);
padding: 13px 15px;
}
.pay-method .logo {
flex: none;
width: 40px;
height: 40px;
border-radius: 10px;
display: grid;
place-items: center;
font-size: 11px;
font-weight: 800;
color: #fff;
}
.pay-method .tick {
flex: none;
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid var(--royal-200);
display: grid;
place-items: center;
}
.pay-method.selected .tick {
background: var(--royal-700);
border-color: var(--royal-700);
color: #fff;
}
.milestone-banner {
background: var(--indigo-950);
color: #fff;
border-radius: var(--r-card);
padding: 16px 18px;
display: flex;
gap: 13px;
align-items: center;
}
.milestone-banner svg {
flex: none;
width: 24px;
height: 24px;
color: var(--indigo-950-soft);
}
.milestone-banner .eyebrow {
color: var(--indigo-950-soft);
}
.capture-zone {
position: relative;
border: 2px dashed var(--royal-200);
border-radius: var(--r-card);
padding: 26px 16px;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
text-align: center;
cursor: pointer;
}
.capture-zone:hover {
border-color: var(--royal-500);
}
.capture-zone:focus-within {
outline: 2px solid var(--royal-700);
outline-offset: 2px;
}
.capture-zone .lens {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--royal-100);
display: grid;
place-items: center;
color: var(--royal-700);
}
.capture-zone .lens svg {
width: 26px;
height: 26px;
}

/* ---- Run-card polish: the cover carries the route, the card sheds its
   boilerplate. The scrim sits under the badges; the slots chip moves to
   the top corner so the route owns the bottom edge. ---- */
.run-cover img {
display: block;
}
.cover-scrim {
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(23, 16, 64, 0) 55%, rgba(23, 16, 64, 0.45));
pointer-events: none;
}
/* The route rides the photo as a contained pill, never bare text: it can
   ellipsize inside its own background and cannot bleed past the image. */
.cover-route {
position: absolute;
left: 10px;
bottom: 10px;
max-width: calc(100% - 20px);
background: rgba(23, 16, 64, 0.62);
color: #fff;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.01em;
padding: 4px 11px;
border-radius: 999px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cover-badge,
.run-cover .cover-badge {
z-index: 1;
}
.cover-badge.cover-slots {
top: 10px;
right: 10px;
bottom: auto;
left: auto;
}
/* One thin line under the title; no reserved second line. */
.run-lands {
font-size: 12px;
line-height: 1.45;
color: var(--ink-60);
margin-top: -4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

/* Micro-delight: on full-size trails the snail crawls out to its milestone
   when the page loads. Mini trails on cards stay still, and so does every
   reduced-motion user. */
@keyframes snail-crawl {
from { left: 2%; opacity: 0.35; }
}
.trail-snail {
animation: snail-crawl 1100ms ease-out both;
}
.mini-trail .trail-snail {
animation: none;
}
@media (prefers-reduced-motion: reduce) {
.trail-snail {
animation: none;
}
}

/* The shop-window strip must never push its price past the card edge: the
   row wraps, so on tight cards the from-price drops to its own line, still
   right-aligned, instead of overflowing the card. */
.run-shelf {
flex-wrap: wrap;
row-gap: 4px;
}

/* Owner call, 3 Sep: shelf thumbnails are circular, like a stack of product
   coins; the +N chip matches so the row reads as one family. */
.run-shelf-thumb {
border-radius: 999px;
}

/* Milestone timeline: the whole road, one stage per row. Recorded rows open
   the evidence sheet, the current row is ringed in royal, upcoming rows say
   which proof the runner still has to seal. Tap targets stay at 44px. */
.mtl {
list-style: none;
padding: 0;
margin: 4px 0 0;
}
.mtl-step {
position: relative;
display: grid;
grid-template-columns: 32px minmax(0, 1fr);
gap: 0 12px;
padding-bottom: 18px;
}
.mtl-step::before {
content: "";
position: absolute;
left: 15px;
top: 34px;
bottom: -2px;
width: 2px;
background: var(--line);
}
.mtl-step.done::before {
background: var(--sky-500);
}
.mtl-step.current::before {
background: var(--line);
}
.mtl-step:last-child::before {
display: none;
}
.mtl-step:last-child {
padding-bottom: 4px;
}
.mtl-node {
width: 32px;
height: 32px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--paper);
border: 2px solid var(--line);
color: var(--ink-40);
font-size: 13px;
font-weight: 600;
font-family: var(--font-display);
}
.mtl-step.done .mtl-node {
background: var(--sky-500);
border-color: var(--sky-500);
color: #fff;
}
.mtl-step.current .mtl-node {
background: var(--surface);
border-color: var(--royal-500);
color: var(--royal-700);
box-shadow: 0 0 0 4px var(--royal-100);
}
.mtl-body {
min-width: 0;
padding-top: 5px;
}
.mtl-head {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.mtl-label {
font-weight: 600;
color: var(--ink);
}
.mtl-step.upcoming .mtl-label {
color: var(--ink-60);
font-weight: 500;
}
.mtl-now {
padding: 2px 8px;
font-size: 11px;
}
.mtl-when {
margin-left: auto;
font-size: 12px;
color: var(--ink-40);
white-space: nowrap;
}
.mtl-meaning {
margin: 2px 0 0;
font-size: 13px;
color: var(--ink-60);
}
.mtl-step.upcoming .mtl-meaning {
color: var(--ink-40);
}
.mtl-proof {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
min-height: 48px;
margin-top: 8px;
padding: 6px 10px;
border: 1px solid var(--line);
border-radius: var(--r-control);
background: var(--surface);
color: var(--ink);
text-align: left;
cursor: pointer;
font: inherit;
}
.mtl-proof:hover {
border-color: var(--sky-700);
background: var(--paper);
}
.mtl-proof:focus-visible {
outline: 2px solid var(--royal-500);
outline-offset: 2px;
}
.mtl-thumbs {
display: flex;
gap: 4px;
flex: none;
}
.mtl-thumbs img,
.mtl-doc {
width: 36px;
height: 36px;
border-radius: 6px;
object-fit: cover;
background: var(--paper);
border: 1px solid var(--line);
}
.mtl-doc {
display: grid;
place-items: center;
font-size: 11px;
font-weight: 600;
color: var(--ink-60);
}
.mtl-seal {
flex: none;
width: 36px;
height: 36px;
border-radius: 6px;
display: grid;
place-items: center;
background: var(--sky-100);
color: var(--sky-700);
font-size: 16px;
}
.mtl-proof-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 1px;
}
.mtl-proof-title {
font-size: 13px;
font-weight: 500;
}
.mtl-proof-text .tiny {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mtl-arrow {
color: var(--ink-40);
font-size: 20px;
line-height: 1;
}
.mtl-needs {
margin: 6px 0 0;
font-size: 12px;
color: var(--ink-40);
}

/* Photo quality tag: set once at upload on evidence that is too small or
   blurry. Honest, not alarming: terracotta text on its own tint. */
.quality-tag {
display: inline-block;
padding: 1px 7px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
line-height: 1.5;
color: var(--accent);
background: var(--accent-100);
white-space: nowrap;
}
/* Evidence sheet seals: one row per file with its fingerprint. */
.seal-list {
list-style: none;
padding: 0;
margin: 14px 0 0;
border-top: 1px solid var(--line);
}
.seal-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 2px 10px;
padding: 10px 0;
border-bottom: 1px solid var(--line);
align-items: center;
}
.seal-kind {
font-size: 13px;
font-weight: 500;
}
.seal-fp {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 8px;
}
.seal-fp code {
font-size: 12px;
color: var(--ink-60);
background: var(--paper);
padding: 2px 6px;
border-radius: 4px;
}
.seal-copy {
min-height: 32px;
padding: 0 10px;
border: 1px solid var(--line);
border-radius: 999px;
background: var(--surface);
color: var(--royal-700);
font: inherit;
font-size: 12px;
font-weight: 500;
cursor: pointer;
}
.seal-how {
margin-top: 12px;
}
.seal-how summary {
cursor: pointer;
font-size: 13px;
color: var(--royal-700);
font-weight: 500;
min-height: 44px;
display: flex;
align-items: center;
}

/* Rich trail (run page, order tracking): every step named, every dot
   tappable, and the now-line saying whose move it is. Compact trails on
   cards keep the original three-label form. */
.trail.full {
padding-bottom: 2px;
}
.trail.full .trail-track {
padding: 0;
}
.trail.full button.trail-dot {
cursor: pointer;
}
.trail.full .trail-step:not(.done) button.trail-dot {
border-style: dashed;
border-color: var(--ink-40);
}
.trail.full .trail-step:not(.done) button.trail-dot:hover,
.trail.full .trail-step:not(.done) button.trail-dot:focus-visible {
border-color: var(--royal-500);
border-style: solid;
}
.trail.full .trail-step.current .trail-dot {
box-shadow: 0 0 0 0 color-mix(in srgb, var(--royal-500) 45%, transparent);
animation: trail-pulse 2.2s ease-out infinite;
}
@keyframes trail-pulse {
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--royal-500) 45%, transparent); }
70% { box-shadow: 0 0 0 9px color-mix(in srgb, var(--royal-500) 0%, transparent); }
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--royal-500) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
.trail.full .trail-step.current .trail-dot {
animation: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--royal-500) 25%, transparent);
}
}
.trail-labels-all {
position: relative;
height: 34px;
margin-top: 8px;
font-size: 11px;
line-height: 1.25;
color: var(--ink-40);
}
.trail-labels-all span {
position: absolute;
top: 0;
transform: translateX(-50%);
text-align: center;
white-space: nowrap;
}
/* Seven labels cannot share one row on a phone: below 700px alternate
   labels drop to a second row, and a faint tick ties each lower label back
   to its dot. Wide screens keep the single calm row. */
@media (max-width: 700px) {
.trail-labels-all {
height: 48px;
}
.trail-labels-all span:nth-child(even) {
top: 22px;
}
.trail-labels-all span:nth-child(even)::before {
content: "";
position: absolute;
left: 50%;
top: -8px;
width: 1px;
height: 7px;
background: var(--line);
}
.trail-labels-all span.last::before {
left: auto;
right: 14px;
}
}
.trail-labels-all span.first {
left: 0;
transform: none;
text-align: left;
}
.trail-labels-all span.last {
left: auto;
right: 0;
transform: none;
text-align: right;
}
.trail-labels-all span em {
display: block;
font-style: normal;
color: var(--ink-40);
font-size: 11px;
margin-top: 1px;
}
.trail-labels-all span.done {
color: var(--sky-700);
font-weight: 600;
}
.trail-labels-all span.current {
color: var(--royal-700);
font-weight: 700;
font-size: 11px;
}
.trail-now {
display: flex;
gap: 10px;
align-items: flex-start;
flex-wrap: wrap;
margin-top: 10px;
border-top: 1px dashed var(--line);
padding-top: 10px;
}
.trail-now p {
margin: 0;
flex: 1;
min-width: 200px;
color: var(--ink-60);
}
.trail-now strong {
color: var(--ink);
}
button.trail-next {
background: none;
border: none;
padding: 0;
cursor: pointer;
font: inherit;
font-weight: 700;
color: var(--royal-700);
}
button.trail-next:hover,
button.trail-next:focus-visible {
text-decoration: underline;
}
@media (max-width: 420px) {
.trail-labels-all {
font-size: 11px;
}
.trail-labels-all span.current {
font-size: 11px;
}
}
/* Run page hero: cover, title, the four facts a buyer asks first (runner,
   close, landing, room), and the money line naming the runner. One surface,
   so the answer to "should I join" needs no scrolling. */
.run-crumbs {
font-size: 12px;
color: var(--ink-40);
margin-bottom: 12px;
}
.run-crumbs a {
color: var(--ink-60);
}
.run-hero {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-hero);
overflow: hidden;
margin-bottom: 24px;
}
.run-hero-cover {
position: relative;
background: var(--royal-100);
}
.run-hero-cover img {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.run-hero-cover .cover-badge {
font-size: 12px;
padding: 5px 12px;
}
.run-hero-ds {
padding: 18px 16px;
}
/* No cover photo: the route banner stands in, so it starts below the
   badge line instead of under the closes pill, and the slots pill joins
   that top line rather than sitting on the destination code. */
.run-hero-route {
padding: 52px 16px 18px;
}
.run-hero-route ~ .cover-slots {
top: 10px;
bottom: auto;
}
.run-hero-body {
padding: 16px;
}
.run-hero-title {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
flex-wrap: wrap;
}
.run-hero-title h1 {
margin-top: 4px;
font-size: 24px;
line-height: 1.15;
}
.run-share {
flex: none;
}
.run-facts {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin-top: 16px;
}
.fact {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
min-height: 64px;
padding: 10px 12px;
border-radius: var(--r-control);
background: var(--paper);
border: 1px solid transparent;
color: var(--ink);
text-align: left;
font-family: inherit;
cursor: default;
}
a.fact,
button.fact {
cursor: pointer;
}
a.fact:hover,
button.fact:hover,
a.fact:focus-visible,
button.fact:focus-visible {
border-color: var(--royal-500);
text-decoration: none;
}
.fact-text {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
}
.fact-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-40);
}
.fact-value {
font-family: var(--font-display);
font-weight: 700;
font-size: 15px;
line-height: 1.2;
margin-top: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fact-value.is-urgent {
color: var(--accent);
}
.fact-sub {
font-size: 12px;
color: var(--ink-60);
margin-top: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fact-track {
display: block;
height: 6px;
margin-top: 6px;
}
.run-hero-foot {
margin-top: 14px;
}
.run-hero-foot .escrow-notice {
width: 100%;
}
@media (min-width: 720px) {
.run-hero-cover img {
aspect-ratio: 21 / 8;
}
.run-hero-body {
padding: 20px 24px 22px;
}
.run-hero-title h1 {
font-size: 30px;
}
.run-facts {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
}
}
/* On the run page the hero already leads with the runner and the money
   line, so the phone keeps the shelf next and the side column after. */
.run-layout > .col-side {
order: 0;
}
/* The shelf inside a two-column page: cards stay a readable size instead
   of the storefront's five-up, and the ask tile sits as one more product. */
@media (min-width: 720px) {
.run-shelf-grid {
grid-template-columns: repeat(auto-fill, minmax(172px, 1fr));
}
}
.ask-tile {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: 6px;
min-height: 220px;
padding: 18px 14px;
border: 1.5px dashed var(--royal-200);
border-radius: var(--r-card);
background: var(--surface);
color: var(--ink);
}
.ask-tile:hover,
.ask-tile:focus-visible {
border-color: var(--royal-500);
background: var(--royal-100);
text-decoration: none;
}
.ask-tile-wide {
min-height: 0;
grid-column: 1 / -1;
padding: 22px 18px;
}
.ask-plus {
width: 40px;
height: 40px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--royal-100);
color: var(--royal-700);
font-size: 24px;
font-weight: 500;
line-height: 1;
}
.ask-title {
font-weight: 700;
font-size: 14px;
color: var(--royal-900);
}
.ask-sub {
font-size: 12px;
color: var(--ink-60);
max-width: 26ch;
}
/* The full step list sits behind a disclosure: open runs collapse it (seven
   future steps are not news yet), travelling runs open it (the record is). */
.mtl-details > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.mtl-details > summary::-webkit-details-marker {
display: none;
}
.mtl-summary-text {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.mtl-chevron {
width: 20px;
height: 20px;
flex: none;
color: var(--ink-40);
transition: transform 150ms ease;
}
.mtl-details[open] > summary .mtl-chevron {
transform: rotate(180deg);
}
.mtl-details-body {
margin-top: 14px;
}
@media (prefers-reduced-motion: reduce) {
.mtl-chevron {
transition: none;
}
}
/* Empty basket as the join panel. */
.join-panel {
display: flex;
flex-direction: column;
gap: 10px;
}
.join-micro {
margin: 0;
}

/* Run manage redesign: the run's next action sits at the top full-width,
   and the add-product form folds behind one unmissable button. */
.manage-next .btn-block,
.manage-next form,
.manage-next > a.btn {
max-width: 460px;
}
details.add-product > summary {
list-style: none;
cursor: pointer;
}
details.add-product > summary::-webkit-details-marker {
display: none;
}
details.add-product > summary:focus-visible .btn {
outline: 2px solid var(--royal-500);
outline-offset: 2px;
}
details.add-product[open] > summary .btn {
background: var(--royal-100);
color: var(--royal-700);
}

/* The buyer pages lead with the trust side-column on mobile (order:-1);
   the manage cockpit must not: the shelf is the work surface, share and
   view-as-buyer belong at the bottom of a phone screen. */
.manage-layout > .col-side {
order: 0;
}
/* Run form (new run, draft edit): the form leads on phones, the tips
   follow; two-up fields; one-tap route chips; percent affixes; the deposit
   field shows only once the toggle is on. */
.form-layout > .col-side {
order: 0;
}
.field-grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 14px;
}
@media (min-width: 620px) {
.field-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.route-picks {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.route-pick {
min-height: 32px;
}
.input-affix {
position: relative;
}
.input-affix input {
padding-right: 44px;
}
.input-affix > span {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
font-weight: 600;
color: var(--ink-40);
pointer-events: none;
}
.deposit-block {
margin-top: 16px;
padding-top: 14px;
border-top: 1px solid var(--line-soft);
}
.toggle-row {
display: flex;
align-items: flex-start;
gap: 12px;
cursor: pointer;
color: var(--ink);
font-size: 14px;
margin: 0;
}
.toggle-row input[type="checkbox"] {
flex: none;
margin-top: 2px;
}
.toggle-title {
display: block;
font-weight: 600;
}
.deposit-block .reveal {
margin-top: 12px;
padding-left: 32px;
}
.deposit-block:not(:has(input[type="checkbox"]:checked)) .reveal {
display: none;
}
/* Page chrome seams (Sep 2026).
   1. A page that ends with a full-bleed band runs straight into the footer:
      no strip of dead paper between two coloured surfaces.
   2. The tab bar is fixed over the page, so the footer keeps its own
      clearance; without it the last line sits under the bar on phones. */
main:has(> .band-flush:last-child) {
padding-bottom: 0;
}
main:has(> .band-flush:last-child) + .site-footer {
margin-top: 0;
}
.site-footer {
padding-bottom: calc(64px + env(safe-area-inset-bottom));
}
@media (min-width: 980px) {
.site-footer {
padding-bottom: 0;
}
}

/* The walkthrough (static/js/tour.js). A ring around the real control, a
   bubble beside it, and nothing else: no dimmed screenshot of the product,
   because the point is that the buyer is looking at the product. */
.tour-invite {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
margin: 14px auto 0;
max-width: 1200px;
padding: 12px 16px;
border: 1px solid var(--royal-100);
border-radius: var(--r-card);
background: var(--royal-100);
}
.tour-invite p {
margin: 0;
min-width: 0;
}
.tour-invite-actions {
gap: 8px;
flex: none;
flex-wrap: wrap;
}
.tour-launch {
flex: none;
}
.tour-launch .icon {
width: 15px;
height: 15px;
margin-right: 5px;
}
.tour-root {
position: fixed;
inset: 0;
z-index: 120;
}
.tour-scrim {
position: absolute;
inset: 0;
background: rgba(23, 16, 64, 0.55);
}
.tour-ring {
position: fixed;
border-radius: 10px;
border: 2px solid var(--royal-500);
box-shadow: 0 0 0 9999px rgba(23, 16, 64, 0.55);
pointer-events: none;
transition: all 180ms ease;
}
.tour-bubble {
position: fixed;
width: min(320px, calc(100vw - 16px));
padding: 16px;
border-radius: var(--r-card);
background: var(--surface);
color: var(--ink);
box-shadow: var(--shadow-pop);
}
.tour-count {
margin: 0;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--royal-700);
}
.tour-title {
margin: 4px 0 0;
font-size: 17px;
line-height: 1.25;
}
.tour-body {
margin: 6px 0 0;
font-size: 13.5px;
color: var(--ink-60);
}
.tour-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 14px;
}
.tour-nav {
display: flex;
gap: 8px;
}
@media (prefers-reduced-motion: reduce) {
.tour-ring {
transition: none;
}
}
/* Card grids where a cell carries its own action (saved items, my orders).
   The card grows to fill the cell, so every card in a row ends on the same
   line; the action sits in a slot that is reserved whether or not this cell
   has one, so the buttons line up too. */
.cell-stack {
display: flex;
flex-direction: column;
gap: 8px;
height: 100%;
min-width: 0;
}
.cell-stack > .card,
.cell-stack > .listing-card {
flex: 1;
}
.cell-stack > .cell-action {
display: flex;
flex-direction: column;
justify-content: flex-end;
min-height: 44px;
}
.cell-stack > .cell-action form {
margin: 0;
}
/* A run with no cover photo keeps the media block, so titles across a row
   start at the same height; the route strip stands in for the photograph. */
.run-cover-blank {
aspect-ratio: 21 / 9;
background: var(--royal-100);
}
/* The stand-in route sits where the photo card's route chip sits, so the
   badges keep the top corners to themselves in both variants. */
.cover-route-panel {
left: 12px;
right: 12px;
bottom: 12px;
max-width: none;
background: transparent;
padding: 0;
}
.cover-route-panel .route {
width: 100%;
background: rgba(255, 255, 255, 0.72);
}
/* The title row keeps the title on its own line, so a status chip beside it
   can never re-centre it and break the row's shared baseline. */
.run-card-title {
align-items: flex-start;
}

/* Policy pages: a rail between the three, and an unmissable marker for a
   fact only the owner can supply. The marker must never read as settled
   policy, so it carries the accent, not the calm ink. */
.legal-rail {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 14px;
}
.legal-rail a {
border: 1px solid var(--line);
border-radius: 999px;
padding: 7px 14px;
font-size: 13px;
font-weight: 600;
color: var(--ink-60);
text-decoration: none;
min-height: 34px;
display: inline-flex;
align-items: center;
}
.legal-rail a:hover {
border-color: var(--royal-500);
color: var(--royal-700);
}
.legal-rail a[aria-current="page"] {
background: var(--royal-100);
border-color: var(--royal-100);
color: var(--royal-700);
}
.legal-todo {
display: inline-block;
background: var(--accent-100);
color: var(--accent-700, var(--accent));
border: 1px dashed var(--accent);
border-radius: var(--r-control);
padding: 8px 12px;
font-size: 13px;
font-weight: 600;
}

/* Delivery: pickup points as searchable cards, and the optional map pin.
   Cards beat a select on a phone: the area and the directions are the
   things a buyer actually chooses between. */
.point-list {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 340px;
overflow-y: auto;
}
.point-card {
display: flex;
align-items: flex-start;
gap: 10px;
border: 1px solid var(--line);
border-radius: var(--r-control);
padding: 11px 13px;
cursor: pointer;
background: var(--surface);
}
.point-card:hover {
border-color: var(--royal-500);
}
.point-card.selected,
.point-card:focus-within {
border-color: var(--royal-500);
box-shadow: inset 0 0 0 1px var(--royal-500);
}
.point-body {
flex: 1;
min-width: 0;
}
.point-name {
display: block;
font-size: 14px;
font-weight: 700;
}
.point-area {
display: block;
font-size: 12px;
color: var(--ink-60);
margin-top: 1px;
}
.point-dir {
display: block;
font-size: 12px;
color: var(--ink-40);
margin-top: 3px;
}
.point-far {
flex: none;
font-size: 11px;
font-weight: 700;
color: var(--royal-700);
background: var(--royal-100);
border-radius: 999px;
padding: 3px 8px;
white-space: nowrap;
}
.point-card .tick {
flex: none;
}
.pin-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.pin-row [data-pin-note] {
flex-basis: 100%;
margin: 0;
}
.btn-linkish {
background: none;
border: none;
padding: 6px 2px;
cursor: pointer;
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--ink-60);
text-decoration: underline;
}
.btn-linkish:hover,
.btn-linkish:focus-visible {
color: var(--royal-700);
}
