/* What Tailwind's utilities cannot express, and nothing else.
 *
 * Every colour, face and radius on every page comes from a theme setting, rendered into CSS
 * custom properties by layout/theme.liquid and consumed by the platform stylesheet's utilities —
 * `bg-background` compiles to `background-color: var(--color-background)`. So there is no literal
 * colour in this file, on purpose: a hardcoded value would be the one thing a merchant could not
 * change, and finding it later means reading the stylesheet. A test fails the build if one
 * appears in any notation CSS has.
 *
 * What is left is the page measure, which is a setting rather than a scale step, and the two
 * tables, whose borders and alignment are not worth a utility on every cell.
 */

html { font-size: var(--font-size); }

/* Merchant-authored HTML carries no classes we control, so the element defaults have to be
 * right. Tailwind's preflight resets links to `color: inherit` and strips their underline,
 * which leaves every link in a description indistinguishable from the text around it. */
.rich-text a { color: var(--color-accent); text-decoration: underline; }
.rich-text h1, .rich-text h2, .rich-text h3, .rich-text h4 { font-family: var(--font-heading); }

.layout { max-width: var(--page-width); margin-inline: auto; }

/* Two things a merchant chooses that the platform's utility vocabulary cannot express.
 *
 * Columns can be classes, because `grid-cols-{1..6}` is safelisted at every breakpoint and a
 * setting bounded to that range only ever composes a class that was compiled. A ratio cannot:
 * the safelist ships `aspect-square` and nothing else, and a ratio is a number rather than a
 * scale step. So it arrives as a custom property on the element, which is the escape hatch this
 * theme's own CSS exists to be. */

.card-media { display: block; width: 100%; }
.card-media--fixed { aspect-ratio: var(--card-ratio, 1); object-fit: var(--card-fit, cover); }

/* Position: sticky has no utility here either, and a header is the only element that wants it. */
.header--sticky { position: sticky; top: 0; z-index: 50; }

/* The hero banner's two merchant-chosen numbers.
 *
 * Everything else the banner needs is a utility: `absolute inset-0 w-full h-full object-cover`
 * for the image, `bg-background` for the scrim. These two are not, and for the same reason the
 * card ratio above is not - the safelist ships a spacing scale, and a height in pixels or an
 * opacity a merchant moved a slider to is a number rather than a step on it. So they arrive as
 * custom properties on the element, which is what this file exists to be. */
.hero--banner { min-height: var(--hero-height, 400px); }
.hero--banner__scrim { opacity: var(--hero-scrim, 0.4); }

/* Sold out, said once and loudly, instead of a quiet line under the price.
 *
 * Its own setting rather than a colour-scheme role. A scheme is a palette a section can be
 * switched between; this is a warning, and it has to read as one whichever scheme a section
 * happens to be wearing - as a role it would be set once per scheme and got wrong in one of
 * them. It is still a setting, so a merchant whose brand already uses the warning colour can
 * move it somewhere else.
 *
 * Not Tailwind utilities: the platform stylesheet is built from an explicit safelist, and a
 * utility nobody listed there compiles to nothing at all, so the band would arrive unstyled. */
/* Over the foot of the picture rather than under it, so a sold out card is exactly as tall above
 * its title as one that is not and a row of them lines up. */
.card__sold-out--over { position: absolute; inset-inline: 0; bottom: 0; }

.card__media-box { display: block; }

.card__sold-out {
  display: block;
  margin: 0;
  padding: .3rem .75rem;
  background: var(--color-sold-out);
  color: var(--color-sold-out-label);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  line-height: 1.4;
  text-align: center;
  text-transform: uppercase;
}

/* `hidden` has to beat a class that sets display.
 *
 * The attribute is a UA rule of the lowest specificity, so every element below that this file
 * gives a display to — the sold-out band, a fact row — would stay on screen while the markup
 * says it is off. The product page toggles exactly that attribute as a buyer changes variant. */
[hidden] { display: none !important; }

/* The product gallery: one picture, and thumbnails that change which one.
 *
 * `:target` rather than a script. The thumbnails are fragment links, so clicking one names a
 * frame in the URL and CSS shows that frame — which is the whole mechanism, and it works with
 * JavaScript switched off. The first frame is shown until a target exists, and `:has()` is what
 * turns it off again; without that rule a browser too old for `:has()` shows the first picture
 * and the chosen one together, which is a worse gallery rather than a broken page.
 *
 * The ratio is the theme's rather than the photograph's because the catalogue carries a URL and
 * nothing else — no width, no height — so there is nothing to reserve the box with but a ratio
 * this file chooses. Without it the column collapses and snaps open as photos land. */
.gallery { min-width: 0; }

.gallery__stage {
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  overflow: hidden;
}

.gallery__frame {
  display: none;
  width: 100%;
  aspect-ratio: 1;
  object-fit: var(--gallery-fit, contain);
}

.gallery__frame--first { display: block; }
.gallery__frame:target { display: block; }
.gallery__stage:has(.gallery__frame:target) .gallery__frame--first { display: none; }

.gallery__empty { aspect-ratio: 1; width: 100%; }

.gallery__thumbs { display: flex; gap: .5rem; overflow-x: auto; margin-top: .75rem; }

.gallery__thumb {
  flex: 0 0 auto;
  width: 5rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  overflow: hidden;
}

/* Which one you picked. A thumbnail that is a link already takes focus when clicked, so this is
 * the selected state and the keyboard ring at once rather than a second thing to maintain. */
.gallery__thumb:focus-within { outline: 2px solid var(--color-accent); outline-offset: 1px; }

.gallery__thumb img { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; }

/* The variant dropdown.
 *
 * Preflight strips a select back to nothing, so every one of these declarations is restoring
 * something a browser would have drawn — this is not decoration on top of a control, it is the
 * control. Full width because it is the widest thing in the column and a variant code is long. */
.pdp__select {
  width: 100%;
  padding: .55rem .75rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  background: var(--color-background);
  color: var(--color-text);
  font: inherit;
}

.pdp__facts { display: grid; gap: .5rem; font-size: .875rem; }
.pdp__fact { display: flex; gap: .75rem; }
.pdp__fact dt { min-width: 5rem; }

/* The same news as the card's band, in the one place a buyer looks before trying to order.
 * Shares the card's two settings rather than carrying its own: a merchant who moved the warning
 * colour moved it everywhere, which is the whole point of its being a setting. */
.pdp__sold-out {
  display: inline-block;
  margin: 0;
  padding: .3rem .75rem;
  border-radius: var(--radius-theme);
  background: var(--color-sold-out);
  color: var(--color-sold-out-label);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  line-height: 1.4;
  text-transform: uppercase;
}

.price--was { text-decoration: line-through; }

/* A custom field shown as a label on the product page. */
.product-label {
  display: inline-block;
  padding: .125rem .5rem;
  border-radius: 9999px;
  background: var(--color-accent);
  color: var(--color-background);
  font-size: .75rem;
  font-weight: 600;
}

/* Quantity.
 *
 * The number input is the control; the two buttons are for a pointer. Preflight strips a button
 * and a number input back to nothing, so everything here is restoring what a browser would
 * otherwise have drawn, not decorating it. The three sit in one bordered box so they read as one
 * control rather than as a field that happens to have buttons near it. */
.pdp__qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  overflow: hidden;
}

.pdp__qty-step {
  padding: 0 .9rem;
  border: 0;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  font: inherit;
  line-height: 1;
}

.pdp__qty-step:hover { background: var(--color-line); }

.pdp__qty-input {
  width: 4rem;
  padding: .55rem .25rem;
  border: 0;
  border-inline: 1px solid var(--color-line);
  background: var(--color-background);
  color: var(--color-text);
  font: inherit;
  text-align: center;
}

/* The spinner the browser adds duplicates the two buttons beside it, in a box too narrow for
 * both. Firefox is told the same thing a different way. */
.pdp__qty-input::-webkit-inner-spin-button,
.pdp__qty-input::-webkit-outer-spin-button { appearance: none; margin: 0; }
.pdp__qty-input { -moz-appearance: textfield; appearance: textfield; }

/* Add to cart. Its colours are utilities, so they follow the scheme the section is wearing; what
 * is left is the size, and what a disabled one looks like - which has no utility here and is the
 * state a buyer meets on every sold-out variant. */
.pdp__add {
  padding: .8rem 1.5rem;
  border: 0;
  font-size: 1rem;
}

.pdp__add:disabled { cursor: not-allowed; opacity: .45; }

/* The variant grid.
 *
 * None of this is a utility and none of it could be. The platform stylesheet is built from an
 * explicit safelist and ships no table vocabulary at all, no `sticky` outside the header, and no
 * `border-spacing` — so a grid written in utilities would have arrived as an unstyled table, which
 * is the trap the safelist's own note warns about. A table is also the right element rather than a
 * fashion: the headings are what name a cell, and that is how a buyer on a screen reader is told
 * which colour and which size the box they are typing into orders.
 *
 * `border-collapse: separate` rather than `collapse`, because a collapsed table draws its borders
 * on the table and sticky cells scroll out from under them — the frozen first column would slide
 * across the grid with no edge of its own. Separate puts every border on a cell, where it travels
 * with it.
 */
.variantgrid { display: flex; flex-direction: column; gap: .5rem; min-width: 0; }

.variantgrid__scroll {
  overflow-x: auto;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
}

.variantgrid__table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: .8125rem;
}

.variantgrid__head,
.variantgrid__corner,
.variantgrid__row-head,
.variantgrid__cell,
.variantgrid__sum {
  border-bottom: 1px solid var(--color-line);
  border-right: 1px solid var(--color-line);
  padding: .55rem .65rem;
  text-align: center;
  vertical-align: top;
}

.variantgrid__head, .variantgrid__corner {
  background: color-mix(in srgb, var(--color-line) 45%, transparent);
  font-weight: 600;
}

.variantgrid__head--start, .variantgrid__cell--start { text-align: start; }

/* The first column is frozen, which is the whole reason this scrolls horizontally rather than
 * wrapping: a buyer eleven sizes along a row has to still be able to see which colour they are
 * ordering. It needs a background of its own or the cells it passes over show through it, and it
 * needs to outrank them or it is painted under them. */
.variantgrid__corner, .variantgrid__row-head {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--color-background);
  font-weight: 600;
  text-align: start;
}

.variantgrid__corner { background: color-mix(in srgb, var(--color-line) 45%, transparent); z-index: 2; }

/* The row head and the column head both pair a label with a thumbnail. Square and small: a swatch
 * is an identifier, not a picture of the thing.
 *
 * The flex goes on a wrapper inside the cell, never on the cell. `display: flex` on a `<th>`
 * replaces its table-cell box, so it stops filling its column and shrink-wraps its own text —
 * which drew every variant name in a little bordered box with the column empty around it. */
.variantgrid__label { display: flex; align-items: center; gap: .5rem; }
.variantgrid__head { vertical-align: bottom; }
.variantgrid__head .variantgrid__swatch { display: block; margin: 0 auto .25rem; }

/* The code a buyer puts on a purchase order, under the name it belongs to rather than in a column
 * that is empty for every product whose variants carry none. */
.variantgrid__sku { display: block; color: var(--color-muted); font-size: .6875rem; font-weight: 400; }

.variantgrid__swatch {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  object-fit: cover;
  border-radius: var(--radius-theme);
}

/* A cell is three things stacked: how deep the shelf is, what it costs, and how many. In that
 * order because that is the order the decision is made in. */
.variantgrid__cell { min-width: 6rem; }

.variantgrid__stock {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  font-size: .75rem;
  line-height: 1.5;
}

.variantgrid__dot {
  width: .5rem;
  height: .5rem;
  border-radius: 9999px;
  background: var(--color-stock-ok);
}

[data-band="low"] .variantgrid__dot { background: var(--color-stock-low); }
[data-band="none"] .variantgrid__dot { background: var(--color-sold-out); }

.variantgrid__count { color: var(--color-muted); }

.variantgrid__price { display: block; font-size: .75rem; line-height: 1.5; }
.variantgrid__rrp { margin-left: .25rem; color: var(--color-muted); font-size: .6875rem; white-space: nowrap; }

/* A combination nobody makes. Not a disabled box: a box says there is something here that cannot
 * be had today, and a gap says it was never cut in this size. */
.variantgrid__absent { color: var(--color-muted); }

/* The box. Preflight strips a number input back to nothing, so everything here is restoring what
 * a browser would have drawn rather than decorating it, and the spinner is removed because there
 * is no room for it in a cell this narrow. */
.variantgrid__qty {
  width: 100%;
  min-width: 3.5rem;
  margin-top: .35rem;
  padding: .4rem .3rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  background: var(--color-background);
  color: var(--color-text);
  font: inherit;
  font-size: .8125rem;
  text-align: center;
}

.variantgrid__qty:disabled { cursor: not-allowed; opacity: .4; }

.variantgrid__qty::-webkit-inner-spin-button,
.variantgrid__qty::-webkit-outer-spin-button { appearance: none; margin: 0; }
.variantgrid__qty { -moz-appearance: textfield; appearance: textfield; }

/* Totals. The grand total is the one a buyer checks, so it is the one that is heavier. */
.variantgrid__sum { background: color-mix(in srgb, var(--color-line) 45%, transparent); font-weight: 600; }
.variantgrid__sum--grand { font-size: .875rem; }

.variantgrid__subtotal { padding-top: .25rem; }

/* The bar that holds the submit button to the bottom of a line sheet, where the alternative is
 * scrolling back past twenty grids to find it. Its own background and a border, because stuck
 * over a scrolling page it would otherwise have the products showing through it. */
.ordersum {
  display: flex;
  justify-content: flex-end;
  padding: .75rem 1rem;
}

.ordersum--sticky { position: sticky; bottom: .5rem; z-index: 20; }

.sheet { min-width: 0; }

/* A line-sheet row. The photograph is an identifier beside the grid rather than the subject of a
 * card, so it is fixed and small - the grid is what the row is for. */
.sheet__media { flex: 0 0 auto; display: block; width: 7rem; }

.sheet__media img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
}

/* The band that carries a third option across the grid.
 *
 * The B2B portal calls it a group separator, and it does the same job here: the rows repeat under
 * each value of the third option, and the band is what says which. It spans every column, so it
 * cannot be sticky with the first column beside it - and it does not need to be, because it is
 * the one row a buyer never has to scroll sideways to read. */
.variantgrid__band { background: color-mix(in srgb, var(--color-line) 70%, transparent); }

.variantgrid__band-cell {
  position: sticky;
  left: 0;
  padding: .45rem .65rem;
  border-bottom: 1px solid var(--color-line);
  background: color-mix(in srgb, var(--color-line) 70%, transparent);
  font-weight: 600;
  text-align: start;
}

/* Compact.
 *
 * The one dimension of a grid a merchant has a real opinion about, because it decides whether a
 * ten-size run fits the page or scrolls. Padding and type size only: the colours stay the colour
 * scheme's, so density cannot be used to smuggle in a palette the rest of the theme does not know
 * about. A merchant who wants more than this has assets/theme.css, which is what it is for. */
.variantgrid--compact .variantgrid__table { font-size: .75rem; }

.variantgrid--compact .variantgrid__head,
.variantgrid--compact .variantgrid__corner,
.variantgrid--compact .variantgrid__row-head,
.variantgrid--compact .variantgrid__cell,
.variantgrid--compact .variantgrid__sum { padding: .2rem .3rem; }

.variantgrid--compact .variantgrid__cell { min-width: 4.25rem; }
.variantgrid--compact .variantgrid__qty { padding: .15rem .2rem; margin-top: .1rem; }
.variantgrid--compact .variantgrid__swatch { width: 1.5rem; height: 1.5rem; }

/* The list, which is a different table from the matrix and should not look like one.
 *
 * A matrix is a grid of equal cells and reads as one; a list is rows, and a border round every
 * cell of it is a cage. So the borders come off, the rows get a rule under them and a stripe, and
 * the whole thing sits in one rounded box the way a panel does.
 *
 * The stripe is mixed from the scheme's own line colour rather than written down. A literal would
 * be the one colour on the page a merchant could not change — the thing this file exists not to
 * do — and there is no spacing-scale equivalent for "a bit of the border colour". */
.variantgrid__table--list .variantgrid__head,
.variantgrid__table--list .variantgrid__cell,
.variantgrid__table--list .variantgrid__row-head,
.variantgrid__table--list .variantgrid__sum {
  border: 0;
  border-bottom: 1px solid var(--color-line);
  padding: .5rem .6rem;
  text-align: start;
  vertical-align: middle;
}

.variantgrid__table--list .variantgrid__row-head { position: static; background: none; }

.variantgrid__table--list tbody tr:nth-child(even) .variantgrid__cell,
.variantgrid__table--list tbody tr:nth-child(even) .variantgrid__row-head {
  background: color-mix(in srgb, var(--color-line) 35%, transparent);
}

.variantgrid__table--list tbody tr:last-child .variantgrid__cell,
.variantgrid__table--list tbody tr:last-child .variantgrid__row-head { border-bottom: 0; }

.variantgrid__table--list .variantgrid__sum--grand { background: var(--color-line); text-align: center; }

/* The thumbnail column carries no heading, so it claims no width of its own; the label column
 * takes every spare pixel, which is what holds the names against the left edge instead of letting
 * a browser share the slack out between all five. */
.variantgrid__thumb { width: 1%; padding-right: 0; }
.variantgrid__stretch { width: 100%; }

.variantgrid__table--list .variantgrid__stock { justify-content: flex-start; font-size: .75rem; }

/* Minus, box, plus, as one control rather than a field with buttons near it. */
.variantgrid__stepper {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  overflow: hidden;
}

.variantgrid__step {
  padding: 0 .55rem;
  border: 0;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  font: inherit;
  line-height: 1;
}

.variantgrid__step:hover:not(:disabled) { background: var(--color-line); }
.variantgrid__step:disabled { cursor: not-allowed; opacity: .4; }

.variantgrid__stepper .variantgrid__qty {
  width: 3.25rem;
  margin: 0;
  border: 0;
  border-inline: 1px solid var(--color-line);
  border-radius: 0;
}

/* What the dots mean. */
.variantgrid__legend { display: flex; flex-wrap: wrap; gap: .75rem; }
.variantgrid__legend .variantgrid__stock { gap: .3rem; }

/* The bag and the account, at the end of the header bar. */
.header__actions { display: flex; align-items: center; gap: .25rem; }

/* The account menu, opened on hover or focus like the main menu. */
.header__account { position: relative; }

.header__menu {
  position: absolute;
  z-index: 20;
  top: 100%;
  inset-inline-end: 0;
  min-width: 12rem;
  padding: .5rem;
  visibility: hidden;
  opacity: 0;
  transition: opacity 150ms;
}

.header__account:hover .header__menu,
.header__account:focus-within .header__menu { visibility: visible; opacity: 1; }

/* Truncated rather than wrapped, so a long trading name keeps the menu its width. */
.header__menu-name {
  max-width: 16rem;
  margin: 0;
  padding: .25rem .75rem .5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.header__menu-item {
  display: block;
  width: 100%;
  padding: .5rem .75rem;
  border: 0;
  background: none;
  font: inherit;
  text-align: start;
  white-space: nowrap;
  cursor: pointer;
}

.header__menu-item:hover { color: var(--color-accent); }

/* Which customer a sale agent is ordering for. Sits beside the account icon, and wraps to its
   own line on a narrow screen rather than squeezing the bag off the end. */
.header__switcher { display: inline-flex; align-items: center; gap: .375rem; margin: 0; }

.header__switcher-label {
  font-size: .8125rem;
  color: var(--color-muted);
  white-space: nowrap;
}

.header__switcher-select {
  max-width: 12rem;
  padding: .25rem .5rem;
  border: 1px solid var(--color-border, currentColor);
  background: var(--color-background);
  color: inherit;
  font: inherit;
  font-size: .8125rem;
}

.header__switcher-go {
  padding: .25rem .625rem;
  border: 0;
  cursor: pointer;
  font: inherit;
  font-size: .8125rem;
}

@media (max-width: 640px) {
  .header__switcher-label { display: none; }
  .header__switcher-select { max-width: 8rem; }
}

/* A form, because signing out is a write. */
.header__signout { margin: 0; }

.header__icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-theme);
}

.header__icon:hover { background: color-mix(in srgb, var(--color-line) 50%, transparent); }
.header__glyph { width: 1.6rem; height: 1.6rem; }

/* Only drawn when there is something to count, so this never shows a nought. */
.header__badge {
  position: absolute;
  top: .25rem;
  inset-inline-end: .2rem;
  min-width: 1.15rem;
  padding: 0 .2rem;
  border-radius: 9999px;
  background: var(--color-accent);
  color: var(--color-button-label);
  font-size: .6875rem;
  font-weight: 600;
  line-height: 1.15rem;
  text-align: center;
}

/* The cart drawer.
 *
 * Shown by `:target` and nothing else — the bag is a link to its fragment and the close is a link
 * away from it — so it opens with no script and cannot fall out of step with the page. Hidden
 * with visibility rather than display so the panel can slide rather than appear, and so what is
 * behind it stays unreachable to a keyboard while it is shut. */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 50;
  visibility: hidden;
  pointer-events: none;
}

.drawer:target { visibility: visible; pointer-events: auto; }

.drawer__scrim {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--color-text);
  opacity: 0;
  transition: opacity .2s ease;
}

.drawer:target .drawer__scrim { opacity: .35; }

.drawer__panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  display: flex;
  flex-direction: column;
  width: min(32rem, 100%);
  transform: translateX(100%);
  transition: transform .2s ease;
}

.drawer:target .drawer__panel { transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .drawer__scrim, .drawer__panel { transition: none; }
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .9rem 1rem;
}

.drawer__close { font-size: 1.5rem; line-height: 1; }

.drawer__body { flex: 1; overflow-y: auto; padding: 0; }

/* The same line as the cart page, in a narrower column: the picture comes down a size and the
 * padding comes in, and nothing else changes - which is the point of sharing it. */
.drawer .cart-line { padding: .85rem 1rem; gap: .75rem; }
.drawer .cart-line__media { width: 3.5rem; height: 3.5rem; }
.drawer .cart-line__body p:first-child { font-size: .875rem; }
.drawer .cart-line:last-child { border-bottom: 0; }

/* The quantity control on a cart line: the stepper, with the way out underneath it. */
.cart-qty { display: flex; flex-direction: column; align-items: flex-start; gap: .25rem; }
.cart-qty .variantgrid__qty { margin-top: 0; }

.cart-remove {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-muted);
  cursor: pointer;
  font: inherit;
  font-size: .6875rem;
  text-decoration: underline;
}

.cart-remove:hover { color: var(--color-text); }

/* The total sits under the lines and above the button, which is the order a buyer reads them in:
 * what is on the order, what it comes to, then the thing that commits to it. */
.drawer__foot { display: flex; flex-direction: column; gap: .75rem; padding: 1rem; }
.drawer__actions { display: flex; gap: .5rem; }
.drawer__actions > * { flex: 1; text-align: center; }

.drawer__update {
  padding: .55rem 1rem;
  background: transparent;
  color: var(--color-text);
  font: inherit;
}

/* Merchant-authored blocks on the cart page. The plain one is a paragraph with no chrome, so its
 * only job is to be a hook a customer's own CSS can hold on to. */
.cart-note { display: block; }
.cart-note--panel { padding: .9rem 1rem; }

.cart-banner { display: flex; gap: 1rem; align-items: center; padding: .9rem 1rem; }

.cart-banner__media {
  flex: 0 0 auto;
  width: 5rem;
  height: 5rem;
  object-fit: cover;
  border-radius: var(--radius-theme);
}

/* The cart page: cards for the lines, and a summary that stays beside them on a wide screen. */
.cart-layout { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: start; }

@media (min-width: 60rem) {
  .cart-layout { grid-template-columns: minmax(0, 1fr) 22rem; }
  .cart-summary { position: sticky; top: 1rem; }
}

/* Above both columns, so the first line card and the summary beside it start on the same line. */
.cart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.cart-head__actions { display: flex; align-items: baseline; gap: 1rem; }

/* Submits the ticked lines to the same field a single remove uses, so selecting three and
 * pressing this posts three removals. It looks like a link because it is destructive and should
 * not read as the thing to press. */
.cart-delete {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-sold-out);
  cursor: pointer;
  font: inherit;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* A card per line. A cart line carries a warning and a control as well as a name, and rules
 * between them read as a table of things that are not columns. */
.cart-line {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
}

.cart-line__pick { margin-top: .35rem; width: 1rem; height: 1rem; flex: 0 0 auto; accent-color: var(--color-accent); }

.cart-line__media {
  flex: 0 0 auto;
  display: block;
  width: 5rem;
  height: 5rem;
  object-fit: contain;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
}

.cart-line__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .3rem; }
.cart-line__fact + .cart-line__fact { margin-inline-start: .6rem; }

/* The shelf is shallower than the order. Said on a tint of the sold-out colour rather than in it,
 * so the line stays readable and the warning is still unmistakably the warning colour whichever
 * scheme the section is wearing. */
.cart-line__warn {
  align-self: flex-start;
  padding: .3rem .55rem;
  border-radius: var(--radius-theme);
  background: color-mix(in srgb, var(--color-sold-out) 12%, transparent);
  color: var(--color-sold-out);
  font-size: .75rem;
  font-weight: 600;
}

.cart-line__money {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .4rem;
  text-align: end;
}

.cart-line__each { display: block; font-size: .75rem; font-weight: 400; }
.cart-line__money .cart-qty { align-items: flex-end; }

.cart-qty { display: flex; align-items: center; gap: .5rem; }
.cart-qty .variantgrid__qty { margin-top: 0; }

.cart-remove {
  display: inline-flex;
  align-items: center;
  padding: .35rem;
  border: 0;
  border-radius: var(--radius-theme);
  background: transparent;
  color: var(--color-muted);
  cursor: pointer;
}

.cart-remove:hover { color: var(--color-sold-out); background: color-mix(in srgb, var(--color-line) 50%, transparent); }
.cart-remove__glyph { width: 1.1rem; height: 1.1rem; }

.cart-summary { display: flex; flex-direction: column; gap: .85rem; padding: 1.25rem; }
.cart-summary__rows { display: flex; flex-direction: column; gap: .4rem; font-size: .875rem; }
.cart-summary__row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }

.cart-summary__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-top: .85rem;
}

.cart-checkout { display: block; width: 100%; padding: .8rem 1rem; border: 0; font-family: inherit; font-size: 1rem; text-align: center; }

.cart-continue {
  display: block;
  width: 100%;
  padding: .7rem 1rem;
  background: transparent;
  font-size: .9375rem;
  text-align: center;
}

/* The sign-in page.
 *
 * Written here rather than in utilities because none of it can be: the safelist carries no
 * `max-w` in the sizes a form wants, no `grid-template-columns` with a fixed track, and no
 * `min-height` tied to anything but the viewport — and a login page that filled the viewport
 * would push the footer off every screen it rendered on. What is left in the markup is colour
 * and spacing, which are utilities, so the scheme a merchant picks still drives the page. */
/* The account pages stand alone: no site header, no footer, nothing to click but the form.
 * A buyer who cannot sign in cannot use a nav, and a cart bag above a sign-in form is an
 * invitation to a page that will send them straight back here.
 *
 * `dvh` rather than `vh` because mobile browsers count the address bar in `vh`, so a form centred
 * in 100vh sits visibly low on a phone and scrolls for no reason. */
.login--card,
.login--split { min-height: 100dvh; }

.login__scrim { opacity: var(--login-scrim, 0.4); }

/* Centred: one column in the middle of the page, with room around it at every width. */
.login--card { display: flex; align-items: center; }
.login--card .login__frame { display: flex; justify-content: center; width: 100%; padding: 3rem 1.5rem; }

/* Split: two halves of the window, and the panel is a half rather than a card sitting in one.
 * It runs edge to edge and top to bottom — no page gutter, no corner radius, nothing between it
 * and the viewport — which is why the frame is not inside the theme's max-width container and
 * the section stretches its children instead of centring them. */
.login--split { display: flex; align-items: stretch; }
.login--split .login__frame { display: grid; grid-template-columns: 1fr; width: 100%; }
.login--split .login__panel { order: 2; min-height: 16rem; }
.login--split .login__column { order: 1; padding: 3rem 1.5rem; }

@media (min-width: 60rem) {
  .login--split .login__frame { grid-template-columns: 1fr 1fr; }
  .login--split .login__panel { order: 1; min-height: 100%; }
  .login--split .login__column { order: 2; justify-content: center; }
}

/* The column holds the logo and the card. They are separate things stacked: the logo belongs to
 * the shop and the card belongs to the form, and a logo inside the card reads as part of the
 * form's heading rather than as the brand above it. */
.login__column {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  width: 100%;
  max-width: var(--login-width, 400px);
  margin-inline: auto;
}

/* The panel's words sit in the middle of it, on a scrim of its own scheme rather than straight on
 * the photograph — a heading over a picture is legible or not depending on the picture. */
.login__panel { display: flex; flex-direction: column; justify-content: center; padding: 2rem; }
/* A block in the middle of the panel rather than a band across it: the scrim is there to make
 * the words legible over a photograph, and stretching it the full width makes it read as a
 * caption bar cutting the picture in half. */
.login__panel-words {
  background: color-mix(in srgb, var(--color-background) 82%, transparent);
  border-radius: var(--radius-theme);
  max-width: 28rem;
  margin-inline: auto;
}

.login__body { display: flex; flex-direction: column; gap: 1rem; width: 100%; }

/* The form always sits on a card. The border is what makes that true on a page whose scheme is
 * the same colour as the card: without it, "on a card" means an invisible rectangle and the page
 * reads as a form floating in space. Behind a background image the border disappears into the
 * edge of the card and costs nothing. */
.login__card {
  padding: 2rem;
  border: 1px solid var(--color-line);
}

/* A card that reaches the edges on a narrow screen is a card with nothing around it. Below that,
 * the padding is what a phone has room for. */
@media (max-width: 26rem) {
  .login__card { padding: 1.5rem 1.25rem; }
}

/* Centred over the card rather than aligned to its left edge. It is the shop's mark, not the
 * first line of the form, and sitting above a card it needs something to be centred on. */
.login__icon { display: block; height: auto; margin-inline: auto; }

/* The line pointing a visitor with no account at somewhere to apply. It sits closer to the
 * forgotten-password link above it than the gap between form rows, because the two are the same
 * kind of thing: what to do when you cannot get in. */
.login__signup { margin-top: -.25rem; }

.login__form { display: flex; flex-direction: column; gap: 1rem; }
.login__field { display: flex; flex-direction: column; gap: .35rem; }
.login__label { font-size: .875rem; color: var(--color-muted); }

/* Preflight strips an input back to nothing, so this is restoring what a browser would otherwise
 * have drawn. The focus ring is not decoration: without it a keyboard buyer cannot tell which of
 * two identical boxes they are typing into. */
.login__input {
  width: 100%;
  padding: .65rem .75rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  background: var(--color-background);
  color: var(--color-text);
  font: inherit;
}

.login__input:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

.login__submit { padding: .8rem 1rem; border: 0; font-size: 1rem; cursor: pointer; }

/* The refusal. It takes the sold-out colour rather than one of its own: it is the theme's single
 * "this went wrong" colour, a merchant has already chosen it, and two reds that nearly match is
 * worse than one that does. */
.login__error {
  margin: 0;
  padding: .75rem 1rem;
  border-radius: var(--radius-theme);
  background: color-mix(in srgb, var(--color-sold-out) 12%, transparent);
  color: var(--color-sold-out);
  font-size: .9375rem;
}

/* The application form is the one account page with enough questions to want two columns. Not a
 * utility grid: the safelist has no `grid-template-columns` with a fixed track, and the fields
 * that must span both (a trading name, an address line, the submit) need a class either way. */
.signup .login__form { display: grid; grid-template-columns: 1fr; gap: 1rem; }
.signup__field--wide { grid-column: 1 / -1; }

@media (min-width: 40rem) {
  .signup .login__form { grid-template-columns: 1fr 1fr; }
}

/* A long form has no business being vertically centred: on a short window the top of it would be
 * above the fold with no way to scroll up to it. */
/* A long form has no business being vertically centred: on a short window the top of it would be
 * above the fold with no way to scroll up to it.
 *
 * Scoped to the centred layout. Written as `.signup` it also beat `.login--split`'s `stretch` —
 * same specificity, later in the file — and collapsed the split panel to the height of its
 * contents. With no panel heading set that is nothing at all, so the panel vanished and took the
 * background image with it: both halves of one bug, from one word of alignment. */
.login--card.signup { align-items: start; }

/* Room above and below the long form — on the centred layout only. On the split it was padding
 * on the grid itself, which pushed the panel down the page and left a band of the page colour
 * above a half that is supposed to reach the top. The column that needs the room already has
 * its own padding. */
.login--card.signup .login__frame { padding-block: 3rem; }

/* Checkout: the form on one half of the window, the order on the other.
 *
 * The portal's col-md-6 / col-md-6, which is why it is two equal halves rather than a form with a
 * sidebar. The summary paints its half edge to edge and top to bottom - so the grid is not inside
 * the theme's centred container, and each half constrains its own content instead.
 *
 * The summary is written first in the markup and moved right on a wide screen: source order is
 * what a screen reader follows and what decides which half is on top when they stack, and "what
 * am I buying" belongs before "where does it go". */
.checkout__layout { display: grid; grid-template-columns: 1fr; align-items: stretch; }

@media (min-width: 60rem) {
  .checkout__layout { grid-template-columns: 1fr 1fr; }
  .checkout__summary { order: 2; }
  .checkout__form-side { order: 1; }
}

.checkout__form-side { padding: 2.5rem 1.5rem; min-width: 0; }
.checkout__summary { padding: 2.5rem 1.5rem; min-width: 0; }

/* Each half holds its content to a readable width rather than letting it run the whole way out,
 * and pulls it toward the middle of the page where the two meet. */
.checkout__inner { display: flex; flex-direction: column; gap: 1rem; width: 100%; max-width: 32rem; }

@media (min-width: 60rem) {
  .checkout__form-side .checkout__inner { margin-inline-start: auto; padding-inline-end: 2rem; }
  .checkout__summary .checkout__inner { margin-inline-end: auto; padding-inline-start: 2rem; }
}

/* Two across for the short fields, full width for the rest. Not a utility grid: the safelist
 * carries no `grid-template-columns` with a fixed track, and the fields that span both need a
 * class either way. */
.checkout__form { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.checkout__field--wide { grid-column: 1 / -1; }

/* A saved address or a new one; the boxes show only for a new one. */
.checkout__choices { grid-column: 1 / -1; display: flex; flex-direction: column; gap: .5rem; margin: 0; padding: 0; border: 0; }
.checkout__choice { display: flex; align-items: flex-start; gap: .75rem; padding: .75rem 1rem; cursor: pointer; }
.checkout__choice:has(input:checked) { border-color: var(--color-accent); }
.checkout__choice input { margin-top: .2rem; }
.checkout__new-address { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.checkout__form:has(.checkout__choice input:checked:not([value="new"])) .checkout__new-address { display: none; }

@media (max-width: 30rem) {
  .checkout__form { grid-template-columns: 1fr; }
}

/* The order requirements, said before the buyer presses anything rather than after. It takes the
 * theme's warning colour rather than one of its own. */
.checkout__notice {
  padding: .75rem 1rem;
  border-radius: var(--radius-theme);
  background: color-mix(in srgb, var(--color-sold-out) 10%, transparent);
  font-size: .875rem;
}

.checkout__lines { display: flex; flex-direction: column; gap: .75rem; }

/* The cards on the thank-you shown straight after placing. */
.order__confirm { display: flex; flex-direction: column; gap: .35rem; padding: 1rem 1.25rem; }

.checkout__line {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: .75rem;
  align-items: center;
}

/* The count rides the corner of the thumbnail, as it does in the portal: it belongs to the
 * picture rather than to the row, and a column of its own would push the names in. */
.checkout__thumb { position: relative; flex-shrink: 0; }
.checkout__thumb img {
  display: block;
  width: 3.5rem;
  height: 3.5rem;
  object-fit: cover;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
}

.checkout__count {
  position: absolute;
  top: -.4rem;
  inset-inline-end: -.4rem;
  min-width: 1.4rem;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--color-text);
  color: var(--color-background);
  font-size: .75rem;
  line-height: 1.4rem;
  text-align: center;
}

.checkout__named { display: flex; flex-direction: column; min-width: 0; }
.checkout__money { white-space: nowrap; font-weight: 600; }

.checkout__totals { display: flex; flex-direction: column; gap: .4rem; margin: 0; padding-top: .85rem; border-top: 1px solid var(--color-line); }
.checkout__row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; font-size: .875rem; }
.checkout__row dd { margin: 0; font-weight: 600; }
.checkout__row--total { padding-top: .5rem; border-top: 1px solid var(--color-line); font-size: 1.125rem; }
.checkout__note { font-size: .8125rem; }

/* Beside the submit, and plainly not it. The portal pairs them across the foot of the form. */
.checkout__back {
  display: block;
  padding: .8rem 1rem;
  border: 1px solid var(--color-line);
  color: var(--color-text);
  font-size: 1rem;
  text-align: center;
  text-decoration: none;
}

/* The account area: a buyer's own orders. */
.account__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }

.account__filters { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.5rem; }

.account__filter {
  padding: .4rem .9rem;
  border: 1px solid var(--color-line);
  border-radius: 999px;
  color: var(--color-text);
  font-size: .875rem;
  text-decoration: none;
}

.account__filter--on { background: var(--color-text); color: var(--color-background); border-color: var(--color-text); }

.account__empty { display: flex; flex-direction: column; align-items: center; gap: .5rem; padding: 4rem 1rem; text-align: center; }

/* A card per order, with the lines folded away behind the header. `details` does the folding, so
 * there is no script to break and the keyboard works for free. */
.order__card { border: 1px solid var(--color-line); margin-bottom: 1rem; overflow: hidden; }

/* The header is a strip, not the top of a plain box. The portal's card-header carries its own
 * background and a rule under it, and that is what makes the card read as a card rather than as
 * a bordered paragraph — which is exactly what it looked like without them. */
.order__summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .5rem;
  padding: 1rem;
  cursor: pointer;
  list-style: none;
  background: color-mix(in srgb, var(--color-line) 28%, transparent);
  border-radius: calc(var(--radius-theme) - 1px) calc(var(--radius-theme) - 1px) 0 0;
}

/* Open, it has a body under it and needs the rule between them. Shut, it is the whole card and a
 * rule along the bottom edge would sit on the border. */
details[open] > .order__summary { border-bottom: 1px solid var(--color-line); }

.order__summary:hover { background: color-mix(in srgb, var(--color-line) 45%, transparent); }

/* The portal's Expand, which here is what the whole header already does. It says so and swaps
 * its word for the open state, so the affordance is named rather than merely implied. */
.order__open { font-size: .75rem; color: var(--color-accent); }
details[open] .order__open { visibility: hidden; }

/* The default disclosure triangle is a browser's own and sits where it likes. The header is the
 * affordance here, so the marker goes and the card reads as one row. */
.order__summary::-webkit-details-marker { display: none; }
.order__summary::marker { content: ""; }

@media (min-width: 48rem) {
  .order__summary { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) auto; align-items: center; }
  .order__total { text-align: end; }
}

.order__id { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.order__ship { display: flex; flex-direction: column; min-width: 0; }
.order__total { display: flex; flex-direction: column; }

.order__status {
  align-self: start;
  padding: .1rem .5rem;
  border: 1px solid var(--color-line);
  border-radius: 999px;
  font-size: .75rem;
}

.order__body { padding: 0 1rem 1rem; border-top: 1px solid var(--color-line); }
.order__lines { display: flex; flex-direction: column; gap: .75rem; padding-top: 1rem; }
.order__line { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: .75rem; align-items: center; }

/* The card-footer: its own strip, tinted like the header, so the money at the bottom is not read
 * as one more line of the order. */
.order__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: .85rem -1rem -1rem;
  padding: .85rem 1rem;
  border-top: 1px solid var(--color-line);
  background: color-mix(in srgb, var(--color-line) 28%, transparent);
  border-radius: 0 0 calc(var(--radius-theme) - 1px) calc(var(--radius-theme) - 1px);
}
.order__foot-money { display: flex; align-items: baseline; gap: .5rem; }

.order__facts { display: flex; flex-wrap: wrap; gap: 2rem; margin: 0 0 1.5rem; }
.order__detail { max-width: 42rem; }

/* Add to cart in the background: a spinner in the button while it runs, a toast only on failure. */
.cart-spinner {
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-inline-end: .5em;
  vertical-align: -.15em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 9999px;
  animation: cart-spin .7s linear infinite;
}
@keyframes cart-spin { to { transform: rotate(360deg); } }

.cart-toast {
  position: fixed;
  inset-inline-end: 1rem;
  bottom: 1rem;
  z-index: 60;
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  max-width: min(24rem, calc(100vw - 2rem));
  padding: .75rem 1rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-theme);
  background: var(--color-background);
  color: var(--color-text);
  box-shadow: 0 4px 16px color-mix(in srgb, var(--color-text) 15%, transparent);
  font-size: .875rem;
}
.cart-toast__close {
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

/* The header search box: grows into the space between the menu and the icons. */
.header__search { flex: 1 1 12rem; max-width: 20rem; margin: 0; }
.header__search-input { width: 100%; padding: .45rem .75rem; font-size: .875rem; }
