/* ==========================================================================
   app.css — loaded async. Below-the-fold components.
   ========================================================================== */

.section { padding-block: clamp(3rem, 6vw, 6rem); }
.section--alt { background: var(--c-ground-alt); }
.section--deep { background: var(--c-ground-deep); color: var(--c-ground); }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 52px;
  padding: 0 var(--s-6);
  background: var(--c-ink);
  color: var(--c-ground);
  border: 1px solid var(--c-ink);
  border-radius: 0;
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.btn:hover { background: transparent; color: var(--c-ink); }

.prose { max-width: 68ch; }
.prose > * + * { margin-top: var(--s-4); }
.prose h2 {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: var(--t-xl);
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin-top: var(--s-7);
}
.prose p { color: var(--c-ink-70); }
.prose a { text-decoration: underline; text-underline-offset: 2px; }
.prose a:hover { text-decoration-thickness: 2px; }

/* --------------------------------------------------------------------------
   LONG-FORM PROSE (T13)

   .prose above was written for a product page's Description panel: two or three
   paragraphs and maybe an h2. The six pages added in T13 are documents — a
   privacy policy runs to a dozen sections with numbered clauses, definition
   lists and links out — and everything below is what a document needs and a
   panel never did. It is scoped to .prose so the product panels inherit the
   parts they can use (h3, lists, strong) and are unaffected by the rest, which
   they never emit.

   HEADINGS DESCEND IN SIZE AND IN WEIGHT BOTH. Three levels of uppercase
   display type separated by size alone stop being distinguishable once the
   sizes clamp together on a phone, so h3 drops to the body face at a smaller
   size with wide tracking — a different kind of thing, not a smaller h2. h4 is
   the same shape at body size. There is deliberately no h5/h6 rule: a legal
   page that needs six levels needs an editor, not a stylesheet.
   -------------------------------------------------------------------------- */

.prose h3 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--t-md);
  line-height: 1.2;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-top: var(--s-6);
}
.prose h4 {
  font-weight: 700;
  font-size: var(--t-base);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-top: var(--s-5);
}

/* A heading immediately after another heading is a section title over a
   subsection title, and the full rhythm above it opens a hole. */
.prose h2 + h3,
.prose h3 + h4 { margin-top: var(--s-4); }

/* The first child carries no top margin: page.php already spaces the body away
   from the masthead, and a doubled gap on a document that opens with a heading
   is the most visible rhythm bug there is. */
.prose > :first-child { margin-top: 0; }

/* Markers OUTSIDE the measure, so the text edge of a list item lines up with
   the text edge of the paragraph above it. `padding-left` rather than a
   negative margin: inside a 68ch column, an outdent that escapes the column is
   an overflow at 320px. */
.prose ul,
.prose ol {
  padding-left: var(--s-5);
  color: var(--c-ink-70);
}
/* ON THE <li>, NOT ON THE LIST. critical.css's reset is `li { list-style: none }`
   — an element selector, which beats a list-style inherited from a `.prose ul`
   — so setting the marker on the parent produces a padded list with no bullets
   in it. Measured, not assumed: that was the first version of this rule. */
.prose ul > li { list-style: disc; }
.prose ol > li { list-style: decimal; }
.prose li + li { margin-top: var(--s-2); }
.prose li::marker { color: var(--c-ink-45); }

/* Nested lists tighten rather than repeat the outer rhythm. */
.prose li > ul,
.prose li > ol { margin-top: var(--s-2); }

.prose strong { font-weight: 700; color: var(--c-ink); }

/* PLACEHOLDER TOKENS. Every [RETURNS WINDOW], [CONTACT EMAIL] and the rest is
   written as <code> by the page seeder, and this is what makes them impossible
   to read past: ink on the alt ground, in the one place on the site that is not
   Archivo. They are meant to look like holes in the document, because they are.
   Remove this rule and the tokens become ordinary prose the owner scrolls by. */
.prose code {
  /* `inherit`, not a monospace stack. The UA default for <code> is monospace,
     and this direction is Archivo and nothing else — a second face introduced
     for a placeholder marker would be the only non-Archivo type on the site. */
  font-family: inherit;
  font-weight: 700;
  background: var(--c-ground-alt);
  padding: 0.1em 0.35em;
  letter-spacing: var(--tracking-wide);
  color: var(--c-ink);
}

/* A quiet aside — the "this is a draft for the owner to review" note at the top
   of each legal page. A left rule, not a tinted box: this theme has no tints. */
.prose blockquote {
  border-left: 2px solid var(--c-ink);
  padding-left: var(--s-4);
  color: var(--c-ink-70);
}

.prose hr {
  border: 0;
  border-top: 1px solid var(--c-ink-15);
  margin-block: var(--s-6);
}

/* A table in a 68ch column overflows the moment it has three columns of real
   words, and the page must not scroll sideways because of it — so the table
   scrolls inside itself. `display: block` on the table is what makes overflow
   apply at all; a <table> is not a scroll container otherwise. */
.prose table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: var(--t-base);
}
.prose th,
.prose td {
  border: 1px solid var(--c-ink-15);
  padding: var(--s-2) var(--s-3);
  text-align: left;
  vertical-align: top;
}
.prose th { font-weight: 700; text-transform: uppercase; letter-spacing: var(--tracking-wide); font-size: var(--t-xs); }

/* --------------------------------------------------------------------------
   page.php — the ordinary page

   THE MEASURE IS ON THE BODY, NOT ON THE PAGE, which is the whole change from
   the foundation-phase `style="max-width:900px"`: that wrapped the <h1> at
   900px as well, and a display face at --t-2xl inside 900px is about twenty
   characters a line. The masthead now runs the full .wrap and only .prose is
   measured.
   -------------------------------------------------------------------------- */

.page__head {
  border-bottom: 1px solid var(--c-ink);
  padding-bottom: var(--s-5);
}
/* .t-page-title's own padding-block is symmetrical; the rule below wants the
   title closer to it than to the header above. */
.page__title { padding-bottom: var(--s-3); }
.page__updated { color: var(--c-ink-45); }

.page__body { padding-block: var(--s-6) var(--s-8); }

@media (min-width: 900px) {
  .page__body { padding-block: var(--s-7) var(--s-8); }
}

/* ==========================================================================
   SHARED ELEMENT LAYER — the CSS WooCommerce would otherwise supply

   WHY THIS FILE HAS THIS SECTION AT ALL. WooCommerce ships woocommerce.css and
   enqueues it on every page of the store. Everywhere this theme has written its
   own component (the card, the bag, the product page) that sheet is invisible,
   because a class beats an element and ours load later. Everywhere it has NOT
   — a queued notice, a login form, an address form, the whole of checkout — the
   plugin's own look shows through: 3px coloured top borders, an icon font, 4px
   rounded grey buttons with grey text, `--wc-form-border-radius: 4px` inputs.

   The owner reported one instance of it ("after add to cart the item view button
   still without stiln" — the grey View bag button in a green-bordered box). It
   is not one instance. It is every element this theme has not claimed, and it
   is claimed HERE, once, rather than page by page as those pages get built.
   Checkout does not exist yet and its inputs and buttons are already themed.

   HOW THE OVERRIDES WIN. WooCommerce's own selectors are not gentle:

     .woocommerce-error, .woocommerce-info, .woocommerce-message  (0,1,0)
     .woocommerce form .form-row .input-text                      (0,3,0)
     .woocommerce:where(body:not(...)) a.button                   (0,2,1)
     .woocommerce:where(body:not(...)) #respond input#submit      (1,1,1)

   :where() contributes nothing, so those are the real numbers. Each block below
   states the specificity it is matching and matches it EXACTLY rather than
   escalating — app.css is enqueued after woocommerce.css, so an equal-specificity
   declaration later in the cascade wins, and staying equal means a component
   class of this theme's own still beats both.

   No !important anywhere in this section. If one becomes necessary, the selector
   is wrong.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fields: input, select, textarea.

   TWO RULES FOR ONE LOOK, deliberately. The first is element-level (0,0,1) so it
   reaches every field on the site — including pages this theme has no template
   for — while still losing to every component class the theme has written
   (.qty__input, .search-form__input, .newsletter__input all keep their own
   shape). The second repeats it at WooCommerce's (0,3,0) because inside
   .woocommerce form .form-row the plugin's rule would otherwise beat the first
   one and restore the 4px radius.

   :where() on the type list keeps the first rule at (0,0,1) — a plain
   `input[type="text"]` would be (0,1,1) and would start beating theme classes.
   -------------------------------------------------------------------------- */

input:where([type="text"], [type="email"], [type="password"], [type="tel"],
             [type="url"], [type="number"], [type="search"], [type="date"]),
select,
textarea {
  width: 100%;
  min-height: 52px;
  padding: 0 var(--s-4);
  background: var(--c-ground);
  color: var(--c-ink);
  border: 1px solid var(--c-ink-15);
  border-radius: 0;
  font: inherit;
  font-size: var(--t-md);
}

textarea {
  min-height: 8rem;
  padding-block: var(--s-3);
  line-height: 1.5;
  resize: vertical;
}

/* The plugin's (0,3,0) form rules, matched exactly. Every property here is one
   woocommerce.css sets on the same selector — nothing new is introduced. */
.woocommerce form .form-row .input-text,
.woocommerce form .form-row select,
.woocommerce form .form-row textarea {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 0 var(--s-4);
  background-color: var(--c-ground);
  color: var(--c-ink);
  border: 1px solid var(--c-ink-15);
  border-radius: 0;
  font-family: var(--f-sans);
  font-size: var(--t-md);
  font-weight: 400;
  line-height: 1.5;
}
.woocommerce form .form-row textarea {
  min-height: 8rem;
  height: auto;
  padding-block: var(--s-3);
}

/* Woo sets appearance:none plus its own base64 chevron. Dropping the image
   without restoring appearance leaves a select with no affordance at all. */
.woocommerce form .form-row select {
  appearance: auto;
  background-image: none;
  padding-right: var(--s-4);
  cursor: pointer;
}

/* A field the visitor is IN is the one thing on the page that should be ink.
   Woo's own :focus rule is `border-color: currentColor`, which on grey text is
   grey — the field looks the same focused as unfocused. */
input:where([type="text"], [type="email"], [type="password"], [type="tel"],
             [type="url"], [type="number"], [type="search"], [type="date"]):focus,
select:focus,
textarea:focus,
.woocommerce form .form-row .input-text:focus,
.woocommerce form .form-row select:focus,
.woocommerce form .form-row textarea:focus {
  border-color: var(--c-ink);
}

/* The invalid/valid states are the plugin's, recoloured to this theme's two
   accents. Green is not in the palette; --c-sale is the only red there is. */
.woocommerce form .form-row.woocommerce-invalid input.input-text,
.woocommerce form .form-row.woocommerce-invalid select {
  border-color: var(--c-sale);
}
.woocommerce form .form-row.woocommerce-validated input.input-text,
.woocommerce form .form-row.woocommerce-validated select {
  border-color: var(--c-ink);
}

/* THE BOX ROUND THE FORM. woocommerce.css puts a 1px #cfc8d8 border with a 5px
   radius and 20px of padding round form.login, form.register and
   form.checkout_coupon — the "bordered box floating in white space" the account
   page was reported for. Nothing in this direction has a card; the panel's own
   heading and the rule between the columns are the structure. (0,1,1) matched
   exactly. */
.woocommerce form.login,
.woocommerce form.register,
.woocommerce form.checkout_coupon,
.woocommerce form.lost_reset_password {
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
}

/* WooCommerce injects its privacy sentence into the register form through the
   woocommerce_register_form hook, so it exists on any store with a privacy page
   set and no template can opt out of it. */
.woocommerce-privacy-policy-text p,
.woocommerce-privacy-policy-text {
  font-size: var(--t-base);
  color: var(--c-ink-45);
}
.woocommerce-privacy-policy-text a { text-decoration: underline; text-underline-offset: 2px; }

/* Woo's .form-row is `padding:3px; margin:0 0 6px` with `label { line-height:2 }`
   — a 1px-scale rhythm from 2013. Rebuilt as a grid so the label and the field
   are one unit and the gap between rows is a token. */
.woocommerce form .form-row {
  display: grid;
  gap: var(--s-2);
  padding: 0;
  margin: 0 0 var(--s-5);
}
.woocommerce form .form-row label {
  line-height: 1.3;
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
/* ...except a checkbox's label, which wraps its own input and must stay inline. */
.woocommerce form .form-row label.woocommerce-form__label-for-checkbox {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-base);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
}
.woocommerce form .form-row label .required { color: var(--c-sale); }

/* "(optional)" is WooCommerce's own span, printed on every field this store has
   made optional — the postcode is the one that matters here. Left at Woo's own
   (0,3,0) rather than raised, and dimmed rather than hidden: an optional field a
   shopper cannot tell is optional is a field they stop and think about. */
.woocommerce form .form-row label .optional {
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--c-ink-45);
}

/* THE TWO-COLUMN ROWS. WooCommerce lays first/last out with `float: left; width:
   47%` and `clear: both` on `.form-row-wide` — a 2013 float grid that cannot be
   placed inside a CSS grid, and whose floats escape the row they were meant to
   pair when the container is narrower than they assume.

   The floats are dropped here and the pairing is done by the grid in
   .checkout-fields__grid below. Matched at Woo's own (0,2,0) — `.woocommerce
   form .form-row-first` is what woocommerce.css writes — so a component class of
   this theme's own still beats it. Nothing outside a .woocommerce wrapper is
   touched, which is why the account page's own two-column form is unaffected. */
.woocommerce form .form-row-first,
.woocommerce form .form-row-last,
.woocommerce form .form-row-wide {
  float: none;
  width: auto;
  clear: none;
}

/* The address-book heading WooCommerce prints above a second address, and the
   checkbox that opens it. Both are core's markup on a `<h3 id>` with a label
   inside it, which is why this is an id selector rather than a class — (1,0,1),
   matched to core's own. */
.woocommerce form #ship-to-different-address,
.woocommerce #ship-to-different-address {
  margin: 0 0 var(--s-4);
  font-family: var(--f-sans);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.woocommerce #ship-to-different-address label {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
}

/* checkout.js adds .woocommerce-invalid to the ROW, not the field, so the label
   is the part a shopper reads first. Woo's own colour here is #a00; ours is the
   only red the palette has. (0,3,0), matched to the border rule above it. */
.woocommerce form .form-row.woocommerce-invalid label {
  color: var(--c-sale);
}

/* --------------------------------------------------------------------------
   Buttons: WooCommerce's `.button`, given the black square of ADD TO BAG.

   The first selector is the bare class, so a `.button` outside a .woocommerce
   wrapper (the notice on the homepage is one) is themed too. The rest match the
   plugin's (0,2,1)/(1,1,1) exactly — see this section's header.

   The treatment is .btn's, property for property, and the two are deliberately
   NOT merged into one selector list: .btn is this theme's own vocabulary and
   .button is a plugin's, and merging them would mean a WooCommerce upgrade
   could silently restyle the theme's own buttons.
   -------------------------------------------------------------------------- */

.button,
.woocommerce .button,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce #respond input#submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 52px;
  padding: 0 var(--s-6);
  background-color: var(--c-ink);
  color: var(--c-ground);
  border: 1px solid var(--c-ink);
  border-radius: 0;
  font-family: var(--f-sans);
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.button:hover,
.woocommerce .button:hover,
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce #respond input#submit:hover {
  background-color: var(--c-ground);
  color: var(--c-ink);
}

/* `.alt` is WooCommerce's own emphasis modifier (Place order, Proceed to
   checkout) and its default is the plugin's purple. Here the ordinary button is
   already the emphatic one, so .alt is identical rather than louder. */
.woocommerce .button.alt,
.woocommerce a.button.alt,
.woocommerce button.button.alt,
.woocommerce input.button.alt {
  background-color: var(--c-ink);
  color: var(--c-ground);
}
.woocommerce .button.alt:hover,
.woocommerce a.button.alt:hover,
.woocommerce button.button.alt:hover,
.woocommerce input.button.alt:hover {
  background-color: var(--c-ground);
  color: var(--c-ink);
}

.woocommerce .button:disabled,
.woocommerce .button.disabled,
.woocommerce button.button:disabled,
.woocommerce input.button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   Notices: .woocommerce-message, .woocommerce-error, .woocommerce-info.

   THE DEFECT THE OWNER SAW. All three arrive as a #f6f5f8 box with a 3px
   coloured TOP border, #515151 text, and an icon painted from the WooCommerce
   icon font in the box's own accent colour — green tick, blue i, red cross.
   None of it is this theme, and the `.button` floated inside it was grey on
   grey. They only appear on the NON-AJAX path — a real form POST, or a visitor
   with JavaScript off — which is why the drawer's own tests never met one.

   THE TREATMENT. One box for all three: --c-ground-alt, ink text, zero radius,
   and the accent moved from a top border to a 3px LEFT rule, which is the same
   hard vertical edge the shop bar and the cart summary already use. The icon
   font is dropped rather than restyled: it is a third party's glyph set loaded
   from a third party's @font-face, and the notice's meaning is in its sentence.

   Only the error state changes colour, to --c-sale. Info and success share the
   ink rule — a palette of exactly two accents cannot express three severities,
   and inventing a third colour is what the direction forbids.
   -------------------------------------------------------------------------- */

.woocommerce-notices-wrapper:not(:empty) { padding-top: var(--s-5); }

.woocommerce-message,
.woocommerce-error,
.woocommerce-info {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
  width: auto;
  padding: var(--s-4) var(--s-5);
  margin: 0 0 var(--s-5);
  background-color: var(--c-ground-alt);
  color: var(--c-ink);
  border: 0;
  border-left: 3px solid var(--c-ink);
  border-radius: 0;
  font-family: var(--f-sans);
  font-size: var(--t-base);
  line-height: 1.5;
  list-style: none;
  word-wrap: break-word;
}

.woocommerce-error { border-left-color: var(--c-sale); }

/* The icon font, removed. `content: none` rather than `""` so the box is not
   also reserving a line box for an empty glyph. */
.woocommerce-message::before,
.woocommerce-error::before,
.woocommerce-info::before { content: none; }

/* An error notice is a <ul> of <li>. A flex parent would lay several errors out
   in a row, so the items stack and the box only spreads its own children. */
.woocommerce-error li,
.woocommerce-info li,
.woocommerce-message li { width: 100%; margin: 0; }

/* Woo floats the button right, which does nothing at all on a flex parent. An
   auto inline-start margin is the flex equivalent, and it is used INSTEAD of
   justify-content: space-between for a measured reason: wc_print_notice() emits
   a trailing tab after the anchor, Chrome makes that whitespace a third flex
   item, and space-between parks the button two thirds of the way across rather
   than at the end. Measured at 1280: button right edge 876 of a 1235-wide box.
   The auto margin eats the free space regardless of what follows. */
.woocommerce-message a.button,
.woocommerce-message button.button,
.woocommerce-error a.button,
.woocommerce-error button.button,
.woocommerce-info a.button,
.woocommerce-info button.button {
  float: none;
  flex: 0 0 auto;
}

/* :last-child, and it is load-bearing. Most notices read "…sentence… [button]",
   but myaccount/orders.php's builds the other way round — "[Confirm email
   address] Confirm your email address to check for past orders" — and an
   unconditional auto margin threw THAT button to the left edge and its sentence
   to the right. The guard means the margin only ever pushes a trailing button.
   The trailing tab wc_print_notice() emits is a text node, which :last-child
   ignores.

   ELEMENT-QUALIFIED for the same reason as above: woocommerce.css declares
   `margin: 0` in its own button rule at (0,2,1), which beats a bare
   `.woocommerce-message .button` at (0,2,0) — measured, the auto margin computed
   to 0px and the button sat mid-box. */
.woocommerce-message a.button:last-child,
.woocommerce-message button.button:last-child,
.woocommerce-error a.button:last-child,
.woocommerce-error button.button:last-child,
.woocommerce-info a.button:last-child,
.woocommerce-info button.button:last-child {
  margin-inline-start: auto;
}

/* Below 600px the sentence has already wrapped and the button is on its own
   line, where a right-aligned 130px block reads as a stray. */
@media (max-width: 599px) {
  .woocommerce-message a.button,
  .woocommerce-message button.button,
  .woocommerce-error a.button,
  .woocommerce-error button.button,
  .woocommerce-info a.button,
  .woocommerce-info button.button {
    width: 100%;
    margin-inline-start: 0;
  }
  .woocommerce-message a.button:last-child,
  .woocommerce-message button.button:last-child,
  .woocommerce-error a.button:last-child,
  .woocommerce-error button.button:last-child,
  .woocommerce-info a.button:last-child,
  .woocommerce-info button.button:last-child { margin-inline-start: 0; }
}

/* Notices sit inside .woocommerce-MyAccount-content on the account routes, where
   the box is already inset — the wrapper's own top padding would double it. */
.woocommerce-MyAccount-content > .woocommerce-message:first-child,
.woocommerce-MyAccount-content > .woocommerce-error:first-child,
.woocommerce-MyAccount-content > .woocommerce-info:first-child { margin-top: 0; }

/* ==========================================================================
   Homepage sections (front-page.php)

   Everything below the hero. The hero itself is in critical.css — see the note
   there. Zero border radius anywhere in this file, by design, not by omission.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Section heading: eyebrow, title, and a "view all" link BESIDE the title
   rather than pushed to the far right — that is where the reference puts it,
   and at 1600px a right-aligned link is a long way from the words it belongs to.
   -------------------------------------------------------------------------- */

.sec-head {
  display: flex;
  flex-wrap: wrap;
  /* flex-end, not baseline. .sec-head__text is a block wrapping the eyebrow AND
     the heading, so its FIRST baseline is the eyebrow's — baseline alignment put
     "View all" level with "Just landed" and left it floating in the gap above
     the heading. Aligning bottoms puts the link on the heading's line, which is
     where the reference has it. */
  align-items: flex-end;
  gap: var(--s-2) var(--s-4);
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
.sec-head__text > .t-eyebrow { margin-bottom: var(--s-2); }
.sec-head__title { font-size: var(--t-xl); }
.sec-head__link {
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.sec-head__link:hover { text-decoration-thickness: 2px; }

/* --------------------------------------------------------------------------
   Product row: one element that is a scroll-snap rail on small screens and a
   four-up grid from 900px.

   The rail is what the reference does on mobile, and native scroll-snap needs
   no JavaScript and no carousel library. From 900px a full row of four fits, so
   the horizontal scroll — and with it the reference's desktop prev/next buttons,
   which would need a scroll-position state machine to stay in sync — is not
   needed at all.

   The negative inline margin lets the rail bleed to the viewport edge from
   inside .wrap while the first card still starts at the gutter.
   -------------------------------------------------------------------------- */

.pgrid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 66%;
  gap: var(--s-3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  padding-inline: var(--gutter);
  /* Without this the snapport starts at the padding edge, so `snap-align:
     start` parks the first card flush against the viewport with its title
     touching x=0 — measured: the browser snapped scrollLeft to 16 on load and
     ate the gutter. scroll-padding insets the snapport to match. */
  scroll-padding-inline: var(--gutter);
  margin-inline: calc(var(--gutter) * -1);
  padding-bottom: var(--s-2);
}
.pgrid::-webkit-scrollbar { display: none; }
.pgrid > * { scroll-snap-align: start; }

@media (min-width: 600px) {
  .pgrid { grid-auto-columns: 38%; }
}

@media (min-width: 900px) {
  .pgrid {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--s-6) var(--s-4);
    overflow-x: visible;
    scroll-snap-type: none;
    padding-inline: 0;
    padding-bottom: 0;
    margin-inline: 0;
  }
}

/* The product card itself is NOT here. It moved to critical.css in T8, because
   the shop archive puts a full row of cards above the fold — see the note above
   .card there, and tests/php/critical-css.test.php, which asserts the split in
   both directions. */

/* --------------------------------------------------------------------------
   Brand story: two-column editorial split, stacked on mobile.
   -------------------------------------------------------------------------- */

.story__grid { display: grid; gap: var(--s-5); align-items: center; }

@media (min-width: 900px) {
  .story__grid { grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 5rem); }
}

/* PLACEHOLDER for the brand photograph — see front-page.php. Portrait when
   stacked, landscape beside the copy: at --max-w a half-width 3:4 block is over
   900px tall, which reads as a void rather than as an image. */
.story__media { aspect-ratio: 4 / 5; background: var(--c-ground-alt); }
@media (min-width: 900px) { .story__media { aspect-ratio: 3 / 2; } }

/* The photograph state. The ratio stays on .story__media so the grid does not
   move when an image is added or removed, and the <img> fills it — object-fit
   crops rather than letterboxing, which is what keeps the two columns level.
   overflow:hidden matters because the source is rarely exactly 4:5 or 3:2. */
.story__media--photo { overflow: hidden; }
.story__img { display: block; width: 100%; height: 100%; object-fit: cover; }

.story__body { display: grid; gap: var(--s-4); justify-items: start; }
.story__title { font-size: var(--t-xl); }
.story__text { color: var(--c-ink-70); max-width: 46ch; }

/* --------------------------------------------------------------------------
   Occasion tiles.

   Two states, because most categories have no thumbnail yet: a tone block with
   ink type, or — once a term image exists — the image with a scrim and light
   type over it. Both are the same box at the same ratio, so adding an image to
   a category does not move the grid.
   -------------------------------------------------------------------------- */

/* Two up, then four up — both divide the four tiles front-page.php asks for
   into full rows. A three-column desktop grid left a hole at either count. */
.tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-2);
}
@media (min-width: 900px) {
  .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); }
}

.tile {
  position: relative;
  display: grid;
  align-items: end;
  overflow: hidden;
  isolation: isolate;
  aspect-ratio: 4 / 5;
  background: var(--c-ground-alt);
  color: var(--c-ink);
}
.tile__img {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tile--media { color: var(--c-ground); }
.tile--media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0) 62%);
}
.tile--media :focus-visible { outline-color: var(--c-ground); }

.tile__body {
  display: grid;
  gap: var(--s-1);
  justify-items: start;
  padding: var(--s-4);
}
.tile__title {
  font-family: var(--f-display);
  font-size: var(--t-lg);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}
.tile__count {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.tile:not(.tile--media) .tile__count { color: var(--c-ink-45); }
.tile:hover .tile__title { text-decoration: underline; text-underline-offset: 4px; }

/* --------------------------------------------------------------------------
   Newsletter.

   --c-ground-alt rather than the reference's ink band: this sits directly above
   an ink footer, and two ink bands with nothing between them read as one slab.
   -------------------------------------------------------------------------- */

.newsletter { background: var(--c-ground-alt); }
.newsletter__inner { display: grid; gap: var(--s-4); max-width: 34rem; }
.newsletter__title { font-size: var(--t-xl); }
.newsletter__text { color: var(--c-ink-70); }

/* Input and button share one edge, both square. */
.newsletter__form { display: flex; flex-wrap: wrap; gap: 0; }
.newsletter__input {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: 52px;
  padding-inline: var(--s-4);
  background: var(--c-ground);
  border: 1px solid var(--c-ink);
  border-radius: 0;
  color: var(--c-ink);
  font: inherit;
}
.newsletter__input::placeholder { color: var(--c-ink-45); }
/* The two controls butt together; without this their borders double up. */
.newsletter__submit { margin-left: -1px; }
.newsletter__note { font-size: var(--t-xs); color: var(--c-ink-45); }

/* ==========================================================================
   Shop archive — the parts that are never in the first screen.

   The head, the sort bar, the grid and the card are in critical.css. What is
   here cannot be above the fold: pagination sits under a full grid, and the
   empty state only renders when the grid does not. The notice that used to be
   styled here moved to the shared element layer above — it is not the shop
   archive's, it appears on every route.
   ========================================================================== */

/* THE CLOSING SPACE ABOVE THE FOOTER.

   main#main has zero bottom padding on every route and stays that way: the
   homepage and the blog index end on the newsletter's filled --c-ground-alt
   band, which is composed to MEET the black footer, and a blanket
   `main { padding-bottom }` would open a white stripe between two filled bands.

   So the space belongs to the routes that end on transparent content, and each
   names itself. Measured at 1280 before this rule, as the distance from the
   last ink in <main> to the top of the footer:

     /shop/, /product-category/*   0px   <- the defect
     /?s=dress                    77px   (.search-wrap already had it)
     product page                 77px   (.section--related's own padding)
     /cart/, empty                125px
     an ordinary page, 404, post   64px
     homepage, blog index          0px   <- correct, and left alone

   The value is .search-wrap's, to the pixel, because the two pages render the
   same grid of the same card and a different closing gap on each would read as
   a mistake. */
.shop-wrap { padding-bottom: clamp(3rem, 6vw, 5rem); }

/* --------------------------------------------------------------------------
   Pagination
   -------------------------------------------------------------------------- */

.shop-pagination { padding-block: clamp(2rem, 5vw, 4rem); }
.shop-pagination .nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
}
.shop-pagination .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--s-2);
  border: 1px solid var(--c-ink-15);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
.shop-pagination a.page-numbers:hover { border-color: var(--c-ink); }
/* The current page is the one square that is filled, so it reads at a glance
   without depending on colour alone. */
.shop-pagination .page-numbers.current {
  background: var(--c-ink);
  border-color: var(--c-ink);
  color: var(--c-ground);
}
.shop-pagination .page-numbers.dots { border-color: transparent; }

/* The empty state is NOT here either: on an archive with no products it is the
   only thing above the fold. It is in critical.css beside the grid it replaces. */

/* ==========================================================================
   Product page — the parts that are never in the first screen.

   The gallery and the whole buy column are in critical.css. What is here sits
   under a buy column that is at least a viewport tall: the collapsible detail
   sections, and the related row.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Collapsible detail sections: <details>/<summary>, no JavaScript at all.

   Ruled top and bottom rather than boxed, so a stack of three reads as one list
   of rules and not as three cards. Zero radius, like everything else.
   -------------------------------------------------------------------------- */

.pdp__details { margin-top: var(--s-2); border-top: 1px solid var(--c-ink-15); }

.detail { border-bottom: 1px solid var(--c-ink-15); }

.detail__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-block: var(--s-4);
  cursor: pointer;
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  /* The UA triangle is replaced by the +/− glyphs on the right. `list-style:
     none` is what removes it in Firefox and Chrome; the -webkit- pseudo-element
     is what removes it in Safari. Both are needed. */
  list-style: none;
}
.detail__summary::-webkit-details-marker { display: none; }
.detail__summary svg { flex: none; width: 18px; height: 18px; }

/* One source of truth for the open/closed glyph — the element's own `open`
   state, not a class a script has to keep in sync. */
.detail__glyph--close { display: none; }
.detail[open] .detail__glyph--open { display: none; }
.detail[open] .detail__glyph--close { display: block; }

.detail__body { padding-bottom: var(--s-4); }
/* .prose caps at 68ch, which is wider than this column; the column decides. */
.detail__body.prose { max-width: none; }
.detail__body > * + * { margin-top: var(--s-3); }

/* --------------------------------------------------------------------------
   The size guide's OPEN state.

   The closed summary row is in critical.css, because it is on screen between two
   inlined blocks — the size grid and the Add to bag button — from the first
   paint. Everything below is hidden by the UA stylesheet until the <details> is
   opened, which is the same trade the bag drawer makes and for the same reason:
   there is nothing to flash, so there is nothing to inline.

   See inc/size-chart.php for the markup and for why this is a disclosure rather
   than a dialog.
   -------------------------------------------------------------------------- */

/* The glyph swap lives here rather than beside the glyph sizing in critical.css:
   both rules describe the OPEN state, and a shut disclosure never needs them. */
.sizeguide[open] .sizeguide__glyph--open { display: none; }
.sizeguide[open] .sizeguide__glyph--close { display: block; }

.sizeguide__body {
  display: grid;
  gap: var(--s-3);
  padding-bottom: var(--s-4);
}

.sizeguide__lede { font-size: var(--t-sm); line-height: 1.5; }
.sizeguide__stock,
.sizeguide__ask { font-size: var(--t-xs); line-height: 1.5; color: var(--c-ink-70); }

/* THE 320px ANSWER.
   A five-column numeric table cannot be squeezed into a 288px content column and
   stay legible, and horizontal PAGE overflow is the failure this project audits
   for — so the table keeps a floor of 19rem and this container scrolls instead.
   19rem is under the 21rem the buy column is never allowed below at desktop
   (see .pdp's grid-template-columns), so this only ever scrolls on the narrowest
   phones, which is where it is the right answer.

   THE AFFORDANCE IS CSS-ONLY AND HONEST. Two ground-coloured gradients painted
   with `background-attachment: local` scroll away with the content; two shadows
   painted with `scroll` stay put. The result is a shadow at an edge if and only
   if there is content past it, so nothing suggests scrolling when the table
   already fits, and the hint disappears when the reader reaches the end.

   tabindex is on the element in the markup, not here: a scrollable region that
   cannot be reached from the keyboard is unreachable content. */
.sizeguide__scroll {
  overflow-x: auto;
  /* -webkit-overflow-scrolling and overscroll-behavior together stop a
     horizontal swipe inside the table from turning into a back-navigation. */
  overscroll-behavior-x: contain;
  background:
    linear-gradient(to right, var(--c-ground) 30%, rgba(255, 255, 255, 0)) left center / 28px 100% no-repeat local,
    linear-gradient(to left, var(--c-ground) 30%, rgba(255, 255, 255, 0)) right center / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0)) left center / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0)) right center / 12px 100% no-repeat scroll;
}
.sizeguide__scroll:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }

.sizeguide__table {
  min-width: 19rem;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}
.sizeguide__table th,
.sizeguide__table td {
  padding: var(--s-2) var(--s-2);
  border-bottom: 1px solid var(--c-ink-15);
  text-align: right;
  white-space: nowrap;
}
.sizeguide__table thead th {
  border-bottom: 1px solid var(--c-ink);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.sizeguide__table tbody tr:last-child th,
.sizeguide__table tbody tr:last-child td { border-bottom: 0; }

/* The row header is the only left-aligned cell: it is a label, not a number. */
.sizeguide__table th[scope="row"] {
  text-align: left;
  padding-left: 0;
  font-weight: 400;
}
.sizeguide__table th[scope="col"]:first-child { text-align: left; padding-left: 0; }
.sizeguide__table td:last-child { padding-right: 0; }
.sizeguide__table th[scope="col"]:last-child { padding-right: 0; }

.sizeguide__unit {
  display: block;
  color: var(--c-ink-45);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: lowercase;
}

/* THE MAPPING, which is the point of the chart: the letter this shop sells in,
   and the UK number the measurements are written against, in one cell. */
.sizeguide__letter { font-weight: 700; }
.sizeguide__uk { color: var(--c-ink-70); }
.sizeguide__letter + .sizeguide__uk { margin-left: 0.4em; }

/* A ROW WE DO NOT STOCK. Greyed, and it also says "not stocked" in words — see
   inc/size-chart.php. Colour is never the only carrier of a state in this theme. */
.sizeguide__row--other th,
.sizeguide__row--other td { color: var(--c-ink-45); }
.sizeguide__off {
  display: block;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* How to measure. A <dl> rather than headings — the buy column's only heading is
   the product's <h1>. */
.sizeguide__how {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-1) var(--s-3);
  font-size: var(--t-xs);
  line-height: 1.5;
}
.sizeguide__how dt { font-weight: 700; }
.sizeguide__how dd { margin: 0; color: var(--c-ink-70); }

/* --------------------------------------------------------------------------
   Related products.

   The row reuses .pgrid and the shared card — see
   woocommerce/single-product/related.php. Only the section's own spacing is
   here, and the top rule that separates it from the product above it.
   -------------------------------------------------------------------------- */

.section--related { border-top: 1px solid var(--c-ink-15); }

/* ==========================================================================
   Footer

   Ink ground, ground ink — the same two colours inverted, no third one. These
   rules used to live in critical.css; the footer is below the fold on every
   page long enough to matter, so it does not belong in the inlined budget.
   ========================================================================== */

.footer {
  background: var(--c-ground-deep);
  color: var(--c-ground);
  padding-block: var(--s-7) var(--s-5);
  font-size: var(--t-sm);
}
.footer a { color: rgba(255, 255, 255, 0.72); }
.footer a:hover {
  color: var(--c-ground);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer__cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-6);
  padding-bottom: var(--s-7);
}
@media (min-width: 720px) {
  .footer__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); }
}

.footer__col-title {
  font-family: var(--f-display);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  line-height: 1;
  margin-bottom: var(--s-4);
}

/* The colour is on the list, not on its links, so a plain <li> (the opening
   hours) and a linked one read at the same weight. */
.footer__col ul {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  color: rgba(255, 255, 255, 0.72);
}
.footer__blurb { color: rgba(255, 255, 255, 0.72); max-width: 34ch; margin-bottom: var(--s-4); }

/* The About column carries two lists now — page links, then social — and a
   column-flex list gives no gap between siblings. */
.footer__col ul + ul { margin-top: var(--s-4); }

/* Overrides the column-list rule above by source order, same specificity. */
.footer__social { flex-direction: row; flex-wrap: wrap; gap: var(--s-4); }
.footer__social a {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s-4) var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.72);
  font-size: var(--t-xs);
}

/* The footer's payment row — a "WE ACCEPT" heading over two typographic chips —
   was REMOVED with the switch to prepayment by card; its rules went with it
   rather than being left here for a future footer to pick up by accident. */

/* The <ul> is the flex container, not the <nav> — footer.php wraps the legal
   items in a list so they keep their list semantics. */
.footer__legal ul { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); }

.footer__legalese { margin-left: auto; }
.footer__legalese p + p { margin-top: var(--s-1); }
@media (max-width: 719px) {
  .footer__legalese { margin-left: 0; }
}

/* ==========================================================================
   The bag drawer (parts/cart-drawer.php)

   HERE AND NOT IN critical.css, deliberately. A <dialog> without the `open`
   attribute is display:none from the UA stylesheet, so the drawer's closed
   state needs no rule of this theme's at all — there is nothing to flash, and
   nothing to inline. Measured rather than assumed: with this whole block
   deleted the closed drawer is still invisible on every route, and only its
   OPEN appearance changes.

   The line, the stepper, the CTAs and the note are NOT here — they are shared
   with the bag page, which does paint them above the fold, so they live in
   critical.css and this reuses them.
   ========================================================================== */

/* The dialog fills the viewport and the visible panel is a child aligned to the
   trailing edge. ::backdrop is not an element and dispatches no events, so the
   region beside the panel has to belong to the dialog for a click out there to
   be seen at all — see the click handler in assets/js/nav.js. */
.drawer {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--c-ink);
  overflow: hidden;
}
/* Scoped to [open]. An unconditional display would beat the UA's
   `dialog:not([open]) { display: none }` and leave the drawer on screen. */
.drawer[open] { display: flex; justify-content: flex-end; }
.drawer::backdrop { background: rgb(0 0 0 / 0.5); }

.drawer__panel {
  display: flex;
  flex-direction: column;
  width: min(100%, 26rem);
  max-height: 100%;
  background: var(--c-ground);
  border-left: 1px solid var(--c-ink-15);
}

.drawer__head {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--c-ink-15);
}
.drawer__title { font-size: var(--t-md); }

/* The fragment root. It is replaced wholesale on every cart change, so it is
   what carries the column layout — putting these on .drawer__panel would work
   equally well until the day the swap changed the nesting. */
.aurena-drawer-body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.drawer__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Without this, scrolling past the end of the bag scrolls the page behind. */
  overscroll-behavior: contain;
  padding-inline: var(--s-4);
  /* This element also carries the bag-line list class, whose own top rule
     would double the head's border. Zeroed here UNQUALIFIED, and that is not
     stylistic: tests/php/critical-css.test.php checks that every inlined cart
     selector is absent from app.css by substring, and a compound written as
     `.drawer__body` + that class contains it. app.css loads after the inlined
     block, so equal specificity already wins. */
  border-top: 0;
}

.drawer__foot {
  display: grid;
  flex: none;
  gap: var(--s-2);
  padding: var(--s-4);
  border-top: 1px solid var(--c-ink-15);
}

.cart-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  font-size: var(--t-md);
  font-weight: 800;
}
.cart-total__label {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.cart-total__value { font-variant-numeric: tabular-nums; }

.drawer__link {
  justify-self: center;
  padding-top: var(--s-1);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.drawer__empty {
  display: grid;
  gap: var(--s-3);
  justify-items: center;
  align-content: center;
  flex: 1 1 auto;
  padding: var(--s-7) var(--s-4);
  text-align: center;
}
.drawer__empty-title { font-size: var(--t-lg); }
.drawer__empty-text { color: var(--c-ink-70); }

/* Scroll lock. showModal() puts the drawer in the top layer and makes the rest
   of the document inert, but it does not stop the page behind from scrolling.
   Added to <html> by nav.js on open and removed on close. */
.is-drawer-open { overflow: hidden; }

/* Entry only, and no @starting-style: the exit would need transition-behavior:
   allow-discrete on `display` and `overlay` to be visible at all, and a drawer
   that lingers after Escape is worse than one that goes. The global
   prefers-reduced-motion rule in critical.css collapses both of these. */
.drawer[open] .drawer__panel { animation: aurena-drawer-in var(--dur) var(--ease); }
.drawer[open]::backdrop { animation: aurena-drawer-fade var(--dur) var(--ease); }

@keyframes aurena-drawer-in { from { translate: 100% 0; } }
@keyframes aurena-drawer-fade { from { opacity: 0; } }

/* ==========================================================================
   The full-screen image viewer (parts/product-viewer.php, assets/js/nav.js)

   HERE AND NOT IN critical.css, for the reason the drawer block above gives:
   a <dialog> without the `open` attribute is display:none from the UA
   stylesheet, so the viewer's closed state — which is its state at first paint
   on every product page — needs no rule of this theme's at all. The ONE rule
   that does affect first paint, .pdp-gallery__open, is in critical.css beside
   the gallery it sizes, and tests/php/critical-css.test.php pins both halves of
   that split.

   WHITE, NOT BLACK. Every other lightbox on the internet dims to a dark scrim;
   this one opens onto the same pure #ffffff the product page is already on,
   with the same ink controls and the same zero radius, because that is the
   brief and because these photographs are shot on white — a dark surround
   would show a bright rectangle with a hard seam rather than the garment.
   ========================================================================== */

.viewer {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--c-ground);
  color: var(--c-ink);
  /* No horizontal page overflow can come from here: the dialog is fixed to the
     viewport's own edges rather than sized in vw, which is the classic way an
     overlay gains a scrollbar's width and pushes the page sideways. */
  overflow: hidden;
}

/* Scoped to [open], like the drawer: an unconditional display would beat the
   UA's `dialog:not([open]) { display: none }` and paint the viewer over every
   product page. The bar is a row of its own rather than an overlay, so it can
   never cover the top of a photograph. */
.viewer[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}
.viewer::backdrop { background: var(--c-ground); }

.viewer__bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s-1);
  padding: var(--s-2) var(--gutter);
  border-bottom: 1px solid var(--c-ink-15);
  background: var(--c-ground);
}

/* "2 / 4". Tabular figures so the row does not twitch as the number changes. */
.viewer__count {
  margin: 0 auto 0 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  font-variant-numeric: tabular-nums;
}

.viewer__btn {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--c-ink);
  cursor: pointer;
}
.viewer__btn:hover { background: var(--c-ink-08); }
.viewer__btn svg { width: 20px; height: 20px; }
/* The zoom toggle's pressed state, in ink. The button carries aria-pressed, so
   this is the visible half of a state assistive tech already hears. */
.viewer__btn[aria-pressed="true"] { background: var(--c-ink); color: var(--c-ground); }

/* THE TRACK IS A NATIVE SCROLL CONTAINER, one slide per view, snapping. That is
   the whole of "move through them one at a time" on a touch screen: a swipe is
   the browser's own scrolling, with the browser's own momentum. The chevrons and
   the arrow keys call scrollTo() into the same mechanism, so there is exactly one
   notion of where the viewer is. */
.viewer__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  min-height: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.viewer__track::-webkit-scrollbar { display: none; }

/* FLEX, NOT GRID, and it was grid until a test caught it. A grid track is sized
   from its item's max-content contribution, so the 1200px-tall photograph sized
   a 1200px row inside an 800px slide and the slide was scrollable before anyone
   had zoomed anything — the `max-height: 100%` below then shrank the image
   inside a track that had already overflowed. A flex item's max-height resolves
   against the container's own definite height, so the photograph fits, and the
   slide only becomes scrollable when nav.js actually magnifies it. */
.viewer__slide {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: var(--s-3);
  overflow: hidden;
  scroll-snap-align: center;
  cursor: zoom-in;
}
.viewer__img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* A dragged ghost image would fight the pan. */
  user-select: none;
  -webkit-user-drag: none;
}

/* ZOOMED. The slide becomes the scroll container and the image is made bigger
   than it, so panning is native scrolling — a touch drag, a trackpad, a wheel and
   the arrow keys all work, and none of them is implemented here. The 2x width
   itself is set inline by nav.js, because it is 2x of the FITTED size and only
   the browser knows what that came to. */
.viewer.is-zoomed .viewer__track { overflow-x: hidden; scroll-snap-type: none; }
/* `safe` matters here: once the photograph is bigger than the slide, plain
   centring pushes its top-left corner out of the scrollable area entirely and no
   amount of panning can reach it. */
.viewer.is-zoomed .viewer__slide {
  overflow: auto;
  align-items: safe center;
  justify-content: safe center;
  cursor: grab;
}
.viewer.is-zoomed .viewer__slide:active { cursor: grabbing; }
.viewer.is-zoomed .viewer__img { max-width: none; max-height: none; height: auto; }

.viewer__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--c-ink-15);
  border-radius: 0;
  background: var(--c-ground);
  color: var(--c-ink);
  cursor: pointer;
}
.viewer__nav:hover:not([disabled]) { background: var(--c-ink); color: var(--c-ground); }
.viewer__nav svg { width: 20px; height: 20px; }
/* Disabled at the ends rather than wrapping, and dimmed so the control tells the
   truth about whether there is anything left in that direction — the same rule,
   and the same treatment, as the gallery chevrons behind the dialog. */
.viewer__nav[disabled] { opacity: 0.3; cursor: default; }
.viewer__nav--prev { left: var(--s-3); }
.viewer__nav--next { right: var(--s-3); }

/* Scroll lock. showModal() puts the viewer in the top layer and makes the rest
   of the document inert, but it does not stop the page behind from scrolling.
   Added to <html> by nav.js on open and removed on close, exactly as the drawer
   does — a second class rather than a shared one, so closing either cannot
   unlock the page while the other is still open. */
.is-viewer-open { overflow: hidden; }

/* ==========================================================================
   Search (search.php, searchform.php)

   IN app.css, NOT critical.css, and the search FORM sits above the fold on
   /?s=… — so this is a deliberate trade rather than an oversight. critical.css
   is inlined into every response on the site; buying a flash-free first paint
   on the one transitional page a visitor passes through would tax the homepage,
   the shop, every product page and the bag with rules none of them use. The
   result grid itself needs nothing here: it is .shop-head, .shop-grid, .card
   and .shop-empty, which the shop archive already put in the inlined sheet.
   tests/php/critical-css.test.php asserts this split in both directions.
   ========================================================================== */

/* The last row of cards must not sit flush against the black footer. .wrap has
   no block padding of its own and woocommerce/archive-product.php gets away
   with it because a paginated archive ends in .shop-pagination — a search
   result set that fits on one page ends in a row of Add-to-bag buttons, and
   they weld themselves to the footer's top edge. Scoped to this template
   rather than fixed on .shop-grid, which is in the inlined sheet. */
.search-wrap { padding-bottom: clamp(3rem, 6vw, 5rem); }

/* The head is the archive's .shop-head plus a form, so only the gap is new. */
.search-head .search-form { margin-top: var(--s-5); }
.search-head .shop-head__count { margin-top: var(--s-5); }

/* --------------------------------------------------------------------------
   One field and its submit sharing a single 1px outline. The input's right
   border is removed rather than the button's left, so the two never double up
   into a 2px seam, and the button's own border keeps the outline continuous.
   -------------------------------------------------------------------------- */

.search-form {
  display: flex;
  align-items: stretch;
  max-width: 36rem;
}

.search-form__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 52px;
  padding: 0 var(--s-4);
  background: var(--c-ground);
  color: var(--c-ink);
  border: 1px solid var(--c-ink);
  border-right: 0;
  border-radius: 0;
  font: inherit;
  /* 16px, not --t-base's 14px: iOS Safari zooms the viewport on focus for any
     font-size below 16px, and a search field is the one input on this site a
     phone user always focuses. */
  font-size: var(--t-md);
  -webkit-appearance: none;
  appearance: none;
}
.search-form__input::placeholder { color: var(--c-ink-45); opacity: 1; }
/* Safari's own clear button is a rounded grey pill; the rest of this theme has
   no rounded anything, and the field is one Escape away from empty. */
.search-form__input::-webkit-search-cancel-button { -webkit-appearance: none; }
/* The global ring is outside-offset, which would be cut off by the button
   sitting flush against the field. Inset instead, so it stays fully visible. */
.search-form__input:focus-visible { outline-offset: -3px; }

.search-form__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  flex: none;
  min-height: 52px;
  padding-inline: var(--s-5);
  background: var(--c-ink);
  color: var(--c-ground);
  border: 1px solid var(--c-ink);
  border-radius: 0;
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.search-form__submit:hover { background: transparent; color: var(--c-ink); }
.search-form__icon { flex: none; }

/* At 320px the word and the glyph together leave the field about 120px wide,
   which is not enough to read a query back. The word is HIDDEN, not deleted:
   the button's accessible name is that span, and display:none would leave the
   submit unnamed. */
@media (max-width: 419px) {
  .search-form__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .search-form__submit { padding-inline: var(--s-4); }
}

/* Products matched nothing but a page did. A note, not an empty state. */
.search-none {
  padding-block: var(--s-5);
  color: var(--c-ink-70);
}

/* --------------------------------------------------------------------------
   Pages and posts — the clearly separated second section.

   The rule above it and the eyebrow-scale heading are what make it read as a
   different kind of answer rather than as more results. It is always below the
   grid; nothing here can reorder it.
   -------------------------------------------------------------------------- */

.search-pages {
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-ink-15);
}

.search-pages__title {
  font-size: var(--t-sm);
  letter-spacing: var(--tracking-wider);
  color: var(--c-ink-45);
}

.search-pages__list {
  display: grid;
  gap: var(--s-5);
  max-width: 68ch;
  margin-top: var(--s-5);
}

.search-pages__item { display: grid; gap: var(--s-1); }

.search-pages__link {
  font-family: var(--f-display);
  font-size: var(--t-md);
  font-weight: 700;
  letter-spacing: -0.01em;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.search-pages__link:hover { text-decoration-thickness: 2px; }

.search-pages__excerpt { color: var(--c-ink-70); }

/* --------------------------------------------------------------------------
   The dead end's way out. Category chips, not a sentence of links: they are
   the same square outline as everything else that can be pressed here, and at
   320px they wrap into a column without any of them being clipped.
   -------------------------------------------------------------------------- */

.search-empty { max-width: 52ch; }

.search-suggest__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.search-suggest__link {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding-inline: var(--s-4);
  border: 1px solid var(--c-ink-15);
  border-radius: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.search-suggest__link:hover {
  background: var(--c-ink);
  border-color: var(--c-ink);
  color: var(--c-ground);
}

/* ==========================================================================
   THE ACCOUNT PAGE (woocommerce/myaccount/*)

   Two screens behind one URL. Signed out it is sign-in beside join; signed in it
   is navigation beside a dashboard. The shared element layer above already owns
   the fields, the buttons and the notices on both, so what is here is only
   layout and the two components that are the account page's own — the benefit
   list and the dashboard tiles.

   In app.css, not critical.css, and that is a deliberate trade rather than an
   oversight. critical.css is inlined into EVERY response on the site; the
   account page is one screen a shopper visits once, and buying it a flash-free
   first paint would tax the homepage, the shop and every product page with rules
   none of them use. The <h1> is the exception and is already covered —
   page.php gives it .t-page-title, which is a critical.css rule.
   ========================================================================== */

.account { padding-bottom: clamp(3rem, 7vw, 6rem); }

/* The heading is .t-page-title's, but the account page's own lede sits under it,
   so its bottom padding closes up. */
.account__title { padding-bottom: var(--s-5); }

/* --------------------------------------------------------------------------
   Signed out: sign in beside join.

   ONE COLUMN BY DEFAULT, and the modifier adds the second. When registration is
   off, form-login.php does not emit the join panel and does not add
   .account-auth--split, so the grid stays single and centred — the reference's
   own shape — rather than leaving a 50% hole where a column would have been.
   -------------------------------------------------------------------------- */

.account-auth {
  display: grid;
  gap: clamp(2.5rem, 6vw, 4rem);
  max-width: 30rem;
}

@media (min-width: 900px) {
  .account-auth--split {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 60rem;
    /* The rule between the columns is the divider; no boxes, no cards. */
    gap: 0;
  }
  .account-auth--split .account-panel:first-child { padding-right: clamp(2.5rem, 5vw, 4rem); }
  .account-auth--split .account-panel--join {
    padding-left: clamp(2.5rem, 5vw, 4rem);
    border-left: 1px solid var(--c-ink-15);
  }
}

/* Below 900px the two panels stack, so the divider becomes horizontal. */
@media (max-width: 899px) {
  .account-auth--split .account-panel--join {
    padding-top: clamp(2.5rem, 6vw, 4rem);
    border-top: 1px solid var(--c-ink-15);
  }
}

.account-panel__title { font-size: var(--t-xl); }

.account-panel__lede {
  max-width: 42ch;
  margin-top: var(--s-3);
  color: var(--c-ink-70);
}

.account-panel__note {
  margin-top: var(--s-4);
  font-size: var(--t-base);
  color: var(--c-ink-70);
}

.account-benefits {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-5);
}
.account-benefits__item {
  display: grid;
  grid-template-columns: 1rem 1fr;
  gap: var(--s-3);
  align-items: start;
  font-size: var(--t-base);
  color: var(--c-ink-70);
}
/* A filled square, not a tick glyph and not a bullet: the same hard 8px block
   the sale badge and the pagination's current page are made of. */
.account-benefits__item::before {
  content: "";
  width: 8px;
  height: 8px;
  margin-top: 0.55em;
  background: var(--c-ink);
}

.account-form { margin-top: var(--s-6); }

/* "Keep me signed in" and the reset link share a row — the reference puts the
   reset link with the fields rather than under the button, because a visitor
   looking for it is looking at the password field. */
.account-form__aside {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-5);
}
.account-form__link {
  font-size: var(--t-base);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.account-form__link:hover { text-decoration-thickness: 2px; }

.account-form__submit-row { margin: 0; }

/* Full-width, as in the reference. The shared element layer gives it the black
   square; only the width is the account page's own decision. */
.woocommerce .account-form__submit,
.account-form__submit { width: 100%; }

/* --------------------------------------------------------------------------
   Signed in: navigation beside the dashboard.
   -------------------------------------------------------------------------- */

.account-dash {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
}

@media (min-width: 900px) {
  /* A FIXED first track, not minmax(0, 15rem): with an fr track beside it, a
     minmax whose min is 0 is maximized only out of the free space the fr has
     already taken. Measured in the browser, not reasoned about. */
  .account-dash { grid-template-columns: 15rem minmax(0, 1fr); }
}

/* woocommerce-layout.css floats the two halves and gives them percentage widths
   — `float:left; width:30%` and `float:right; width:68%`. Inside a grid the
   floats are ignored but the widths are not, so the 240px nav track held a 72px
   nav and "DASHBOARD" was clipped to "DASHBO". Measured: 30% of 240 is 72.
   (0,2,0), matched exactly. */
.woocommerce-account .woocommerce-MyAccount-navigation,
.woocommerce-account .woocommerce-MyAccount-content {
  float: none;
  width: auto;
}

.account-nav__list { border-top: 1px solid var(--c-ink-15); }

.account-nav__link {
  display: block;
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--c-ink-15);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.account-nav__link:hover { background: var(--c-ground-alt); }

/* The current route is the one row that is filled — the same device
   .shop-pagination uses, so it reads without depending on colour alone. */
.account-nav__item.is-active > .account-nav__link,
.account-nav__link[aria-current="page"] {
  background: var(--c-ink);
  color: var(--c-ground);
  border-bottom-color: var(--c-ink);
}

.account-hello {
  font-size: var(--t-lg);
  line-height: 1.4;
}
.account-hello__out {
  display: inline-block;
  margin-left: var(--s-3);
  font-size: var(--t-base);
  color: var(--c-ink-70);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.account-hello__out:hover { color: var(--c-ink); }

.account-tiles {
  display: grid;
  gap: 1px;
  margin-top: var(--s-6);
}
@media (min-width: 600px) {
  /* TWO columns, fixed, not auto-fit. Four tiles in a three-wide track is a full
     row and a widow, and with the rules drawn below the widow's empty cell was a
     visible grey rectangle. Two columns is 2x2 at every width that has room. */
  .account-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.account-tile {
  display: grid;
  align-content: start;
  gap: var(--s-2);
  height: 100%;
  padding: var(--s-5);
  background: var(--c-ground);
  /* THE RULES ARE A SHADOW, not a border and not a background showing through
     the gap. A 1px gap over a grey container paints the grey wherever a cell is
     EMPTY, which is what the fourth tile's missing neighbour looked like. A 1px
     spread shadow belongs to the tile instead, so an absent tile draws nothing,
     and adjacent shadows meet inside the 1px gap as a single rule rather than
     doubling to 2px. */
  box-shadow: 0 0 0 1px var(--c-ink-15);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.account-tile:hover { background: var(--c-ink); color: var(--c-ground); }
.account-tile__title { font-size: var(--t-md); }
.account-tile__text { font-size: var(--t-base); color: var(--c-ink-70); }
.account-tile:hover .account-tile__text { color: var(--c-ground); }

/* WooCommerce's own tables — the orders list, the saved addresses — are the one
   part of these routes this theme does not template. Given edges rather than
   rebuilt: square, ruled, and Archivo. */
.woocommerce-MyAccount-content table.shop_table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--c-ink-15);
  border-radius: 0;
  font-size: var(--t-base);
}
.woocommerce-MyAccount-content table.shop_table th,
.woocommerce-MyAccount-content table.shop_table td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--c-ink-15);
  text-align: left;
}
.woocommerce-MyAccount-content table.shop_table th {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
/* A table is the one thing on these pages that cannot be made narrower than its
   content, so it scrolls inside itself rather than pushing the page sideways. */
.woocommerce-MyAccount-content .woocommerce-orders-table__wrapper,
.woocommerce-MyAccount-content .woocommerce-MyAccount-orders-wrapper { overflow-x: auto; }

.woocommerce-MyAccount-content h2,
.woocommerce-MyAccount-content h3 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--t-lg);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin-bottom: var(--s-4);
}
.woocommerce-MyAccount-content fieldset { border: 1px solid var(--c-ink-15); padding: var(--s-5); margin: var(--s-6) 0; }
.woocommerce-MyAccount-content legend { padding-inline: var(--s-2); font-size: var(--t-sm); font-weight: 700; letter-spacing: var(--tracking-wide); text-transform: uppercase; }

/* ==========================================================================
   CHECKOUT (page-checkout.php, woocommerce/checkout/*)

   IN app.css AND NOT critical.css, which is the opposite of the call the bag
   page made, so it is worth saying why rather than leaving it to look like an
   oversight.

   The rule this project has followed four times — the card, the product page's
   buy column, the collection banner, the bag — is "the first screen of a route
   goes in the inlined sheet". Checkout is the one route where the other side of
   that trade wins:

     - critical.css is inlined in the HTML of EVERY page, including the many
       thousands of views that will never reach checkout. The bag page's rules
       already cost that; the longest form on the site would cost several times
       more, on the homepage.
     - /checkout/ is the only route on this store that CANNOT be a landing page.
       There is no link to it from anywhere but the bag, an empty bag redirects
       away from it, and reaching it means having loaded at least two other
       pages first — so app.css is a versioned, already-cached file by the time
       it is asked for, and it is not a second request at all.

   WHAT IS NOT HERE, deliberately: the fields, the labels, the buttons, the
   notices and the bordered form box. Those are the SHARED ELEMENT LAYER at the
   top of this file, which already claims them at WooCommerce's own specificity,
   and every input on this page is themed by it before a single rule below runs.
   The rules here are the route's own composition — two columns, a list of order
   lines, a payment chooser, a thank-you page — and nothing below re-states a
   border, a radius or a font size the shared layer has already set.
   ========================================================================== */

.checkout-wrap { padding-bottom: clamp(2.5rem, 6vw, 5rem); }
.checkout-title { font-size: var(--t-2xl); padding-block: clamp(1.5rem, 4vw, 3rem) var(--s-5); }

/* --------------------------------------------------------------------------
   The discount disclosure, above the form.

   .detail is the product page's collapsible rule and is already defined above;
   only the top border is added, because a single <details> has a bottom rule
   from .detail and would otherwise hang off nothing.
   -------------------------------------------------------------------------- */

.checkout-coupon { border-top: 1px solid var(--c-ink-15); margin-bottom: var(--s-6); }

.checkout-coupon__form { display: grid; gap: var(--s-3); }

@media (min-width: 480px) {
  /* The field takes the room and the button takes its own width. auto rather
     than a fixed track, so a translated "Apply" is never clipped. */
  .checkout-coupon__form { grid-template-columns: minmax(0, 1fr) auto; align-items: start; }
}

.checkout-coupon__submit {
  min-height: 52px;
  padding-inline: var(--s-5);
  background: var(--c-ink);
  color: var(--c-ground);
  border: 1px solid var(--c-ink);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.checkout-coupon__submit:hover { background: var(--c-ground); color: var(--c-ink); }

/* --------------------------------------------------------------------------
   The two columns.

   Same 900px hinge and the same source order as the bag page: the work first,
   the summary second, so the reading order and the tab order match the visual
   one at both widths. The aside sticks only where there is height to stick in —
   a sticky panel on a short laptop screen scrolls its own payment box out of
   reach, which is worse than not sticking.
   -------------------------------------------------------------------------- */

.checkout-grid { display: grid; gap: var(--s-6); align-items: start; }

@media (min-width: 900px) {
  .checkout-grid {
    grid-template-columns: minmax(0, 1fr) minmax(20rem, 26rem);
    gap: clamp(2rem, 4vw, 4.5rem);
  }
}

@media (min-width: 900px) and (min-height: 800px) {
  .checkout-aside {
    position: sticky;
    /* Clear of the fixed header, plus a gutter. */
    top: calc(var(--header-h) + var(--s-4));
  }
}

.checkout-aside__title { font-size: var(--t-lg); margin-bottom: var(--s-4); }

.checkout-login-required { font-size: var(--t-md); }

/* --------------------------------------------------------------------------
   The fields.

   .checkout-fields__grid is a real grid, and WooCommerce's float pairing has
   already been switched off in the shared layer above. First/last sit side by
   side from 600px; everything else spans both tracks. The `p.form-row` elements
   are grid ITEMS here, so their own `display: grid` from the shared layer still
   lays the label over the field inside each one.
   -------------------------------------------------------------------------- */

.checkout-fields + .checkout-fields,
.woocommerce-shipping-fields,
.woocommerce-additional-fields { margin-top: var(--s-6); }

/* "Additional information" is core's own <h3>, printed by form-shipping.php
   above the order-notes field, and there is no template override that reaches
   it without forking a fifth file. It is given the same treatment as this
   theme's own section headings so the left column reads as two sections and not
   as one section plus a stray line of body copy. */
.checkout-fields__title,
.woocommerce-additional-fields h3,
.woocommerce-shipping-fields h3:not(#ship-to-different-address) {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--t-lg);
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0 0 var(--s-5);
}

.checkout-fields__grid,
.woocommerce-shipping-fields__field-wrapper,
.woocommerce-additional-fields__field-wrapper { display: grid; column-gap: var(--s-4); }

@media (min-width: 600px) {
  .checkout-fields__grid,
  .woocommerce-shipping-fields__field-wrapper,
  .woocommerce-additional-fields__field-wrapper { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .woocommerce .checkout-fields__grid > .form-row-wide,
  .woocommerce .woocommerce-shipping-fields__field-wrapper > .form-row-wide,
  .woocommerce .woocommerce-additional-fields__field-wrapper > .form-row-wide,
  .woocommerce .checkout-fields__grid > .notes,
  .woocommerce .woocommerce-additional-fields__field-wrapper > .notes { grid-column: 1 / -1; }
}

/* The country row on a store that ships one country is a select with one option
   in it. It stays — WooCommerce needs the field to exist for the state select to
   rebind — but it does not need to be the widest thing on the form. */
.woocommerce .checkout-fields__grid > #billing_country_field,
.woocommerce .checkout-fields__grid > #billing_state_field { grid-column: auto; }

/* Woo's per-field hint, printed under the input by `description`. This is where
   "We call before delivery." lands, and it is the sentence that explains why a
   phone number is required on a cash-on-delivery order. */
.woocommerce form .form-row .description {
  font-size: var(--t-xs);
  line-height: 1.5;
  color: var(--c-ink-45);
}

/* --------------------------------------------------------------------------
   The order review.

   The same list-of-rows shape as the bag, one size smaller: this is a summary
   of a decision already made, not a place to change it. No stepper, no remove
   link, no minimum width — see the note in review-order.php.
   -------------------------------------------------------------------------- */

.order-review { border-top: 1px solid var(--c-ink-15); }

.order-review__lines { display: grid; }

.order-line {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: var(--s-3);
  align-items: start;
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--c-ink-15);
}

.order-line__media { position: relative; aspect-ratio: 3 / 4; background: var(--c-ground-alt); }

/* THE COMPOUND FORM, and it is the same trap T9 hit on the collection banner.
   woocommerce-layout.css ships `.woocommerce img, .woocommerce-page img { height:
   auto }` at (0,1,1), and the body of every checkout carries `woocommerce`. A
   single-class `.order-line__img` is (0,1,0) and loses silently, which lays the
   thumbnail out at its own 2:3 and leaves a band of empty ground under every
   line in the order summary. Measured on the live site before it was written
   this way. */
.order-line__media .order-line__img { width: 100%; height: 100%; object-fit: cover; }
.order-line__ph { display: block; width: 100%; height: 100%; background: var(--c-ground-alt); }

/* The quantity over the corner of the thumbnail rather than in a column of its
   own — the column is what gives a summary row a minimum width. Ink on white
   with an ink rule, so it reads at 320px against a pale photograph. */
.order-line__qty {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 20px;
  height: 20px;
  padding-inline: 4px;
  display: grid;
  place-items: center;
  background: var(--c-ink);
  color: var(--c-ground);
  font-size: var(--t-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.order-line__body { display: grid; gap: var(--s-1); min-width: 0; }

.order-line__name {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  /* A product name with no spaces would otherwise set this track's minimum and
     scroll the page sideways at 320px. */
  overflow-wrap: anywhere;
}

.order-line__variant {
  font-size: var(--t-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--c-ink-45);
}

.order-line__price {
  font-size: var(--t-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The delivery-method table. It is a <table> because wc_cart_totals_shipping_html()
   emits a bare <tr> — see review-order.php — so the table furniture is stripped
   back to a plain block and the radios inside it are laid out as a list. */
.order-review__shipping { width: 100%; border-collapse: collapse; }
.order-review__shipping th,
.order-review__shipping td {
  display: block;
  width: auto;
  padding: 0;
  text-align: left;
}
.order-review__shipping tr { display: block; padding-block: var(--s-4); border-bottom: 1px solid var(--c-ink-15); }
.order-review__shipping th {
  margin-bottom: var(--s-2);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--c-ink-45);
}
.woocommerce-shipping-methods { display: grid; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.woocommerce-shipping-methods label { display: inline-flex; gap: var(--s-2); font-size: var(--t-base); }
.woocommerce-shipping-destination { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-ink-45); }

/* The totals. A <dl>, laid out as rows rather than as a table — see
   review-order.php for why this page has no shop_table in it. */
.order-review__totals { display: grid; gap: var(--s-2); margin: var(--s-4) 0 0; }

.order-review__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  font-size: var(--t-base);
}
.order-review__row dt,
.order-review__row dd { margin: 0; }
.order-review__row dd { font-variant-numeric: tabular-nums; white-space: nowrap; }
.order-review__row--discount { color: var(--c-sale); }

.order-review__row--total {
  margin-top: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-ink);
  font-size: var(--t-md);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   How you pay.

   Two offline methods and no cards, so the choice IS the payment step. Each
   option is a full-width target with the gateway's own description inside it,
   and the chosen one is filled in ink — the same "current" treatment the size
   buttons and the paginator already use.

   THE RADIO IS NOT DISPLAY:NONE. It is a real, focusable radio moved off-screen,
   with the visible mark drawn by .pay-option__mark and the focus ring drawn on
   the label — because checkout.js reads `:checked` on it and a keyboard shopper
   has to be able to see where they are.
   -------------------------------------------------------------------------- */

/* THE FOUR RULES THAT HAVE TO BE BEATEN BY ID, and the only place in this
   theme's CSS where an id appears in a selector.

   woocommerce.css writes the whole payment area as `.woocommerce-checkout
   #payment …` — a BODY class plus an id, (1,1,0) and up. `.checkout-payment` is
   (0,1,0) and loses every one of them, which is why the box arrived as a
   rounded lavender panel (rgba(129,110,153,.14)) with a lavender speech-bubble
   under the selected method: measured on the live site, not assumed.

   Each selector below is core's own, character for character, so the cascade
   decides on source order — app.css is enqueued after woocommerce.css — and
   nothing is escalated past it. Every property is one core sets on that same
   selector; nothing new is introduced here. */
.woocommerce-checkout #payment {
  background: transparent;
  border-radius: 0;
}
.woocommerce-checkout #payment ul.payment_methods {
  padding: 0;
  margin: 0;
  border-bottom: 0;
}
.woocommerce-checkout #payment ul.payment_methods li {
  line-height: 1.3;
}
.woocommerce-checkout #payment div.form-row {
  padding: 0;
}
.woocommerce-checkout #payment div.payment_box {
  width: auto;
  margin: 0;
  font-size: var(--t-base);
  border-radius: 0;
  background-color: var(--c-ground-alt);
  color: var(--c-ink);
}
/* Core's speech-bubble arrow, pointed at the method above the box. This theme
   has no bubbles and no diagonals; the box is a ruled continuation of the row,
   so the triangle is removed rather than recoloured. (1,1,3), matched. */
.woocommerce-checkout #payment div.payment_box::before {
  display: none;
}

.checkout-payment { margin-top: var(--s-6); }
.checkout-payment__title { font-size: var(--t-md); margin-bottom: var(--s-4); }

.checkout-payment__list { display: grid; gap: var(--s-3); list-style: none; margin: 0; padding: 0; }

.pay-option { position: relative; border: 1px solid var(--c-ink-15); }

.pay-option__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
}

.pay-option__label {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  cursor: pointer;
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.pay-option__mark {
  flex: none;
  width: 16px;
  height: 16px;
  border: 1px solid var(--c-ink);
  /* A square, like everything else here. The fill is an inset box-shadow rather
     than a pseudo-element so there is one thing to switch on :checked. */
  box-shadow: inset 0 0 0 0 var(--c-ink);
  transition: box-shadow var(--dur) var(--ease);
}

/* The sibling combinator, not :has() — the input precedes the label, so this
   works in every browser that can render a radio at all. */
.pay-option__input:checked + .pay-option__label { background: var(--c-ink); color: var(--c-ground); }
.pay-option__input:checked + .pay-option__label .pay-option__mark {
  border-color: var(--c-ground);
  box-shadow: inset 0 0 0 3px var(--c-ground);
}
.pay-option__input:focus-visible + .pay-option__label {
  outline: 2px solid var(--c-ink);
  outline-offset: 3px;
}
.pay-option:hover { border-color: var(--c-ink); }

/* The gateway's own text — for PayHere, the line naming the cards and wallets
   it carries plus its sandbox warning. Woo ships this as a 1em-padded box with
   a 2px radius and a grey background; it is a ruled continuation of the row
   instead. */
.pay-option__box {
  padding: var(--s-4);
  border-top: 1px solid var(--c-ink-15);
  background: var(--c-ground-alt);
  font-size: var(--t-base);
  line-height: 1.55;
}
.pay-option__box p { margin: 0; }
.pay-option__box p + p { margin-top: var(--s-3); }
.pay-option__box ul { margin: var(--s-2) 0 0; padding-left: var(--s-4); }

.checkout-payment__none { list-style: none; }

/* --------------------------------------------------------------------------
   Place order.
   -------------------------------------------------------------------------- */

.checkout-place { display: grid; gap: var(--s-4); margin-top: var(--s-5); }

.checkout-place__btn {
  width: 100%;
  min-height: 56px;
  padding-inline: var(--s-5);
  background: var(--c-ink);
  color: var(--c-ground);
  border: 1px solid var(--c-ink);
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.checkout-place__btn:hover { background: var(--c-ground); color: var(--c-ink); }

/* The scripts-off "Update totals" button and the sentence above it. Ghost, not
   ink: it is not the action of the page, and a second black block above Place
   order would read as two equal choices. */
.checkout-place noscript { display: block; font-size: var(--t-base); line-height: 1.55; }
.checkout-place__update {
  min-height: 44px;
  margin-top: var(--s-3);
  padding-inline: var(--s-4);
  background: var(--c-ground);
  color: var(--c-ink);
  border: 1px solid var(--c-ink);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.woocommerce-terms-and-conditions-wrapper { font-size: var(--t-base); }
.woocommerce-terms-and-conditions-wrapper p { margin: 0; color: var(--c-ink-45); }
.woocommerce-terms-and-conditions-wrapper a { text-decoration: underline; text-underline-offset: 2px; }

/* WooCommerce's blockUI overlay, thrown over #order_review while
   update_order_review is in flight.

   ITS BOX IS AN INLINE STYLE that jQuery blockUI writes on the element, so no
   stylesheet can reach it without !important and this section does not use
   !important — that box is a white fill at 0.6, which is this theme's ground
   anyway, so there is nothing to fix. What IS a stylesheet rule is the spinner:
   `.blockUI.blockOverlay::before` at (0,2,1) with `background: url(loader.svg)`,
   a third party's grey graphic loaded from the plugin's own directory. Replaced
   at the same (0,2,1) with a rotating ink square — no request, no radius. */
.woocommerce .blockUI.blockOverlay::before {
  height: 1em;
  width: 1em;
  background: none;
  border: 2px solid var(--c-ink);
  border-right-color: transparent;
  border-radius: 0;
}

/* --------------------------------------------------------------------------
   THE THANK-YOU PAGE (checkout/thankyou.php, order-received.php, and the two
   core sections that print under them).

   The last thing a customer sees, and the one page on the site whose job is
   purely reassurance. Everything here is at reading size rather than at the
   compressed summary scale of the aside — there is one column and no hurry.
   -------------------------------------------------------------------------- */

.checkout-wrap--received { max-width: 60rem; }

.order-done { display: grid; gap: var(--s-6); }

.order-received__lede {
  margin: 0;
  font-size: var(--t-lg);
  line-height: 1.35;
  max-width: 34ch;
  text-wrap: balance;
}

/* The four facts. A definition list laid out as ruled rows: label left, value
   right on a wide screen, stacked under 420px where a payment method name
   beside its label has nowhere to go. */
.order-facts { display: grid; margin: 0; border-top: 1px solid var(--c-ink); }

.order-facts__row {
  display: grid;
  gap: var(--s-1);
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--c-ink-15);
}

@media (min-width: 420px) {
  .order-facts__row {
    grid-template-columns: minmax(8rem, 12rem) minmax(0, 1fr);
    gap: var(--s-4);
    align-items: baseline;
  }
}

.order-facts__row dt {
  margin: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--c-ink-45);
}
.order-facts__row dd {
  margin: 0;
  font-size: var(--t-base);
  font-weight: 700;
  overflow-wrap: anywhere;
}

/* The gateway's own after-the-fact block. Empty for PayHere, which says its
   piece through the order-received sentence instead; still carries the account
   details on the bank-transfer orders placed before the model changed. Hence
   :not(:empty) — an empty box with a border is worse than no box. */
.order-done__gateway:not(:empty) {
  padding: var(--s-5);
  background: var(--c-ground-alt);
  border-left: 3px solid var(--c-ink);
  font-size: var(--t-base);
  line-height: 1.55;
}
.order-done__gateway p { margin: 0 0 var(--s-3); }
.order-done__gateway p:last-child { margin-bottom: 0; }

.wc-bacs-bank-details-heading,
.wc-bacs-bank-details-account-name {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: var(--s-4) 0 var(--s-2);
}
.wc-bacs-bank-details-heading { font-size: var(--t-md); }
.wc-bacs-bank-details-account-name { font-size: var(--t-base); }

/* THE BANK DETAILS ARE A `ul.order_details`, which is why these selectors look
   nothing like the class the gateway names them by. woocommerce.css floats every
   `ul.order_details li` left with a `1px dashed #cfc8d8` right border and a 2em
   gutter, and blocks out the `<strong>` inside it at 1.4em — a horizontal strip
   of dashed-off cells built for the four facts on core's own thank-you page. On
   an account name, a bank, an account number and a branch code it produced four
   floated columns of dashed boxes, each with its label and value on separate
   lines. Both selectors are core's exactly — (0,2,2) and (0,2,3). */
.woocommerce ul.order_details {
  display: grid;
  gap: var(--s-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.woocommerce ul.order_details li {
  float: none;
  margin: 0;
  padding: 0;
  border-right: 0;
  font-size: var(--t-base);
  line-height: 1.5;
  text-transform: none;
  overflow-wrap: anywhere;
}
.woocommerce ul.order_details li strong {
  display: inline;
  font-size: inherit;
  line-height: inherit;
}

.order-done__failed { margin: 0; font-size: var(--t-lg); color: var(--c-sale); }
.order-done__none { margin: 0; font-size: var(--t-md); max-width: 44ch; }

.order-done__actions { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; margin: 0; }

.order-done__more { margin: 0; }
.order-done__link {
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* The two CORE sections under it: the order table and the addresses. Not forked
   into theme templates — see the note in thankyou.php — so they are claimed here
   at the plugin's own specificity, exactly as the shared element layer does.
   `.woocommerce table.shop_table` is (0,2,1) in woocommerce.css, and that is
   what is matched. */
.woocommerce-order-details__title,
.woocommerce-column__title,
.woocommerce-customer-details h2 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--t-lg);
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0 0 var(--s-4);
}

.woocommerce table.shop_table {
  width: 100%;
  border: 0;
  border-top: 1px solid var(--c-ink);
  border-collapse: collapse;
  border-radius: 0;
  margin: 0;
}
.woocommerce table.shop_table th,
.woocommerce table.shop_table td {
  padding: var(--s-3) 0;
  border: 0;
  border-bottom: 1px solid var(--c-ink-15);
  text-align: left;
  font-size: var(--t-base);
  overflow-wrap: anywhere;
}
.woocommerce table.shop_table thead th,
.woocommerce table.shop_table tfoot th {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.woocommerce table.shop_table td:last-child,
.woocommerce table.shop_table th:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* (0,2,3), not (0,2,2): core writes `.woocommerce table.shop_table tbody th,
   … tfoot td, … tfoot th { border-top: 1px solid … }`, which beats the (0,2,2)
   rule above and would draw a rule across the top of every totals row. Matched
   exactly rather than escalated. */
.woocommerce table.shop_table tbody th,
.woocommerce table.shop_table tfoot td,
.woocommerce table.shop_table tfoot th {
  border-top: 0;
  font-weight: 700;
}
.woocommerce table.shop_table tfoot tr:last-child th,
.woocommerce table.shop_table tfoot tr:last-child td {
  border-bottom: 0;
  border-top: 1px solid var(--c-ink);
  font-weight: 800;
}

.woocommerce-customer-details { margin: 0; }
.woocommerce-columns--addresses { display: grid; gap: var(--s-6); }

@media (min-width: 600px) {
  .woocommerce-columns--addresses { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* (0,2,1), matched: core's is `.woocommerce .woocommerce-customer-details
   address`, a 5px-rounded box with 1px/2px asymmetric borders — the "bordered
   box floating in white space" the shared element layer already removed from
   the login and coupon forms. Same treatment, same reason. */
.woocommerce .woocommerce-customer-details address {
  padding: 0;
  border: 0;
  border-radius: 0;
  font-style: normal;
  font-size: var(--t-base);
  line-height: 1.6;
}

/* The phone and envelope are glyphs from WooCommerce's own icon font, painted
   through ::before with a negative indent. The shared element layer already
   dropped that font from the notices, on the grounds that the meaning is in the
   sentence and the font is a third party's; two lines of an address are no
   different. (0,3,2) for the ::before, (0,3,1) for the indent it compensates. */
.woocommerce .woocommerce-customer-details .woocommerce-customer-details--phone,
.woocommerce .woocommerce-customer-details .woocommerce-customer-details--email {
  padding-left: 0;
  margin: var(--s-2) 0 0;
  font-size: var(--t-base);
  overflow-wrap: anywhere;
}
.woocommerce .woocommerce-customer-details .woocommerce-customer-details--phone::before,
.woocommerce .woocommerce-customer-details .woocommerce-customer-details--email::before {
  content: none;
}
