/*
 * NILOSI base layer: reset, typography, layout primitives, controls, and the
 * shared components every page composes from (label, tile, plate, section
 * head, buttons, inputs, film).
 *
 * Loaded on every page. Component and page styles build on this and never
 * redefine a colour or size literal -- everything comes from tokens.css.
 *
 * Contract with the page files (home.css, shop.css, chrome.css): they USE
 * .nd-label, .nd-tile*, .nd-plate*, .nd-head*, .nd-btn*, .nd-video* and never
 * redeclare them. Context classes (.nd-shelf__item, .nd-pcard, .nd-cats__tile)
 * carry the per-page layout.
 */

/* --------------------------------------------------------------- reset ---- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Fraunces carries an optical-size axis; the browser picks the right
     instance for each size. No font-variation-settings anywhere. */
  font-optical-sizing: auto;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Nothing on the storefront may create a horizontal scrollbar. The routine
   row scrolls on its own axis inside its own box; the hero and film bleed
   with negative margins equal to the gutter. clip, not hidden: hidden would
   turn html/body into scroll containers and break position: sticky. */
html,
body { overflow-x: clip; }

body {
  margin: 0;
  background: var(--nd-bg);
  color: var(--nd-ink-700);
  font-family: var(--nd-font);
  font-size: var(--nd-fs-base);
  font-weight: var(--nd-fw-regular);
  line-height: var(--nd-lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Stops a long product title or an unbroken URL forcing a horizontal
     scrollbar on a 360px phone, which is the width the brief tests at. */
  overflow-wrap: break-word;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }

/* Zero specificity on purpose: any single-class list rule in the page files
   (.nd-trust, .nd-routine__list, .nd-announce__list, .nd-drawer__facts)
   must be able to restore its own margin and padding. */
:where(ul[class], ol[class]) { list-style: none; margin: 0; padding: 0; }

img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
  height: auto;
}

/* An image with width/height attributes keeps its aspect box while loading,
   which is most of how this theme holds CLS at zero. */
img[width][height] { height: auto; }

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; }

table { border-collapse: collapse; width: 100%; }

/* --------------------------------------------------------- typography ----

   Two faces. Fraunces 400/500 for the display line, section titles and the
   handful of serif names (routine steps, category leads); Inter 400/500/600
   for everything else. Weight is spent in two places: the hero line and the
   section titles are 400, so they outrank by size and face, not by weight.

   The ladder at 1440:  92 / 56 / 36 / 22 / 18 / 16 / 15 / 13 / 12
   The ladder at  390:  44 / 32 / 24 / 22 / 18 / 16 / 15 / 13 / 12           */

h1, h2, h3, h4 {
  font-family: var(--nd-font-display);
  font-weight: var(--nd-fw-regular);
  line-height: var(--nd-lh-tight);
  letter-spacing: var(--nd-track-title);
  color: var(--nd-heading-ink);
  text-wrap: balance;
}

h1 { font-size: var(--nd-fs-2xl); }                                   /* 32 -> 56 */
h2 { font-size: var(--nd-fs-2xl); }                                   /* 32 -> 56 */
h3 {
  font-size: var(--nd-fs-xl);                                         /* 24 -> 36 */
  font-weight: var(--nd-fw-medium);
  line-height: var(--nd-lh-snug-title);
}
h4 {
  font-size: var(--nd-fs-lg);                                         /* 22 */
  font-weight: var(--nd-fw-medium);
  line-height: var(--nd-lh-snug);
}

p {
  max-width: var(--nd-measure);                /* 36em ~ 75 chars */
  text-wrap: pretty;
}

/* The wordmark is one unit and wants to be tighter than a headline. */
.nd-logo__link,
.nd-footer__name { letter-spacing: var(--nd-track-mark); }

/* Figures that must align. A grid of prices where $19.99 and $8.00 misalign
   column to column is exactly the small wrongness that reads as untrustworthy
   on a store. Both faces ship tnum. */
.nd-plate__price,
.nd-pcard__price,
.nd-bundles__price,
.nd-product__summary .price,
.nd-table td,
.nd-archive__count,
.nd-cart-count,
.nd-cats__count,
.nd-routine__num,
.nd-routine__price,
.nd-hero__caption-p,
.nd-trust__v,
.nd-steps__num { font-variant-numeric: tabular-nums lining-nums; }

a { color: var(--nd-link); text-decoration-thickness: 1px; text-underline-offset: .18em; }
strong, b { font-weight: var(--nd-fw-semibold); }
small { font-size: var(--nd-fs-xs); }

/* The FAQ list is also printed by the [nilosi_faq] shortcode on interior
   pages where home.css never loads, so its type sits here. */
.nd-faq__q {
  font-size: var(--nd-fs-base);
  font-weight: var(--nd-fw-medium);
  color: var(--nd-ink-900);
  text-wrap: balance;
}

.nd-faq__a p {
  font-size: var(--nd-fs-base);
  line-height: var(--nd-lh-base);
  color: var(--nd-ink-700);
  max-width: var(--nd-measure);
}

/* ------------------------------------------------------------- label ----- */
/*
 * THE label. One recipe for every eyebrow, footer column title, trust key,
 * badge, chip, routine key and card brand line. 12px, Inter 500, uppercase,
 * .12em. Anything that wants to read as a label adds this class rather than
 * copying the five declarations.
 */
.nd-label {
  font-family: var(--nd-font);
  font-size: var(--nd-fs-micro);
  font-weight: var(--nd-fw-medium);
  line-height: var(--nd-lh-label);
  letter-spacing: var(--nd-track-micro);
  text-transform: uppercase;
  color: var(--nd-ink-500);
  margin: 0;
}

.nd-stage .nd-label,
.nd-footer .nd-label { color: var(--nd-stage-ink-2); }

/* ------------------------------------------------------------- focus ----- */

/*
 * One rule for everything focusable. :focus-visible means a mouse click does
 * not draw the ring but a keyboard tab does.
 *
 * Two rings: a 2px ink outline at a 3px offset, and a paper halo filling the
 * offset so the ring reads on the accent fill, on the oat tile and on a photo
 * alike. The dark stage and the footer re-declare --nd-focus and
 * --nd-focus-halo for their subtree.
 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--nd-focus-width) solid var(--nd-focus);
  outline-offset: var(--nd-focus-offset);
  box-shadow: 0 0 0 calc(var(--nd-focus-offset)) var(--nd-focus-halo);
}

/* Remove the UA ring only where ours has replaced it. */
:where(a, button, input, select, textarea, summary):focus:not(:focus-visible) {
  outline: none;
}

/* ------------------------------------------------------- accessibility --- */

.nd-sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The skip link is invisible until focused, then lands in the top-left. */
.nd-skip {
  position: absolute;
  left: var(--nd-s-4);
  top: calc(var(--nd-s-4) * -4);
  z-index: 999;
  background: var(--nd-ink);
  color: var(--nd-text-invert);
  padding: var(--nd-s-3) var(--nd-s-4);
  border-radius: var(--nd-r-sm);
  text-decoration: none;
  transition: top var(--nd-dur-fast) var(--nd-ease);
}

.nd-skip:focus {
  top: var(--nd-s-4);
  color: var(--nd-text-invert);
}

/* ------------------------------------------------------------ layout ----- */

/* The gutter token is responsive (20 / 32 / 48) in tokens.css, so the
   container carries one declaration at every width. */
.nd-container {
  width: 100%;
  max-width: var(--nd-container);
  margin-inline: auto;
  padding-inline: var(--nd-gutter);
}

.nd-content { max-width: var(--nd-content); margin-inline: auto; }
.nd-narrow { max-width: var(--nd-narrow); margin-inline: auto; }

/* Chapters are separated by whitespace and 1px rules: a full band between
   sections, a strip for thin bands. Every section clips its own x overflow
   so a bleed inside it can never widen the page. */
.nd-section { padding-block: var(--nd-rhythm-band); overflow-x: clip; }
.nd-section--strip { padding-block: var(--nd-rhythm-strip); }

/* Retired as a section ground; neutralised so legacy parts stay flat. */
.nd-section--tint { background: none; }

.nd-stack > * + * { margin-top: var(--nd-s-4); }
.nd-stack-lg > * + * { margin-top: var(--nd-s-6); }

.nd-grid { display: grid; gap: var(--nd-s-5); }

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

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

/* ------------------------------------------------------ section head ----- */
/*
 * Every section opens the same way: a rule across the full width, the
 * eyebrow in the left quarter, the title on the same baseline in the right
 * three quarters, an optional lede under the title and an optional "Shop
 * all" link on the title's baseline at the right edge. One axis for the
 * whole page. No counter numerals: the rule rests drawn, nothing animates.
 *
 * Printed by nilosi_dtc_section_head():
 *   div.nd-head > p.nd-head__eyebrow.nd-label + h2.nd-head__title
 *               + p.nd-head__lede + a.nd-head__link
 */
.nd-head {
  position: relative;
  display: grid;
  gap: var(--nd-s-3);
  justify-items: start;
  text-align: start;
  margin-bottom: var(--nd-rhythm-head);
  padding-block-start: calc(var(--nd-s-5) + var(--nd-hair));
}

.nd-head::before {
  content: "";
  position: absolute;          /* NOT a grid item. .nd-head is display:grid. */
  inset-inline: 0;
  inset-block-start: 0;
  block-size: var(--nd-hair);
  background: var(--nd-rule-strong);
  pointer-events: none;
}

/* Kept for the gated parts that still pass it; nothing on the homepage does. */
.nd-head--center { text-align: center; justify-items: center; }

/* The eyebrow takes the label recipe outright, so the bare eyebrows in the
   story and sign-up parts match the helper's output whether or not they also
   carry .nd-label. */
.nd-head__eyebrow {
  font-family: var(--nd-font);
  font-size: var(--nd-fs-micro);
  font-weight: var(--nd-fw-medium);
  line-height: var(--nd-lh-label);
  letter-spacing: var(--nd-track-micro);
  text-transform: uppercase;
  color: var(--nd-ink-500);
  margin: 0;
}

.nd-stage .nd-head__eyebrow,
.nd-footer .nd-head__eyebrow { color: var(--nd-stage-ink-2); }

.nd-head__title {
  font-family: var(--nd-font-display);
  font-size: var(--nd-fs-2xl);
  font-weight: var(--nd-fw-regular);
  line-height: var(--nd-lh-tight);
  letter-spacing: var(--nd-track-title);
  color: var(--nd-heading-ink);
  text-wrap: balance;
  margin: 0;
}

.nd-head__lede {
  color: var(--nd-ink-500);
  font-size: var(--nd-fs-md);
  line-height: var(--nd-lh-title);
  max-width: var(--nd-measure);
  margin: 0;
}

.nd-stage .nd-head__lede { color: var(--nd-stage-ink-2); }

/* The head link and the arrow link share one recipe: 15/500 ink, no
   underline at rest, an accent underline on hover. */
.nd-head__link,
.nd-link-arrow {
  font-family: var(--nd-font);
  font-size: var(--nd-fs-sm);
  font-weight: var(--nd-fw-medium);
  line-height: 1.5;
  color: var(--nd-ink-900);
  text-decoration: none;
  white-space: nowrap;
  min-height: var(--nd-tap-min);
  display: inline-flex;
  align-items: center;
}

.nd-stage .nd-head__link,
.nd-stage .nd-link-arrow { color: var(--nd-stage-ink); }

@media (min-width: 1024px) {
  .nd-head {
    grid-template-columns: minmax(0, 3fr) minmax(0, 9fr);
    /* A third, implicit column exists only when a link is printed, so a
       head without one keeps the plain 3/9 rail and no stray gap. */
    grid-auto-columns: max-content;
    column-gap: var(--nd-s-6);
    align-items: start;
  }
  .nd-head__eyebrow { grid-column: 1; grid-row: 1; }
  .nd-head__title   { grid-column: 2; grid-row: 1; align-self: last baseline; }
  .nd-head__lede    { grid-column: 2; grid-row: 2; }
  .nd-head__link    { grid-column: 3; grid-row: 1; justify-self: end; align-self: last baseline; min-height: 0; }
  .nd-head--center .nd-head__eyebrow,
  .nd-head--center .nd-head__title,
  .nd-head--center .nd-head__lede,
  .nd-head--center .nd-head__link { grid-column: 1 / -1; justify-self: center; }
}

/* -------------------------------------------------------------- tile ----- */
/*
 * THE product tile. Every product on the site sits on the same oat ground:
 * a cut-out photo multiplied onto the tint so its white studio background
 * disappears, inset 12%, square. A lifestyle shot (product tag `tile-cover`)
 * fills the box instead: no inset, no blend, cover crop.
 *
 * Markup contract (every builder prints exactly this):
 *   a.nd-tile[.nd-tile--cover][tabindex=-1][aria-hidden=true]
 *     > span.nd-tile__media > img.nd-tile__img
 *     | span.nd-tile__plate{short name}          (no image)
 *     + span.nd-label.nd-tile__badge{Sale}       (optional, first)
 *     + span.nd-label.nd-tile__chip{Made in USA} (optional, second)
 *
 * The tile is a div in the hero and a real link on the category grid; the
 * rules below do not care which.
 */
.nd-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: var(--nd-r-lg);
  background: var(--nd-tint);
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  transition: background-color var(--nd-dur-snap) var(--nd-ease-mech);
}

.nd-tile__media {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / 1;
  width: 100%;
  padding: 12%;
  overflow: hidden;
}

.nd-tile__img {
  width: 100%;
  height: 100%;
  /* A grid item defaults to min-height: auto, which would let the image's
     intrinsic height stretch the media row and defeat cover cropping. */
  min-width: 0;
  min-height: 0;
  object-fit: contain;
  mix-blend-mode: multiply;
  transition: transform var(--nd-dur-travel) var(--nd-ease-mech);
}

/* wp_get_attachment_image() prints width and height attributes, and the
   reset's img[width][height] { height: auto } outranks a single class. The
   tile image must fill its box for contain/cover to mean anything, so this
   selector carries the two attributes too and wins. */
.nd-tile__media > .nd-tile__img[width][height] { height: 100%; }

.nd-tile--cover .nd-tile__media { padding: 0; }
.nd-tile--cover .nd-tile__img { object-fit: cover; mix-blend-mode: normal; }

/* No image: the short name set in the serif on the oat tile, never an SVG. */
.nd-tile__plate {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / 1;
  width: 100%;
  padding: 12%;
  font-family: var(--nd-font-display);
  font-size: var(--nd-fs-lg);
  font-weight: var(--nd-fw-regular);
  line-height: var(--nd-lh-snug);
  text-align: center;
  color: var(--nd-ink-500);
  text-wrap: balance;
}

/* Sale badge and USA chip: both carry .nd-label as well. */
.nd-tile__badge,
.nd-tile__chip {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 1;
  padding: 4px 8px;
  border-radius: var(--nd-r-md);
  background: var(--nd-accent-soft);
  color: var(--nd-accent-deep);
}

.nd-tile__badge + .nd-tile__chip { top: 44px; }

@media (hover: hover) and (pointer: fine) {
  .nd-plate:hover .nd-tile,
  .nd-tile:hover { background: var(--nd-tint-deep); }
  .nd-plate:hover .nd-tile__img,
  .nd-tile:hover .nd-tile__img { transform: scale(1.04); }
  .nd-tile--cover:hover .nd-tile__img,
  .nd-plate:hover .nd-tile--cover .nd-tile__img { transform: scale(1.02); }
}

@media (prefers-reduced-motion: reduce) {
  .nd-tile__img { transition: none; }
  .nd-plate:hover .nd-tile__img,
  .nd-tile:hover .nd-tile__img,
  .nd-tile--cover:hover .nd-tile__img,
  .nd-plate:hover .nd-tile--cover .nd-tile__img { transform: none; }
}

/* ------------------------------------------------------------- plate ----- */
/*
 * The product card anatomy, shared by the homepage shelf and the catalogue:
 * tile, brand label, name, price. The title link is stretched over the whole
 * card; the tile link underneath is aria-hidden so the card is one tab stop.
 *
 *   li.nd-plate > a.nd-tile + p.nd-label.nd-plate__brand
 *                + h3.nd-plate__title > a + p.nd-plate__price
 */
.nd-plate {
  position: relative;
  display: grid;
  grid-template-rows: auto auto auto auto;
  align-content: start;
  row-gap: 4px;
}

.nd-plate .nd-tile { margin-bottom: 10px; }

.nd-plate__brand { min-height: 0; }

.nd-plate__title {
  margin: 0;
  font-family: var(--nd-font);
  font-size: var(--nd-fs-sm);
  font-weight: var(--nd-fw-medium);
  line-height: 1.4;
  letter-spacing: 0;
  color: var(--nd-ink-900);
  text-wrap: initial;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.nd-plate__title a { color: inherit; text-decoration: none; }

/* The stretched link: the whole card is the target. */
.nd-plate__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.nd-plate__price {
  margin: 0;
  font-size: var(--nd-fs-sm);
  line-height: 1.5;
  color: var(--nd-ink-700);
  font-variant-numeric: tabular-nums;
}

.nd-plate__price del { color: var(--nd-ink-500); text-decoration: line-through; }
.nd-plate__price ins { color: var(--nd-ink-900); text-decoration: none; }

/* Focus moves to the whole card, because the link's own box is the title. */
.nd-plate:has(a:focus-visible) {
  outline: var(--nd-focus-width) solid var(--nd-focus);
  outline-offset: var(--nd-focus-offset);
  border-radius: var(--nd-r-sm);
}

.nd-plate:has(a:focus-visible) a:focus-visible {
  outline: none;
  box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
  .nd-plate:hover .nd-plate__title {
    text-decoration: underline;
    text-decoration-color: var(--nd-accent);
    text-underline-offset: .2em;
  }
  .nd-head__link:hover,
  .nd-link-arrow:hover {
    color: var(--nd-ink-900);
    text-decoration: underline;
    text-decoration-color: var(--nd-accent);
    text-underline-offset: .2em;
  }
}

/* ----------------------------------------------------------- buttons ----- */
/*
 * Two buttons and a ghost. 48px, 4px radius, Inter 15/500, no shadow, no
 * inset hairline. The primary is the one moss fill per screen.
 */
.nd-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nd-s-2);
  min-height: var(--nd-control-h);
  min-width: var(--nd-tap-min);
  padding: 0 28px;
  border: 1px solid transparent;
  border-radius: var(--nd-r-md);
  background: none;
  font-family: var(--nd-font);
  font-size: var(--nd-fs-sm);
  font-weight: var(--nd-fw-medium);
  line-height: 1;
  letter-spacing: 0;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  box-shadow: none;
  transition: background-color var(--nd-dur-snap) var(--nd-ease-mech),
              color var(--nd-dur-snap) var(--nd-ease-mech),
              border-color var(--nd-dur-snap) var(--nd-ease-mech),
              scale var(--nd-dur-snap) var(--nd-ease-mech);
}

.nd-btn--primary {
  background: var(--nd-accent);
  border-color: var(--nd-accent);
  color: var(--nd-accent-ink);
}

.nd-btn--secondary {
  background: transparent;
  color: var(--nd-ink-900);
  border-color: var(--nd-border-control);
}

.nd-btn--ghost {
  background: transparent;
  color: var(--nd-accent);
  padding-inline: var(--nd-s-3);
}

.nd-btn--block { width: 100%; }

/* 16px, sentence case, 56px tall. The hero and the closer use it. */
.nd-btn--lg { min-height: var(--nd-control-h-lg); font-size: var(--nd-fs-base); }

/* The press: 1.5% on the independent `scale` property, so it never collides
   with a transform set elsewhere and stays on the compositor. */
.nd-btn:active { scale: .985; transition-duration: var(--nd-dur-tick); }

/*
 * A disabled control still has to be legible: text stays at full contrast
 * and the state is signalled through the surface instead of opacity.
 */
.nd-btn:disabled,
.nd-btn[aria-disabled="true"] {
  background: var(--nd-tint);
  color: var(--nd-ink-500);
  border-color: var(--nd-border-control);
  cursor: not-allowed;
}

/* ------------------------------------------------------------- forms ----- */

.nd-field { display: grid; gap: var(--nd-s-2); }

/* The visible form label. (.nd-label is the uppercase micro label now.) */
.nd-field__label,
.nd-field > label {
  font-size: var(--nd-fs-sm);
  font-weight: var(--nd-fw-medium);
  color: var(--nd-ink-900);
}

.nd-input,
.nd-select,
.nd-textarea {
  width: 100%;
  min-height: var(--nd-control-h);
  padding: 0 var(--nd-s-4);
  background: var(--nd-white);
  color: var(--nd-ink);
  border: 1px solid var(--nd-border-control);
  border-radius: var(--nd-r-md);
  /* 16px minimum: anything smaller makes iOS zoom the page on focus. */
  font-size: var(--nd-fs-base);
  line-height: 1.5;
}

.nd-select {
  appearance: none;
  padding-inline-end: calc(var(--nd-s-4) * 2.5);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231F1F1D' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--nd-s-4) center;
  background-size: 18px 18px;
}

.nd-textarea { min-height: 120px; padding-block: var(--nd-s-3); line-height: var(--nd-lh-base); }

.nd-input::placeholder,
.nd-textarea::placeholder {
  color: var(--nd-ink-500);
  opacity: 1; /* Firefox dims placeholders by default, which breaks contrast. */
}

.nd-input[aria-invalid="true"],
.nd-select[aria-invalid="true"],
.nd-textarea[aria-invalid="true"] {
  border-color: var(--nd-error);
  border-width: 2px;
}

.nd-error {
  color: var(--nd-error);
  font-size: var(--nd-fs-sm);
  font-weight: var(--nd-fw-medium);
}

/* A checkbox or radio must stay a comfortable target even though the control
   itself is small, so the label carries the hit area. */
.nd-check {
  display: flex;
  align-items: flex-start;
  gap: var(--nd-s-3);
  min-height: var(--nd-tap-min);
  padding-block: var(--nd-s-2);
  cursor: pointer;
}

.nd-check input {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  accent-color: var(--nd-accent);
}

/* ------------------------------------------------------------ pieces ----- */

.nd-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--nd-s-1);
  background: var(--nd-accent-soft);
  color: var(--nd-accent-deep);
  border-radius: var(--nd-r-md);
  padding: var(--nd-s-1) var(--nd-s-2);
  font-size: var(--nd-fs-micro);
  font-weight: var(--nd-fw-medium);
  line-height: var(--nd-lh-label);
  letter-spacing: var(--nd-track-micro);
  text-transform: uppercase;
}

.nd-icon {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
}

.nd-muted { color: var(--nd-text-muted); }
.nd-center { text-align: center; }

/* --------------------------------------------------- anchor landing ----- */

/*
 * An in-page anchor -- and "Skip to content" above all -- must not come to
 * rest underneath the sticky bar it just jumped past.
 */
html {
  scroll-padding-top: calc(var(--nd-header-h-sm) + var(--nd-s-4));
}

@media (min-width: 1024px) {
  html { scroll-padding-top: calc(var(--nd-header-h) + var(--nd-s-4)); }
}

/* scroll-margin-top on top of the scroll-padding the scrollport carries:
   scroll-padding belongs to the scroll container, so a heading inside any
   nested scroller would not benefit from it. */
:where(h1, h2, h3, h4, [id]) { scroll-margin-top: calc(var(--nd-header-h-sm) + var(--nd-s-4)); }

@media (min-width: 1024px) {
  :where(h1, h2, h3, h4, [id]) { scroll-margin-top: calc(var(--nd-header-h) + var(--nd-s-4)); }
}

/* ------------------------------------------------------------ detent ---- */

/* Icon buttons press 5%; the hand feels an answer, nobody calls it motion. */
.nd-action,
.nd-header__toggle,
.nd-drawer__close,
.nd-hsearch__submit {
  transition: background-color var(--nd-dur-snap) var(--nd-ease-mech),
              border-color var(--nd-dur-snap) var(--nd-ease-mech),
              scale var(--nd-dur-snap) var(--nd-ease-mech);
}

.nd-action:active,
.nd-header__toggle:active,
.nd-drawer__close:active,
.nd-hsearch__submit:active { scale: .95; transition-duration: var(--nd-dur-tick); }

/*
 * Reduced motion has to null the scale itself, not only the duration. With the
 * durations already zeroed, leaving the scale in place would hand exactly the
 * people who asked for less motion an instant jump instead of a movement.
 * They get a colour step instead.
 */
@media (prefers-reduced-motion: reduce) {
  .nd-btn:active,
  .nd-action:active,
  .nd-header__toggle:active,
  .nd-drawer__close:active,
  .nd-hsearch__submit:active { scale: 1; }

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

/*
 * Every hover in the theme lives inside this query.
 *
 * iOS synthesises a hover on tap and holds it until you tap somewhere else, so
 * an ungated hover means tapping a card leaves it zoomed after you navigate
 * back, and tapping the cart leaves it filled as though it were toggled.
 */
@media (hover: hover) and (pointer: fine) {
  a:hover { color: var(--nd-link-hover); }
  .nd-btn--primary:hover { background: var(--nd-accent-deep); border-color: var(--nd-accent-deep); color: var(--nd-accent-ink); }
  .nd-btn--secondary:hover { border-color: var(--nd-ink-900); color: var(--nd-ink-900); }
  .nd-btn--ghost:hover { color: var(--nd-accent-deep); }
}

/* ------------------------------------------------------ the page floor -- */

/*
 * A short page must still put its footer on the floor. svh rather than vh: on
 * a phone, vh is the viewport with the browser chrome hidden, so using it
 * here would guarantee a scrollbar on every page.
 */
body {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

.nd-main { flex: 1 0 auto; }

/* ================================================================= film === */
/*
 * The demonstration film, shared by the homepage and the product page.
 *
 * In base.css deliberately: home.css is served only on the front page and
 * shop.css only on the catalogue, so a component used on both and written into
 * either one is dead on the other.
 *
 *   figure.nd-video[data-nd-video]
 *     > div.nd-video__frame[style=--nd-video-ratio] > video.nd-video__el
 *     + figcaption.nd-video__bar
 *         > span.nd-video__caption (optional, first)
 *         + button.nd-video__btn[data-nd-video-play]
 *         + button.nd-video__btn[data-nd-video-mute]
 */

.nd-video {
  display: grid;
  gap: 0;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
}

.nd-video__frame {
  /* The video's own ratio, written by PHP from the attachment metadata, so
     the box is the right shape before any of the file has arrived. */
  position: relative;
  aspect-ratio: var(--nd-video-ratio, 16 / 9);
  width: 100%;
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--nd-r-xl);
  background: var(--nd-tint);
}

.nd-video__el {
  display: block;
  width: 100%;
  height: 100%;
  /* contain, never cover: a future film of a different shape is letterboxed
     on the oat ground rather than cropped. */
  object-fit: contain;
  background: var(--nd-tint);
}

/* The play mark: a 56px paper disc with a charcoal triangle, centred over
   the frame while the film is paused. It is decoration, not a control: the
   real buttons sit under the frame and the disc lets clicks through. */
.nd-video__frame::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56px;
  height: 56px;
  translate: -50% -50%;
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--nd-paper) 92%, transparent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231F1F1D' d='M8 5.5l11 6.5-11 6.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: calc(50% + 2px) 50%;
  background-size: 24px 24px;
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--nd-dur-travel) var(--nd-ease-mech);
}

.nd-video.is-playing .nd-video__frame::after { opacity: 0; }

/* The controls sit UNDER the frame, right-aligned; an optional caption
   takes the left. Nothing interactive is placed over the picture. */
.nd-video__bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--nd-s-4);
  width: 100%;
  margin-top: var(--nd-s-3);
}

.nd-video__caption {
  font-size: var(--nd-fs-xs);
  line-height: 1.5;
  color: var(--nd-ink-500);
  margin-right: auto;
  min-width: 0;
}

.nd-video__caption a {
  color: var(--nd-ink-900);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
}

.nd-video__btn {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--nd-tap-min);
  height: var(--nd-tap-min);
  padding: 0;
  border: 0;
  border-radius: var(--nd-r-md);
  background: none;
  color: var(--nd-ink-500);
  cursor: pointer;
  transition: background-color var(--nd-dur-snap) var(--nd-ease-mech),
              color var(--nd-dur-snap) var(--nd-ease-mech);
}

.nd-video__btn .nd-icon { width: 20px; height: 20px; }

@media (hover: hover) and (pointer: fine) {
  .nd-video__btn:hover { color: var(--nd-ink-900); background: var(--nd-tint); }
  .nd-video__caption a:hover { color: var(--nd-accent-deep); }
}

/* Which glyph each control shows is a function of state, not of script poking
   at style properties. */
.nd-video__pause,
.nd-video__loud { display: none; }

.nd-video.is-playing .nd-video__play { display: none; }
.nd-video.is-playing .nd-video__pause { display: inline; }

.nd-video.is-unmuted .nd-video__muted { display: none; }
.nd-video.is-unmuted .nd-video__loud { display: inline; }

/* -------------------------------------------------------- the section ---- */

.nd-film { padding-block: var(--nd-rhythm-band); }
