/* ============================================================
   goen — application layer.

   base.css owns the tokens, the element defaults and the shared
   primitives. This file owns page composition and the responsive
   behaviour that turns the 1440 and 375 artboards into one document.
   Load it last.

   Breakpoints are written as literals, because a custom property is
   not valid in @media.
     md = 768px   layout goes multi-column
     lg = 1024px  the full desktop header fits on one row
   ============================================================ */

/* The blue is 2.4:1 against the body ink, short of the 3:1 that lets colour
   alone mark a link inside a sentence (WCAG 1.4.1), so a link keeps a light
   underline unless its component stands it apart from any sentence. */
a {
  color: var(--accent-text);
  text-decoration-color: color-mix(in srgb, currentcolor 40%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a:hover {
  color: var(--accent-text-hover);
  text-decoration-color: currentcolor;
}

/* An author display (a flex or grid class) must not undo hidden: a control
   that needs script stays away without it. */
[hidden] {
  display: none !important;
}

/* Motion a reader has asked not to see. The shared primitives carry
   transitions of their own, and this is where they are calmed. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Shell ----------

   The frame, and the one measurement the storefront shares: a content column
   that stops at 1376px inside a gutter of 16px to 48px. The back office keeps
   the measure base.css gives it.
   The `edges` comparison in check-layout's `PROBE` compares the header bar's,
   the home page's and the footer grid's content edges and fails when they
   disagree, so the three read that column from one pair of properties rather
   than each writing the numbers again.
   ------------------------------------------------------------------------ */

:root {
  --page-width: 1376px;
  --page-gutter: clamp(16px, 3.4vw, 48px);
  /* Read by the product grid and the footer grid once they are rebuilt. */
  --col-gap: clamp(16px, 1.7vw, 24px);
}

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

.goen-shell > main {
  flex: 1;
}

/* Off the canvas until it is focused, which is the one moment it has to be
   reachable. `check-layout.mjs:443-445` knows it parks at left:-9999px and
   measures only the right edge because of it. */
.goen-skip {
  background: var(--panel);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  font-size: var(--fs-14);
  left: -9999px;
  padding: 8px 16px;
  position: absolute;
  top: 0;
  z-index: var(--z-toast);
}

.goen-skip:focus {
  left: 8px;
  top: 8px;
}

/* Reachable by assistive technology, absent from the visual layout. */
.goen-sr-only {
  border: 0;
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* ---------- Promo banner ----------

   One row above the header and IN FLOW, not fixed: it is rendered on first
   paint and removed by a form, so it never moves the page after load. The bar
   carries the accent because it is the shop speaking in its own voice, and its
   dismiss control is a full 44px because `check-layout.mjs:1300` sweeps every
   button outside the header and the footer.
   ------------------------------------------------------------------------ */

.goen-promo {
  background: var(--n-900);
  color: var(--on-accent);
  display: block;
  font-size: var(--fs-14);
  margin: 0;
}

.goen-promo__bar {
  align-items: center;
  container-type: inline-size;
  display: flex;
  gap: 12px;
  justify-content: space-between;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 0 var(--page-gutter);
}

.goen-promo__link {
  align-items: center;
  color: inherit;
  display: flex;
  flex: 1;
  gap: 8px;
  min-width: 0;
  padding: 8px 0;
  text-decoration: none;
}

/* One line, clipped rather than wrapped: a two-line bar changes the page's
   height between one promotion and the next. */
.goen-promo__message {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Container rem follows the root font size, so this fires only when text is
   enlarged; a media query's rem would fire on every phone. */
@container (width < 20rem) {
  .goen-promo__message {
    overflow: visible;
    white-space: normal;
  }
}

/* Two copies of the copy, one shown at a time. The short version is different
   WRITING, not a truncation — which is why promo_banners carries both. */
.goen-promo__wide {
  display: none;
}

.goen-promo__narrow {
  display: inline;
}

/* The discount code reads as something to be copied rather than as part of the
   sentence, and user-select: all makes one tap take the whole of it. */
.goen-promo__code {
  background: var(--n-0);
  border-radius: var(--r-sm);
  color: var(--n-900);
  flex: none;
  font-size: var(--fs-12);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  padding: 2px 6px;
  -webkit-user-select: all;
  user-select: all;
}

.goen-promo__cta {
  display: none;
  flex: none;
  font-weight: var(--fw-medium);
  text-decoration: underline;
  text-underline-offset: 4px;
}

.goen-promo__chevron {
  display: inline-flex;
  flex: none;
  margin-left: auto;
}

/* 44 x 44, which is why the glyph inside it is 16px. A close control smaller
   than the minimum target is one people miss and then resent. */
.goen-promo__close {
  align-items: center;
  background: none;
  border: none;
  border-radius: var(--r-md);
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  flex: none;
  height: 44px;
  justify-content: center;
  margin-right: -8px;
  padding: 0;
  transition: background-color var(--dur-base) var(--ease-standard);
  width: 44px;
}

.goen-promo__close:hover {
  background: var(--n-700);
}

@media (min-width: 768px) {
  .goen-promo__wide {
    display: inline;
  }

  .goen-promo__narrow {
    display: none;
  }

  /* The message takes the slack and the call to action sits at the right,
     which is only possible once there is room for two targets on one row. */
  .goen-promo__message {
    flex: 1;
  }

  .goen-promo__cta {
    display: inline;
  }

  .goen-promo__chevron {
    margin-left: 0;
  }
}

/* A heading is the body face. The display family and its separate weights are
   gone with the second typeface. */
h1,
h2,
h3,
h4,
.goen-pdp__title,
.goen-pdp__sectiontitle,
.goen-pdp__score {
  font-family: var(--font-sans);
  letter-spacing: 0;
}

/* Emphasis is 500, never the browser's 700: a large title is light (350) on
   its own class, and 600 is the wordmark's alone. */
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b,
th {
  font-weight: var(--fw-medium);
}

/* Long text reads at a looser measure than the controls around it. */
.goen-doc__article p,
.about p {
  line-height: 1.7;
}

/* ---------- Components ----------

   goen's own presentation layer: the shapes a page repeats, ported from the
   design the shop is being rebuilt against and written on the tokens above.
   The .ui-* primitives in base.css stay until the last surface using one is
   rebuilt, and nothing wears both -- an element carrying a .goen- class and
   its .ui- ancestor is two shapes painting one box.

   A button is 48px tall and a field 44px, which is goen's rule and not the
   reference's: check-layout's tap-target sweeps fail a control under
   `MIN_TAP` on every route. The reference's 36px default is a desktop density
   this gate rejects, so what is ported from it is the proportion — padding,
   type step, radius, weight — and not the height. A media query for coarse pointers was
   the wrong answer: headless Chrome reports a fine pointer, and more to the
   point a 36px target is small for a mouse too.
   ------------------------------------------------------------------------ */

.goen-btn {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  font-family: inherit;
  font-size: var(--fs-15);
  font-weight: var(--fw-medium);
  gap: 8px;
  justify-content: center;
  letter-spacing: 0.02em;
  line-height: 1.25;
  min-height: 3rem;
  padding: 0.5rem 1.25rem;
  text-align: center;
  text-decoration: none;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.goen-btn svg {
  flex: none;
  height: 1.0625em;
  width: 1.0625em;
}

.goen-btn:focus-visible,
.goen-swatch:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* Small is less padding and a smaller type step, never a smaller target. */
.goen-btn--sm {
  font-size: var(--fs-13);
  padding-inline: 0.75rem;
}

.goen-btn--lg {
  font-size: var(--fs-16);
  min-height: 3.25rem;
  padding-inline: 1.5rem;
}

.goen-btn--icon {
  min-height: 2.75rem;
  padding: 0;
  width: 2.75rem;
}

.goen-pdp__links .goen-btn svg,
.goen-pdp__add svg,
.goen-buybar__btn svg {
  height: 1.25rem;
  width: 1.25rem;
}

.goen-btn--block {
  display: flex;
  width: 100%;
}

/* One filled control to a surface. Everything else is quieter on purpose: a
   row of equally weighted buttons makes the shopper choose before they act. */
.goen-btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: var(--sh-xs);
  color: var(--on-accent);
}

.goen-btn--primary:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

/* The secondary button, under either name: a blue tint and no outline. The
   transparent border is what forced colours draws as its edge. */
.goen-btn--secondary,
.goen-btn--outline {
  background: var(--accent-faint);
  border-color: transparent;
  color: var(--accent-deep);
}

.goen-btn--secondary:hover,
.goen-btn--outline:hover {
  background: var(--accent-muted);
}

.goen-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--fg);
}

.goen-btn--ghost:hover {
  background: var(--overlay);
}

/* A disabled control keeps its place and stops responding. It is not hidden: a
   shopper who cannot buy still needs to see what they cannot buy, and the
   sentence beside it saying why. A quiet grey control, whatever it was: at half
   opacity the ink would be a mid-grey slab, the heaviest shape on the product
   page, for a button whose whole message is "not yet". */
.goen-btn:disabled {
  background: var(--wash);
  border-color: var(--line);
  box-shadow: none;
  color: var(--muted);
  cursor: not-allowed;
}

/* aria-disabled is the moment a form is in flight, and the button the shopper
   just pressed should dim, not change colour. */
.goen-btn[aria-disabled='true'] {
  opacity: 0.5;
  pointer-events: none;
}

.goen-badge {
  align-items: center;
  background: var(--elevated);
  border: 1px solid transparent;
  border-radius: var(--r-full);
  color: var(--fg);
  display: inline-flex;
  font-size: var(--fs-12);
  font-weight: var(--fw-medium);
  gap: 4px;
  line-height: 1.4;
  padding: 0.125rem 0.625rem;
  white-space: nowrap;
  width: fit-content;
}

.goen-badge--accent {
  background: var(--accent-muted);
  color: var(--accent-deep);
}

.goen-badge--warn {
  background: var(--warn-bg);
  color: var(--warn);
}

.goen-badge--danger {
  background: var(--error-bg);
  color: var(--error);
}

/* A panel on the quiet ground: one surface, never a border and a shadow and a
   tint together. It carries no heading, because the heading belongs to
   the section around it, which is what decides the level announced. */
.goen-card {
  background: var(--n-50);
  border-radius: var(--r-lg);
  padding: 1.5rem;
}

.goen-notice {
  background: var(--elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  color: var(--fg-muted);
  font-size: var(--fs-14);
  margin: 0;
  padding: 0.625rem 0.75rem;
}

.goen-notice--accent {
  background: var(--accent-faint);
  border-color: transparent;
  color: var(--accent-deep);
}

/* A soft button on a blue notice would vanish into it: it takes the white ground. */
.goen-notice--accent :is(.goen-btn--outline, .goen-btn--secondary):not(:disabled) {
  background: var(--n-0);
}

.goen-notice--accent :is(.goen-btn--outline, .goen-btn--secondary):not(:disabled):hover {
  background: var(--accent-muted);
}

/* With payments off the pay page's notice is a plain sentence under the total, not a blue panel. */
.goen-pay__summary .goen-notice--accent {
  background: none;
  border: 0;
  color: var(--fg-muted);
  padding: 0;
}

.goen-notice--warn {
  background: var(--warn-bg);
  border-color: transparent;
  color: var(--fg);
}

.goen-notice--danger {
  background: var(--error-bg);
  border-color: transparent;
  color: var(--fg);
}

@media (forced-colors: active) {
  .goen-notice--danger {
    border: 2px solid CanvasText;
  }
}

/* Forced colours drop the tint and the inset edge that mark the current item,
   so it draws its edge in Highlight; the focus ring still wins while focused. */
@media (forced-colors: active) {
  .goen-pagehead__chip[aria-current='page']:not(:focus-visible) {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }

  .goen-account__navitem[aria-current='page']:not(:focus-visible) {
    outline: 1px solid Highlight;
    outline-offset: -1px;
  }
}

/* A quantity field with a step on each side, as one control. The field is the
   control and the buttons are the enhancement: they are out of sight until the
   stylesheet is told scripting is on, so a browser without it -- or without the
   query -- shows the plain field the keyboard already steps.

   Hidden by default and revealed, rather than shown by default and hidden: a
   query that never matches then leaves a working field instead of two buttons
   that do nothing. */
.goen-stepper {
  align-items: stretch;
  border: 1px solid var(--control-boundary);
  border-radius: var(--r-sm);
  display: inline-flex;
  height: 3rem;
  overflow: hidden;
}

.goen-stepper__step {
  align-items: center;
  background: transparent;
  border: 0;
  color: var(--fg-muted);
  cursor: pointer;
  display: none;
  font-size: var(--fs-16);
  justify-content: center;
  line-height: 1;
  transition: background-color var(--dur-base) var(--ease-standard);
  width: 2.5rem;
}

.goen-stepper__step:hover {
  background: var(--overlay);
  color: var(--fg);
}

.goen-stepper__step:disabled,
.goen-stepper__step[aria-disabled='true'] {
  cursor: not-allowed;
  opacity: 0.4;
}

.goen-stepper__value {
  appearance: textfield;
  background: transparent;
  border: 0;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  text-align: center;
  width: 3.25rem;
}

.goen-stepper__value:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: -2px;
}

/* The spinners the browser draws inside a number input are a second stepper
   beside this one. */
.goen-stepper__value::-webkit-inner-spin-button,
.goen-stepper__value::-webkit-outer-spin-button {
  appearance: none;
  margin: 0;
}

@media (scripting: enabled) {
  .goen-stepper__step {
    display: inline-flex;
  }
}

.goen-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.goen-label {
  color: var(--fg);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  line-height: 1;
}

.goen-input {
  background: var(--n-0);
  border: 1px solid var(--control-boundary);
  border-radius: var(--r-sm);
  box-sizing: border-box;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--fs-16);
  height: 2.75rem;
  padding: 0.25rem 0.75rem;
  width: 100%;
}

.goen-input:hover,
.contact__select:hover,
.goen-checkout__select:hover {
  border-color: var(--ink-2);
}

/* Focus darkens the border and the ring sits just outside it. */
.goen-input:focus-visible,
.goen-filters .ui-input:focus-visible,
.goen-filters .ui-select:focus-visible,
.goen-search-sort .ui-select:focus-visible,
.contact__select:focus-visible,
.goen-checkout__select:focus-visible {
  border-color: var(--ink);
  outline: 2px solid var(--ring);
  outline-offset: 1px;
}

.goen-input::placeholder {
  color: var(--muted);
}

.goen-input:disabled {
  background: var(--wash);
  border-color: var(--line-2);
  color: var(--muted);
}

/* A refused field is marked by its border AND by the sentence beside it. A
   colour alone is not a reason. */
.goen-input--invalid,
.goen-input[aria-invalid='true'] {
  border-color: var(--error);
}

@media (min-width: 640px) {
  .goen-input,
  .contact__select,
  .goen-checkout__select,
  .goen-filters .ui-input,
  .goen-filters .ui-select {
    font-size: var(--fs-15);
  }
}

.goen-input--area {
  height: auto;
  line-height: var(--lh-normal);
  min-height: 0;
  padding: 0.5rem 0.75rem;
  resize: vertical;
}

.ui-crumbs {
  align-items: center;
  color: var(--fg-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-13);
  gap: 2px 8px;
  line-height: 1.5;
}

.ui-crumbs__link {
  color: var(--fg-muted);
  text-decoration: none;
  transition: color var(--dur-base) var(--ease-standard);
}

.ui-crumbs__link:hover,
.ui-crumbs__link:focus-visible {
  color: var(--fg);
  text-decoration: underline;
}

.ui-crumbs__sep {
  opacity: 0.45;
}

/* A phone needs the way back, not the whole path: only the parent shows, with a
   chevron. The rest is clipped away rather than removed, so a screen reader
   still reads the whole trail and the current page. */
@media (max-width: 600px) {
  .ui-crumbs > * {
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }

  .ui-crumbs > .ui-crumbs__link:last-of-type {
    align-items: center;
    clip-path: none;
    display: inline-flex;
    gap: 6px;
    height: auto;
    min-height: 2.75rem;
    overflow: visible;
    position: static;
    white-space: normal;
    width: auto;
  }

  .ui-crumbs > .ui-crumbs__link:last-of-type::before {
    border: solid currentcolor;
    border-width: 0 0 1.5px 1.5px;
    content: '';
    height: 7px;
    margin-left: 3px;
    transform: rotate(45deg);
    width: 7px;
  }

  .ui-crumbs > .ui-crumbs__current:only-child {
    clip-path: none;
    height: auto;
    overflow: visible;
    position: static;
    white-space: normal;
    width: auto;
  }
}

.ui-crumbs__current {
  color: var(--fg);
  font-weight: var(--fw-medium);
}

/* ---------- Header ----------

   Two rows on a hairline. The bar: the brand, a search field underlined on the
   3:1 line, and the language, account, wishlist and cart controls. Under it the
   departments, always on a row of their own (see .goen-header__nav). Static and
   not sticky on purpose: a bar that follows the scroll takes 64px out of an
   844px phone and moves every scroll position the follow-up issues are
   measured against.

   From 1024 a department with sub-categories shows a full-width panel under
   the row on hover or focus. The panel hangs from .goen-header, which is why
   nothing between the two is positioned.

   Below 1024 the menu button opens a native <details data-menu> that covers
   the page with every department, the account links and the language; the
   search field wraps onto its own row of the bar. `assets/js/goen.js` closes
   the menu on Escape and on an outside click; nothing here needs script to
   open it.
   ------------------------------------------------------------------------ */

.goen-header {
  background: var(--n-0);
  border-bottom: 1px solid var(--n-200);
  position: relative;
  /* The view-transition-name below makes the header a stacking context, so the
     drawer's own z-index is only ever compared inside it. Without a level of its
     own the header sits under any later positioned block, the hero and the
     gallery photo included, and the open drawer is painted over. */
  z-index: var(--z-dropdown);
}

.goen-header__bar {
  align-items: center;
  column-gap: 12px;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  margin: 0 auto;
  max-width: var(--page-width);
  min-height: 4rem;
  padding: 8px var(--page-gutter);
  position: relative;
  row-gap: 8px;
}

.goen-header__brand {
  align-items: center;
  color: var(--n-900);
  display: flex;
  gap: 8px;
  margin-right: auto;
  text-decoration: none;
}

.goen-header__brand:hover {
  color: var(--n-900);
}

.goen-header__mark {
  flex: none;
  height: 24px;
  width: 24px;
}

.goen-header__wordmark {
  font-size: var(--fs-18);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.03em;
}

/* The departments, always on a row of their own under the bar, in either
   language and for any number of them. Wide screens wrap it a department at a
   time; a phone scrolls it sideways and goen.js centres the current one. The
   `ui-navitem` spelling is kept because `internal/ui/pages/backoffice_test.go:22`
   locates the drawer's /admin entrance by the whole tag; every declaration
   base.css gives that name is replaced here. The current department is
   underlined as text, not boxed: a border would show on every link in forced
   colours. */
.goen-header__nav {
  column-gap: clamp(1.25rem, 2vw, 1.75rem);
  display: flex;
  flex-wrap: wrap;
  padding-inline: max(var(--page-gutter), calc((100% - var(--page-width)) / 2));
}

.goen-header__nav .ui-navitem {
  align-items: center;
  background: none;
  border-radius: 0;
  color: var(--n-700);
  display: inline-flex;
  flex: none;
  font-size: var(--fs-14);
  min-height: 2.75rem;
  padding: 0;
  text-decoration: none;
  text-decoration-thickness: 2px;
  text-underline-offset: 8px;
  transition: color var(--dur-base) var(--ease-standard);
  white-space: nowrap;
}

.goen-header__nav .ui-navitem:hover {
  background: none;
  color: var(--n-900);
}

.goen-header__nav .ui-navitem[aria-current='page'] {
  background: none;
  color: var(--n-900);
  text-decoration-color: var(--accent);
  text-decoration-line: underline;
}

@media (max-width: 700px) {
  .goen-header__nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .goen-header__nav::-webkit-scrollbar {
    display: none;
  }
}

/* The panels are for a pointer and a keyboard on a wide row. Below 1024 the
   menu lists the departments only, so a department's sub-categories are
   reached from its own page. */
@media (max-width: 1023px) {
  .goen-header__nav .goen-dept__panel {
    display: none !important;
  }
}

/* A department. The wrapper is a plain flex box so the link keeps the row's
   full height and the pointer never crosses a gap between link and panel. */
.goen-dept {
  display: flex;
  flex: none;
}

.goen-dept__panel {
  background: var(--n-0);
  border-bottom: 1px solid var(--n-200);
  box-shadow: var(--sh-lg);
  display: none;
  left: 0;
  position: absolute;
  right: 0;
  top: 100%;
}

.goen-dept:hover > .goen-dept__panel,
.goen-dept:focus-within > .goen-dept__panel {
  animation: goen-panel-in 100ms var(--ease-standard);
  display: block;
}

.goen-dept[data-dismissed] > .goen-dept__panel {
  display: none;
}

/* With script, focus alone does not open a panel: its sub-links would be a tab
   stop each before the search field. goen.js opens one from the department's
   link (data-open); hover still opens it. Without script the rule above keeps
   the panel open under focus, so it stays reachable. */
@media (scripting: enabled) {
  .goen-dept:not([data-open]):not(:hover) > .goen-dept__panel {
    display: none;
  }

  .goen-dept[data-open] > .goen-dept__panel {
    animation: goen-panel-in 100ms var(--ease-standard);
    display: block;
  }
}

@keyframes goen-panel-in {
  from {
    opacity: 0;
  }
}

/* The panel is as tall as what it holds: the sub-categories, three of the
   department's newest products and a small photograph, on one row with the
   free width between them. The products' track may shrink, so a long list of
   sub-categories in English at 1024 squeezes the thumbnails rather than
   pushing the row past the container. */
.goen-dept__inner {
  align-items: start;
  column-gap: 3rem;
  display: grid;
  grid-template-columns: max-content minmax(0, max-content) max-content;
  justify-content: space-between;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 1.5rem var(--page-gutter) 2rem;
}

/* One column of sub-categories; from five, columns of three, read top to
   bottom, so the list is never taller than the products beside it. */
.goen-dept__links {
  column-gap: 2.5rem;
  display: grid;
  grid-auto-columns: max-content;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-dept__links:has(> li:nth-child(5)) {
  grid-auto-flow: column;
  grid-template-rows: repeat(3, auto);
}

.goen-dept__link {
  color: var(--n-700);
  display: block;
  font-size: var(--fs-15);
  padding: 0.375rem 0;
  text-decoration: none;
}

.goen-dept__link:hover {
  color: var(--n-900);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.goen-dept__picks {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(3, minmax(0, 8rem));
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-dept__pick {
  color: var(--n-900);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  text-decoration: none;
}

.goen-dept__pick:hover {
  color: var(--n-900);
}

/* The tile's well at a smaller size, on the photographs' own ground. At this
   size the photograph fills it: held in from the edge, a 144px product would
   shrink to a stamp. */
.goen-dept__well {
  aspect-ratio: 1;
  background: var(--photo);
  border-radius: var(--r-lg);
  display: block;
  margin-bottom: 0.5rem;
  overflow: hidden;
}

.goen-dept__well img {
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.goen-dept__name {
  -webkit-box-orient: vertical;
  display: -webkit-box;
  font-size: var(--fs-14);
  -webkit-line-clamp: 2;
  line-clamp: 2;
  line-height: 1.45;
  overflow: hidden;
  text-wrap: pretty;
}

.goen-dept__price {
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
}

.goen-dept__pick:hover .goen-dept__name {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.goen-dept__pick:hover img {
  opacity: 0.85;
}

/* Pinned to the last track: a department with nothing buyable has no product
   list, and the link would otherwise fall into the middle of the row. */
.goen-dept__browse {
  color: var(--accent-text);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  grid-column: 3;
  text-decoration: none;
}

.goen-dept__browse:hover {
  color: var(--accent-text-hover);
}

/* Below 1280 the row has no room for a fourth picture, so the photograph goes
   and its caption stays as the department's text link. Lazy and display:none,
   it is never fetched there. */
.goen-dept__photo {
  display: none;
}

.goen-dept__caption {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  padding-top: 0.375rem;
  white-space: nowrap;
}

.goen-dept__browse:hover .goen-dept__caption {
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

@media (min-width: 1280px) {
  .goen-dept__picks {
    grid-template-columns: repeat(3, minmax(0, 9rem));
  }

  .goen-dept__photo {
    aspect-ratio: 4 / 3;
    background: var(--photo);
    border-radius: var(--r-lg);
    display: block;
    object-fit: cover;
    width: 15rem;
  }

  .goen-dept__browse:hover .goen-dept__photo {
    opacity: 0.85;
  }

  .goen-dept__caption {
    padding-top: 0;
  }
}

.goen-header .goen-header__nav .goen-header__navitem--deal {
  padding-left: 0;
}

/* Deals is not a department, so a 2.5rem space (this margin plus the row's gap)
   sets it apart. The margin sits on the item before it, so a wrapped Deals starts
   at the row's edge. */
.goen-header__nav > :has(+ .goen-header__navitem--deal) {
  margin-inline-end: calc(2.5rem - clamp(1.25rem, 2vw, 1.75rem));
}

.goen-header__search {
  flex: 0 0 100%;
  order: 10;
  position: relative;
}

.goen-header__searchicon {
  align-items: center;
  bottom: 0;
  color: var(--ink-2);
  display: flex;
  left: 0;
  pointer-events: none;
  position: absolute;
  top: 0;
}

.goen-header__searchicon svg {
  height: 1rem;
  width: 1rem;
}

/* Page chrome, not a form: the field is its line. The line is the control's
   boundary (1.4.11, 3.66:1) and focus draws the shared ring around the whole
   field on top of it. */
.goen-header .goen-header__field {
  background: none;
  border-radius: 0;
  border-width: 0 0 1px;
  padding-left: 1.75rem;
}

.goen-header .goen-header__field:hover {
  border-bottom-color: var(--ink-2);
}

.goen-header .goen-header__field:focus-visible {
  outline-offset: 4px;
}

.goen-header__actions {
  align-items: center;
  display: flex;
  gap: 4px;
}

.goen-header__actions svg,
.goen-header__menu > summary svg {
  height: 1.25rem;
  width: 1.25rem;
}

.goen-header__actions .goen-btn,
.goen-header__actions .ui-btn,
.goen-header__menu > summary {
  color: var(--ink-2);
}

.goen-header__actions .goen-btn:hover,
.goen-header__actions .ui-btn:hover,
.goen-header__menu > summary:hover {
  background: var(--n-100);
  color: var(--ink);
}

/* The wishlist heart and, for staff, the words "Back office" before the language
   menu. Both keep the shared `ui-btn` spelling that `backoffice_test.go` reads,
   so the ghost recipe is written out here rather than taken from `.goen-btn`. */
.goen-header__action--wide,
.goen-header__backoffice {
  align-items: center;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  box-sizing: border-box;
  cursor: pointer;
  display: none;
  height: 44px;
  justify-content: center;
  text-decoration: none;
  transition: background-color var(--dur-base) var(--ease-standard);
}

.goen-header__action--wide {
  padding: 0;
  width: 44px;
}

.goen-header__backoffice {
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
  padding: 0 0.75rem;
  white-space: nowrap;
}

.goen-header__actions .goen-header__cart {
  gap: 4px;
  min-width: 2.75rem;
  padding-inline: 0.625rem;
  width: auto;
}

/* The count is a number beside the bag, not a badge on it. `check-layout.mjs`
   reads its text through this exact pair of names after an add to cart, so the
   spelling stays and the declarations are goen's. */
.goen-header__cart .ui-badge--count {
  background: none;
  border: none;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  height: auto;
  line-height: 1;
  min-width: 0;
  padding: 0;
}

/* The language control, in the footer: two plain words, the current one set. */
.goen-lang {
  display: inline-flex;
  gap: 4px;
}

.goen-lang__item {
  background: none;
  border: none;
  border-radius: var(--r-md);
  color: var(--n-500);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  min-height: 44px;
  padding: 0 12px;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.goen-lang__item:hover {
  color: var(--n-900);
}

.goen-lang__item[aria-pressed='true'] {
  color: var(--n-900);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* The language menu of the header and the back office's bar: a globe and the
   current language's short name on a button, a small panel of the two choices
   under it. The name is visible from 1280 and in the back office; where the
   header is tight it is only the globe, and the name stays in the accessible
   name. */
.goen-langmenu {
  position: relative;
}

.goen-langmenu > summary {
  align-items: center;
  border-radius: var(--r-md);
  color: var(--n-700);
  cursor: pointer;
  display: inline-flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 6px;
  justify-content: center;
  list-style: none;
  min-height: 44px;
  min-width: 44px;
  padding: 0 10px;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.goen-langmenu > summary::-webkit-details-marker {
  display: none;
}

.goen-header__actions .goen-langmenu > summary {
  box-sizing: border-box;
  color: var(--ink-2);
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
}

.goen-header__actions .goen-langmenu > summary:hover,
.goen-header__actions .goen-langmenu[open] > summary {
  color: var(--ink);
}

.goen-langmenu > summary:hover,
.goen-langmenu[open] > summary {
  background: var(--n-100);
  color: var(--n-900);
}

.goen-langmenu svg {
  flex: none;
  height: 1.25rem;
  width: 1.25rem;
}

.goen-header__actions .goen-langmenu svg {
  height: 1rem;
  width: 1rem;
}

.goen-header .goen-langmenu__current {
  border: 0;
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.goen-langmenu__panel {
  background: var(--n-0);
  border: 1px solid var(--n-200);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  min-width: 11rem;
  padding: 4px;
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: var(--z-dropdown);
}

.goen-langmenu__item {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--r-md);
  color: var(--n-700);
  cursor: pointer;
  display: flex;
  font-family: inherit;
  font-size: var(--fs-15);
  gap: 12px;
  justify-content: space-between;
  min-height: 44px;
  padding: 0 12px;
  text-align: left;
  width: 100%;
}

.goen-langmenu__item:hover {
  background: var(--n-100);
  color: var(--n-900);
}

.goen-langmenu__item[aria-pressed='true'] {
  color: var(--n-900);
  font-weight: var(--fw-medium);
}

/* The disclosure menu: a native <details>, no script involved in opening it. */
.goen-header__menu > summary {
  list-style: none;
}

.goen-header__menu > summary::-webkit-details-marker {
  display: none;
}

/* A 20rem panel from the left edge, under the bar so the menu button stays
   where it was and closes it. */
.goen-header__drawer {
  background: var(--n-0);
  box-sizing: border-box;
  border-bottom: 1px solid var(--n-200);
  border-right: 1px solid var(--n-200);
  box-shadow: var(--sh-xl);
  display: flex;
  flex-direction: column;
  gap: 2px;
  height: var(--drawer-room, calc(100dvh - 7rem));
  left: 0;
  overflow-y: auto;
  padding: 12px;
  position: absolute;
  top: 100%;
  width: min(20rem, 100%);
  z-index: var(--z-dropdown);
}

/* The page behind an open menu is covered, so a tap beside the drawer closes it
   instead of landing on a product. goen.js reads a click on the menu's own box
   as outside the drawer. */
.goen-header__menu[open]::after {
  background: rgb(0 0 0 / 0.4);
  content: '';
  height: var(--drawer-room, 100dvh);
  left: 0;
  position: absolute;
  right: 0;
  top: 100%;
}

.goen-header__drawer .ui-navitem {
  align-items: center;
  background: none;
  border-radius: var(--r-md);
  color: var(--n-900);
  display: flex;
  font-size: var(--fs-16);
  font-weight: var(--fw-medium);
  justify-content: space-between;
  min-height: 44px;
  padding: 0 12px;
  text-decoration: none;
  transition: background-color var(--dur-base) var(--ease-standard);
}

.goen-header__drawerheading {
  color: var(--n-500);
  font-size: var(--fs-12);
  padding: 8px 12px 4px;
}

.goen-header__drawer small {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
}

.goen-header__drawer .ui-navitem:hover {
  background: var(--n-200);
  color: var(--n-900);
}

.goen-header__drawer .ui-navitem[aria-current='page'] {
  background: none;
  color: var(--n-900);
  font-weight: var(--fw-medium);
}

/* The name only: the count beside it is not part of what is current. */
.goen-header__drawer .ui-navitem[aria-current='page'] .goen-header__navname,
.goen-header__drawer .goen-header__navitem--deal[aria-current='page'] {
  text-decoration: underline 2px var(--accent);
  text-underline-offset: 0.45em;
}

/* The way out of the drawer, and what the phone's bar has no room for. The
   language menu opens in the flow here: an absolutely placed panel would be
   clipped by the drawer's own scroll. Below 768 the bar's copy of the menu is
   dropped, so one language control shows at a time. */
.goen-header__close {
  align-self: flex-end;
}

.goen-header__drawertools {
  border-top: 1px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 8px;
  padding-top: 8px;
}

.goen-header__drawerlang > summary {
  justify-content: flex-start;
  width: 100%;
}

.goen-header .goen-header__drawerlang .goen-langmenu__current {
  clip-path: none;
  height: auto;
  margin: 0;
  overflow: visible;
  position: static;
  width: auto;
}

.goen-header__drawerlang .goen-langmenu__panel {
  box-shadow: none;
  position: static;
}

@media (max-width: 767px) {
  .goen-header__lang {
    display: none;
  }
}

@media (min-width: 768px) {
  .goen-header__drawerlang {
    display: none;
  }
}

@media (min-width: 1024px) {
  .goen-header__bar {
    column-gap: 1.25rem;
    padding: 0 var(--page-gutter);
  }

  .goen-header__action--wide,
  .goen-header__backoffice {
    display: inline-flex;
  }

  .goen-header__menu {
    display: none;
  }

  .goen-header__brand {
    margin-right: 0;
  }

  .goen-header .goen-header__field {
    font-size: var(--fs-14);
  }

  .goen-header__search {
    flex: 1 1 8.75rem;
    margin-left: auto;
    max-width: 15.5rem;
    min-width: 8.75rem;
    order: 0;
  }
}

@media (min-width: 640px) and (max-width: 1023.98px) {
  .goen-header__search {
    flex: 1 1 12rem;
    max-width: 24rem;
    order: 0;
  }
}

@media (min-width: 1024px) and (max-width: 1279px) {
  .goen-header__bar {
    column-gap: 0.75rem;
  }

  .goen-header__actions {
    gap: 0;
  }

  .goen-header__search {
    flex-basis: 7.5rem;
    min-width: 7.5rem;
  }
}

@media (min-width: 1280px) {
  .goen-header .goen-langmenu__current {
    clip-path: none;
    height: auto;
    margin: 0;
    overflow: visible;
    position: static;
    width: auto;
  }
}

/* Forced colours replace the greys, so the glyphs and the count take the
   button colour the system chose for controls. */
@media (forced-colors: active) {
  .goen-header__actions .goen-btn,
  .goen-header__actions .ui-btn,
  .goen-header__actions .goen-langmenu > summary,
  .goen-header__menu > summary,
  .goen-header__cart .ui-badge--count {
    color: ButtonText;
  }
}

/* ---------- Footer ----------

   A quiet ground with a hairline above it. Brand and shop facts, three link
   columns (departments, help, policies) and the newsletter at the right, over
   a bottom row with the company line and the language control. One column in
   source order on a phone.
   ------------------------------------------------------------------------ */

.goen-footer {
  background: var(--n-50);
  border-top: 1px solid var(--line);
}

.goen-footer__grid {
  display: grid;
  gap: 2.5rem;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 3rem var(--page-gutter);
}

.goen-footer__brandline {
  align-items: center;
  display: flex;
  gap: 8px;
}

.goen-footer__mark {
  flex: none;
  height: 24px;
  width: 24px;
}

.goen-footer__wordmark {
  font-size: var(--fs-18);
  font-weight: var(--fw-semibold);
}

.goen-footer__contact {
  color: var(--n-500);
  font-size: var(--fs-14);
  line-height: var(--lh-read);
  margin: 1rem 0 0;
}

.goen-footer__links {
  display: flex;
  flex-direction: column;
}

.goen-footer__heading {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
}

.goen-footer__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  list-style: none;
  margin: 1.25rem 0 0;
  padding: 0;
}

.goen-footer__link {
  color: var(--n-500);
  font-size: var(--fs-14);
  text-decoration: none;
  transition: color var(--dur-base) var(--ease-standard);
}

.goen-footer__link:hover {
  color: var(--n-900);
}

.goen-footer__news {
  display: flex;
  flex-direction: column;
}

.goen-footer__newsnote {
  color: var(--n-500);
  font-size: var(--fs-14);
  line-height: var(--lh-normal);
  margin: 0.5rem 0 0;
  text-wrap: pretty;
}

.goen-footer__signup {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 1rem;
}

.goen-footer__signuplabel {
  flex: 0 0 100%;
}

/* One shape: the field's own box, with the button set inside it 4px from every
   edge. The field reserves the button's width, so typed text never runs under
   it. */
.goen-footer__field {
  display: grid;
  flex: 1 1 100%;
  grid-template-columns: minmax(0, 1fr) auto;
  min-width: 0;
}

.goen-footer__field > .goen-input {
  border-radius: var(--r-lg);
  grid-area: 1 / 1 / 2 / 3;
  height: 3.25rem;
  padding-right: 8.5rem;
}

.goen-footer__field > .goen-btn {
  border-radius: var(--r-md);
  grid-area: 1 / 2;
  margin: 4px;
  max-width: 100%;
  min-height: 2.75rem;
  overflow-wrap: anywhere;
  z-index: 1;
}

.goen-footer__field > .goen-btn:focus-visible {
  outline-offset: -2px;
}

/* A column too narrow for the field and the action side by side puts the
   action under the field. */
.goen-footer__signup {
  container-type: inline-size;
}

@container (max-width: 17rem) {
  .goen-footer__field {
    grid-template-columns: minmax(0, 1fr);
  }

  .goen-footer__field > .goen-input {
    grid-area: auto;
    padding-right: 0.75rem;
  }

  .goen-footer__field > .goen-btn {
    grid-area: auto;
    justify-self: start;
    margin: 0.25rem 0 0;
  }
}

.goen-footer__field > .goen-btn--ghost {
  background: none;
  color: var(--accent);
  font-weight: var(--fw-medium);
  gap: 0.375rem;
  padding-inline: 0.75rem 1rem;
}

.goen-footer__field > .goen-btn--ghost:hover {
  background: none;
  color: var(--accent-deep);
  text-decoration: underline 1px;
  text-underline-offset: 3px;
}

.goen-footer__field > .goen-btn--ghost > [aria-hidden] {
  transition: transform var(--dur-base) var(--ease-standard);
}

.goen-footer__field > .goen-btn--ghost:hover > [aria-hidden] {
  transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
  .goen-footer__field > .goen-btn--ghost:hover > [aria-hidden] {
    transform: none;
  }
}

.goen-footer__signup-done {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-footer__signup-error {
  flex: 0 0 100%;
  margin: 0;
}

.goen-footer__bottominner {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 1rem var(--page-gutter);
}

.goen-footer__copyright {
  color: var(--n-500);
  font-size: var(--fs-13);
}

.goen-footer__contentnote {
  color: var(--n-500);
  font-size: var(--fs-13);
  margin: 0;
}

@media (min-width: 768px) {
  .goen-footer__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .goen-footer__bottominner {
    align-items: center;
    flex-direction: row;
    justify-content: space-between;
  }
}

@media (min-width: 1024px) {
  .goen-footer__grid {
    gap: 2rem;
    grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)) minmax(0, 1.6fr);
  }
}

/* ---------- Home ----------

   The carousel, the department cards, one product row, one department band,
   one photograph and the shop rules. Sections are direct children of <main>;
   every horizontal edge comes from the one container width, so `.goen-home`
   (compared against the header bar and the footer grid, `check-layout.mjs`)
   and `.goen-container` agree. Tones come from the [data-tone] blocks.
   ------------------------------------------------------------------------ */

:root {
  --home-section: 4rem;
}

@media (min-width: 40rem) {
  :root {
    --home-section: 6rem;
  }
}

/* Content-box, like the header bar and the footer grid: the width is the
   content's, so all three put their text on one left edge. */
.goen-container,
.goen-home {
  margin-inline: auto;
  max-width: var(--page-width);
  padding-inline: var(--page-gutter);
}

.goen-home__section {
  padding-top: var(--home-section);
}

.goen-home__head {
  align-items: baseline;
  display: flex;
  container-type: inline-size;
  flex-wrap: wrap;
  gap: 0 1rem;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}

.goen-home__heading {
  font-size: var(--t-32);
  font-weight: var(--fw-display);
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0;
}

/* A phone at 200% text leaves a head under 16rem, where "Departments" at its
   own size is wider than the line. */
@container (width < 16rem) {
  .goen-home__heading {
    font-size: min(var(--t-24), 14cqi);
    overflow-wrap: anywhere;
  }
}

.goen-home__more {
  align-items: center;
  color: var(--accent-text);
  display: inline-flex;
  flex: none;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  min-height: 2.75rem;
  text-decoration: none;
  white-space: nowrap;
}

.goen-home__more:hover {
  color: var(--accent-text-hover);
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

@media (prefers-reduced-motion: no-preference) {
  .goen-home__more > span {
    display: inline-block;
    transition: transform var(--dur-base) var(--ease-standard);
  }

  .goen-home__more:hover > span {
    transform: translateX(3px);
  }
}

/* ----- Carousel: native scroll-snap does the moving (swipe, trackpad, keyboard,
   the named tabs); nothing moves on its own, and goen.js only wires the arrows
   and the count. A slide is copy on its tone and the photograph in its own
   column, a white seam of --col-gap between them. ----- */

.goen-hero {
  position: relative;
}

.goen-hero__track {
  display: flex;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.goen-hero__track::-webkit-scrollbar {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  .goen-hero__track {
    scroll-behavior: smooth;
  }
}

.goen-hero__slide {
  background: var(--tone-ground);
  box-sizing: border-box;
  display: flex;
  flex: 0 0 100%;
  flex-direction: column;
  min-width: 0;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

.goen-hero__media {
  aspect-ratio: 4 / 3;
  background: var(--n-0);
  overflow: hidden;
  position: relative;
}

.goen-hero__media img {
  height: 100%;
  object-fit: cover;
  object-position: 82% 50%;
  width: 100%;
}

.goen-hero__body {
  flex: 1;
  padding: 1.5rem var(--page-gutter) 2rem;
}

.goen-hero__inner {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  max-width: 32rem;
  width: 100%;
}

.goen-hero__title {
  font-size: var(--fs-ed-38);
  font-weight: var(--fw-display);
  line-height: 1.25;
  margin: 0;
  text-wrap: balance;
}

.goen-hero__title--long {
  font-size: var(--fs-ed-30);
  line-height: 1.3;
}

.goen-hero__lede {
  color: var(--tone-text);
  line-height: 1.7;
  margin: 0;
  text-wrap: pretty;
}

/* Chinese wraps at punctuation and spaces here, not mid-word. */
.goen-hero__title,
.goen-hero__lede {
  overflow-wrap: anywhere;
  word-break: keep-all;
}

/* The slide's ink comes from its tone, so a statline and a period read on every
   ground, the dark one included. */
.goen-hero__slide {
  color: var(--tone-text);
}

.goen-hero .ui-statline dt,
.goen-hero .ui-statline dd small,
.goen-hero .ui-statline__note {
  color: var(--tone-muted);
}

.goen-hero__slide[data-tone] .ui-period {
  --period-fill: var(--tone-mark);
}

/* On ink the fill is --on-ink-accent, so the track is a tint of that blue. */
.goen-hero__slide[data-tone="ink"] .ui-period {
  --period-track: var(--period-track-on-ink);
}

/* The track belongs to the facts above it, so it sits closer to them than to the button. */
.goen-hero__inner > .ui-period {
  margin-bottom: var(--s-2);
  margin-top: 0;
}

.goen-pagehead[data-tone] .ui-period {
  --period-fill: var(--tone-mark);
}

.goen-pagehead[data-tone="ink"] .ui-period {
  --period-track: var(--period-track-on-ink);
}

.goen-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: var(--s-2);
}

.goen-hero__actions .ui-btn {
  text-align: center;
  white-space: normal;
}

/* The blue is 2.5:1 on the ink tone; the light blue with dark text is 9.6:1. */
.goen-hero__slide[data-tone="ink"] .goen-btn--primary {
  background: var(--on-ink-accent);
  border-color: var(--on-ink-accent);
  color: var(--n-900);
}

.goen-hero__slide[data-tone="ink"] .goen-btn--primary:hover {
  background: var(--n-0);
  border-color: var(--n-0);
}

.goen-hero__controls {
  align-items: center;
  display: flex;
  gap: var(--s-5);
  margin-inline: auto;
  max-width: var(--page-box);
  padding: var(--s-3) var(--page-gutter) 0;
}

.goen-hero__tabs {
  display: flex;
  gap: var(--s-1);
  margin-inline-start: calc(var(--s-4) * -1);
  mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
  min-width: 0;
  overflow-x: auto;
  /* The fade lands on this room, not on the last tab's words, when the tabs fit. */
  padding-inline-end: 1.5rem;
  scrollbar-width: none;
}

.goen-hero__tabs::-webkit-scrollbar {
  display: none;
}

.goen-hero__tabs a:focus-visible {
  outline-offset: -2px;
}

.goen-hero__tabs a {
  border-radius: var(--r-full);
  text-decoration: none;
  color: var(--fg-muted);
  flex: none;
  font-size: var(--t-14);
  line-height: 1.5rem;
  max-width: 14em;
  overflow: hidden;
  padding: 0.625rem var(--s-4);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.goen-hero__tabs a:hover {
  color: var(--fg);
}

@media (max-width: 47.99rem) {
  .goen-hero__tabs {
    margin-inline-start: 0;
  }
}

/* The current tab is told apart by its weight and its fill, so colour is not
   the only cue; forced colours drop the fill and draw the edge instead. */
.goen-hero__tabs a[aria-current="true"] {
  background: var(--accent-faint);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}

@media (forced-colors: active) {
  .goen-hero__tabs a[aria-current="true"]:not(:focus-visible) {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
}

.goen-hero__nav {
  display: flex;
  gap: var(--s-2);
  margin-left: auto;
}

.goen-hero__arrow {
  background: var(--n-0);
  border: 1px solid var(--n-300);
  border-radius: 50%;
  color: var(--n-900);
  cursor: pointer;
  display: grid;
  height: 2.75rem;
  padding: 0;
  place-items: center;
  width: 2.75rem;
}

.goen-hero__arrow:hover {
  background: var(--n-50);
}

.goen-hero__arrow svg {
  height: 1.25rem;
  width: 1.25rem;
}

/* A slide with no photograph is colour and type. */
.goen-hero__slide--text .goen-hero__body {
  padding-block: 2rem 2.5rem;
}

/* Beside photo slides the track makes it as tall as they are, so under 1024px
   it is a poster: the name at the top, the facts and the button at the foot,
   instead of the copy in the corner of a tall field of tone. */
@media (max-width: 63.99rem) {
  .goen-hero__slide--text .goen-hero__body {
    display: flex;
  }

  .goen-hero__slide--text .goen-hero__inner {
    flex: 1;
    max-width: none;
  }

  .goen-hero__slide--text .goen-hero__title {
    margin-block-end: auto;
  }
}

@media (min-width: 40rem) and (max-width: 63.99rem) {
  .goen-hero__slide--text .goen-hero__title {
    font-size: 3rem;
  }

  .goen-hero__slide--text .goen-hero__title--long {
    font-size: var(--fs-ed-38);
  }
}

@media (max-width: 50rem) {
  /* The card is the tone; the photograph sits inside an 8px page-white ring
     with 12px of tone around it, so it never touches the tone. */
  .goen-hero__slide {
    padding: var(--s-3) var(--s-3) 0;
  }

  .goen-hero__media {
    border: var(--s-2) solid var(--n-0);
    order: -1;
  }

  .goen-hero__body {
    padding: var(--s-5) var(--s-2) var(--s-6);
  }
}

/* ----- Departments: one square photograph each, the name under it. The
   columns follow the section's own width in rem, so larger text folds them
   sooner. ----- */

.goen-cats {
  container: cats / inline-size;
}

.goen-cats__grid {
  --cols: 3;
  display: grid;
  gap: 1.5rem 1rem;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

@container cats (inline-size < 40rem) {
  .goen-cats__grid:not(:has(> li:nth-child(3))),
  .goen-cats__grid:has(> li:nth-child(4):last-child) {
    --cols: 2;
  }
}

@container cats (inline-size < 17rem) {
  .goen-cats__grid {
    --cols: 2;
  }
}

@container cats (inline-size >= 40rem) {
  .goen-cats__grid {
    --cols: 6;
    column-gap: 1.5rem;
  }
}

.goen-cat {
  color: var(--n-900);
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  text-decoration: none;
}

.goen-cat__well {
  aspect-ratio: 1;
  background: var(--photo);
  border-radius: var(--r-lg);
  display: block;
  overflow: hidden;
}

.goen-cat__photo {
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.goen-cat__mark {
  background: var(--tone-ground);
  color: var(--tone-text);
  display: grid;
  font-size: 32cqi;
  font-weight: var(--fw-display);
  height: 100%;
  line-height: 1;
  place-items: center;
}

.goen-cat__name {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  hyphens: auto;
  line-height: 1.4;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.goen-cat:hover .goen-cat__photo {
  opacity: 0.85;
}

.goen-cat:hover .goen-cat__name {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.goen-cat:focus-visible {
  outline: none;
}

.goen-cat:focus-visible .goen-cat__well {
  outline: 2px solid var(--ring);
  outline-offset: 3px;
}

@container cats (inline-size >= 56rem) {
  .goen-cats__grid {
    row-gap: 2rem;
  }

  .goen-cat {
    gap: 0.75rem;
  }

  .goen-cat__name {
    font-size: var(--fs-15);
  }
}

/* ----- One department, the band: its photograph, lifted out of the tone, beside
   three of its products. The tone starts --lift below the band's top and the
   photograph does not stop at it; goen's white runs along the photograph's
   right and bottom edge, so photograph and tone never touch whatever ground
   the photograph was shot on. ----- */

.goen-band {
  --lift: 2rem;
  background: linear-gradient(var(--n-0) 0 var(--lift), var(--tone-ground) var(--lift));
  color: var(--tone-text);
  margin-top: var(--home-section);
}

/* minmax(0, ...) because the sideways product scroller would otherwise set the
   track's minimum width and push the page wider than the viewport. */
.goen-band__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

.goen-band__media {
  background: var(--n-0);
  margin-right: var(--s-6);
  padding: 0 var(--col-gap) var(--col-gap) 0;
}

.goen-band__media img {
  aspect-ratio: 4 / 3;
  display: block;
  height: auto;
  object-fit: cover;
  width: 100%;
}

.goen-band__media--well img {
  background: var(--well);
  object-fit: contain;
}

.goen-band__body {
  min-width: 0;
  padding: var(--s-6) var(--page-gutter) var(--s-8);
}

.goen-band .goen-band__name--long {
  font-size: var(--fs-ed-24);
}

.goen-band .goen-home__more {
  color: var(--tone-mark);
}

.goen-band .goen-tile__name,
.goen-band .goen-tile__price {
  color: var(--tone-text);
}

.goen-band .goen-band__fact,
.goen-band .goen-tile__brand,
.goen-band .goen-tile__was,
.goen-band .goen-tile__state,
.goen-band .goen-tile__colours {
  color: var(--tone-muted);
}

/* The four products scroll sideways until the shelf's container reaches 48rem. */
.goen-band__tiles {
  display: grid;
  gap: 1rem;
  grid-auto-columns: 42%;
  grid-auto-flow: column;
  list-style: none;
  margin: 0 calc(var(--page-gutter) * -1);
  overflow-x: auto;
  padding: 0 var(--page-gutter);
  scroll-padding-inline: var(--page-gutter);
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.goen-band__tiles::-webkit-scrollbar {
  display: none;
}

.goen-band__tiles > li {
  scroll-snap-align: start;
}

/* ----- The shop's rules, as a stat line above the footer. ----- */

.goen-rules {
  margin-top: var(--home-section);
  padding-block: var(--s-7) var(--s-8);
}

@media (min-width: 40rem) {
  .goen-hero__media {
    aspect-ratio: 16 / 9;
  }

  .goen-hero__body {
    padding-block: 2rem 2.5rem;
  }
}

@media (min-width: 48rem) {
  .goen-band__media img {
    aspect-ratio: 16 / 9;
  }
}

@media (min-width: 64rem) {
  /* Copy on the tone and the photograph in its own column. The body is a
     full-height cell, so its top is the photograph's top, which is how the gate
     reads a side-by-side hero. The photograph is inset by --col-gap, so a
     white seam always lies between it and the tone. */
  .goen-hero__slide {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    height: clamp(27.5rem, 38vw, 35rem);
  }

  .goen-hero__slide--text {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
  }

  .goen-hero__media {
    aspect-ratio: auto;
  }

  .goen-hero__media img {
    height: 100%;
    inset: 0 0 0 var(--col-gap);
    object-position: 72% 50%;
    position: absolute;
    width: calc(100% - var(--col-gap));
  }

  .goen-hero__body {
    align-items: center;
    display: flex;
    padding: var(--s-7) 2rem var(--s-7) max(var(--page-gutter), calc((100% - var(--page-width)) / 2));
  }

  .goen-hero__slide--text .goen-hero__body {
    padding-right: var(--page-gutter);
  }

  .goen-hero__title {
    font-size: 3rem;
  }

  .goen-hero__title--long {
    font-size: var(--fs-ed-38);
  }

  .goen-hero__lede {
    font-size: 1.0625rem;
  }

  .goen-band {
    --lift: 4rem;
  }

  .goen-band__grid {
    align-items: start;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  }

  .goen-band__media {
    margin: 0 0 var(--lift);
  }

  .goen-band__media img {
    aspect-ratio: 4 / 3;
  }

  .goen-band__body {
    padding: calc(var(--lift) + var(--s-7)) var(--page-edge) var(--s-8) clamp(2rem, 4.5vw, 4.5rem);
  }

  /* With no photograph the body takes the whole row, on the page's column. */
  .goen-band__body:first-child {
    grid-column: 1 / -1;
    padding-left: var(--page-edge);
  }
}

.goen-band:not(:has(.goen-band__media)) {
  --lift: 0px;
}

/* The home page's band is the department's shelf: no photograph, because the
   tile above shows it. From 60rem it shares the tiles' six columns and their
   1.5rem gap, the name in two and the four products under tiles 3 to 6. The
   department page's head is built from .goen-band too and takes none of this. */
.goen-band--shelf {
  background: var(--tone-ground);
  container: band / inline-size;
}

.goen-band--shelf .goen-band__body {
  display: grid;
  gap: var(--s-5) var(--col-gap);
  grid-template-columns: minmax(0, 1fr);
  padding: var(--s-6) var(--page-edge) var(--s-7);
}

.goen-band--shelf .goen-band__head {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
}

.goen-band--shelf .goen-band__name {
  font-family: var(--font-sans);
  font-size: var(--t-32);
  font-weight: var(--fw-display);
  hyphens: auto;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0;
  overflow-wrap: break-word;
  text-wrap: balance;
}

.goen-band--shelf .goen-band__name:lang(zh) {
  font-family: var(--font-editorial);
  font-weight: 500;
  letter-spacing: 0;
}

.goen-band--shelf .goen-band__fact {
  font-size: var(--t-15);
  margin: 0;
  overflow-wrap: break-word;
}

.goen-band--shelf .goen-band__fact:lang(zh) {
  word-break: keep-all;
}

.goen-band--shelf .goen-home__more {
  align-self: flex-start;
  margin-top: var(--s-2);
}

.goen-band--shelf .goen-band__tiles {
  grid-auto-columns: max(42%, 8rem);
}

@container band (width >= 48rem) {
  .goen-band--shelf .goen-band__tiles {
    gap: var(--col-gap);
    grid-auto-flow: row;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-inline: 0;
    overflow: visible;
    padding-inline: 0;
  }
}

@container band (width >= 60rem) {
  .goen-band--shelf .goen-band__body {
    column-gap: 1.5rem;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    padding-block: var(--s-7);
  }

  .goen-band--shelf .goen-band__head {
    grid-column: 1 / 3;
    padding-right: var(--s-5);
  }

  .goen-band--shelf .goen-band__tiles {
    gap: 1.5rem;
    grid-column: 3 / -1;
  }
}

@media (forced-colors: active) {
  .goen-band--shelf {
    border-block: 1px solid CanvasText;
  }
}

/* The head is a listing's first screen. The copy sets its height and the
   photograph fills it, seven parts to the copy's five, at least 9:4: the floor
   is a pseudo-element's padding because a grid item with an aspect-ratio that is
   stretched taller widens with it. The room a short copy leaves goes between the
   crumbs and the name, not under the sub-categories. */
@media (min-width: 48rem) {
  .goen-pagehead .goen-band {
    --lift: 2rem;
  }

  .goen-pagehead .goen-band__grid {
    align-items: stretch;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  }

  .goen-pagehead .goen-band__media {
    margin: 0 0 var(--lift);
    position: relative;
  }

  .goen-pagehead .goen-band__media::before {
    content: "";
    display: block;
    padding-top: calc(100% * 4 / 9);
  }

  .goen-pagehead .goen-band__media img {
    aspect-ratio: auto;
    height: calc(100% - var(--col-gap));
    inset: 0 auto auto 0;
    object-fit: cover;
    position: absolute;
    width: calc(100% - var(--col-gap));
  }

  .goen-pagehead .goen-band__media--well img {
    object-fit: contain;
  }

  .goen-pagehead .goen-band__body {
    gap: var(--s-3);
    justify-content: flex-end;
    padding: calc(var(--lift) + var(--s-5)) var(--page-edge) var(--s-5) clamp(1.5rem, 4vw, 4rem);
  }

  .goen-pagehead .goen-band__body > .ui-crumbs {
    margin-bottom: auto;
  }

  .goen-pagehead .goen-band:has(> .goen-deptnotice) .goen-band__media {
    margin-bottom: 0;
  }

  .goen-pagehead .goen-band__body:first-child {
    grid-column: 1 / -1;
    padding-left: var(--page-edge);
  }
}

/* ---------- Product tile ----------

   One card, shared by the home page, the listing, search, the deals page, a
   campaign and the wishlist. The whole card is one link, so the compare
   checkbox sits outside it. The grid's column counts are the gate's literal —
   2 / 3 / 4 / 4 at 375 / 768 / 1024 / 1440 (`check-layout.mjs:24-27`).
   ------------------------------------------------------------------------ */

.goen-tiles__grid {
  --tiles-gap-x: 1rem;
  display: grid;
  gap: 2.5rem var(--tiles-gap-x);
  /* Two columns for as long as two cards of 6.5rem fit, so a narrow window or
     text at 200% does not drop to one photograph per row. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(6.5rem, 50% - var(--tiles-gap-x) / 2)), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-tile__cell {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  position: relative;
}

.goen-tile {
  color: var(--n-900);
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 1rem;
  text-decoration: none;
}

.goen-tile:hover {
  color: var(--n-900);
}

/* The ring goes around the photograph, not the card's text. */
.goen-tile:focus-visible {
  outline: none;
}

.goen-tile:focus-visible .goen-tile__media {
  outline: 2px solid var(--ring);
  outline-offset: 3px;
}

.goen-tile:hover .goen-tile__img,
.goen-tile:hover .goen-tile__ph {
  opacity: 0.85;
}

.goen-tile:hover .goen-tile__name {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The ground is the photographs' own, so the picture's edge disappears and
   the product floats. Every product photograph is shot on exactly that ground,
   so the picture can be held in from the well's edge without drawing one: the
   larger the card, the more air, so a 300px card is not a product pressing on
   its corners, and a phone's 170px card keeps its product at full size.
   Contain, not cover, so a photo of another proportion is never cropped. */
.goen-tile__media {
  aspect-ratio: 1;
  background: var(--photo);
  border-radius: var(--r-lg);
  overflow: hidden;
  position: relative;
}

.goen-tile__img {
  box-sizing: border-box;
  display: block;
  height: 100%;
  object-fit: contain;
  padding: max(0px, calc((100% - 12rem) * 0.22));
  width: 100%;
}

.goen-tile__ph {
  box-sizing: border-box;
  color: var(--n-500);
  display: grid;
  font-size: var(--fs-13);
  height: 100%;
  padding: 1rem;
  place-items: center;
  text-align: center;
  width: 100%;
}

/* Sold out is said in words beside the price; the photograph only steps back. */
.goen-tile--out .goen-tile__img,
.goen-tile--out:hover .goen-tile__img {
  opacity: 0.5;
}

.goen-tile__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
}

.goen-tile__brand {
  color: var(--n-500);
  font-size: var(--fs-13);
  line-height: var(--lh-normal);
}

.goen-tile__name {
  -webkit-box-orient: vertical;
  color: var(--n-900);
  display: -webkit-box;
  font-size: var(--fs-15);
  font-weight: var(--fw-regular);
  -webkit-line-clamp: 2;
  line-clamp: 2;
  line-height: var(--lh-normal);
  margin: 0;
  overflow: hidden;
  /* A two-line name never leaves one Han character alone on the second. */
  text-wrap: pretty;
}

.goen-tile__price {
  align-items: baseline;
  position: relative;
  color: var(--n-900);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-16);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  gap: 8px;
  padding-top: 4px;
}

.goen-tile__amount {
  overflow-wrap: normal;
}

.goen-tile__state {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
}

.goen-tile__colours {
  align-items: center;
  color: var(--n-500);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-12);
  gap: 6px;
  padding-top: 4px;
}

.goen-tile__dot {
  display: block;
  flex: none;
  height: 12px;
  width: 12px;
}

.goen-tile__dot circle {
  stroke: var(--swatch-ring);
  stroke-width: 1;
}

@media (forced-colors: active) {
  .goen-tile__dot circle {
    stroke: CanvasText;
  }
}

.goen-tile__was {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
  text-decoration: line-through;
}

/* Outside the card, never inside: the whole card is one link, and a control
   nested in an anchor is neither clickable nor valid. It is a bare checkbox on
   the photograph's corner, shown while the card is hovered or focused and
   whenever it is ticked; the label is only the 44px hit area around it. */
.goen-tile__compare {
  align-items: center;
  cursor: pointer;
  display: flex;
  height: 44px;
  justify-content: center;
  opacity: 0;
  position: absolute;
  right: 0;
  top: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
  width: 44px;
  z-index: 1;
}

.goen-tile__cell:hover .goen-tile__compare,
.goen-tile__cell:focus-within .goen-tile__compare,
.goen-tile__compare:has(:checked) {
  opacity: 1;
}

@media (hover: none) {
  .goen-tile__compare {
    opacity: 0.7;
  }

  .goen-tile__compare:has(:checked) {
    opacity: 1;
  }
}

.goen-tile__compare .goen-check {
  background: var(--n-0);
  border-radius: var(--r-sm);
}

/* The bare checkbox; a label that carries the same name (the back office's)
   must not inherit the 16px box, or its text overflows the row. */
input.goen-check {
  accent-color: var(--accent);
  flex: none;
  height: 16px;
  width: 16px;
}

.goen-compare-pick {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.goen-compare-pick__go {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.goen-compare-pick__note {
  color: var(--fg-muted);
  font-size: var(--fs-14);
}

@media (min-width: 768px) {
  .goen-tiles__grid {
    --tiles-gap-x: 1.5rem;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .goen-tiles__grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ---------- Home product row ----------
   A campaign with at least four products whose first photograph is wide enough:
   the first product is a 2×2 tile on the campaign's tone, three more cards, and
   the campaign card as the eighth cell. Under 1024px the lead takes a full row
   and the card a full row after the cards. */

.goen-home__facts {
  margin: calc(var(--s-3) * -1) 0 var(--s-6);
}

.goen-tiles__grid--lead > .goen-tile__cell--lead,
.goen-tiles__grid--lead > .goen-rowcard {
  grid-column: 1 / -1;
}

.goen-tile__cell--lead .goen-tile__media {
  aspect-ratio: 4 / 3;
  background: var(--tone-ground);
}

.goen-tile__cell--lead .goen-tile__img {
  background: var(--photo);
  border-radius: var(--r-md);
  height: calc(100% - 2 * var(--s-5));
  inset: var(--s-5) auto auto var(--s-5);
  padding: 0;
  position: absolute;
  width: calc(100% - 2 * var(--s-5));
}

.goen-tile__cell--lead .goen-tile__name,
.goen-tile__cell--lead .goen-tile__price {
  font-size: var(--fs-18);
}

.goen-rowcard {
  background: var(--tone-ground);
  border-radius: var(--r-lg);
  color: var(--tone-text);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
  padding: var(--s-4) var(--s-5);
}

.goen-rowcard .ui-statline dt,
.goen-rowcard .ui-statline dd small,
.goen-rowcard .ui-statline__note {
  color: var(--tone-muted);
}

.goen-tiles__grid--lead[data-tone] .ui-period {
  --period-fill: var(--tone-mark);
}

.goen-tiles__grid--lead[data-tone="ink"] .ui-period {
  --period-track: var(--period-track-on-ink);
}

.goen-rowcard > .ui-period {
  margin-top: var(--s-2);
}

.goen-rowcard .goen-home__more {
  align-self: flex-start;
  color: var(--tone-mark);
  margin-top: auto;
  white-space: normal;
}

@media (min-width: 64rem) {
  .goen-tiles__grid--lead > .goen-tile__cell--lead {
    grid-column: 1 / 3;
    grid-row: 1 / 3;
  }

  .goen-tiles__grid--lead > .goen-rowcard {
    grid-column: auto;
  }

  .goen-tile__cell--lead .goen-tile__media {
    aspect-ratio: auto;
    flex: 1;
    min-height: 16rem;
  }

  .goen-tile__cell--lead .goen-tile__body {
    flex: none;
  }
}

/* ---------- Listing and search ----------

   The category page, search results, the deals page, a campaign, the wishlist
   and the comparison all sit on `.goen-listing` or `.goen-compare`: the same
   content column the header and the footer use, a page head, and then whatever
   the page is about.

   The filters are a native <details> at every width. Above 1024, where the
   browser has ::details-content, its summary is hidden and the slot is forced
   open, because a descendant display rule alone cannot override the closed
   details slot in Chrome; elsewhere the toolbar stays behind its summary.
   ------------------------------------------------------------------------ */

.goen-listing {
  display: flex;
  flex-direction: column;
  gap: 24px;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 24px var(--page-gutter) 56px;
}

.goen-listing__layout {
  display: grid;
  gap: 24px;
}

.goen-listing__filters {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0 12px;
  min-width: 0;
}

.goen-listing__main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

#filters-applied:empty {
  display: none;
}

.goen-listing__results {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

.goen-filters__shell {
  min-width: 0;
}

.goen-listing__filters .goen-listing__count {
  margin-inline-start: auto;
}

/* An open sheet takes the row; the count goes under it, not beside it. */
@media (max-width: 63.99rem) {
  .goen-filters__shell[open] {
    flex: 1 1 100%;
  }
}

.goen-filters__shell-summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 8px;
  list-style: none;
  min-height: 44px;
}

.goen-filters__shell-summary::-webkit-details-marker {
  display: none;
}

.goen-filters__shell-summary::after,
.goen-filters__summary::after {
  background: var(--chevron) center / 1.15em no-repeat;
  content: '';
  flex: none;
  height: 1.15em;
  transition: transform var(--dur-base) var(--ease-standard);
  width: 1.15em;
}

.goen-filters__shell[open] > .goen-filters__shell-summary::after,
.goen-filters__group[open] > .goen-filters__summary::after {
  transform: rotate(180deg);
}

/* A closed disclosure hides its form outright. Chrome keeps the layout of the
   subtree ::details-content skips, so a form that has been measured once goes
   on measuring — which is the mobile half of #390. */
.goen-filters__shell:not([open]) > .goen-filters {
  display: none;
}

.goen-filters {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 0 0 16px;
}

/* The heading keeps the outline unbroken at every width, but on a phone the
   disclosure's own summary already says it, so it is read, not shown. */
.goen-filters__title {
  border: 0;
  clip-path: inset(50%);
  font-size: var(--fs-16);
  font-weight: var(--fw-medium);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.goen-filters__menus,
.goen-filters__end {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.goen-filters__group {
  padding-bottom: 12px;
}

.goen-filters__summary {
  align-items: center;
  cursor: pointer;
  color: var(--ink-2);
  display: flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 8px;
  list-style: none;
  min-height: 44px;
}

.goen-filters__summary:hover,
.goen-filters__group[open] > .goen-filters__summary {
  color: var(--ink);
}

.goen-filters__summary::-webkit-details-marker {
  display: none;
}

.goen-filters__summary::after {
  margin-inline-start: auto;
}

.goen-filters__options {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
}

/* The whole row is the target, not the box inside it. */
.goen-filters__option {
  align-items: center;
  border-radius: var(--r-md);
  cursor: pointer;
  display: flex;
  font-size: var(--fs-14);
  gap: 8px;
  min-height: 44px;
  padding: 0 8px;
  transition: background-color var(--dur-base) var(--ease-standard);
}

.goen-filters__option:hover {
  background: var(--elevated);
}

.goen-filters__label {
  flex: 1;
  min-width: 0;
}

.goen-filters__count {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  font-variant-numeric: tabular-nums;
}

/* How many of a group's options are on. Empty when none, and then not drawn;
   the element stays so a swap has somewhere to put the number. */
.goen-filters__selected {
  align-items: center;
  background: var(--accent-faint);
  border-radius: var(--r-full);
  color: var(--accent-deep);
  display: inline-flex;
  font-size: var(--fs-12);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  justify-content: center;
  line-height: 1;
  min-height: 1.25rem;
  min-width: 1.25rem;
  padding: 0 0.375rem;
}

.goen-filters__selected:empty {
  display: none;
}

.goen-filters__prices {
  align-items: center;
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

.goen-filters__dash {
  color: var(--fg-muted);
  flex: none;
}

.goen-filters__field {
  align-items: center;
  display: flex;
  gap: 4px;
}

.goen-filters__field > .goen-label {
  color: var(--fg-muted);
  font-weight: var(--fw-regular);
}

/* The price pair and the sort control keep the shared names: the shell probe
   at `check-layout.mjs:668` decides the desktop rail rendered at all by finding
   one of them, and the tap sweep at `:805` measures them. */
.goen-filters .ui-input {
  background: var(--bg);
  border: 0;
  border-radius: var(--r-md);
  box-sizing: border-box;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--fs-16);
  outline: 1px solid var(--control-boundary);
  outline-offset: -1px;
  min-height: 44px;
  min-width: 0;
  padding: 0 12px;
  width: 100%;
}

/* The sort is words and an arrow, not a box. The arrow is its cue (1.4.11,
   5.70:1 on white) and the target stays 44px tall. */
.goen-filters .ui-select,
.goen-search-sort .ui-select {
  appearance: none;
  background: transparent var(--chevron) no-repeat right 0.25rem center / 1.15em;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  color: var(--fg);
  cursor: pointer;
  field-sizing: content;
  font-family: inherit;
  font-size: var(--fs-16);
  font-weight: var(--fw-medium);
  min-height: 44px;
  min-width: 0;
  outline: 0;
  padding: 0 1.75em 0 0.25rem;
  width: auto;
}

@media (min-width: 640px) {
  .goen-filters .ui-select,
  .goen-search-sort .ui-select {
    font-size: var(--fs-14);
  }
}

.goen-filters__apply {
  margin-top: 8px;
}

.goen-filters__sort {
  display: contents;
}

.goen-filters__sort:empty,
.goen-listing__count:empty {
  display: none;
}

/* The search's one control sits above the grid, on the right where a phone and
   a desktop both find it. */
.goen-search-sort {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0 12px;
  justify-content: flex-end;
  margin-bottom: 16px;
}

.goen-search-sort .goen-listing__count {
  align-items: center;
  display: flex;
  margin-inline-end: auto;
  min-height: 44px;
  order: -1;
}

.goen-search-sort .goen-filters__field > label {
  flex: none;
  white-space: nowrap;
}

.goen-search-sort .goen-filters__apply {
  margin-top: 0;
}

.goen-listing__subhead {
  font-size: var(--fs-ed-20);
  margin: 32px 0 16px;
}

/* Where scripting is on, a changed filter or sort applies itself, so the button
   goes. Hidden when the sheet is told scripting is on, so a browser that has
   never heard of the query keeps the button that always works. */
@media (scripting: enabled) {
  .goen-filters__apply {
    display: none;
  }
}

/* Present even when nothing is filtered so a swap can fill or clear it; an
   empty one must not add a flex gap. */
#filters-applied:empty {
  display: none;
}

.goen-filters__error {
  color: var(--error);
  font-size: var(--fs-14);
  margin: 0;
}

/* The results region takes focus when a filtered page loads, so the next Tab
   starts at the products. It is not a control and must not draw a ring. */
#listing-results:focus,
#listing-results:focus-visible {
  outline: none;
}

.goen-filters__applied {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.goen-filters__applied-label {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  font-weight: var(--fw-medium);
}

.goen-filters__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-filters__chip {
  background: var(--n-100);
  border-radius: var(--r-md);
  color: var(--n-900);
  font-size: var(--fs-13);
  padding: 4px 10px;
}

/* The cross is nine pixels wide: a 44px target centred on it, which takes no
   room, so the chip is the size it was. */
.goen-filters__chip-remove {
  position: relative;
}

.goen-filters__chip-remove::after {
  content: '';
  height: 44px;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 44px;
}

.goen-filters__applied .ui-filterbar__clear {
  color: var(--fg-muted);
  font-family: inherit;
  font-size: var(--fs-14);
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 4px;
}

.goen-filters__applied .ui-filterbar__clear:hover {
  color: var(--fg);
}

.goen-pager {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.goen-pager__note {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  line-height: var(--lh-normal);
  margin: 0;
}

.goen-pager__at {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  margin: 0;
}

/* The next page is a link under a line that says how far along the run is. */
.goen-pager--more {
  gap: 12px;
}

.goen-pager__progress {
  appearance: none;
  background: var(--n-200);
  block-size: 2px;
  border: 0;
  color: var(--n-900);
  display: block;
  inline-size: min(100%, 20rem);
  min-inline-size: 220px;
}

.goen-pager__progress::-webkit-progress-bar {
  background: var(--n-200);
}

.goen-pager__progress::-webkit-progress-value {
  background: var(--n-900);
}

.goen-pager__progress::-moz-progress-bar {
  background: var(--n-900);
}

.goen-pager__links {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: center;
}

.goen-pager__prev {
  color: var(--n-700);
  font-size: var(--fs-14);
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* The department head is the band: its grid, photograph and body are the
   band's. */
.goen-pagehead__text {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

.goen-pagehead[data-tone] .ui-crumbs,
.goen-pagehead[data-tone] .ui-crumbs__link,
.goen-pagehead[data-tone] .ui-crumbs__sep {
  color: var(--tone-muted);
}

.goen-pagehead[data-tone] .ui-crumbs__link:hover,
.goen-pagehead[data-tone] .ui-crumbs__link:focus-visible {
  color: var(--tone-text);
}

.goen-pagehead[data-tone] .ui-crumbs__current {
  color: var(--tone-text);
}

.goen-pagehead__chips,
.goen-empty__links {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* A phone's chips scroll sideways in one row; a wrapped second row would push
   the first product down. The row bleeds by the gutter, which is in px: a rem
   bleed grows with the reader's text and runs past the screen. */
.goen-pagehead__chips {
  margin-inline: calc(var(--page-gutter) * -1);
  overflow-x: auto;
  padding-inline: var(--page-gutter);
  scrollbar-width: none;
}

@media (min-width: 48rem) {
  .goen-pagehead__chips {
    flex-wrap: wrap;
    margin-inline: 0;
    overflow: visible;
    padding-inline: 0;
  }
}

.goen-pagehead__chip {
  align-items: center;
  background: var(--n-0);
  border: 1px solid var(--tone-rule, var(--n-200));
  border-radius: var(--r-md);
  color: var(--n-900);
  display: inline-flex;
  flex: none;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  min-height: 2.75rem;
  padding: 0 1rem;
  text-decoration: none;
  white-space: nowrap;
}

.goen-pagehead__chip:hover {
  background: var(--n-100);
  color: var(--n-900);
}

/* The department head's sub-categories are words on the tone, each a pill-sized
   target on hover; only the current one is filled. They do not scroll as a
   boxed button row: the empty search's shortcuts above keep the box. */
.goen-listing__head .goen-pagehead__chips {
  gap: 0;
  margin-top: var(--s-2);
  padding-inline-start: var(--page-gutter);
}

@media (min-width: 48rem) {
  .goen-listing__head .goen-pagehead__chips {
    flex-wrap: wrap;
    margin-inline: calc(var(--s-4) * -1) 0;
    overflow: visible;
    padding-inline: 0;
  }
}

.goen-listing__head .goen-pagehead__chip {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-full);
  color: var(--ink-2);
  font-size: var(--fs-15);
  font-weight: var(--fw-regular);
  min-height: 2.75rem;
  padding: 0 var(--s-4);
}

.goen-listing__head .goen-pagehead__chip:hover {
  background: rgb(255 255 255 / 0.62);
  color: var(--tone-text);
}

.goen-listing__head .goen-pagehead__chip[aria-current='page'] {
  background: var(--accent-faint);
  border-color: var(--accent);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}

.goen-listing__head [data-tone='ink'] .goen-pagehead__chip {
  color: var(--tone-text);
}

.goen-listing__head [data-tone='ink'] .goen-pagehead__chip:hover {
  background: rgb(255 255 255 / 0.1);
}

.goen-listing__head [data-tone='ink'] .goen-pagehead__chip[aria-current='page'] {
  background: rgb(255 255 255 / 0.1);
  border-color: var(--tone-text);
  color: var(--on-ink-accent);
}

.goen-empty__links {
  flex-wrap: wrap;
  justify-content: center;
}

.goen-listing__count {
  color: var(--n-500);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  margin: 0;
  white-space: nowrap;
}

/* The running-promotions strip above the deals grid. A campaign is a
   destination and a marked-down product is an item; they must not read as the
   same kind of thing stacked together. */
.goen-campaigns {
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.goen-campaigns__heading {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-campaigns__list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-campaigns__item {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--fg);
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 44px;
  padding: 12px;
  text-decoration: none;
  transition: border-color var(--dur-base) var(--ease-standard);
}

.goen-campaigns__item:hover {
  border-color: var(--accent);
  color: var(--fg);
}

.goen-campaigns__title {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
}

.goen-campaigns__meta {
  color: var(--fg-muted);
  font-size: var(--fs-12);
}

/* The comparison table. It scrolls inside its own box so the PAGE never moves
   sideways, and the label column stays put while the products scroll past it —
   without that a reader loses which row they are on the moment they move. */
.goen-compare {
  display: flex;
  flex-direction: column;
  gap: 24px;
  margin: 0 auto;
  max-width: var(--page-width);
  padding: 24px var(--page-gutter) 56px;
}

.goen-compare__scroll {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow-x: auto;
  position: relative;
}

.goen-compare__table {
  border-collapse: collapse;
  font-size: var(--fs-14);
  min-width: 100%;
}

.goen-compare__table td,
.goen-compare__table th {
  border-bottom: 1px solid var(--border);
  min-width: 160px;
  padding: 12px;
  text-align: left;
  vertical-align: top;
}

.goen-compare__table tbody th,
.goen-compare__table thead th:first-child {
  background: var(--bg);
  color: var(--fg-muted);
  font-weight: var(--fw-medium);
  left: 0;
  min-width: 140px;
  position: sticky;
  text-align: left;
  z-index: var(--z-base);
}

.goen-compare__head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.goen-compare__image {
  border-radius: var(--r-md);
  height: 96px;
  margin: 0;
  object-fit: cover;
  width: 96px;
}

.goen-compare__name {
  color: var(--fg);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin-top: 8px;
  text-decoration: none;
}

.goen-compare__name:hover {
  color: var(--fg);
  text-decoration: underline;
}

.goen-compare__brand {
  color: var(--fg-muted);
  font-size: var(--fs-12);
}

.goen-compare__remove {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  margin-top: 8px;
}

.goen-compare__price {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
}

/* A row only some products state is a footnote, not a comparison. It reads
   quieter so the eye lands on the rows that can actually be compared. */
.goen-compare__table .is-partial td,
.goen-compare__table .is-partial th {
  color: var(--fg-muted);
}

/* A row the products disagree on: a ground and the ink colour, and the row
   label says so aloud. Shown by more than colour for a reader who cannot see
   the ground. */
.goen-compare__table .is-differs td,
.goen-compare__table .is-differs th {
  background: var(--n-50);
  color: var(--n-900);
}

/* A fact line on a head laid on a tone's ground takes the tone's text colours. */
.goen-pagehead[data-tone] .ui-statline {
  margin-top: var(--s-3);
  color: var(--tone-text);
}

.goen-pagehead[data-tone] .ui-statline dd {
  color: var(--tone-text);
}

.goen-pagehead[data-tone] .ui-statline dt,
.goen-pagehead[data-tone] .ui-statline dd small,
.goen-pagehead[data-tone] .ui-statline__note {
  color: var(--tone-muted);
}

/* The band's last row, on the tone and with no wash of its own. */
.goen-band > .goen-deptnotice {
  padding: var(--s-3) var(--page-edge) var(--s-4);
}

/* The whole line is the link, read as words: the campaign's name and the arrow
   take the tone's link colour, the rest the tone's text. */
.goen-deptnotice__name {
  color: var(--tone-text);
  font-size: var(--fs-14);
  text-decoration: none;
}

.goen-deptnotice__name b {
  color: var(--tone-mark);
  font-weight: var(--fw-medium);
}

.goen-deptnotice__name:hover b {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.goen-deptnotice__eyebrow {
  color: var(--tone-muted);
  font-size: var(--fs-12);
  margin-inline-end: var(--s-2);
}

.goen-deptnotice__until {
  color: var(--tone-muted);
  white-space: nowrap;
}

.goen-deptnotice__name > [aria-hidden="true"],
.goen-deptnotice__until > [aria-hidden="true"] {
  color: var(--tone-muted);
  margin-inline: 0.25em;
}

.goen-deptnotice__left {
  font-weight: var(--fw-medium);
  word-break: keep-all;
}

.goen-deptnotice__left > [aria-hidden] {
  color: var(--tone-mark);
}

.goen-deptcompare__head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--s-4);
  margin-bottom: var(--s-4);
}

.goen-deptcompare__title {
  font-size: var(--fs-ed-24);
  font-weight: var(--fw-display);
  margin: 0;
}

.goen-deptcompare__meta {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-deptcompare__more {
  color: var(--accent-text);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin-left: auto;
  text-underline-offset: 3px;
}

.goen-story {
  align-items: center;
  display: grid;
  gap: var(--s-6);
}

.goen-story__eyebrow {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  margin: 0;
}

.goen-story__title {
  font-size: var(--fs-ed-24);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  margin: var(--s-2) 0 0;
  text-wrap: balance;
}

:lang(zh) .goen-story__title {
  font-family: var(--font-editorial);
}

.goen-story__deck {
  color: var(--fg-muted);
  line-height: 1.75;
  margin: var(--s-3) 0 0;
  max-width: 26em;
}

.goen-story__buy {
  margin: var(--s-4) 0 0;
}

.goen-story__link {
  color: var(--accent-text);
  display: inline-block;
  font-weight: var(--fw-medium);
  margin-top: var(--s-4);
  text-decoration: none;
  text-underline-offset: 3px;
}

.goen-story__plates {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.goen-story__plate {
  margin: 0;
  min-width: 0;
}

.goen-story__well {
  aspect-ratio: 4 / 3;
  background: var(--well);
  border-radius: var(--r-md);
  overflow: hidden;
}

.goen-story__well img {
  display: block;
  height: 100%;
  object-fit: contain;
  width: 100%;
}

.goen-story__plate figcaption {
  align-items: center;
  display: flex;
  font-size: var(--fs-14);
  gap: var(--s-2);
  padding-top: var(--s-2);
}

.goen-story__swatch circle {
  stroke: var(--swatch-ring);
  stroke-width: 1;
}

@media (forced-colors: active) {
  .goen-story__swatch circle {
    stroke: CanvasText;
  }
}

@media (min-width: 1024px) {
  .goen-story {
    gap: clamp(32px, 5vw, 88px);
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  }

  .goen-story__title {
    font-size: var(--fs-ed-30);
  }
}

/* The first product stays in the first viewport at 375px (check-layout.mjs:1126), so the
   editorial gives way below the prototype's stack breakpoint. */
@media (max-width: 900px) {
  .goen-deptcompare,
  .goen-story {
    display: none;
  }
}

/* Where products are put side by side, a row holds five and a card says what it is under
   its name. */
.goen-tile__specs {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  line-height: var(--lh-normal);
}

@media (min-width: 1280px) {
  .goen-listing[data-comparable='true'] .goen-tiles__grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

/* Two products stay in view on a phone; the rest scroll past a label column that stays. */
@media (max-width: 639px) {
  .goen-compare__table td,
  .goen-compare__table th {
    min-width: 7.5rem;
    padding: 12px 6px;
  }

  .goen-compare__table tbody th,
  .goen-compare__table thead th:first-child {
    max-width: 4.5rem;
    min-width: 4.5rem;
  }
}

/* A lone product's way on: others from its shelf, each with the one action. */
.goen-compare__suggest {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.goen-compare__suggesthead {
  color: var(--n-900);
  font-size: var(--fs-ed-24);
  font-weight: var(--fw-display);
  line-height: 1.35;
  margin: 0;
}

.goen-compare__suggestlist {
  display: grid;
  gap: 2.5rem 1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-compare__suggestion {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.goen-compare__suggestmedia {
  aspect-ratio: 1;
  background: var(--photo);
  border-radius: var(--r-lg);
  display: block;
  margin-bottom: 0.75rem;
  overflow: hidden;
  width: 100%;
}

.goen-compare__suggestmedia img {
  display: block;
  height: 100%;
  object-fit: contain;
  width: 100%;
}

.goen-compare__suggestion .goen-compare__price {
  font-weight: var(--fw-medium);
}

.goen-compare__suggestion .goen-btn {
  margin-top: 0.5rem;
}

.goen-compare__shelf {
  margin: 0;
}

.goen-compare__shelf a {
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.goen-compare__shelf a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

@media (min-width: 768px) {
  .goen-compare__suggestlist {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 1280px) {
  .goen-compare__suggestlist {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

/* One saved product: the card, and under it the form that removes it. The form
   stays outside the card because the card is a link. */
.goen-wish {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.goen-wish__remove,
.goen-wish__add {
  margin: 0;
}

.goen-wish__remove {
  align-self: flex-start;
}

.goen-wish__remove .goen-btn {
  background: none;
  color: var(--fg-muted);
  padding-inline: 0;
  width: auto;
}

.goen-wish__remove .goen-btn:hover {
  background: none;
  color: var(--fg);
}

.goen-wish__stock {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  line-height: var(--lh-normal);
  margin: 0;
}

@media (min-width: 768px) {
  .goen-campaigns {
    padding: 24px;
  }

  .goen-campaigns__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .goen-pager {
    flex-direction: row;
    justify-content: space-between;
  }

  .goen-pager--more {
    flex-direction: column;
    justify-content: center;
  }
}

@media (min-width: 1024px) {
  .goen-listing {
    gap: 32px;
    padding: 32px var(--page-gutter) 56px;
  }

  .goen-campaigns__list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .goen-compare {
    gap: 32px;
    padding: 32px var(--page-gutter) 56px;
  }

  /* The filters are a toolbar above the grid: a chip for each facet, each a
     disclosure whose panel hangs under it, and the sort and the count at the far
     end. The shell's summary is for a phone, so the rules that open the shell
     are guarded: where ::details-content does not exist the toolbar stays behind
     its summary, which works. */
  .goen-listing__filters {
    gap: 8px 24px;
  }

  .goen-filters__shell {
    flex: 1 1 auto;
  }

  @supports selector(details::details-content) {
    .goen-filters__shell-summary {
      display: none;
    }

    .goen-filters__shell::details-content {
      content-visibility: visible;
    }

    .goen-filters__shell:not([open]) > .goen-filters {
      display: flex;
    }
  }

  .goen-filters {
    align-items: center;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px 16px;
    padding: 0;
  }

  .goen-filters__menus,
  .goen-filters__end {
    align-items: center;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px 24px;
  }

  .goen-filters__end {
    margin-inline-start: auto;
  }

  .goen-filters__group {
    border: 0;
    padding: 0;
    position: relative;
  }

  .goen-filters__summary {
    gap: 6px;
    padding: 0 4px 0 0;
  }

  .goen-filters__summary::after {
    margin-inline-start: 0;
  }

  .goen-filters__options,
  .goen-filters__prices {
    background: var(--n-0);
    border: 1px solid var(--n-200);
    border-radius: var(--r-lg);
    box-shadow: 0 16px 32px -16px rgb(0 0 0 / 0.2);
    left: 0;
    margin: 0;
    min-width: 15rem;
    padding: 12px;
    position: absolute;
    top: calc(100% + 0.75rem);
    z-index: 4;
  }

  .goen-filters__end .goen-filters__apply {
    margin-top: 0;
    width: auto;
  }
}

/* ---------- Product page ----------

   A breadcrumb over two columns: the gallery 7/12 and the buy column 5/12 from
   1024, stacked below. Under them an accordion, the reviews, the questions and
   a row of similar products, in source order.

   Two choices are not the obvious ones, each for a reason the page's own
   behaviour sets:

   - A variant is chosen by a LINK, because the choice is a URL. A scripted
     select would make it unreachable with scripting off.
   - The gallery is a radio group drawn with CSS: nothing to initialise, so the
     swap of #gallery by a colour swatch has no state to carry across.
   ------------------------------------------------------------------------ */

/* overflow-anchor is what moved the swatch row under a thumb, and it was trying
   to help: choosing a variant removes the "choose every option" line, the
   browser picks a node below it to hold still, and scrolls to compensate. The
   row's own position in the document never changed, so the compensation was the
   whole of the movement. It is switched off here, on the page, rather than on
   the region that swaps — the node the browser anchors to is below that region,
   which is why doing it there had no effect. The band below the buy box is such
   a node too, so it is switched off there as well. */
.goen-pdp {
  display: flex;
  flex-direction: column;
  gap: 4rem;
  margin: 0 auto;
  max-width: var(--page-width);
  overflow-anchor: none;
  padding: 1.5rem var(--page-gutter) 4rem;
}

.goen-pdp--after {
  padding-top: 0;
}

.goen-pdp__intro {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.goen-pdp__top {
  display: grid;
  gap: 2rem;
}

.goen-pdp__sectiontitle {
  color: var(--n-900);
  font-size: var(--fs-ed-24);
  font-weight: var(--fw-display);
  line-height: 1.35;
  margin: 0;
}

/* Gallery. The stage and the thumbnails share one grid: every shot sits in the
   first row across all columns, so only the chosen one is visible and the row
   keeps its height as the choice changes. Five columns so a thumbnail stays a
   comfortable target at 375 and a sixth wraps rather than shrinks. */
.goen-pdp__gallery {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-auto-rows: min-content;
  min-width: 0;
  position: relative;
}

/* The item is only a grouping for the markup: its children are laid out by the
   gallery's grid. */
.goen-pdp__item {
  display: contents;
}

.goen-pdp__shot {
  background: var(--tone-ground);
  border-radius: var(--r-lg);
  grid-column: 1 / -1;
  grid-row: 1;
  margin: 0;
  overflow: hidden;
  padding: clamp(0.75rem, 2.2vw, 2rem);
}

.goen-pdp__pick:not(:checked) ~ .goen-pdp__shot {
  visibility: hidden;
}

.goen-pdp__shotimg {
  aspect-ratio: 4 / 3;
  background: var(--photo);
  background-clip: content-box;
  border: var(--col-gap) solid var(--n-0);
  box-sizing: border-box;
  display: block;
  height: auto;
  object-fit: contain;
  width: 100%;
}

.goen-pdp__shot--empty {
  align-items: center;
  aspect-ratio: 4 / 3;
  color: var(--tone-muted);
  display: flex;
  font-size: var(--fs-14);
  justify-content: center;
}

.goen-pdp__thumb {
  border-bottom: 2px solid transparent;
  cursor: pointer;
  grid-row: 2;
  min-width: 0;
  padding-bottom: 0.375rem;
}

.goen-pdp__thumbimg {
  aspect-ratio: 4 / 3;
  background: var(--photo);
  border-radius: 2px;
  display: block;
  height: auto;
  object-fit: contain;
  width: 100%;
}

.goen-pdp__pick:checked ~ .goen-pdp__thumb {
  border-bottom-color: var(--accent);
}

.goen-pdp__pick:focus-visible ~ .goen-pdp__thumb {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

@media (forced-colors: active) {
  .goen-pdp__thumb {
    border-bottom-color: Canvas;
  }

  .goen-pdp__pick:checked ~ .goen-pdp__thumb {
    border-bottom-color: CanvasText;
  }
}

/* Buy column. scroll-margin-top is for the visitor with no script: adding to
   the cart is a POST, a 303 and a landing on #buybox, and without it the
   browser puts the column's first pixel flush against the top of the window. */
.goen-pdp__buy {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  min-width: 0;
  scroll-margin-top: 24px;
}

.goen-pdp__head {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.goen-pdp__brand {
  color: var(--n-500);
  font-size: var(--fs-14);
  line-height: 1.5;
  margin: 0;
}

.goen-pdp__titlerow {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

.goen-pdp__title {
  color: var(--n-900);
  font-size: 1.75rem;
  font-weight: var(--fw-display);
  line-height: 1.3;
  margin: 0;
  text-wrap: balance;
}

/* Values only; a line breaks between them, never inside one. */
.goen-pdp__keyspecs {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  line-height: 1.6;
  margin: 0;
}

.goen-pdp__keyspecs span {
  display: inline-block;
  max-width: 100%;
}

.goen-pdp__keyspecs span:not(:last-child)::after {
  content: '·';
  content: '·' / '';
  margin: 0 0.5rem;
}

/* A choice the product does not offer: stated, not asked. */
.goen-pdp__fact {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-pdp__lede {
  color: var(--n-700);
  line-height: 1.7;
  margin: 0;
}

.goen-pdp__rating {
  align-items: center;
  align-self: flex-start;
  color: var(--n-500);
  display: inline-flex;
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  gap: 0.5rem;
  text-decoration: none;
}

.goen-pdp__rating:hover {
  color: var(--n-900);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The star is a graphic, so its fill may be the one amber the page uses; the
   words beside it are --n-500 and carry the number. An unfilled star is the
   control border grey. */
.goen-pdp__rate {
  display: inline-flex;
  gap: 2px;
}

.goen-pdp__rate svg {
  color: var(--n-300);
  display: block;
  height: 1rem;
  width: 1rem;
}

.goen-pdp__rate--on svg,
.goen-pdp__ratehalf svg {
  color: var(--star);
  fill: var(--star);
}

/* A half star is the grey star with the amber one laid over its left half. */
.goen-pdp__rate--half {
  position: relative;
}

.goen-pdp__ratehalf {
  inset: 0 50% 0 0;
  overflow: hidden;
  position: absolute;
}

.goen-pdp__pricebox {
  align-items: baseline;
  container-type: inline-size;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
}

.goen-pdp__price {
  color: var(--n-900);
  font-size: var(--fs-ed-30);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-regular);
  line-height: 1.3;
}

/* A phone at 200% text leaves the box under 20rem, where "NT$19,900" at its own
   size is wider than the line. */
@container (width < 20rem) {
  .goen-pdp__price {
    font-size: min(var(--fs-ed-30), 14cqi);
    overflow-wrap: anywhere;
  }
}

.goen-pdp__was {
  color: var(--n-500);
  font-size: var(--fs-16);
  font-variant-numeric: tabular-nums;
}

.goen-pdp__source {
  color: var(--n-700);
  font-size: var(--fs-14);
  margin: 0.125rem 0 0;
}

.goen-pdp__left {
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
  word-break: keep-all;
}

/* An option group. The fieldset keeps the legend, which is the only thing that
   tells a screen reader what the row of choices is choosing between. */
.goen-pdp__option {
  border: 0;
  margin: 0;
  min-width: 0;
  padding: 0;
}

.goen-pdp__optname {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  line-height: 1.5;
  padding: 0 0 0.75rem;
}

/* A colour has no words on it, so the chosen one says its name here. */
.goen-pdp__optchosen {
  color: var(--n-500);
  font-weight: var(--fw-regular);
}

.goen-pdp__values {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.goen-pdp__values--swatches {
  gap: 0.25rem;
}

/* An option value. A LINK, not a button: choosing navigates to the URL for
   that combination, so the choice survives a reload, a shared address and
   scripting being off. */
.goen-swatch {
  align-items: center;
  background: var(--n-0);
  border: 1px solid var(--control-boundary);
  border-radius: var(--r-md);
  box-sizing: border-box;
  color: var(--n-900);
  display: inline-flex;
  flex-direction: column;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  justify-content: center;
  line-height: 1.3;
  min-height: 3rem;
  min-width: 6rem;
  padding: 0.25rem 1rem;
  text-decoration: none;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.goen-swatch:hover {
  background: var(--n-50);
  color: var(--n-900);
}

/* The choice is an outline, which forced colours keep where a fill is lost. */
.goen-swatch--on,
.goen-swatch--on:hover {
  border-color: var(--n-900);
  outline: 2px solid var(--n-900);
  outline-offset: 1px;
}

/* A text option is chosen in the accent, drawn inside its own edge: an outline
   would read as focus. Colour discs keep the ink ring, which a blue ring would
   blur into a blue disc. */
.goen-swatch--on:not(.goen-swatch--dot) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
  outline: none;
}

.goen-swatch--on:not(.goen-swatch--dot):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.goen-swatch__note {
  color: var(--n-500);
  font-size: var(--fs-12);
  font-weight: var(--fw-regular);
}

/* Unavailable in combination with the other choices. Shown, not hidden: a
   visitor who cannot have this one still needs to know the shop sells it, and
   the link still leads to that variant's page. */
.goen-swatch--out {
  border-style: dashed;
  color: var(--n-500);
}

/* A colour swatch: a 2rem disc inside a 44px target, so a thumb has the room a
   finger needs and the colours still read as one row. The ring is an outline on
   the disc, two pixels off its edge. */
.goen-swatch--dot,
.goen-swatch--dot:hover,
.goen-swatch--dot.goen-swatch--on:hover {
  background: transparent;
  border: 0;
  border-radius: 50%;
  min-width: 2.75rem;
  padding: 0;
  width: 2.75rem;
}

.goen-swatch__dot {
  border-radius: 50%;
  display: block;
  height: 2rem;
  width: 2rem;
}

.goen-swatch__dot circle {
  stroke: var(--swatch-ring);
  stroke-width: 1;
}

.goen-swatch--dot.goen-swatch--on .goen-swatch__dot {
  outline: 2px solid var(--n-900);
  outline-offset: 2px;
}

/* Unavailable in this combination: struck rather than dimmed, because a fainter
   disc reads as a paler colour instead of one they cannot have. The strike is
   two lines on one diagonal, the wider in the page's ground and the narrower in
   its ink, so it reads on a near-white disc and a near-black one alike. */
.goen-swatch--dot.goen-swatch--out {
  border: 0;
}

.goen-swatch__bar,
.goen-swatch__barground {
  stroke-linecap: round;
}

.goen-swatch__barground {
  stroke: var(--bg);
  stroke-width: 4;
}

.goen-swatch__bar {
  stroke: var(--fg);
  stroke-width: 2;
}

.goen-pdp__note {
  margin: 0;
}

/* An instruction rather than a state: no panel, no border, the same muted
   voice the assurances use. */
.goen-pdp__hint {
  align-items: center;
  color: var(--n-500);
  display: flex;
  font-size: var(--fs-14);
  gap: 0.5rem;
  margin: 0;
}

.goen-pdp__hint svg {
  flex: none;
  height: 1rem;
  width: 1rem;
}

/* The one primary action, and the outcome of the last press directly under it
   with nothing in between. A phone has to show both at once, or the
   confirmation is a sentence about a button that has scrolled away. */
.goen-pdp__form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

.goen-pdp__buyrow {
  align-items: stretch;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.goen-pdp__qty {
  flex: none;
}

/* The one button in goen whose label is a sentence: 目前全部規格都已售完 must
   wrap rather than make the row as wide as the sentence, so min-width lets flex
   shrink it below its text. Full width of the column below 1024, beside the
   stepper from there. */
.goen-pdp__add {
  flex: 1 1 100%;
  min-width: 0;
}

/* visibility, not just transform, keeps it out of the tab order and away from
   assistive technology until goen.js sets data-buybar on <html>; it lives there
   so a swapped-in bar is born in the state the old one left and never slides. */
.goen-buybar {
  display: none;
}

@media (max-width: 768px) {
  .goen-buybar {
    align-items: center;
    background: var(--bg);
    border-top: 1px solid var(--n-200);
    bottom: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    left: 0;
    padding: 0.75rem var(--page-gutter) calc(0.75rem + env(safe-area-inset-bottom));
    position: fixed;
    right: 0;
    transform: translateY(100%);
    visibility: hidden;
    z-index: var(--z-dropdown);
  }

  .goen-buybar:empty {
    display: none;
  }

  :root[data-buybar='out'] .goen-buybar {
    transform: none;
    visibility: visible;
  }

  .goen-buybar__price {
    color: var(--n-900);
    flex: none;
    font-size: var(--fs-18);
    font-variant-numeric: tabular-nums;
    font-weight: var(--fw-medium);
    margin: 0;
  }

  .goen-buybar__msg {
    color: var(--n-700);
    flex: 1 1 auto;
    font-size: var(--fs-14);
    margin: 0;
  }

  .goen-buybar__btn {
    flex: 1 1 9rem;
    height: auto;
    min-height: 3rem;
    text-wrap: balance;
  }

  /* The menu drawer belongs to the header, which this later fixed block would
     paint over; hidden, the bar also leaves the tab order. */
  :root[data-buybar='out'] body:has(.goen-header__menu[open]) .goen-buybar {
    transform: translateY(100%);
    visibility: hidden;
  }

  :root[data-buybar='out'] body:has(.goen-buybar:not(:empty)) .goen-footer {
    padding-bottom: calc(8rem + env(safe-area-inset-bottom));
  }
}

@media (max-width: 768px) and (prefers-reduced-motion: no-preference) {
  .goen-buybar {
    transition:
      transform var(--dur-slow) var(--ease-standard),
      visibility var(--dur-slow);
  }
}

.goen-pdp__added {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  scroll-margin-top: 12px;
}

.goen-pdp__addedlink {
  color: var(--accent-text);
  font-weight: var(--fw-medium);
  text-decoration: underline;
}

.goen-pdp__notify {
  border-top: 1px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding-top: 1.5rem;
}

.goen-pdp__notifyhead {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-pdp__notifyrow {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.goen-pdp__notifyrow .goen-input {
  flex: 1 1 14ch;
  min-width: 14ch;
}

/* Wishlist and compare: text buttons. The row is pulled out by the buttons'
   own padding so the first label lines up with the column's edge. */
.goen-pdp__links,
.goen-pdp__similar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.5rem;
  margin: -0.75rem 0 0 -0.75rem;
}

/* With the specs, not pulled up into the buy column's rhythm. */
.goen-pdp__similar {
  margin: 0.5rem 0 0 -0.75rem;
}

.goen-pdp__links .goen-btn,
.goen-pdp__similar .goen-btn {
  color: var(--n-700);
  padding: 0 0.75rem;
}

.goen-pdp__wish {
  display: flex;
  margin: 0;
}

/* Description, then the accordion. Never behind tabs, and the specifications
   open: what a shopper compares is on the page without a press. */
.goen-pdp__more {
  border-bottom: 1px solid var(--n-200);
  min-width: 0;
}

.goen-pdp__desc {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 3rem;
  min-width: 0;
}

.goen-pdp__prose {
  color: var(--n-700);
  line-height: 1.7;
  margin: 0;
  max-width: 68ch;
}

.goen-pdp__acc {
  border-top: 1px solid var(--n-200);
}

.goen-pdp__accsummary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  list-style: none;
  min-height: 3.5rem;
}

.goen-pdp__accsummary::-webkit-details-marker {
  display: none;
}

.goen-pdp__accsummary:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: -2px;
}

/* The second value is the accessible text: the mark is a picture of a state the
   open attribute already states. */
.goen-pdp__accsummary::after {
  color: var(--n-500);
  content: '+';
  content: '+' / '';
  font-size: 1.5rem;
  line-height: 1;
}

.goen-pdp__acc[open] > .goen-pdp__accsummary::after {
  content: '\2212';
  content: '\2212' / '';
}

.goen-pdp__acctitle {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  line-height: 1.5;
  margin: 0;
}

.goen-pdp__accpanel {
  max-width: 48rem;
  padding-bottom: 1.5rem;
}

.goen-pdp__warranty {
  color: var(--n-700);
  line-height: 1.7;
  margin: 0;
}

.goen-pdp__warranty + .goen-pdp__warranty {
  margin-top: 0.5rem;
}

.goen-pdp__policies {
  display: grid;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-pdp__policies a {
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.goen-pdp__policies a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The table's hairlines run the accordion's full width, like the hairlines
   between the sections above and below it; the panel's prose measure is for
   paragraphs, and stopping these at it would stack two lengths of rule. */
.goen-pdp__accpanel:has(> .goen-pdp__speclist) {
  max-width: none;
}

.goen-pdp__speclist {
  margin: 0;
}

.goen-pdp__specrow {
  display: grid;
  gap: 0.25rem 1.5rem;
  padding: 0.75rem 0;
}

.goen-pdp__specrow + .goen-pdp__specrow {
  border-top: 1px solid var(--n-200);
}

.goen-pdp__specterm {
  color: var(--n-500);
  font-size: var(--fs-14);
}

.goen-pdp__specvalue {
  color: var(--n-900);
  margin: 0;
}

/* Reviews, questions and the two tile rows: the same section rhythm. */
.goen-pdp__reviews,
.goen-pdp__qa,
.goen-pdp__related,
.goen-pdp__also {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  min-width: 0;
}

.goen-band--product {
  margin-block: 0 4rem;
  overflow-anchor: none;
}

.goen-band--product .goen-pdp__desc {
  margin-bottom: 0;
}

.goen-band--product .goen-pdp__sectiontitle,
.goen-band--product .goen-pdp__prose {
  color: var(--tone-text);
}

.goen-band--product .goen-pdp__brand {
  color: var(--tone-muted);
}

/* No photograph: the copy sits on the page's own column, as the hero's does. */
.goen-band--text .goen-band__grid {
  grid-template-columns: minmax(0, 1fr);
}

.goen-band--text .goen-band__body {
  padding-inline: max(var(--page-gutter), calc((100% - var(--page-width)) / 2));
}

/* Similar products are a 4-up row from 1024 and two across below: four tiles
   in the listing's three columns would leave one on a line of its own. */
.goen-pdp .goen-tiles__grid {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(6.5rem, 50% - var(--tiles-gap-x) / 2)), 1fr));
}

.goen-pdp__reviewlayout {
  display: grid;
  gap: 2rem;
}

.goen-pdp__summary {
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.goen-pdp__score {
  color: var(--n-900);
  font-size: var(--fs-ed-38);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-display);
  line-height: 1.1;
  margin: 0;
}

.goen-pdp__scorecount {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-pdp__bars {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.goen-pdp__bar {
  align-items: center;
  display: grid;
  gap: 0.5rem;
  grid-template-columns: 3.5em 1fr 2.5em;
}

.goen-pdp__barlabel,
.goen-pdp__barcount {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
}

.goen-pdp__barcount {
  text-align: right;
}

/* The bar's track and fill. The width is a class and not an inline style,
   because style-src carries no 'unsafe-inline' and a refused width leaves a row
   with a label, a count and nothing between them. Ten steps; five buckets show
   no finer ratio. */
.goen-pdp__bartrack {
  background: var(--n-200);
  border-radius: var(--r-full);
  display: block;
  height: 0.5rem;
  overflow: hidden;
}

.goen-pdp__barfill {
  background: var(--n-900);
  border-radius: inherit;
  display: block;
  height: 100%;
}

.goen-pdp__barfill--0 {
  width: 0;
}

.goen-pdp__barfill--10 {
  width: 10%;
}

.goen-pdp__barfill--20 {
  width: 20%;
}

.goen-pdp__barfill--30 {
  width: 30%;
}

.goen-pdp__barfill--40 {
  width: 40%;
}

.goen-pdp__barfill--50 {
  width: 50%;
}

.goen-pdp__barfill--60 {
  width: 60%;
}

.goen-pdp__barfill--70 {
  width: 70%;
}

.goen-pdp__barfill--80 {
  width: 80%;
}

.goen-pdp__barfill--90 {
  width: 90%;
}

.goen-pdp__barfill--100 {
  width: 100%;
}

/* A review is a row between hairlines, not a card. */
.goen-pdp__reviewlist {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.goen-pdp__review {
  border-top: 1px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.5rem 0;
}

.goen-pdp__review:first-child {
  border-top: 0;
  padding-top: 0;
}

.goen-pdp__reviewhead {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-14);
  gap: 0.5rem;
}

.goen-pdp__reviewstars .goen-pdp__rate svg {
  height: 0.875rem;
  width: 0.875rem;
}

.goen-pdp__reviewname {
  color: var(--n-900);
  font-weight: var(--fw-medium);
}

.goen-pdp__reviewdate {
  color: var(--n-500);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}

.goen-pdp__reviewtitle {
  color: var(--n-900);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-pdp__reviewbody {
  color: var(--n-700);
  line-height: 1.7;
  margin: 0;
}

.goen-pdp__noreviews,
.goen-pdp__reviewnote,
.goen-pdp__meta {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

/* The review form is the one panel on the page: --n-50 ground, no border. */
.goen-pdp__reviewform {
  background: var(--n-50);
  border-radius: var(--r-lg);
  max-width: 40rem; /* content-box: the form inside is as wide as .goen-qa__form */
  padding: 1.5rem;
}

.goen-pdp__reviewform form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.goen-pdp__reviewformhead {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  margin: 0;
}

/* The radios stay real so the arrow keys set the rating; a star is lit when it
   or any later star is checked, but only while the row is not hovered: while it
   is, the pointer's star and the ones before it are the preview. */
.goen-pdp__stars {
  border: 0;
  margin: 0;
  padding: 0;
}

.goen-pdp__starrow {
  display: inline-flex;
}

.goen-pdp__star {
  align-items: center;
  cursor: pointer;
  display: inline-flex;
  justify-content: center;
  min-height: 2.75rem;
  min-width: 3rem;
  position: relative;
}

.goen-pdp__star:has(.goen-pdp__starinput:focus-visible) {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* The outline carries the shape's 3:1 boundary on every state, and the fill is
   3:1 on the form's ground and on the outline, so a selected star differs from
   an empty one by shape and by contrast, not by hue. */
.goen-pdp__starmark svg {
  color: var(--n-900);
  display: block;
  fill: none;
  height: 1.75rem;
  width: 1.75rem;
}

.goen-pdp__starrow:not(:hover) .goen-pdp__star:has(.goen-pdp__starinput:checked) .goen-pdp__starmark svg,
.goen-pdp__starrow:not(:hover) .goen-pdp__star:has(~ .goen-pdp__star .goen-pdp__starinput:checked) .goen-pdp__starmark svg,
.goen-pdp__starrow:hover .goen-pdp__star:hover .goen-pdp__starmark svg,
.goen-pdp__starrow:hover .goen-pdp__star:has(~ .goen-pdp__star:hover) .goen-pdp__starmark svg {
  fill: var(--star);
}

@media (forced-colors: active) {
  .goen-pdp__starmark svg {
    color: CanvasText;
  }

  .goen-pdp__starrow:not(:hover) .goen-pdp__star:has(.goen-pdp__starinput:checked) .goen-pdp__starmark svg,
  .goen-pdp__starrow:not(:hover) .goen-pdp__star:has(~ .goen-pdp__star .goen-pdp__starinput:checked) .goen-pdp__starmark svg,
  .goen-pdp__starrow:hover .goen-pdp__star:hover .goen-pdp__starmark svg,
  .goen-pdp__starrow:hover .goen-pdp__star:has(~ .goen-pdp__star:hover) .goen-pdp__starmark svg {
    fill: CanvasText;
  }
}

.goen-pdp__reviewbody.goen-input {
  min-height: 0;
}

/* Questions and answers, as hairline rows. An answer is indented under its
   question by a rule on the left, so the relationship survives at any width
   without a nested grid. */
.goen-qa {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-qa__item {
  border-bottom: 1px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 1rem 0;
}

.goen-qa__item:first-child {
  padding-top: 0;
}

.goen-qa__item:last-child {
  border-bottom: 0;
}

.goen-qa__answers {
  border-left: 2px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0 0 0 1rem;
}

.goen-qa__answer {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.goen-qa__answerbody {
  color: var(--n-700);
  line-height: 1.7;
  margin: 0;
}

@media (min-width: 640px) {
  .goen-pdp {
    gap: 6rem;
    padding-bottom: 6rem;
  }
}

@media (min-width: 768px) {
  .goen-pdp__specrow {
    grid-template-columns: 12rem 1fr;
  }
}

@media (min-width: 1024px) {
  .goen-pdp__top {
    align-items: start;
    gap: 3rem;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  }

  .goen-pdp__add {
    flex: 1 1 0;
  }

  .goen-pdp__reviewlayout {
    gap: 4rem;
    grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  }

  .goen-pdp .goen-tiles__grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ---------- Cart, checkout, order, pay, returns and order lookup ----------

   Six screens carrying one transaction, so they share one composition: a page
   head, hairline-divided lines, and a summary holding the money and the single
   action the screen exists for. Cart and checkout put the summary in a right
   column and keep it in view from 1024; the phone puts the cart's summary in a
   bar fixed to the bottom edge, so the total is on screen however many lines
   sit above it.

   - The chosen option is read from :has(input:checked) and nothing else: a
     class the server renders would be a second source for one fact.
   - The radio inside a chooser card is visually hidden rather than removed, so
     the group keeps its place in the tab order and the arrow keys still move
     between options.
   - Control heights are 44px, 48px for the one primary a screen is about.
     `scripts/check-layout.mjs:1300` measures them on every route here.
   ------------------------------------------------------------------------ */

.goen-cart,
.goen-checkout,
.goen-order,
.goen-pay,
.goen-returns,
.goen-findorder {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin: 0 auto;
  max-width: var(--page-box);
  padding: 1.5rem var(--page-gutter) 4rem;
  width: 100%;
}

/* The return request is one column of short rows: a second column would only put
   a control further from the line it belongs to. */
.goen-returns {
  max-width: 720px;
}

/* The order and the payment hand-off are one reading column: a wider page
   around this little content reads as one with something missing. The width
   is added to the gutters because the box sizes itself with border-box. */
.goen-order,
.goen-pay {
  max-width: calc(48rem + 2 * var(--page-gutter));
}

/* The lookup is a credential form, so it sits in the middle of the fold the
   way every other one does. */
.goen-findorder {
  align-items: center;
  justify-content: center;
  min-height: 60vh;
}

.goen-pagehead {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.goen-pagehead__eyebrow {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-pagehead__title {
  color: var(--fg);
  font-size: var(--fs-ed-30);
  font-weight: var(--fw-display);
  margin: 0;
}

.goen-pagehead--search .goen-pagehead__title {
  overflow-wrap: anywhere;
}

.goen-order .goen-pagehead__eyebrow,
.goen-pay .goen-pagehead__eyebrow {
  color: var(--accent-deep);
  font-size: var(--fs-14);
}

.goen-pagehead__sub,
.goen-order__meta {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  margin: 0;
}

/* The stock hold in words. While a payment can start it sits on the blue that
   marks a period running for the customer; once none can, on the neutral wash.
   The transparent outline draws the panel's edge in forced colours, which drop
   the background. */
.goen-pay__window {
  background: var(--accent-faint);
  border-radius: var(--r-lg);
  margin-top: var(--s-6);
  outline: 1px solid transparent;
  padding: 1.25rem 1.5rem;
}

.goen-pay__window--closed {
  background: var(--wash);
}

.goen-pay__deadline {
  color: var(--accent-deep);
  font-size: var(--fs-18);
  font-weight: var(--fw-medium);
  margin: 0;
  text-wrap: balance;
}

.goen-pay__window--closed .goen-pay__deadline {
  color: var(--fg);
}

.goen-pay__hold {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  margin: 0.125rem 0 0;
  overflow-wrap: anywhere;
  word-break: keep-all;
  text-wrap: pretty;
}

.goen-pagehead > .goen-pay__window + .ui-statline {
  margin-top: var(--s-5);
}

@media (max-width: 30rem) {
  .goen-pay__window {
    padding: 1rem;
  }
}

/* Department and campaign tone. data-tone is one of the closed set pages.Tone
   names, on the page head; a custom property is never set inline because
   style-src has no 'unsafe-inline'. Only these properties vary: accent, type,
   radii and buttons do not. A tone never touches a photograph: wherever the two
   meet, a seam of goen's white (--n-0) lies between them, which is why a tone
   can be a flat colour and not the ground its photographs were shot on.
   assets/contrast_test.go holds the text and mark colours to their
   ratios on every ground. */
[data-tone="paper"] {
  --tone-ground: #eae1d6;
  --tone-rule: #ddd3c6;
  --tone-text: var(--n-900);
  --tone-muted: #4d4d53;
  --tone-mark: var(--mark);
}

[data-tone="stone"] {
  --tone-ground: #dddddc;
  --tone-rule: #cfcfce;
  --tone-text: var(--n-900);
  --tone-muted: #4d4d53;
  --tone-mark: var(--mark);
}

[data-tone="mist"] {
  --tone-ground: #d4e3f2;
  --tone-rule: #c5d6e6;
  --tone-text: var(--n-900);
  --tone-muted: #4d4d53;
  --tone-mark: var(--mark);
}

[data-tone="sage"] {
  --tone-ground: #d5e2d2;
  --tone-rule: #c7d6c4;
  --tone-text: var(--n-900);
  --tone-muted: #4d4d53;
  --tone-mark: var(--mark);
}

[data-tone="blush"] {
  --tone-ground: #f1d9d6;
  --tone-rule: #e3cac6;
  --tone-text: var(--n-900);
  --tone-muted: #4d4d53;
  --tone-mark: var(--mark);
}

.goen-promo,
.goen-tiles__grid--lead[data-tone="ink"] > .goen-rowcard,
.goen-pagehead[data-tone="ink"],
.goen-hero__slide[data-tone="ink"],
.goen-band[data-tone="ink"] {
  --ring: var(--n-0);
}

[data-tone="ink"] {
  --tone-ground: #18181b;
  --tone-rule: #27272a;
  --tone-text: #ffffff;
  --tone-muted: #a1a1aa;
  --tone-mark: var(--on-ink-accent);
}

.goen-pagehead[data-tone] {
  background: var(--tone-ground);
  border-radius: var(--r-lg);
  padding: 1.25rem 1rem;
}

@media (min-width: 768px) {
  .goen-pagehead[data-tone] {
    padding: 2rem 1.5rem;
  }
}

.goen-pagehead .goen-band {
  margin-top: 0;
}

.goen-listing__head .goen-pagehead[data-tone] {
  background: none;
  border-radius: 0;
  margin-top: 0;
  padding: 0;
}

/* A phone's photograph is a 5:2 strip above the copy, goen's white between it and
   the tone; the head's first row of products still starts inside the first
   screen. A crumb whose only step is home repeats the logo. */
@media (max-width: 47.99rem) {
  .goen-pagehead .goen-band {
    background: none;
  }

  .goen-pagehead .goen-band__media {
    margin: 0;
    padding: 0 0 var(--col-gap);
  }

  .goen-pagehead .goen-band__body,
  .goen-pagehead .goen-band > .goen-deptnotice {
    background: var(--tone-ground);
  }

  .goen-pagehead .goen-band__media img {
    aspect-ratio: 5 / 2;
  }

  .goen-pagehead .goen-band__body {
    gap: var(--s-2);
    padding-block: var(--s-4);
  }

  .goen-listing__head .ui-crumbs:not(:has(.ui-crumbs__link ~ .ui-crumbs__link)) {
    display: none;
  }

  .goen-listing {
    padding-top: var(--s-4);
  }
}

.goen-listing__head .goen-pagehead__title {
  font-size: clamp(2.75rem, 1.7rem + 3.4vw, 5.5rem);
  letter-spacing: -0.02em;
  line-height: 1.05;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.goen-listing__head .goen-pagehead__title.goen-band__name--long {
  font-size: clamp(2rem, 1.2rem + 2.2vw, 3.25rem);
}

.goen-pagehead[data-tone] .goen-pagehead__title {
  color: var(--tone-text);
}

.goen-pagehead[data-tone] .goen-pagehead__sub,
.goen-pagehead[data-tone] .goen-pagehead__eyebrow {
  color: var(--tone-muted);
}

.goen-cart__layout,
.goen-checkout__layout,
.goen-order__layout {
  align-items: start;
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
  min-width: 0;
}

/* The line list. No rule and no box: each line's photograph is its shape, and
   the space between lines parts them. The one column is the list's width, not
   its widest line's: a long price at large text wraps inside the line instead
   of widening the page. */
.goen-cart__lines,
.goen-order .goen-order__lines,
.goen-pay__lines {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1fr);
  list-style: none;
  margin: 0;
  min-width: 0;
  padding: 0;
}

.goen-cart__lines {
  container: cartlines / inline-size;
}

.goen-line {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
}

/* A line the catalogue can no longer honour: its photograph dimmed, the line not
   hidden. A shopper has to see what they must remove before checkout will let
   them past, and the control that removes it stays at full contrast. */
.goen-line--out .goen-line__thumb {
  opacity: 0.6;
}

.goen-line__thumb {
  align-self: start;
  aspect-ratio: 1 / 1;
  background: var(--photo);
  border-radius: var(--r-md);
  display: block;
  overflow: hidden;
  width: 100%;
}

.goen-line__img,
.goen-line__ph {
  display: block;
  height: 100%;
  object-fit: contain;
  width: 100%;
}

/* The cart's totals sit under the title as a fact line; the sentence that says
   what blocks checkout comes first, on its own ground so it is not read as a figure. */
.goen-cart__facts {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 1rem;
}

.goen-cart__alert {
  background: var(--warn-bg);
  border-radius: var(--r-md);
  color: var(--warn);
  font-size: var(--fs-14);
  margin: 0;
  max-width: 44rem;
  outline: 1px solid transparent;
  padding: 0.75rem 1rem;
}

/* The notice region is always in the document so an update can fill it; it
   draws nothing when empty and lets a notice sit in the page's own flow. */
.goen-cart__notices {
  display: contents;
}

/* A line in the cart is a grid, so the money and the controls can sit under
   the name at 375 without the name being squeezed to a word a row. The
   thumbnail spans both rows. */
.goen-line--cart {
  align-items: start;
  column-gap: 1rem;
  display: grid;
  grid-template-areas:
    'thumb body money'
    'thumb controls controls';
  grid-template-columns: 6rem minmax(0, 1fr) auto;
  row-gap: 0.75rem;
}

.goen-line--cart .goen-line__thumb {
  grid-area: thumb;
}

.goen-line--cart .goen-line__body {
  grid-area: body;
}

.goen-line--cart .goen-line__money {
  grid-area: money;
}

.goen-line--cart .goen-line__controls {
  align-self: end;
  grid-area: controls;
}

/* Remove reads as text, not as a button: it is the quietest of the three
   things a line offers. The box stays 44px tall, which is the target. */
.goen-line__remove {
  background: transparent;
  color: var(--ink-2);
  font-size: var(--fs-13);
  font-weight: var(--fw-medium);
  justify-content: flex-start;
  min-width: 2.75rem;
  padding: 0;
  text-decoration: none;
  text-underline-offset: 3px;
}

.goen-line__remove:hover {
  background: transparent;
  color: var(--n-900);
  text-decoration: underline;
}

/* The 更新 button is the way to apply a quantity with scripting off. With it
   on, changing the quantity submits the form, so the button goes. Hidden when
   the sheet is told scripting is on, so a browser that has never heard of the
   query keeps the button that always works. */
@media (scripting: enabled) {
  .goen-line__update {
    display: none;
  }
}

.goen-line__body {
  align-items: flex-start;
  display: flex;
  flex: 1 1 10rem;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.goen-line__brand,
.goen-line__meta,
.goen-returns__meta {
  color: var(--fg-muted);
  font-size: var(--fs-13);
}

.goen-line__name {
  color: var(--n-900);
  font-size: 0.9375rem;
  font-weight: var(--fw-medium);
  margin: 0;
  text-decoration: none;
}

.goen-line__name:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.goen-line__controls {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 0;
}

.goen-returns__qty .goen-input {
  font-variant-numeric: tabular-nums;
  padding-left: 8px;
  padding-right: 8px;
  text-align: center;
  width: 80px;
}

.goen-line__money {
  align-items: flex-end;
  display: flex;
  flex: none;
  flex-direction: column;
  font-variant-numeric: tabular-nums;
  gap: 0.125rem;
  margin-left: auto;
  text-align: right;
}

.goen-line__total,
.goen-order__carrier,
.goen-order__steplabel,
.goen-returns__name {
  color: var(--fg);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
}

.goen-order__steplabel {
  margin: 0;
}

.goen-line__total {
  font-size: var(--fs-16);
}

/* The money panel. .goen-card carries the ground, the radius and the padding;
   these carry only what goes on inside it. */
.goen-cart__summary,
.goen-checkout__summary,
.goen-order__summary,
.goen-pay__summary,
.goen-checkout__section {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}

/* The checkout's summary is a white face on the grey page, as the form beside
   it is. The transparent border is what forced colours draws as its edge. */
.goen-checkout__summary {
  background: var(--n-0);
  border: 1px solid transparent;
}

.goen-checkout__summary .goen-summary {
  margin-block-end: 0.5rem;
}

/* The other money panels are wash faces; the transparent outline is what forced
   colours draws as their edge. */
.goen-cart__summary,
.goen-order__summary,
.goen-pay__summary {
  background: var(--wash);
  outline: 1px solid transparent;
}

/* With scripting off the 更新 button answers with the whole form again and its
   fragment names the section it changed, so the browser lands on this heading
   rather than the top of the form. The margin keeps it off the viewport edge. */
.goen-checkout__section {
  scroll-margin-top: 24px;
}

.goen-summary__title,
.goen-checkout__heading,
.goen-order__subhead {
  color: var(--n-900);
  font-size: var(--fs-18);
  font-weight: var(--fw-medium);
  line-height: var(--lh-normal);
  margin: 0;
}

.goen-order__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.goen-summary__actions,
.goen-checkout__choices {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* The money rows are a description list. The two columns part the rows; the
   one rule sits over the total and says the rows above add up to it. */
.goen-summary {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  margin: 0;
}

.goen-summary__row {
  align-items: baseline;
  color: var(--fg-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-14);
  gap: 0.25rem 1rem;
  justify-content: space-between;
}

.goen-summary__row dt {
  margin: 0;
}

.goen-summary__row dd {
  margin: 0 0 0 auto;
}

/* A label stays whole while the row can wrap it onto its own line; one longer than the panel
   (a shop-entered discount reason at 200% text) still breaks rather than spilling. */
.goen-summary__row :is(dt, dd) {
  overflow-wrap: anywhere;
  word-break: keep-all;
}

.goen-summary__value {
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}

.goen-summary__row--total {
  border-top: 1px solid var(--line-2);
  color: var(--fg);
  flex-wrap: wrap;
  font-size: var(--fs-15);
  font-weight: var(--fw-medium);
  margin-top: 0.375rem;
  padding-top: 1rem;
}

.goen-summary__row--total .goen-summary__value {
  font-size: 1.625rem;
  font-weight: var(--fw-display);
  letter-spacing: -0.01em;
  line-height: 1.1;
}

/* The payment state under the total is a note on it, not a money row. */
.goen-summary__row--total + .goen-summary__row {
  font-size: var(--fs-13);
  margin-top: 0.25rem;
}

/* A panel narrower than the large figure (a phone at 200% text) sets it at the
   row's earlier size, so the total stays inside its panel. Sized in rem, so the
   query follows the text, which a viewport query cannot see. */
.goen-checkout__summary,
.goen-order__summary,
.goen-pay__summary {
  container: summary / inline-size;
}

@container summary (max-width: 10rem) {
  .goen-summary__row--total .goen-summary__value {
    font-size: var(--fs-18);
  }
}

/* The browse screens join this rule rather than declare a second one. They
   cannot join it by class name: `compare_test.go:33` reads `class="ui-empty"`
   as the whole attribute and `check-layout.mjs:75,99` uses the same name as a
   row marker, so their box keeps that spelling and this rule reaches it by
   descent. base.css sets the same six properties on `.ui-empty` from a sheet
   the document links earlier, so every one of them is decided here. */
.goen-empty,
.goen-compare .ui-empty,
.goen-listing .ui-empty {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 56px 16px;
  text-align: center;
}

/* The disc a state announces itself from: an empty cart, an order that has just
   been placed. One recipe, because they are the same gesture — a glyph large
   enough to read across a page, in a circle that says it is not a control. */
.goen-medallion {
  align-items: center;
  background: var(--elevated);
  border-radius: var(--r-full);
  color: var(--fg-muted);
  display: flex;
  flex: none;
  height: 96px;
  justify-content: center;
  width: 96px;
}

.goen-medallion svg {
  height: 40px;
  width: 40px;
}

.goen-empty__art {
  display: block;
  height: 96px;
  width: 128px;
}

.goen-empty__title {
  color: var(--n-900);
  font-size: 1rem;
  font-weight: var(--fw-medium);
  line-height: 1.5;
  margin: 0;
}

.goen-empty__text {
  color: var(--n-500);
  font-size: var(--fs-14);
  line-height: 1.5;
  margin: 0;
  max-width: 40ch;
}

.goen-checkout__main,
.goen-order__panels {
  display: flex;
  flex-direction: column;
  gap: 3rem;
  min-width: 0;
}

/* The grey page the checkout sits on. Painted past the column on both sides
   and clipped top and bottom, so the header and footer keep their own
   grounds and no shadow reaches the scroll width. */
.goen-checkout {
  background: var(--n-50);
  box-shadow: 0 0 0 100vmax var(--n-50);
  clip-path: inset(0 -100vmax);
}

/* A choice adds the new form before removing the old one; anchoring would follow the old one down. */
#checkout-region {
  overflow-anchor: none;
}

/* The sections are one white block, parted by the space between them rather
   than boxed or ruled one by one. */
.goen-checkout__main {
  background: var(--n-0);
  border-radius: var(--r-lg);
  gap: 0;
}

.goen-checkout__main > .ui-alert {
  margin: 1.5rem 1.25rem 0;
}

.goen-checkout__section {
  background: transparent;
  border-radius: 0;
  padding: 1.5rem 1.25rem;
}

/* One option in a chooser. The card is the tap target — the radio inside it is
   one pixel and reachable only by keyboard — so the height minimum lives here
   and check-layout measures this element by name. */
.goen-checkout__ship {
  align-items: flex-start;
  background: var(--n-0);
  border: 1px solid var(--n-300);
  border-radius: var(--r-lg);
  box-sizing: border-box;
  color: inherit;
  cursor: pointer;
  display: flex;
  gap: 0.75rem;
  justify-content: space-between;
  min-height: 44px;
  padding: 1rem;
  text-decoration: none;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard);
}

.goen-checkout__ship:hover {
  background: var(--n-50);
}

/* The one source for "this is the chosen one": the accent, where a shopper chooses.
   The extra pixel makes the outline two without moving the content. */
.goen-checkout__ship:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

/* The card shows the focus its hidden radio has, or a keyboard user moving
   through the group would see nothing move. */
.goen-checkout__ship:has(input:focus-visible) {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.goen-checkout__shipname {
  color: var(--fg);
  display: flex;
  flex-direction: column;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 2px;
  min-width: 0;
}

.goen-checkout__shipnote {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  font-weight: var(--fw-regular);
}

.goen-checkout__shipfee {
  color: var(--fg);
  flex: none;
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
}

/* The row the 更新 button sits in. Right-aligned and quiet: it is a step inside
   the form, not the thing the customer came to press. */
.goen-checkout__apply {
  display: flex;
  justify-content: flex-end;
}

/* Where scripting is on, changing a chooser applies it, and two ways to apply
   one choice on the same screen is one way too many. Hidden when the sheet is
   told scripting is on rather than shown when it is told scripting is off, so a
   browser that has never heard of the query keeps the button that always works.
   check-layout measures visible controls only, and a hidden button has no box. */
@media (scripting: enabled) {
  .goen-checkout__apply {
    display: none;
  }
}

.goen-checkout .goen-checkout__fields {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}

.goen-checkout .goen-checkout__wide {
  grid-column: 1 / -1;
}

.goen-checkout__hint {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  margin: 0;
}

/* The chosen convenience store, and the control that opens the carrier's map
   to choose one. The store's name and address are what the shopper reads
   before paying, and that reading is the only check goen has that the store
   reported back is the store they picked — so they are ordinary page text at
   the body size, not a quiet caption. The ground is its shape; the transparent
   border is what forced colours draws. */
.goen-pickup {
  background: var(--elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
  padding: 12px;
}

.goen-pickup__name {
  color: var(--fg);
  font-size: var(--fs-16);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-pickup__addr {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  line-height: var(--lh-normal);
  margin: 0;
}

/* The hop back from the carrier's page. No header, no footer: it exists for
   the fraction of a second before the refresh lands, and a category query for
   a header nobody reads is a database round trip per cross-site post. */
.goen-handoff {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
  justify-content: center;
  min-height: 100dvh;
  padding: 24px;
  text-align: center;
}

/* Delayed, so a fast return shows the page ground and not a flash of words
   nobody had time to read. Reduced motion shows them at once: a reader who
   has asked for no motion has not asked to be told nothing. */
.goen-handoff__text {
  animation: goen-handoff-in var(--dur-base) var(--ease-standard) 400ms both;
  color: var(--fg-muted);
  font-size: var(--fs-16);
  margin: 0;
}

.goen-handoff__link {
  color: var(--accent-text);
  font-size: var(--fs-14);
  /* 44px, so the one control on this page is a tap target on a phone. */
  line-height: 44px;
  min-height: 44px;
}

@keyframes goen-handoff-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .goen-handoff__text {
    animation-delay: 0ms;
  }
}

/* A native select wearing the input recipe. It stays a select: a listbox built
   from divs needs script, and the write-face rule forbids script being the only
   way a form works. The contact form joins this rule rather than declaring a
   second one — it is the same control asking the same kind of question. */
.contact__select,
.goen-checkout__select {
  appearance: none;
  background: var(--n-0) var(--chevron) no-repeat right 0.75rem center;
  border: 1px solid var(--control-boundary);
  border-radius: var(--r-sm);
  box-sizing: border-box;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--fs-16);
  height: 2.75rem;
  padding: 0.25rem 2.5rem 0.25rem 0.75rem;
  width: 100%;
}

.goen-checkout__lines {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-checkout__lines li {
  color: var(--n-700);
  display: flex;
  font-size: var(--fs-14);
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
  justify-content: space-between;
}

.goen-checkout__lines li > :first-child {
  flex: 1 1 10rem;
  min-width: 0;
}

/* The coupon sits under the totals, not above them: a shopper reads the total
   first and looks for a code only if they have one. The block itself is an
   ordinary .goen-field; only the row holding the input and its button is new. */
.goen-checkout__couponrow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.goen-checkout__couponrow .goen-input {
  flex: 1 1 10rem;
  min-width: 0;
}

.goen-checkout__couponrow .goen-btn {
  flex: none;
}

/* The small print under a control: what pressing it will do, and who takes the
   card. Quiet, and deliberately present — a customer about to leave the site for
   a card form is being asked to trust the handover. */
.goen-checkout__note,
.goen-cart__hold,
.goen-pay__assurance {
  color: var(--fg-muted);
  font-size: var(--fs-12);
  line-height: var(--lh-normal);
  margin: 0;
}

.goen-order__meta {
  overflow-wrap: anywhere;
}

.goen-order__parcel,
.goen-order__returned,
.goen-order__items,
.goen-order__history,
.goen-order__delivery {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

/* The history, drawn as a rail of dots. It is on the page rather than behind a
   tab because "where is my order" is the question the page is opened with. */
.goen-order__steps,
.goen-returns__records {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-order__step {
  display: flex;
  gap: 12px;
}

.goen-order__rail {
  align-items: center;
  display: flex;
  flex: none;
  flex-direction: column;
}

.goen-order__dot {
  background: var(--n-300);
  border-radius: var(--r-full);
  flex: none;
  height: 10px;
  margin-top: 7px;
  width: 10px;
}

.goen-order__step:last-child .goen-order__dot {
  background: var(--accent);
}

.goen-order__connector {
  background: var(--border);
  flex: 1;
  margin-top: 4px;
  width: 2px;
}

.goen-order__stepbody {
  flex: 1;
  min-width: 0;
  padding-bottom: 24px;
}

.goen-order__step:last-child .goen-order__stepbody {
  padding-bottom: 0;
}

.goen-order__shipments {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-order__shipment {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.goen-order__cancel {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}

.goen-pagehead > .ui-statline {
  margin-top: var(--s-6);
}

/* Each parcel is counted on its own, so its heading starts after a wider space
   than the one between sections. */
.goen-order__parcel + .goen-order__parcel {
  padding-top: 1rem;
}

.goen-line--order {
  align-items: start;
  column-gap: 1rem;
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr) auto;
  row-gap: 0.75rem;
}

.goen-line--order .goen-line__thumb {
  width: auto;
}

.goen-line--order .ui-statline {
  align-self: stretch;
  margin-top: var(--s-2);
  max-width: 26rem;
}

/* The parcel's flex gap already parts the facts from the track, so it adds only
   what sets the track 16px under them. */
.goen-order__parcel > .ui-period {
  margin-top: 0;
}

.goen-line__act {
  color: var(--accent);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  text-decoration: none;
  text-underline-offset: 3px;
}

.goen-line__act:hover {
  text-decoration: underline;
}

@media (max-width: 30rem) {
  .goen-line--order {
    grid-template-columns: min(4.5rem, 25%) minmax(0, 1fr);
  }

  .goen-line--order .goen-line__money {
    align-items: flex-start;
    grid-column: 2;
    margin-left: 0;
    text-align: left;
  }
}

.goen-returns__form,
.goen-returns__existing,
.goen-returns__panel {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.goen-returns__subhead {
  color: var(--fg);
  font-size: var(--fs-16);
  font-weight: var(--fw-medium);
  margin: 0;
  padding: 0;
}

.goen-returns__lines {
  border: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
}

.goen-returns__line {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding: 12px 0;
}

.goen-returns__record {
  align-items: flex-start;
  display: flex;
  gap: 12px;
  justify-content: space-between;
  padding: 12px 0;
}

.goen-returns__recordbody {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.goen-returns__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.goen-findorder__card {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 400px;
  width: 100%;
}

.goen-findorder__title {
  color: var(--fg);
  font-size: var(--fs-ed-20);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-findorder__sub,
.goen-findorder__alt {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  margin: 0;
  text-align: center;
}

.goen-findorder__sub {
  text-align: start;
}

.goen-findorder__alt a {
  color: var(--accent-text);
  text-decoration: underline;
  text-underline-offset: 4px;
}

@media (min-width: 640px) {
  .goen-cart .goen-pagehead__title,
  .goen-checkout .goen-pagehead__title,
  .goen-order .goen-pagehead__title,
  .goen-pay .goen-pagehead__title {
    font-size: var(--t-44);
  }

  .goen-line--cart {
    column-gap: 1.5rem;
    grid-template-columns: 8rem minmax(0, 1fr) auto;
  }
}

/* Sized in rem so it follows the text: at 200% the line is narrow in the
   same way a small screen is, and the viewport cannot say so. */
@container cartlines (max-width: 28rem) {
  .goen-line--cart {
    grid-template-areas:
      'thumb body'
      'thumb controls'
      'thumb money';
    grid-template-columns: min(4.5rem, 24%) minmax(0, 1fr);
  }

  .goen-line--cart .goen-line__money {
    align-items: flex-start;
    margin-left: 0;
    text-align: left;
  }
}

/* Below this the stepper and the remove button no longer fit beside the photo. */
@container cartlines (max-width: 17.5rem) {
  .goen-line--cart {
    grid-template-areas:
      'thumb body'
      'controls controls'
      'money money';
  }
}

@media (min-width: 768px) {
  .goen-cart,
  .goen-checkout,
  .goen-order,
  .goen-pay,
  .goen-returns,
  .goen-findorder {
    gap: 2rem;
    padding: 2rem var(--page-gutter) 4rem;
  }

  .goen-checkout .goen-checkout__fields {
    grid-template-columns: 1fr 1fr;
  }

  .goen-checkout__section {
    padding: 1.5rem;
  }

  .goen-checkout__main > .ui-alert {
    margin: 1.5rem 1.5rem 0;
  }
}

/* Under 1024 the cart's summary is a bar fixed to the bottom edge: the total
   and the button, nothing that would make it tall. It stays last in the
   document, so it rests under the lines when the list ends. */
@media (max-width: 1023.98px) {
  .goen-cart__summary {
    align-items: center;
    background: var(--n-0);
    border-radius: 0;
    border-top: 1px solid var(--line);
    bottom: 0;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    justify-content: space-between;
    margin-inline: calc(-1 * var(--page-gutter));
    outline: 0;
    padding: 0.75rem var(--page-gutter);
    position: sticky;
    z-index: 5;
  }

  .goen-cart__summary .goen-summary__title {
    block-size: 1px;
    clip-path: inset(50%);
    inline-size: 1px;
    overflow: hidden;
    position: absolute;
  }

  .goen-cart__summary .ui-alert,
  .goen-cart__summary .goen-notice {
    flex: 1 1 100%;
    order: -1;
  }

  .goen-cart__summary .goen-cart__hold {
    display: none;
  }

  .goen-cart__summary .goen-summary {
    flex: 1 1 auto;
  }

  .goen-cart__summary .goen-summary__row:not(.goen-summary__row--total),
  .goen-cart__summary .goen-summary__actions .goen-btn--ghost {
    display: none;
  }

  .goen-cart__summary .goen-summary__row--total {
    align-items: flex-start;
    border-top: 0;
    flex-direction: column;
    gap: 0;
    margin-top: 0;
    padding: 0;
  }

  .goen-cart__summary .goen-summary__row--total .goen-summary__value {
    margin-left: 0;
    overflow-wrap: normal;
    white-space: nowrap;
  }

  .goen-cart__summary .goen-summary__row--total dt {
    color: var(--fg-muted);
    font-size: var(--fs-13);
    font-weight: var(--fw-regular);
  }

  .goen-cart__summary .goen-summary__actions {
    flex: none;
  }

  .goen-cart__summary .goen-btn--block {
    display: inline-flex;
    width: auto;
  }
}

/* The fixed cart bar and buy bar must not hide a focused control (WCAG 2.4.11):
   the browser scrolls a focused element clear of this much of the bottom edge. */
@media (max-width: 1023.98px) {
  :root:has(.goen-cart__summary) {
    scroll-padding-block-end: 9rem;
  }
}

@media (max-width: 768px) {
  :root[data-buybar='out'] {
    scroll-padding-block-end: 8rem;
  }
}

@media (min-width: 1024px) {
  .goen-cart,
  .goen-checkout,
  .goen-order,
  .goen-pay,
  .goen-returns,
  .goen-findorder {
    padding: 2.5rem var(--page-gutter) 5rem;
  }

  .goen-cart__layout,
  .goen-checkout__layout {
    gap: 3rem;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  }

  .goen-cart__summary,
  .goen-checkout__summary {
    padding: 2rem;
  }

  /* The money stays in view while the lines are read and the address typed. */
  .goen-cart__summary,
  .goen-checkout__summary {
    position: sticky;
    top: 2rem;
  }
}

/* ---------- Account, sign-in, content and campaign pages ----------

   Every surface here is a single column a visitor reads or fills in, so they
   share one rhythm: a page title, 1.5rem to the content, and sections parted by
   a hairline rather than boxed. Auth is a bare 24rem form. The account is a
   column of links beside its sections. Policy, FAQ and about are one 48rem
   prose column. Contact puts the form beside the facts. The 404 and the
   campaign page close the set.

   Every section is in the document and the account nav moves the page it is
   already on, which is what keeps the whole surface working with scripting off.
   ------------------------------------------------------------------------ */

/* Page title and display sizes, written once for the four heads below. */
.goen-auth__title,
.goen-doc__head .goen-pagehead__title,
.contact .goen-pagehead__title,
.about__title,
.notice__title {
  font-size: 1.875rem;
  line-height: 1.3;
}

@media (min-width: 640px) {
  .goen-auth__title,
  .goen-doc__head .goen-pagehead__title,
  .contact .goen-pagehead__title,
  .about__title,
  .notice__title {
    font-size: 2.25rem;
  }
}

/* ----- Sign in, register, forgot, reset and the order lookup ----- */

.goen-auth {
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  margin: 0 auto;
  max-width: var(--page-box);
  min-height: 60vh;
  padding: 3rem var(--page-gutter) 4rem;
  width: 100%;
}

/* No card at any width: a form on a white page needs a column, not a box. */
.goen-auth__panel {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  margin: 0 auto auto;
  max-width: 24rem;
  width: 100%;
}

/* Registration is the same column. The modifier stays as a marker the markup
   already carries. */
.goen-auth__panel--wide {
  max-width: 24rem;
}

.goen-auth__title {
  color: var(--n-900);
  font-weight: var(--fw-display);
  margin: 0;
}

.goen-auth__lede,
.goen-auth__hint {
  color: var(--n-500);
  font-size: var(--fs-14);
  line-height: 1.5;
  margin: 0;
}

.goen-auth__form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* The way out of a forgotten password sits on the label's own row, beside the
   field it is about, rather than under the button where it reads as a second
   action of equal weight. */
.goen-auth__labelrow {
  align-items: center;
  display: flex;
  gap: 0.5rem;
  justify-content: space-between;
}

.goen-auth__forgot {
  color: var(--accent-text);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.goen-auth__forgot:hover {
  color: var(--accent-text-hover);
  text-decoration: underline;
}

/* A rule and not a word: a sentence goen says lives in internal/i18n, and the
   shop has not written one for this. */
.goen-auth__divider {
  background: var(--n-200);
  border: 0;
  height: 1px;
  margin: 0;
}

.goen-auth__oauth {
  width: 100%;
}

.goen-auth__alt {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
  text-align: center;
}

.goen-auth__alt a {
  color: var(--accent-text);
  font-weight: var(--fw-medium);
  text-underline-offset: 4px;
}

/* The shared demo account: quieter than the form it fills, and its credentials
   in a face that tells 0 from O and l from 1. */
.goen-auth__demo {
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--fg-muted);
  display: flex;
  flex-direction: column;
  font-size: var(--fs-14);
  gap: 0.75rem;
  padding: 1rem;
}

.goen-auth__demotitle {
  color: var(--fg);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-auth__democreds {
  display: grid;
  gap: 0.375rem;
  margin: 0;
}

.goen-auth__democreds div {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.goen-auth__democreds dt {
  min-width: 4.5rem;
}

.goen-auth__democreds dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.goen-auth__democreds code {
  color: var(--fg);
  font-family: var(--font-mono);
  user-select: all;
}

.goen-auth__demonote {
  line-height: 1.5;
  margin: 0;
}

/* ----- The account, and the pages that hang off it ----- */

.goen-account {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin: 0 auto;
  max-width: var(--page-box);
  padding: 2rem var(--page-gutter) 4rem;
  width: 100%;
}

/* Points and warranty are one column of short rows. A 1280px column around
   that much content reads as a page with something missing. */
.goen-account--narrow {
  max-width: 48rem;
}

/* The head also carries the shared page-head spelling, because the layout
   gate measures the sign-out control through it (scripts/check-layout.mjs:1542).
   Every property that sheet sets on it is set again here, so the spelling is a
   handle and the appearance is decided in this file. */
.goen-account__head {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  padding-bottom: 1.5rem;
}

.goen-account__headactions {
  flex: none;
}

.goen-account__layout {
  align-items: start;
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
  min-width: 0;
}

/* Below 1024 the nav is a strip of soft buttons that scrolls inside itself.
   The strip scrolls; the page does not, which is the thing the overflow gate
   measures. */
.goen-account__nav {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding-bottom: 0.5rem;
}

.goen-account__navitem {
  align-items: center;
  background: var(--n-100);
  border-radius: var(--r-md);
  box-sizing: border-box;
  color: var(--n-900);
  display: inline-flex;
  flex: none;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0 1rem;
  text-decoration: none;
  white-space: nowrap;
}

.goen-account__navitem:hover {
  background: var(--n-200);
  color: var(--n-900);
}

/* The current item. A link to a section of this page is current while its
   section is the target of the address, which needs no script; a page of its
   own would say aria-current. */
.goen-account__navitem[aria-current='page'],
.goen-account__layout:has(#standing-heading:target) .goen-account__navitem[href='#standing-heading'],
.goen-account__layout:has(#orders-heading:target) .goen-account__navitem[href='#orders-heading'],
.goen-account__layout:has(#profile-heading:target) .goen-account__navitem[href='#profile-heading'],
.goen-account__layout:has(#addresses-heading:target) .goen-account__navitem[href='#addresses-heading'],
.goen-account__layout:has(#email-heading:target) .goen-account__navitem[href='#email-heading'],
.goen-account__layout:has(#password-heading:target) .goen-account__navitem[href='#password-heading'],
.goen-account__layout:has(#google-heading:target) .goen-account__navitem[href='#google-heading'],
.goen-account__layout:has(#erase-heading:target) .goen-account__navitem[href='#erase-heading'] {
  background: var(--accent-faint);
  color: var(--accent-deep);
}

.goen-account__navname {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
}

/* The hint says what the page behind a link holds. The strip has room for one
   line, and there the link's own name is the whole answer. */
.goen-account__navhint {
  display: none;
}

/* Between the sections of this page and the pages of their own. */
.goen-account__navrule {
  background: var(--n-200);
  border: 0;
  flex: none;
  height: 2.75rem;
  margin: 0;
  width: 1px;
}

.goen-account__panels {
  display: flex;
  flex-direction: column;
  max-width: 48rem;
  min-width: 0;
}

/* A section is parted from the one above by 3rem, not boxed or ruled. The
   .goen-card on the markup is a panel recipe this page does not want, so the
   pair of classes cancels it. */
.goen-account__section.goen-card {
  background: none;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
  padding: 1.5rem 0;
  scroll-margin-top: 1.5rem;
}

.goen-account__section.goen-card:first-child {
  padding-top: 0;
}

/* Deleting an account is irreversible, so its section reads as the exception it
   is: the control that opens the form takes the error colour. */
.goen-account__section--danger .goen-account__summary {
  color: var(--error);
}

/* The nav's anchors aim at these headings, so the margin that keeps a jumped-to
   heading off the top edge belongs here rather than on the section around it. */
.goen-account__heading {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  line-height: 1.5;
  margin: 0;
  scroll-margin-top: 1.5rem;
}

.goen-account__standing {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.goen-account__tier {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  margin: 0;
}

/* No tier reached is a sentence, not an empty medallion. */
.goen-account__tier--none {
  color: var(--n-500);
  font-size: var(--fs-14);
  font-weight: var(--fw-regular);
}

.goen-account__meta,
.goen-account__hint {
  color: var(--n-500);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  margin: 0;
}

/* Store credit sits inside the standing section: it is one figure about where
   a shopper stands, so it is a panel on the quiet ground rather than a section
   of its own. */
.goen-account__credit {
  background: var(--n-50);
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 1rem 1.25rem;
}

.goen-account__creditlabel {
  color: var(--n-500);
  font-size: 0.8125rem;
}

.goen-account__creditamount {
  color: var(--n-900);
  font-size: 1.125rem;
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
}

/* The order history. One table at every width; each row's facts sit under its number.
   A row is a wrapping flex line so the status drops under the facts when an
   unbreakable total ("NT$1,234,567") and the status no longer fit side by side. */
.goen-account__orders {
  border-collapse: collapse;
  font-size: var(--fs-14);
  width: 100%;
}

.goen-account__orders tr {
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
}

.goen-account__orders tbody tr:last-child {
  border-bottom: 0;
}

/* The status column is as wide as its badge and pay link, never narrower:
   a fixed basis broke 「前往付款」 across two lines beside the badge. */
.goen-account__orders th,
.goen-account__orders td {
  flex: 0 0 auto;
  min-inline-size: 7rem;
  padding: 0.5rem 0;
  text-align: end;
}

.goen-account__orders th:first-child,
.goen-account__orders td:first-child {
  flex: 1 1 0;
  min-width: min-content;
  text-align: start;
}

.goen-account__orders th {
  color: var(--n-500);
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
}

.goen-account__orders td {
  padding-block: 0.75rem;
  vertical-align: top;
}

.goen-account__number {
  color: var(--n-900);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.goen-account__number:hover {
  color: var(--n-900);
  text-decoration: underline;
}

.goen-account__orderpay {
  font-size: var(--fs-14);
  white-space: nowrap;
  font-weight: var(--fw-medium);
  margin-inline-start: 0.5rem;
  text-decoration: none;
}

.goen-account__orderpay:hover {
  text-decoration: underline;
}

.goen-account__ordermeta {
  color: var(--n-500);
  display: block;
  font-size: 0.8125rem;
  margin-top: 0.125rem;
}

.goen-account__empty {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-account__form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.goen-account__fields {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

.goen-account__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.goen-account__value {
  align-items: center;
  color: var(--n-900);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-14);
  gap: 0.5rem;
  margin: 0;
}

/* Saved addresses are rows parted by space. */
.goen-account__addresses {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-account__address {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem 0;
}

.goen-account__address:first-child {
  padding-top: 0;
}

.goen-account__address:last-child {
  padding-bottom: 0;
}

.goen-account__addrbody {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.goen-account__addrlabel {
  align-items: center;
  color: var(--n-900);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 0.5rem;
}

.goen-account__addractions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* A native <details> used as "show me the form". The forms are long and mostly
   unused; collapsing them keeps the page readable without hiding anything from
   a visitor with scripting off. */
.goen-account__disclosure {
  padding-top: 0;
}

.goen-account__summary {
  align-items: center;
  color: var(--accent-text);
  cursor: pointer;
  display: flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  list-style: none;
  min-height: 2.75rem;
}

.goen-account__summary::-webkit-details-marker {
  display: none;
}

.goen-account__disclosure[open] > .goen-account__summary {
  margin-bottom: 0.5rem;
}

/* The storefront's one destructive control: white, error text, the error
   outline. It is not the action its section is about, and it is not pressed by
   accident. */
.goen-account__erase {
  background: var(--n-0);
  border-color: var(--error-outline);
  color: var(--error);
}

.goen-account__erase:hover {
  background: var(--error-bg);
  color: var(--error);
}

/* A native checkbox, sized up to the row it sits in rather than replaced by a
   box drawn in CSS: the native control is the one a screen reader and a
   keyboard already know. */
.goen-check {
  align-items: center;
  color: var(--n-900);
  cursor: pointer;
  display: flex;
  font-size: var(--fs-14);
  gap: 0.5rem;
  min-height: 2.75rem;
}

.goen-check__box {
  accent-color: var(--accent);
  height: 1rem;
  margin: 0;
  width: 1rem;
}

/* ----- Points and warranty registration ----- */

.goen-points__figures {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-points__figure {
  background: var(--n-50);
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 1.25rem 1.5rem;
}

.goen-points__label {
  color: var(--n-500);
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
}

.goen-points__value {
  color: var(--n-900);
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-display);
  letter-spacing: -0.02em;
}

.goen-points__meta {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-points__rule {
  color: var(--n-500);
  font-size: 0.8125rem;
  margin: 0;
}

.goen-points__field {
  max-width: 10rem;
  text-align: right;
}

/* The redemption field keeps the shared spelling beside its own, because the
   layout gate measures this control through it (scripts/check-layout.mjs:1490).
   base.css reaches it on hover and on focus with a higher specificity than a
   single class, so those two states are decided here instead. */
.goen-qa__form .ui-input:focus,
.goen-qa__form .ui-input:hover {
  background: transparent;
  border-color: var(--control-boundary);
}

/* One control, not a bar: the form asks for a number and then offers the one
   thing to do with it. */
.goen-points__submit {
  align-self: flex-start;
  max-width: 100%;
  min-width: 0;
  overflow-wrap: anywhere;
  white-space: normal;
}

.goen-points__ledger {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.goen-points__ledgerhead {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  margin: 0;
}

/* The ledger. The signed amount leads, because a ledger is read down that
   column. */
.goen-points {
  display: grid;
  column-gap: 0.75rem;
  grid-template-columns: max-content minmax(0, 1fr);
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-points__item {
  align-items: baseline;
  border-bottom: 1px solid var(--n-200);
  display: grid;
  gap: 0.25rem 0.75rem;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  padding: 0.75rem 0;
}

.goen-points__item:last-child {
  border-bottom: 0;
}

/* An expired line stays visible and reads as past: hiding it would leave a
   customer wondering whether the points were ever there. */
.goen-points__item.is-expired,
.goen-points__item.is-expired .goen-points__what {
  color: var(--fg-muted);
}

.goen-points__amount {
  justify-self: end;
  text-align: end;
  color: var(--n-500);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
}

.goen-points__amount.is-earned {
  color: var(--success);
}

.goen-points__what {
  color: var(--n-900);
  font-size: var(--fs-14);
}

.goen-points .goen-points__meta {
  font-size: 0.8125rem;
  grid-column: 2;
}

/* Registered cover, and the lines that can still be registered. One column of
   hairline-divided rows: each entry is read rather than compared against its
   neighbours, so a table would make a customer scan across for one date. */
.goen-warranty {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-warranty__item {
  border-bottom: 1px solid var(--n-200);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 1rem 0;
}

.goen-warranty__item:first-child {
  padding-top: 0;
}

.goen-warranty__item.is-expired,
.goen-warranty__item.is-expired .goen-warranty__name {
  color: var(--fg-muted);
}

.goen-warranty__head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: space-between;
}

.goen-warranty__name {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.goen-warranty__name:hover {
  color: var(--n-900);
  text-decoration: underline;
}

.goen-warranty__meta {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 0;
}

.goen-warranty__hint {
  color: var(--n-500);
  font-size: 0.8125rem;
  margin: 0;
}

.goen-warranty__form {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.goen-warranty__field {
  flex: 1 1 16rem;
}

/* The empty state's two sentences are compared by the whole class attribute
   (internal/ui/pages/warranty_test.go:180,184), so the paragraphs carry the
   shared spelling alone and every declaration is made here. */
.goen-empty .ui-empty__title {
  color: var(--n-900);
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: var(--fw-medium);
  line-height: 1.5;
  margin: 0;
}

.goen-empty .ui-empty__desc {
  color: var(--n-500);
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  line-height: 1.5;
  margin: 0;
  max-width: 40ch;
}

/* ----- Policy, FAQ, shipping and the public warranty ----- */

/* One 48rem column of prose. */
.goen-doc {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  margin: 0 auto;
  max-width: 48rem;
  padding: 2rem var(--page-gutter) 4rem;
  width: 100%;
}

.goen-doc__head {
  gap: 0.5rem;
}

/* The eyebrow is a sentence-case line in the blue ink, not a small-caps label:
   a label, so not the blue a link wears. */
.goen-doc__head .goen-pagehead__eyebrow {
  color: var(--accent-deep);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
}

.goen-doc__head .goen-pagehead__title {
  color: var(--n-900);
  font-weight: var(--fw-display);
}

.goen-doc__head .goen-pagehead__sub {
  color: var(--n-700);
  font-size: 1.25rem;
  line-height: 1.6;
}

.goen-doc__layout {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* The section list is a row of soft buttons above the text, because a column
   of 48rem has no room for a rail beside it. */
.goen-doc__toc {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-width: 0;
}

.goen-doc__tochead {
  color: var(--n-900);
  flex: 0 0 100%;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0;
}

.goen-doc__toclink {
  align-items: center;
  background: var(--n-100);
  border-radius: var(--r-md);
  color: var(--n-900);
  display: inline-flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  min-height: 2.75rem;
  padding: 0 1rem;
  text-decoration: none;
}

.goen-doc__toclink:hover {
  background: var(--n-200);
  color: var(--n-900);
}

.goen-doc__article {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.goen-doc__section {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 3rem;
  scroll-margin-top: 1.5rem;
}

.goen-doc__section:first-child {
  margin-top: 0;
}

.goen-doc__section h2 {
  color: var(--n-900);
  font-size: 1.5rem;
  font-weight: var(--fw-display);
  line-height: 1.35;
  margin: 0;
}

.goen-doc__section p {
  color: var(--n-700);
  font-size: 1rem;
  line-height: 1.7;
  margin: 0;
}

/* A section describing what has not been decided reads differently from one
   describing a rule. A reader must be able to tell a promise from a gap, and a
   shop that sets both in the same ink is making a promise by accident. The
   difference is a rule beside the text rather than a lighter grey: grey enough
   to read as pending is grey enough to fail a contrast check. */
.goen-doc__section.is-pending {
  border-left: 2px solid var(--n-300);
  padding-left: 1rem;
}

.goen-doc__qa {
  border-bottom: 1px solid var(--n-200);
}

.goen-doc__qa:first-of-type {
  border-top: 1px solid var(--n-200);
}

.goen-doc__qa summary {
  align-items: center;
  color: var(--n-900);
  cursor: pointer;
  display: flex;
  font-size: 1rem;
  font-weight: var(--fw-medium);
  gap: 1rem;
  justify-content: space-between;
  list-style: none;
  min-height: 2.75rem;
  padding: 1rem 0;
}

.goen-doc__qa summary::-webkit-details-marker {
  display: none;
}

/* A flex summary drops the marker the browser would draw, so the affordance is
   drawn here: a plus that becomes a minus. */
.goen-doc__qa summary::after {
  color: var(--n-500);
  content: '+';
  flex: none;
  font-size: 1.25rem;
  font-weight: var(--fw-regular);
  line-height: 1;
}

.goen-doc__qa[open] summary::after {
  content: '\2212';
}

.goen-doc__qa p {
  padding-bottom: 1rem;
}

.goen-doc__methods {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.goen-doc__method {
  align-items: baseline;
  display: flex;
  gap: 0.75rem;
  justify-content: space-between;
  padding: 0.75rem 0;
}

.goen-doc__methodname {
  color: var(--n-900);
  display: flex;
  flex-direction: column;
  font-size: 1rem;
  font-weight: var(--fw-medium);
  gap: 0.125rem;
}

.goen-doc__methodfee {
  color: var(--n-900);
  display: flex;
  flex-direction: column;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  gap: 0.125rem;
  text-align: right;
}

.goen-doc__methodmeta {
  color: var(--n-500);
  font-size: 0.8125rem;
  font-weight: var(--fw-regular);
}

.goen-doc__contact {
  color: var(--n-500);
  font-size: var(--fs-14);
  margin: 3rem 0 0;
}

/* ----- About ----- */

/* One 48rem column: the mark drawn large, the title, a lead, a paragraph, three
   short sections in the policy rhythm, and the lines a company has to print. */
.about {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin: 0 auto;
  max-width: 48rem;
  padding: 2rem var(--page-gutter) 4rem;
  width: 100%;
}

/* The paper tone's ground, the mark's two inks, and lines that stay fine at
   every width: the rings carry non-scaling strokes. */
.about__art {
  aspect-ratio: 16 / 9;
  background: var(--tone-ground);
  border-radius: var(--r-lg);
  display: block;
  height: auto;
  width: 100%;
}

.about__ring {
  fill: none;
  stroke: var(--n-900);
  stroke-width: 1.5;
}

.about__ring--accent {
  stroke: var(--accent);
}

/* Where the two rings meet is lighter than the paper around it, not darker:
   a tint of either ink there reads as a grey shadow on the warm ground. */
.about__lens {
  fill: var(--n-0);
  fill-opacity: 0.6;
}

@media (forced-colors: active) {
  .about__ring {
    stroke: CanvasText;
  }

  .about__lens {
    fill: none;
  }
}

.about__title {
  color: var(--n-900);
  font-weight: var(--fw-display);
  margin: 0;
}

.about__lede {
  color: var(--n-700);
  font-size: 1rem;
  line-height: 1.7;
  margin: 0;
}

/* The first paragraph under the title is the lead. */
.about__title + .about__lede {
  font-size: 1.25rem;
  line-height: 1.6;
}

.about__values {
  display: flex;
  flex-direction: column;
}

.about__value {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 3rem;
}

.about__value:first-child {
  margin-top: 1.5rem;
}

.about__valuetitle {
  color: var(--n-900);
  font-size: 1.5rem;
  font-weight: var(--fw-display);
  line-height: 1.35;
  margin: 0;
}

.about__valuebody {
  color: var(--n-700);
  font-size: 1rem;
  line-height: 1.7;
  margin: 0;
}

/* ----- Contact ----- */

/* The form beside the facts. The two banners inside the form are written in the
   page rather than through the notice component, because the layout gate reads
   the refusal's sentence through a shared descendant pair
   (scripts/check-layout.mjs:2068) and one caller does not earn a Prop. */
.contact {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  margin: 0 auto;
  max-width: var(--page-box);
  padding: 2rem var(--page-gutter) 4rem;
  width: 100%;
}

.contact .goen-pagehead {
  gap: 0.5rem;
}

.contact .goen-pagehead__title {
  color: var(--n-900);
  font-weight: var(--fw-display);
}

.contact .goen-pagehead__sub {
  color: var(--n-500);
  font-size: 1rem;
}

.contact__layout {
  align-items: start;
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(0, 1fr);
}

.contact__methods {
  display: grid;
  gap: 0.5rem;
  grid-template-columns: 1fr;
}

.contact-method {
  align-items: center;
  box-sizing: border-box;
  color: var(--n-900);
  display: flex;
  gap: 0.75rem;
  min-height: 2.75rem;
  text-decoration: none;
}

.contact-method:hover .contact-method__value {
  text-decoration: underline;
}

.contact-method__icon {
  align-items: center;
  color: var(--n-700);
  display: inline-flex;
  flex: none;
  justify-content: center;
}

.contact-method__icon svg {
  height: 1.5rem;
  width: 1.5rem;
}

.contact-method__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.contact-method__label {
  color: var(--n-500);
  font-size: 0.8125rem;
}

.contact-method__value {
  color: var(--n-900);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  overflow-wrap: anywhere;
}

.contact__panel {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.contact__panel--done {
  align-items: flex-start;
  flex-direction: row;
  gap: 0.75rem;
}

.contact__donemark {
  color: var(--success);
  display: inline-flex;
  flex: none;
}

.contact__formtitle {
  color: var(--n-900);
  font-size: 1.125rem;
  font-weight: var(--fw-medium);
  line-height: 1.5;
  margin: 0;
}

.contact__req {
  color: var(--error);
  margin-left: 0.125rem;
}

.contact__fields {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

.contact__field--wide {
  grid-column: 1 / -1;
}

.contact__ref {
  font-variant-numeric: tabular-nums;
}

.contact__message {
  min-height: 8rem;
}

.contact__submit {
  align-self: stretch;
}

.contact__aside {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* The refusal's sentence is read through the shared descendant pair, so the
   span keeps that spelling; the muted ink it would otherwise take from
   base.css is decided here. */
.goen-notice .ui-alert__body {
  color: inherit;
}

/* ----- The 404 and the other one-message pages ----- */

/* The 404, the two grant failures, the newsletter links and /verify. One
   centred stack: the code where there is one, the heading, the sentence, and
   at most two ways on, one filled and one outline. On a 404 the departments
   follow as a plain list. */
.notice {
  align-items: center;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  justify-content: center;
  margin: 0 auto;
  max-width: 40rem;
  min-height: 50vh;
  padding: 4rem var(--page-gutter) 6rem;
  text-align: center;
  width: 100%;
}

/* A sentence-case line above the heading, in the blue ink a label wears. */
.notice__code {
  color: var(--accent-deep);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  line-height: 1.5;
}

.notice__title {
  color: var(--n-900);
  font-weight: var(--fw-display);
  margin: 0;
  text-wrap: balance;
}

.notice__body {
  color: var(--n-700);
  font-size: 1rem;
  line-height: 1.7;
  margin: 0;
}

.notice__form {
  display: flex;
  width: 100%;
}

.notice__actions {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-top: 0.75rem;
  width: 100%;
}

.notice__back {
  width: 100%;
}

.notice__departments {
  margin-top: 3.5rem;
  width: 100%;
}

.notice__departmentshead {
  color: var(--n-500);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  margin: 0 0 0.5rem;
}

.notice__departmentlist {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.5rem;
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

.notice__department {
  align-items: center;
  color: var(--n-700);
  display: inline-flex;
  font-size: 0.9375rem;
  font-weight: var(--fw-medium);
  min-height: 2.75rem;
  text-decoration: none;
}

.notice__department:hover {
  color: var(--n-900);
  text-decoration: underline;
}

/* ----- A campaign page ----- */

/* The band takes its ground from the campaign's tone. A campaign carries no
   tone yet, so the fallback is the stone ground: oklch(97% 0.006 70). The
   photograph sits flush on the band, above the title at 375 and on the right
   half from 1024. */
/* The head itself is the ground: it carries the campaign's tone and fills its
   cell, and the band only rounds the corners. A ground on the band as well
   would show as a second beige beside the head wherever the photograph is
   missing. */
.goen-campaign__band {
  border-radius: var(--r-lg);
  display: grid;
  gap: 0;
  overflow: hidden;
}

.goen-campaign__header {
  aspect-ratio: 8 / 3;
  border: 0 solid var(--n-0);
  border-bottom-width: var(--col-gap);
  box-sizing: border-box;
  display: block;
  height: auto;
  object-fit: cover;
  width: 100%;
}

.goen-campaign__band .goen-pagehead {
  border-radius: 0;
  gap: 0.5rem;
  padding: 1.5rem 1.25rem;
}

.goen-campaign__band .goen-pagehead__title {
  font-size: 2.25rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.goen-campaign__band .ui-statline {
  margin-top: 1rem;
}

.goen-campaign__band .ui-period {
  margin-top: 0.5rem;
}

/* ----- Wider than a phone ----- */

@media (min-width: 640px) {
  .goen-account__address {
    align-items: center;
    flex-direction: row;
    justify-content: space-between;
  }

  .goen-doc {
    padding: 3rem var(--page-gutter) 6rem;
  }

  .about {
    padding: 3rem var(--page-gutter) 6rem;
  }

  .contact {
    padding: 3rem var(--page-gutter) 6rem;
  }

  .goen-auth {
    padding: 4rem var(--page-gutter) 6rem;
  }

  .notice {
    padding: 6rem var(--page-gutter) 8rem;
  }
}

@media (min-width: 768px) {
  .goen-account__fields,
  .contact__fields {
    grid-template-columns: 1fr 1fr;
  }

  .goen-account__field--wide {
    grid-column: 1 / -1;
  }

  .goen-points__figures {
    grid-template-columns: 1fr 1fr;
  }

  .notice__actions {
    flex-direction: row;
    justify-content: center;
  }

  .notice__back,
  .notice__form {
    width: auto;
  }
}

@media (min-width: 1024px) {
  .goen-account {
    gap: 2rem;
  }

  .goen-account__layout {
    gap: 3rem;
    grid-template-columns: 14rem minmax(0, 1fr);
  }

  .goen-account__nav {
    align-self: start;
    flex-direction: column;
    gap: 0.125rem;
    overflow-x: visible;
    padding-bottom: 0;
    position: sticky;
    top: 2rem;
  }

  .goen-account__navitem {
    align-items: flex-start;
    background: transparent;
    flex-direction: column;
    gap: 0.125rem;
    justify-content: center;
    padding: 0.5rem 0.75rem;
    white-space: normal;
  }

  .goen-account__navitem:hover {
    background: var(--n-100);
  }

  .goen-account__navitem[aria-current='page'],
  .goen-account__layout:has(#standing-heading:target) .goen-account__navitem[href='#standing-heading'],
  .goen-account__layout:has(#orders-heading:target) .goen-account__navitem[href='#orders-heading'],
  .goen-account__layout:has(#profile-heading:target) .goen-account__navitem[href='#profile-heading'],
  .goen-account__layout:has(#addresses-heading:target) .goen-account__navitem[href='#addresses-heading'],
  .goen-account__layout:has(#email-heading:target) .goen-account__navitem[href='#email-heading'],
  .goen-account__layout:has(#password-heading:target) .goen-account__navitem[href='#password-heading'],
  .goen-account__layout:has(#google-heading:target) .goen-account__navitem[href='#google-heading'],
  .goen-account__layout:has(#erase-heading:target) .goen-account__navitem[href='#erase-heading'] {
    background: var(--accent-faint);
    color: var(--accent-deep);
  }

  .goen-account__navhint {
    color: var(--n-500);
    display: block;
    font-size: 0.8125rem;
  }

  .goen-account__navrule {
    height: 1px;
    margin: 0.5rem 0;
    width: auto;
  }

  .contact__layout {
    gap: 4rem;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }

  .goen-campaign__band:has(.goen-campaign__header) {
    grid-template-columns: 1fr 1fr;
  }

  .goen-campaign__header {
    aspect-ratio: 3 / 2;
    border-bottom-width: 0;
    border-left-width: var(--col-gap);
    grid-column: 2;
    grid-row: 1;
  }

  .goen-campaign__band .goen-pagehead {
    grid-column: 1;
    grid-row: 1;
    justify-content: center;
    padding: 3rem;
  }

  .goen-campaign__band .goen-pagehead__title {
    font-size: 3rem;
  }
}

/* Names the customer's order and checkout render, and the back office does too;
   admin.css keeps its own copy. */
.goen-order__lines {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  min-width: 0;
  padding: 0;
}

.goen-checkout__fields {
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .goen-checkout__fields {
    gap: 16px;
    grid-template-columns: 1fr 1fr;
  }
}

/* A panel the page can shut. On a stockroom phone 23 links are a screen of
   their own before the work starts, so it renders closed; at the desk the
   summary is gone and the panel is the rail, forced open through
   ::details-content. The summary is only hidden where that forcing is
   supported — otherwise there would be no control left to open it with. */
.goen-disclosure {
  min-width: 0;
}

.goen-disclosure__summary {
  align-items: center;
  border-radius: var(--r-sm);
  color: var(--fg);
  cursor: pointer;
  display: flex;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  gap: 6px;
  justify-content: space-between;
  list-style: none;
  min-height: 44px;
  padding: 0 12px;
}

.goen-disclosure__panel {
  min-width: 0;
}

.goen-admin__navlabel::-webkit-details-marker,
.goen-disclosure__summary::-webkit-details-marker {
  display: none;
}

/* A caret of our own, and it is DECORATION: which state the group is in is
   announced by <details> itself, so this must not be readable text. */
.goen-admin__navlabel::after,
.goen-disclosure__summary::after {
  border-bottom: 1.5px solid var(--fg-muted);
  border-right: 1.5px solid var(--fg-muted);
  content: '';
  height: 6px;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform var(--dur-base) var(--ease-standard);
  width: 6px;
}

.goen-admin__navgroup[open] > .goen-admin__navlabel::after,
.goen-disclosure[open] > .goen-disclosure__summary::after {
  transform: rotate(-135deg) translate(-1px, -1px);
}

.goen-qa__question {
  margin: 0;
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  color: var(--fg);
}

.goen-qa__form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 60ch;
}

.goen-pdp .goen-qa__form {
  max-width: 40rem;
}

.goen-order__cancel {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 4px;
}

/* The layout gate finds the order screen's notice by the shared spelling, so
   the element keeps it (#380 R8). The box base.css brings with it is
   decided here instead. */
.goen-notice.ui-alert {
  display: block;
  line-height: var(--lh-read);
}

/* ---------- Motion ----------

   Page to page. Every link stays a plain <a href> and every navigation stays a
   full document load; what changes is that the browser cross-fades the two
   documents instead of cutting between them, and carries a clicked photo across
   to where it lands. No router, and a browser without it navigates exactly as
   it did before.

   The chrome both documents share is named, so the browser lifts it out of the
   root snapshot and paints it once instead of fading it out and back in. That
   is also why `main` is not named: with the header and footer already lifted
   out, the root cross-fade IS the content cross-fade, while a name on `main`
   would animate its group between two page heights, which is a scale.
   ------------------------------------------------------------------------ */

@view-transition {
  navigation: auto;
  types: page;
}

.goen-header {
  view-transition-name: header;
}

.goen-footer {
  view-transition-name: footer;
}

/* The two regions the product page swaps in place carry the same names here as
   they do there, so one set of rules covers the navigation into the page and
   the swap inside it. */
.goen-pdp__gallery {
  view-transition-name: gallery;
}

.goen-pdp__buy {
  view-transition-name: buybox;
}

/* The group carries the geometry, and its keyframes are what would move or
   resize a named region when the two pages put it in different places: a page
   read halfway down leaves its header above the viewport, and the browser
   would otherwise slide the header back down into the new page. So the regions
   that must stay put have no group animation, and the photos, which are meant
   to travel, keep it. The timing is set on the group as well because the user
   agent puts its own 0.25s there and the pair inherits from it.

   A region swapped in place shares these snapshots and keeps the base step: the
   old content stays on screen for the length of the fade, and a swap the
   visitor is waiting on should not be slower than the request. */
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
  animation-duration: var(--dur-base);
  animation-timing-function: var(--ease-standard);
}

::view-transition-group(header),
::view-transition-group(footer),
::view-transition-group(gallery),
::view-transition-group(buybox) {
  animation-name: none;
}

/* A navigation replaces the whole page, and a page that arrives with a new
   layout and new photographs reads as a cut when it dissolves as fast as a
   swapped region does, so only navigations take the slow step. */
html:active-view-transition-type(page)::view-transition-group(*),
html:active-view-transition-type(page)::view-transition-old(*),
html:active-view-transition-type(page)::view-transition-new(*) {
  animation-duration: var(--dur-slow);
}

/* A photo that leaves one page and arrives on the next travels as one image:
   assets/js/transitions.js names the clicked photo on the way out and the shown
   one on the way in, and only for that navigation. The old photo stays opaque the whole way
   and the new one fades in on top of it, so there is no half-transparent dip
   in the middle; an unmatched old photo just fades out. */
::view-transition-group(*.photo),
html:active-view-transition-type(page)::view-transition-group(*.photo) {
  animation-duration: var(--dur-move);
  animation-timing-function: var(--ease-move);
}

::view-transition-old(*.photo),
::view-transition-new(*.photo) {
  block-size: 100%;
  mix-blend-mode: normal;
  object-fit: cover;
}

::view-transition-old(*.photo) {
  animation: none;
}

::view-transition-old(*.photo):only-child {
  animation: goen-photo-out var(--dur-base) var(--ease-standard) both;
}

@keyframes goen-photo-out {
  to { opacity: 0; }
}

/* The global reduce block at the top of this file matches elements, and these
   snapshots are not elements, so reduced motion is spelled again. Motion that
   moves, scales or resizes is dropped and the two pages dissolve at the base
   step: a hard cut between two very different layouts reads as more of a jump
   than the dissolve does. The same rules cover a region swapped in place. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-name: none;
  }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*),
  html:active-view-transition-type(page)::view-transition-group(*),
  html:active-view-transition-type(page)::view-transition-old(*),
  html:active-view-transition-type(page)::view-transition-new(*) {
    animation-duration: var(--dur-base);
  }
}

/* Request feedback shares the buttons' existing disabled treatment without
   removing a named submitter from the form payload. */
form[data-request-pending],
form[data-request-pending] button,
form[data-request-pending] input[type="submit"] {
  cursor: progress;
}

form[data-request-pending] [aria-disabled="true"] {
  opacity: 0.5;
}

.goen-btn:active:not(:disabled):not([aria-disabled="true"]) {
  opacity: 0.85;
}

/* A page on its way (assets/js/goen.js marks it). A bar runs along the top of
   the screen, not the header, which a page read halfway down has left behind.
   It waits a moment first, so a page that arrives quickly, a prerendered one
   above all, never shows it. It is the old page's own paint and goes with
   that page. */
[data-navigating] body::before {
  animation:
    goen-navigating-in var(--dur-base) var(--ease-standard) 200ms both,
    goen-navigating 1.2s ease-in-out 200ms infinite;
  background: var(--accent);
  block-size: 3px;
  content: "";
  inline-size: 40vw;
  inset: 0 auto auto 0;
  pointer-events: none;
  position: fixed;
  z-index: var(--z-toast);
}

@keyframes goen-navigating-in {
  from { opacity: 0; }
}

@keyframes goen-navigating {
  from { translate: -100% 0; }
  to { translate: 100vw 0; }
}

/* The reduce block above stops the run after one frame; a still bar across the
   whole width says the same without the movement. */
@media (prefers-reduced-motion: reduce) {
  [data-navigating] body::before {
    inline-size: 100%;
  }
}

/* A sub-category chip takes the current fill the moment it is pressed, and the
   one it replaces lets go of it, as a segmented control would. */
.goen-listing__head .goen-pagehead__chip[data-navigation-pending] {
  background: var(--accent-faint);
  border-color: var(--accent);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}

.goen-listing__head [data-tone='ink'] .goen-pagehead__chip[data-navigation-pending] {
  background: rgb(255 255 255 / 0.1);
  border-color: var(--tone-text);
  color: var(--on-ink-accent);
}

.goen-listing__head .goen-pagehead__chips:has([data-navigation-pending]) [aria-current='page']:not([data-navigation-pending]) {
  background: transparent;
  border-color: transparent;
  color: var(--ink-2);
  font-weight: var(--fw-regular);
}

.goen-listing__head [data-tone='ink'] .goen-pagehead__chips:has([data-navigation-pending]) [aria-current='page']:not([data-navigation-pending]) {
  color: var(--tone-text);
}

@media (forced-colors: active) {
  [data-navigating] body::before {
    background: Highlight;
    forced-color-adjust: none;
  }

  .goen-pagehead__chip[data-navigation-pending]:not(:focus-visible) {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }

  .goen-pagehead__chips:has([data-navigation-pending]) [aria-current='page']:not([data-navigation-pending], :focus-visible) {
    outline: none;
  }
}

/* These are entry fades, never timers that dismiss unread feedback. Removal
   during a request uses the same view transition as its surrounding region. */
.goen-notice {
  transition: opacity var(--dur-base) var(--ease-standard);
}

@starting-style {
  .goen-notice { opacity: 0; }
}

@supports selector(details::details-content) {
  .goen-header__menu::details-content {
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease-standard), content-visibility var(--dur-base);
    transition-behavior: allow-discrete;
  }

  .goen-header__menu[open]::details-content { opacity: 1; }

  @starting-style {
    .goen-header__menu[open]::details-content { opacity: 0; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .goen-notice,
  .goen-header__menu::details-content {
    transition: none;
  }

  @starting-style {
    .goen-notice,
    .goen-header__menu[open]::details-content { opacity: 1; }
  }
}

/* Tailwind Plus alert-with-link: keep the continuation below the message on
   narrow screens and beside it when both fit. */
.goen-cart__adjustment-content { flex: 1; min-width: 0; }
.goen-cart__adjustment-message { margin: 0; }
.goen-cart__adjustment-action { margin: 0.75rem 0 0; }
.goen-cart__adjustment-action a { font-weight: var(--fw-medium); white-space: nowrap; }
@media (min-width: 48rem) {
  .goen-cart__adjustment-content { display: flex; justify-content: space-between; gap: 1.5rem; }
  .goen-cart__adjustment-action { margin: 0; }
}

/* Native constraints wait for a completed interaction, so an untouched checkout
   does not start by telling the shopper every field is wrong. */
.goen-field__constraint {
  display: none;
}

[data-checkout-constraint]:user-invalid ~ .goen-field__constraint,
[data-checkout-constraint][aria-invalid="true"] ~ .goen-field__constraint {
  display: block;
}

[data-checkout-constraint]:user-invalid {
  border-color: var(--error);
}

/* A field the script marked refused looks as the server's 422 renders it. */
[data-rule][aria-invalid="true"] {
  border-color: var(--error);
}

.goen-compare__picker {
  align-items: center;
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
}

.goen-compare__picker .ui-input {
  flex: 1;
  min-width: 0;
}

.goen-compare__chosen {
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
}

.goen-compare__chosen li {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.goen-compare__note {
  color: var(--fg-muted);
  font-size: var(--fs-14);
  margin: 0 0 16px;
}

@page {
  margin: 12mm;
  size: A4;
}

@media print {
  .goen-admin .goen-admin__batchcontrols,
  .goen-skip,
  .goen-adminbar,
  .goen-admin__nav,
  .goen-adminfoot,
  .ui-crumbs,
  .ui-alert,
  .goen-admin__op:has(form),
  .goen-admin__op form,
  .goen-admin__op .ui-btn {
    display: none;
  }

  .goen-shell {
    min-height: 0;
  }

  body {
    background: var(--bg);
    color: var(--fg);
  }
}

/* Forced colours remove the chosen card's shadow and equalise its border.
   The native radio keeps the checked mark the user's colour scheme supplies. */
@media (forced-colors: active) {
  .goen-checkout__ship > input[type=radio] {
    align-self: center;
    clip-path: none;
    flex: none;
    height: 1rem;
    margin: 0;
    overflow: visible;
    position: static;
    width: 1rem;
  }

  .goen-checkout__shipname {
    flex: 1;
  }

  /* Weight and underline keep the chosen label distinct from ordinary links. */
  .goen-swatch--on:not(.goen-swatch--dot),
  .goen-swatch--on:not(.goen-swatch--dot):hover {
    font-weight: var(--fw-semibold);
    outline: 2px solid Highlight;
    outline-offset: 2px;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 0.2em;
  }

  /* SVG preserves author colours, so its selected ring must follow the palette. */
  .goen-swatch--dot.goen-swatch--on .goen-swatch__dot {
    outline-color: Highlight;
  }
}

@media (forced-colors: active) {
  .goen-band__media img,
  .goen-cat__well,
  .goen-campaign__header {
    outline: 1px solid CanvasText;
  }
}
