/*
 * Editorial content: blog posts, plain pages, search results and the 404.
 * Plus the accessibility utilities the whole theme depends on.
 *
 * HAND MAINTAINED, like product.css. Not generated by bin/port-preview-css.py.
 *
 * The concept preview was two pages, neither of which was a blog post, a legal
 * page or a 404, so there is nothing to port for any of them. These are built
 * from the same tokens and type scale the client approved.
 *
 * Loaded on every page, deliberately. It is small, and it carries
 * .screen-reader-text, which the header and archive markup rely on. Those were
 * previously being hidden only because WooCommerce's own stylesheet happens to
 * define the class, which would break the moment that stylesheet is dequeued
 * or WooCommerce changes it.
 */

/* --- accessibility utilities --- */
.screen-reader-text {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * The skip link is the exception: it must become visible when focused, or it
 * is useless to the keyboard users it exists for.
 */
.skip-link:focus {
  position: fixed !important;
  top: 12px;
  left: 12px;
  z-index: 999;
  width: auto;
  height: auto;
  margin: 0;
  padding: 12px 20px;
  clip-path: none;
  background: var(--espresso);
  color: var(--cream);
  border-radius: 2px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* --- editorial layout --- */
.entry {
  padding: clamp(36px, 5vw, 72px) 0;
}

.entry__head {
  max-width: 68ch;
  margin-bottom: clamp(24px, 3vw, 40px);
}

.entry__title {
  font-size: clamp(30px, 4vw, 52px);
  font-weight: 300;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--espresso);
  margin: 0 0 14px;
}

.entry__meta {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olive-deep);
}

/*
 * Post featured image.
 *
 * single.php has always output this class and nothing has ever styled it,
 * because until 2026-09-02 no post had a featured image. Unstyled it renders at
 * the full column width: a 4:5 source became a 781x973 block that pushed a
 * 2000 word article entirely below the fold and made an editorial page read
 * like a product listing.
 *
 * Capped by HEIGHT rather than cropped to a fixed ratio. The sources here are
 * portrait, and forcing a wide editorial band onto a tall product packshot
 * slices the bottle in half. Constraining the height and centring keeps any
 * aspect intact, portrait or landscape, and simply stops it dominating.
 *
 * Deliberately narrower than .entry__body's measure at the top end, so the
 * image sits inside the column rather than fighting the text for width.
 */
.entry__image {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: clamp(320px, 48vh, 520px);
  margin: 28px 0 4px;
  border-radius: 2px;
}

/*
 * Measure is capped at 72 characters. Line length is the single biggest
 * readability lever on a text page, and the theme's 1240px wrap is far too wide
 * for body copy.
 */
.entry__body {
  max-width: 72ch;
  font-size: 16px;
  font-weight: 300;
  line-height: 1.8;
  color: var(--espresso-soft);
}

.entry__body > * + * {
  margin-top: 1.1em;
}

.entry__body h2,
.entry__body h3,
.entry__body h4 {
  color: var(--espresso);
  font-weight: 600;
  line-height: 1.25;
  margin-top: 1.8em;
}

.entry__body h2 { font-size: clamp(21px, 2.2vw, 28px); }
.entry__body h3 { font-size: clamp(17px, 1.7vw, 21px); }

.entry__body a {
  color: var(--terracotta-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.entry__body img {
  max-width: 100%;
  height: auto;
  border-radius: 2px;
}

.entry__body blockquote {
  margin: 1.6em 0;
  padding-left: 22px;
  border-left: 2px solid var(--terracotta);
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-size: 21px;
  color: var(--espresso);
}

.entry__body ul,
.entry__body ol {
  padding-left: 20px;
}

.entry__body li + li {
  margin-top: 6px;
}

.entry__body code {
  background: var(--sand);
  padding: 2px 6px;
  border-radius: 2px;
  font-size: 0.9em;
}

/* --- post list --- */
/* -------------------------------------------------------------------------
 * Blog index
 *
 * Brought in line with the rest of the site on client feedback 2026-08-29. It
 * previously used a 300-weight title and a bare text list, which read as a
 * different website to the one the nav leads to.
 *
 * Columns are capped by the post count, the same device the category and
 * Instagram grids use. `auto-fill` on its own put the single existing post in
 * a 280px track hard against the left edge with the rest of the row empty,
 * which looks like a rendering fault rather than a short list.
 * ---------------------------------------------------------------------- */
.post-list {
  display: grid;
  grid-template-columns: repeat(min(var(--post-count, 3), 3), minmax(0, 1fr));
  gap: clamp(24px, 3vw, 40px);

  /*
   * Cap the grid to the width its columns actually need.
   *
   * Without this a lone post stretches across the full wrap, and because the
   * media holds a 4:5 ratio the card's image panel becomes over 1000px tall:
   * one blog post rendering as a full page of empty sand. Capping at a normal
   * card width keeps a single post looking like a card.
   *
   * A full row of three is unaffected: 3 x 380 plus two gaps is already wider
   * than the wrap, so the cap never binds.
   */
  max-width: calc(
    min(var(--post-count, 3), 3) * 380px +
    (min(var(--post-count, 3), 3) - 1) * clamp(24px, 3vw, 40px)
  );
}

@media (max-width: 1024px) {
  .post-list {
    grid-template-columns: repeat(min(var(--post-count, 2), 2), minmax(0, 1fr));
    max-width: calc(
      min(var(--post-count, 2), 2) * 380px +
      (min(var(--post-count, 2), 2) - 1) * clamp(24px, 3vw, 40px)
    );
  }
}

@media (max-width: 640px) {
  .post-list {
    grid-template-columns: 1fr;
    /* Full width on a phone: a 380px cap would leave a margin down one side. */
    max-width: none;
  }
}

.post-card__link {
  display: block;
  color: inherit;
}

.post-card__media {
  overflow: hidden;
  border-radius: 2px;
  background: var(--sand);
}

.post-card__media img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
  transition: transform .8s var(--ease);
}

.post-card__link:hover .post-card__media img {
  transform: scale(1.05);
}

/*
 * Placeholder for a post with no featured image. Holds the card's shape so a
 * list mixing posts with and without images still lines up, and says the brand
 * rather than sitting empty. The migrated post has no image.
 */
.post-card__placeholder {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  background: var(--sand-deep);
}

.post-card__placeholder .brand__mark {
  font-size: clamp(20px, 2.4vw, 28px);
  color: var(--terracotta);
  opacity: .55;
}

.post-card__date {
  display: block;
  margin-top: 16px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--espresso-soft);
}

.post-card__title {
  margin: 8px 0 10px;
  font-size: clamp(17px, 1.6vw, 20px);
  font-weight: 700;
  line-height: 1.32;
  color: var(--espresso);
}

.post-card__link:hover .post-card__title {
  color: var(--terracotta-deep);
}

.post-card__excerpt {
  font-size: 14px;
  font-weight: 300;
  line-height: 1.7;
  color: var(--espresso-soft);
  margin-bottom: 14px;
}

/*
 * The "Read more" is aria-hidden and out of the tab order in the markup: the
 * card title above is already a link to the same place, so exposing this would
 * make every card two identical links for a keyboard or screen reader user.
 * It is here for the eye only.
 */
.post-card .link-more {
  font-size: 11px;
}

/* --- 404 --- */
.notfound {
  padding: clamp(60px, 10vw, 140px) 0;
  text-align: center;
}

.notfound__code {
  display: block;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-size: clamp(64px, 12vw, 132px);
  line-height: 1;
  color: var(--sand-deep);
}

.notfound__title {
  font-size: clamp(24px, 3vw, 36px);
  font-weight: 300;
  color: var(--espresso);
  margin: 6px 0 14px;
}

.notfound__lede {
  max-width: 46ch;
  margin: 0 auto 30px;
  font-size: 15px;
  font-weight: 300;
  line-height: 1.75;
  color: var(--espresso-soft);
}

.notfound__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

/* --- pagination, shared by blog and search --- */
.pagination {
  margin-top: clamp(32px, 4vw, 56px);
  display: flex;
  justify-content: center;
}

.pagination .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 2px;
  margin: 0 3px;
  font-size: 13px;
  color: var(--espresso-soft);
}

.pagination .page-numbers.current {
  background: var(--espresso);
  border-color: var(--espresso);
  color: var(--cream);
}

/* --- homepage additions ---------------------------------------------------
 *
 * The approved preview hardcoded a three column category grid because it had
 * exactly three invented categories. The real site has two categories with
 * products (Hair Care and Body Care), because Fragrance and Trial Kits are both
 * empty and correctly hidden.
 *
 * The grid therefore adapts to how many cards there actually are, so two read
 * as deliberate rather than as one having failed to load. Confirmed decision,
 * recorded as amendment 2 in WP Architecture Decisions.md.
 */
/*
 * Doubled class for specificity, NOT for style. home.css is enqueued after
 * content.css on the front page, so its plain `.cat-grid { repeat(3, 1fr) }`
 * was overriding a single-class modifier here and the grid stayed three
 * columns regardless. `.cat-grid.cat-grid--2` outranks it whatever the load
 * order, which also means this keeps working if the enqueue order changes.
 */
.cat-grid.cat-grid--1 { grid-template-columns: minmax(0, 620px); justify-content: center; }
.cat-grid.cat-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (max-width: 700px) {
  .cat-grid.cat-grid--1,
  .cat-grid.cat-grid--2 { grid-template-columns: 1fr; }
}

/*
 * Hero with no image set. The approved layout is two columns, copy beside a
 * portrait photograph. With no photo the second column is simply empty, which
 * leaves the heading stranded in the left half of a very wide band. Until the
 * client supplies hero photography, the copy takes the full width and centres.
 */
.hero__inner.hero__inner--no-image {
  grid-template-columns: minmax(0, 760px);
  justify-content: center;
  text-align: center;
}

.hero__inner--no-image .hero__cta,
.hero__inner--no-image .offer-pill {
  justify-content: center;
}

.hero__inner--no-image .hero__lede {
  margin-left: auto;
  margin-right: auto;
}

/*
 * Headings that sit on imagery must opt out of theme.json's global heading
 * colour.
 *
 * theme.json sets `styles.elements.heading.color.text` to espresso, which
 * WordPress emits as a rule applied DIRECTLY to h1-h6. A directly applied
 * colour always beats an inherited one, so `.cat__body { color: #fff }` styled
 * the count and description white while the category name stayed dark espresso
 * on a dark photograph. It was very close to unreadable.
 *
 * `inherit` rather than a hardcoded white, so these keep following their
 * container if the card design ever changes.
 */
.cat__body .cat__name,
.cat__body h3,
.kits__media h3,
.rev__who .rev__name {
  color: inherit;
}

/*
 * Header logo.
 *
 * The client's wordmark is landscape (728x454 after trimming) where the text
 * lockup it replaces is a compact two-line stack. Constrained by HEIGHT rather
 * than width so it occupies the same vertical space as the text version and the
 * header does not change height when a logo is set or removed.
 */
/*
 * Selector is `.brand .brand__logo`, not `.brand__logo`.
 *
 * The single-class version was being out-cascaded: the declaration was present
 * and valid in the served stylesheet, and setting the identical value inline
 * worked, but the computed height stayed at the image's natural 434px and the
 * header grew to 435px to contain it. Raising specificity settles it rather
 * than leaving the layout dependent on whatever was winning.
 *
 * max-height as well as height, so the header cannot be blown open again by
 * anything that resets height to auto.
 */
.brand .brand__logo {
  display: block;
  width: auto;
  max-height: clamp(42px, 5vw, 58px);
  /*
   * Taller than a single-line wordmark would need. This logo is a TWO-LINE
   * lockup ("Nura" over "Beauty"), so a height that suits one line makes each
   * line half that and the whole mark unreadable. At 46px it rendered 74px
   * wide and was noticeably fainter and smaller than the text lockup it
   * replaced.
   */
  height: clamp(42px, 5vw, 58px);
  object-fit: contain;
}

@media (max-width: 1024px) {
  .brand .brand__logo {
    height: 40px;
    max-height: 40px;
  }
}

/* -------------------------------------------------------------------------
 * Navigation lists
 *
 * base.css styles `.nav a` and `.drawer a` directly, written when the header
 * had no menu assigned and the fallback in template-tags.php emitted bare
 * anchors. A real WordPress menu emits <li> elements, and assigning one broke
 * two things at once:
 *
 *   1. The <li>s rendered their default markers, putting a bullet before every
 *      item in the header.
 *   2. `.drawer a:last-child { border-bottom: 0 }` in base.css stopped meaning
 *      "the final link". Once each anchor is the only child of its own <li>,
 *      every anchor is a :last-child, so EVERY divider disappeared instead of
 *      just the last one.
 *
 * base.css is generated by bin/port-preview-css.py, so the correction lives
 * here rather than being written into a file the next generator run overwrites.
 * ---------------------------------------------------------------------- */

.nav__list,
.drawer__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.9vw, 28px);
}

/*
 * Re-assert the divider on every drawer link, then remove it from the last
 * item only. This has to be more specific than base.css's `.drawer a`
 * selectors, which is why both rules name `.drawer__list`.
 */
/*
 * CORRECTED 2026-08-24. The selectors below used to read `.drawer__list li a`
 * and `.drawer__list li:last-child a`, and neither ever won.
 *
 * base.css sets `.drawer a:last-child { border-bottom: 0 }`. Every anchor is
 * the only child of its own <li>, so that matches EVERY drawer link, and its
 * specificity is (0,2,1): one class plus one pseudo-class plus one element.
 * The old fix was (0,1,2), which loses, because class count is compared before
 * element count. The result was that no drawer link had a divider at all,
 * while this file carried a comment stating the problem was solved.
 *
 * Adding `.drawer` to the front takes these to (0,2,2) and (0,3,2), which
 * clear base.css in both cases. Verified by measuring computed
 * border-bottom-width rather than by looking at the page, since the difference
 * between a 1px hairline and none is not reliable to eyeball.
 */
.drawer .drawer__list li a {
  border-bottom: 1px solid var(--line);
}

.drawer .drawer__list li:last-child a {
  border-bottom: 0;
}

/* -------------------------------------------------------------------------
 * Contact form
 *
 * Replaces the Shopstar form that died with the old site. Styled here rather
 * than in a generated file, and deliberately narrow: a full-width text input
 * on a desktop is both ugly and harder to read.
 * ---------------------------------------------------------------------- */

.contact-form {
  max-width: 34rem;
  margin-block: clamp(24px, 4vw, 40px);
}

.contact-form__field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-block: 0 18px;
}

.contact-form__field label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--espresso);
}

.contact-form__field input,
.contact-form__field textarea {
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  font-size: 15px;
  color: var(--espresso);
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 2px;
  transition: border-color 0.25s var(--ease);
}

.contact-form__field textarea {
  resize: vertical;
  min-height: 8rem;
}

.contact-form__field input:focus,
.contact-form__field textarea:focus {
  outline: none;
  border-color: var(--terracotta);
}

/*
 * Honeypot. Positioned off-canvas rather than display:none, because some bots
 * skip fields that are explicitly hidden, which is exactly the ones we want to
 * catch filling it in.
 */
.contact-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact-form__notice {
  padding: 13px 16px;
  border-radius: 2px;
  border-left: 3px solid;
  font-size: 15px;
}

.contact-form__notice--success {
  border-color: var(--sage, #6b7a55);
  background: color-mix(in srgb, var(--sage, #6b7a55) 10%, transparent);
}

.contact-form__notice--error {
  border-color: var(--terracotta-deep);
  background: color-mix(in srgb, var(--terracotta-deep) 10%, transparent);
}

/* -------------------------------------------------------------------------
 * Review photos
 *
 * Customer-uploaded product photos, on the single product review list and on
 * the homepage review cards.
 * ---------------------------------------------------------------------- */

.comment-form-photos {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-block: 0 18px;
}

.comment-form-photos label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--espresso);
}

.comment-form-photos__hint {
  font-size: 13px;
  opacity: 0.7;
}

.review-photos {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.review-photos__item {
  display: block;
  width: 72px;
  height: 72px;
  border-radius: 2px;
  overflow: hidden;
  border: 1px solid var(--line);
}

.review-photos__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /*
   * Uploads are whatever shape the customer's phone produced. Cropping to a
   * square keeps the row even instead of a ragged line of mixed aspect ratios.
   */
}

/*
 * Homepage review card photo. Wide and short rather than square, so the card
 * keeps the quote as its main element and the photo reads as supporting it.
 */
.rev__photo {
  display: block;
  margin-bottom: 14px;
  border-radius: 2px;
  overflow: hidden;
  aspect-ratio: 3 / 2;
}

.rev__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease);
}

.rev__photo:hover img {
  transform: scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  .rev__photo img,
  .rev__photo:hover img {
    transition: none;
    transform: none;
  }
}

/* -------------------------------------------------------------------------
 * Review list and review form
 *
 * WooCommerce ships these unstyled, and against the rest of the theme they
 * read as an unfinished third-party block: browser-default inputs, a stock
 * star widget and a grey submit button. Styled to match the contact form so
 * the two forms on the site behave and look the same.
 * ---------------------------------------------------------------------- */

#reviews .commentlist {
  list-style: none;
  margin: 0 0 32px;
  padding: 0;
}

#reviews .commentlist .comment_container {
  display: flex;
  gap: 14px;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

#reviews .commentlist img.avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  flex: 0 0 44px;
}

#reviews .comment-text {
  flex: 1 1 auto;
  min-width: 0;
}

#reviews .woocommerce-review__author {
  font-weight: 600;
  color: var(--espresso);
}

#reviews .woocommerce-review__published-date {
  font-size: 13px;
  opacity: 0.65;
}

#reviews .description p:last-child {
  margin-bottom: 0;
}

#review_form_wrapper {
  max-width: 34rem;
}

#reviews .comment-reply-title {
  display: block;
  font-size: clamp(20px, 2.4vw, 26px);
  margin-bottom: 6px;
}

#reviews .comment-form label {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--espresso);
}

#reviews .comment-form input[type="text"],
#reviews .comment-form input[type="email"],
#reviews .comment-form textarea {
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  font-size: 15px;
  color: var(--espresso);
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 2px;
  transition: border-color 0.25s var(--ease);
}

#reviews .comment-form input[type="text"]:focus,
#reviews .comment-form input[type="email"]:focus,
#reviews .comment-form textarea:focus {
  outline: none;
  border-color: var(--terracotta);
}

#reviews .comment-form p {
  margin-block: 0 18px;
}

/* The "save my name" consent line is a checkbox, not a field label. */
#reviews .comment-form-cookies-consent label {
  display: inline;
  margin: 0 0 0 6px;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.8;
}

#reviews .comment-form .stars a {
  color: var(--terracotta);
}

#reviews .form-submit input[type="submit"] {
  display: inline-block;
  padding: 13px 26px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream);
  background: var(--espresso);
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  transition: background 0.25s var(--ease);
}

#reviews .form-submit input[type="submit"]:hover {
  background: var(--terracotta-deep);
}

/* -------------------------------------------------------------------------
 * Mobile header alignment
 *
 * The header is a three column grid: nav | brand | tools. Below 1024px the nav
 * is hidden, but the COLUMN it occupied is not, so the brand fell into column
 * one and the tools into column two, leaving a full empty column on the right.
 * The icons ended up bunched against the logo with roughly 100px of dead space
 * beside them, which read as broken rather than deliberate.
 *
 * Flex with space-between drops the empty column entirely: logo hard left,
 * icons hard right against the page padding, which is where a phone user
 * reaches for them.
 *
 * base.css is generated by bin/port-preview-css.py, so this correction lives
 * here rather than in a file the next generator run would overwrite.
 * ---------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .head-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }

  /*
   * Let the icon row size to its contents rather than stretching, so
   * space-between actually has something to push apart.
   */
  .head-tools {
    flex: 0 0 auto;
    margin-left: auto;
  }

  .brand {
    flex: 0 0 auto;
  }
}

/* Tighten the icon spacing on the narrowest phones so four icons plus the
   logo never crowd or wrap. */
@media (max-width: 400px) {
  .head-tools {
    gap: 12px;
  }
}

/* -------------------------------------------------------------------------
 * Long URLs in editorial content
 *
 * The client's Privacy Policy prints bare URLs as their own link text, and the
 * longest of them is 810px wide as one unbreakable string. On a 390px phone
 * that forced the whole document to 850px, so the page could be dragged
 * sideways with nothing visible to explain why. It only showed up on /privacy/
 * because no other page carries text like that.
 *
 * Applied to editorial content only, not site-wide: this must not affect
 * product names or navigation, where mid-word breaking would look like a bug.
 * ---------------------------------------------------------------------- */

.entry__body {
  /* Breaks only where a normal break is impossible, so ordinary prose is
     unaffected and only the runaway URL is broken. */
  overflow-wrap: break-word;
}

.entry__body a {
  /* `anywhere` rather than `break-word` on links specifically, because a long
     URL has no spaces at all and break-word alone still leaves it overflowing
     inside its own inline box. */
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* -------------------------------------------------------------------------
 * Footer logo
 *
 * Added 2026-08-24. The footer previously drew the CSS text wordmark while the
 * header drew the uploaded logo, so one brand appeared two different ways on
 * every page. This is the header rule adapted to the footer's dark ground.
 *
 * Sized a little larger than the header's because the footer has room and no
 * sticky-header height to preserve, and left aligned rather than centred to
 * match the column of text beneath it.
 * ---------------------------------------------------------------------- */
.foot__brand .brand__logo {
  display: block;
  width: auto;
  height: clamp(46px, 5.5vw, 62px);
  max-height: clamp(46px, 5.5vw, 62px);
  object-fit: contain;
  object-position: left center;
  margin-bottom: 18px;
}

@media (max-width: 1024px) {
  .foot__brand .brand__logo {
    height: 44px;
    max-height: 44px;
  }
}

/* -------------------------------------------------------------------------
 * Dark header
 *
 * Chosen by the client 2026-08-24 over the cream original, so the header and
 * footer present the logo identically. Her logo is drawn for a dark ground:
 * it measures 5.91:1 on the espresso and 2.33:1 on the cream it replaced.
 *
 * Lives here rather than in base.css because base.css is GENERATED by
 * bin/port-preview-css.py from the approved concept and is overwritten on
 * every run. Every rule below therefore overrides a base.css rule that assumes
 * a light header, and each is listed so a future regeneration cannot quietly
 * strand one of them.
 *
 * The nav accent moves from --terracotta-deep to --terracotta. The deep tone
 * was picked to sit on cream and drops to roughly 2.5:1 on espresso, so the
 * current page would have been the hardest nav item to read rather than the
 * most obvious one.
 * ---------------------------------------------------------------------- */

.site-head {
  /* Matches the footer. Alpha and blur kept so it still frosts on scroll. */
  background: rgba(46, 38, 32, .94);
}

/*
 * base.css sets this from --line, which is espresso at 14% and therefore
 * invisible against an espresso bar.
 */
.site-head.is-stuck {
  border-bottom-color: rgba(250, 246, 240, .16);
}

/*
 * Fallback wordmark, for an install with no logo uploaded. Without this it
 * would be espresso text on an espresso bar: the header would look empty
 * rather than broken, which is the harder version to notice.
 */
.site-head .brand__mark {
  color: var(--cream);
}

.site-head .brand__sub {
  color: var(--terracotta);
}

/* Nav and icons inherit --espresso from body in base.css. */
.site-head .nav a,
.site-head .icon-btn {
  color: var(--cream);
}

.site-head .nav a[aria-current] {
  color: var(--terracotta);
}

.site-head .nav a:hover,
.site-head .icon-btn:hover {
  color: var(--terracotta);
}

/* --- mobile drawer, which is part of the header --- */

.drawer {
  background: var(--espresso);
  border-top-color: rgba(250, 246, 240, .16);
}

.drawer a {
  color: var(--cream);
}

/*
 * Named with .drawer__list to stay ahead of the divider rules further up this
 * file, which set --line and would otherwise win on source order.
 */
.drawer .drawer__list li a {
  border-bottom-color: rgba(250, 246, 240, .16);
}

.drawer .drawer__list li:last-child a {
  border-bottom: 0;
}

/*
 * The drawer is a programmatic focus target (tabindex="-1"), so that opening
 * it moves a screen reader into the menu. That is a reading position, not an
 * interactive control, so it must not paint a ring: a box drawn around the
 * whole menu on every open would read as an error state.
 *
 * The LINKS inside it keep a real focus ring, branded to match the rest of the
 * theme, and :focus-visible so it appears for keyboard users without flashing
 * at anyone who tapped.
 */
.drawer:focus {
  outline: 0;
}

.drawer .drawer__list a:focus-visible {
  outline: 2px solid var(--terracotta);
  outline-offset: -2px;
}

/* -------------------------------------------------------------------------
 * Referral panel, My Account > Refer a friend
 *
 * The code is the whole point of the page, so it is set at display size in the
 * monospace-ish tracking the rest of the theme uses for codes, and is
 * selectable. It is read off a screen and typed into a phone, or said out
 * loud, which is also why includes/referrals.php excludes characters that are
 * easy to transcribe wrongly.
 * ---------------------------------------------------------------------- */
.refer-panel__lede {
  max-width: 54ch;
  margin-bottom: 22px;
}

.refer-panel__code {
  display: inline-block;
  padding: 14px 26px;
  border: 1px dashed var(--terracotta);
  border-radius: 2px;
  background: var(--sand);
  color: var(--espresso);
  font-size: clamp(20px, 3vw, 26px);
  font-weight: 700;
  letter-spacing: .18em;
  /* Trailing letter-spacing would otherwise push the code off centre. */
  text-indent: .18em;
  user-select: all;
}

.refer-panel__link {
  margin-top: 26px;
}

.refer-panel__link label {
  display: block;
  margin-bottom: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--espresso-soft);
}

.refer-panel__link input {
  width: 100%;
  max-width: 46ch;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: #fff;
  font-size: 13.5px;
  color: var(--espresso);
}

.refer-panel__link input:focus {
  outline: 0;
  border-color: var(--terracotta);
  box-shadow: 0 0 0 3px rgba(192, 132, 87, .15);
}

.refer-panel__count {
  margin-top: 22px;
  font-size: 13.5px;
  color: var(--espresso-soft);
}

/* -------------------------------------------------------------------------
 * Instagram overlay
 *
 * home.css was ported from the preview, where every tile carried invented
 * engagement counts ("412 likes, 18 comments") as two separate spans. Those
 * numbers are not published, see includes/instagram.php, so the overlay holds
 * one label instead and needs two corrections to the ported rules.
 *
 * `fill: currentColor` in home.css turns the outlined Instagram glyph into a
 * solid block. The inline fill="none" on the SVG cannot win: a CSS declaration
 * beats a presentation attribute regardless of order. Overridden by selector
 * instead, hence the extra class.
 *
 * The ported gap of 16px was spacing between two counts. With one label it
 * would push the glyph away from its own text.
 * ---------------------------------------------------------------------- */
/*
 * Column count capped by the number of tiles.
 *
 * home.css hardcodes 6 / 3 / 2 columns, which assumes a full strip. With three
 * images the desktop grid drew three tiles into a six-column track and left
 * half the row empty, reading as broken rather than as a shorter strip.
 *
 * min() against the breakpoint maximum, so a full strip is unchanged and a
 * partial one closes up. Six remains the approved layout and the only count
 * that divides evenly at all three breakpoints.
 */
.ig .ig__grid {
  grid-template-columns: repeat(min(var(--ig-count, 6), 6), 1fr);
}

@media (max-width: 1024px) {
  .ig .ig__grid {
    grid-template-columns: repeat(min(var(--ig-count, 3), 3), 1fr);
  }
}

@media (max-width: 640px) {
  .ig .ig__grid {
    grid-template-columns: repeat(min(var(--ig-count, 2), 2), 1fr);
  }

  /*
   * Three is the one count the two-column mobile grid handles badly: it fills
   * a row, then leaves the third tile alone with an empty cell beside it,
   * which reads as an image that failed to load rather than a short strip.
   * Three across keeps it to one clean row.
   *
   * A class rather than another min() because CSS cannot branch on the value
   * of a custom property, and this is a rule about one specific count. Six,
   * the approved full strip, still divides evenly into two columns and is
   * deliberately left alone. Same for 1, 2 and 4.
   *
   * Must stay AFTER the rule above: both are (0,2,0) specificity, so source
   * order is what decides it.
   */
  .ig .ig__grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

.ig .ig__cell .ig__ov svg {
  fill: none;
  stroke: currentColor;
}

.ig .ig__cell .ig__ov {
  gap: 0;
  padding: 0 10px;
  text-align: center;
}

.ig .ig__cell .ig__ov span {
  gap: 8px;
}

/* -------------------------------------------------------------------------
 * Newsletter result notice
 *
 * home.css hides .news__ok and reveals it with `.news.is-done`, which was the
 * class the old fake-submit script added. The form is a real POST now, so the
 * notice is rendered by PHP only when there is something to say, and the state
 * comes from the server rather than from a class the browser added to itself.
 *
 * Specificity, not source order: content.css is enqueued BEFORE home.css, so a
 * rule matching `.news__ok` at equal weight would lose silently. Three classes
 * beats home.css's one and this actually applies.
 * ---------------------------------------------------------------------- */
.news .news__ok.is-shown {
  display: block;
}

/*
 * The base style is olive, which reads as confirmation. An error wearing it
 * would say "we could not accept that" in the visual language of success, so
 * the failure case gets its own colour.
 */
.news .news__ok.is-error {
  background: rgba(176, 92, 74, .12);
  border-color: rgba(176, 92, 74, .45);
  color: #8C4436;
}

/* -------------------------------------------------------------------------
 * Drawer backdrop
 *
 * Reported by the client on the 2026-08-25 preview: with the menu open, the
 * page behind it still scrolled and clicking that page did nothing. The scroll
 * lock is in assets/js/scroll-lock.js; this is the other half, the visible
 * signal that the page is inert plus the surface that catches the click.
 *
 * z-index 50 sits under the header's 60, so the header and the drawer inside
 * it stay lit while everything below dims. Above that value and it would cover
 * the menu it exists to support.
 *
 * `visibility` is animated alongside opacity, not left out. Opacity alone
 * leaves a fully transparent element covering the viewport and swallowing
 * every click on the page while the drawer is closed.
 * ---------------------------------------------------------------------- */
.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(35, 28, 22, .55);
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s var(--ease), visibility .3s var(--ease);
}

.drawer-scrim.is-open {
  opacity: 1;
  visibility: visible;
}

/*
 * The burger only exists below 1024px, so above it the scrim must not be able
 * to appear at all. This is the same upper bound the drawer itself needs, for
 * the same reason: see the stranded-drawer note below.
 */
@media (min-width: 1025px) {
  .drawer-scrim.is-open {
    opacity: 0;
    visibility: hidden;
  }
}

/* -------------------------------------------------------------------------
 * Stranded mobile drawer
 *
 * base.css sets `.drawer.is-open { display: block }` with no upper bound,
 * while `.burger` only appears at max-width:1024px. So an open drawer stays
 * open past the breakpoint with no control left on screen to close it: open
 * the menu on a tablet held in portrait, rotate to landscape, and the nav
 * covers the page permanently until a reload.
 *
 * Not reachable by resizing a desktop window in normal use, which is why it
 * survived: it needs a device that crosses 1024px, and a rotation is exactly
 * that.
 * ---------------------------------------------------------------------- */
@media (min-width: 1025px) {
  .drawer.is-open {
    display: none;
  }
}

/* -------------------------------------------------------------------------
 * Category card scrim
 *
 * home.css fades to rgba(35,28,22,.78) at the base of each card. That is
 * plenty over a dark photograph and not enough over a light one, so whether
 * the white caption is readable depends entirely on which image happens to be
 * uploaded.
 *
 * Measured over the three current cards, contrast of the white text against
 * the pixels actually behind it:
 *
 *     Hair Care    title 8.03   tagline 10.92
 *     Body Care    title 5.92   tagline  8.16
 *     Fragrance    title 3.45   tagline  4.45   <- fails AA, needs 4.5
 *
 * The Fragrance photograph is high-key, so the same gradient leaves its
 * tagline below the threshold. Darkening the gradient fixes that card, and
 * more importantly stops the next uploaded image from reintroducing it: the
 * client controls category images from wp-admin and will upload her own
 * photography, which cannot be checked in advance.
 *
 * The stop positions are unchanged, so the cards keep the same shape. Only
 * the two lower opacities move, and the top of each image stays untouched.
 *
 * SELECTOR IS `.cat-grid .cat::after`, NOT `.cat::after`.
 *
 * This file is loaded BEFORE home.css and shop.css, not after. The enqueue
 * order is fonts, tokens, base, content, home. So overriding base.css works by
 * position, but matching a home.css or shop.css rule at equal specificity
 * loses silently: the declaration is present and correct in the served
 * stylesheet and simply never applies. The first version of this block used
 * `.cat::after` and changed nothing at all, which only showed up because the
 * contrast was re-measured afterwards rather than assumed.
 * ---------------------------------------------------------------------- */
.cat-grid .cat::after {
  background: linear-gradient(
    to top,
    rgba(35, 28, 22, .92) 0%,
    rgba(35, 28, 22, .55) 38%,
    rgba(35, 28, 22, 0) 66%
  );
}

/* -------------------------------------------------------------------------
 * Before / after review comparison
 *
 * Two states, and the fallback is the default.
 *
 * WITHOUT JavaScript the two photographs stack, each with its caption. The
 * comparison is lost but nothing is hidden, and no control is shown that
 * cannot work. before-after.js adds `.is-live`, which switches to the overlay.
 * Written this way round so a script that fails to load degrades instead of
 * leaving an inert handle over a single image.
 * ---------------------------------------------------------------------- */

.ba {
  margin: 16px 0 4px;
  max-width: 460px;
}

.ba__stage {
  display: grid;
  gap: 10px;
}

.ba__layer {
  position: relative;
  border-radius: 2px;
  overflow: hidden;
}

.ba__img {
  display: block;
  width: 100%;
  height: auto;
}

.ba__tag {
  position: absolute;
  left: 10px;
  bottom: 10px;
  padding: 4px 10px;
  border-radius: 2px;
  background: rgba(46, 38, 32, .82);
  color: var(--cream);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.ba__tag--after { left: auto; right: 10px; }

/*
 * Stacked, the pair must read Before then After.
 *
 * The DOM has the AFTER layer first, because the overlay needs the before
 * layer painted on top of it to clip. That order is correct for the slider and
 * backwards for a human reading two stacked photographs, so the fallback
 * reverses it visually. Only the fallback: in the live state the two layers
 * are stacked in the z axis and `order` does not apply.
 */
.ba:not(.is-live) .ba__layer--before { order: -1; }

/* The range is useless until the script can act on it. */
.ba__range { display: none; }
.ba__divider { display: none; }

/* --- interactive state --- */

.ba.is-live .ba__stage {
  display: block;
  position: relative;
  touch-action: pan-y;      /* let the page scroll vertically over the image */
  cursor: ew-resize;
  border-radius: 2px;
  overflow: hidden;
}

.ba.is-live .ba__layer {
  border-radius: 0;
}

/*
 * The after layer is the base; the before layer sits over it and is clipped.
 * Both must occupy the same box or the clip reveals the page behind.
 */
.ba.is-live .ba__layer--before {
  position: absolute;
  inset: 0;
}

.ba.is-live .ba__img {
  height: 100%;
  object-fit: cover;
}

.ba.is-live .ba__divider {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: var(--cream);
  box-shadow: 0 0 0 1px rgba(46, 38, 32, .28);
  pointer-events: none;
}

.ba.is-live .ba__range {
  display: block;
  width: 100%;
  /*
   * 44px tall for the touch target, not for the look. A range input renders
   * its track around 16px, which is a fiddly thing to grab on a phone and
   * below the 44px minimum. The extra height is empty space around the same
   * thin track, so it costs nothing visually and makes the control reachable.
   *
   * Dragging the photograph itself is the primary interaction and always was.
   * This is the control that keyboard and assistive tech use, which is exactly
   * why it should not be the hardest one to hit.
   */
  height: 44px;
  margin: 2px 0 0;
  background: transparent;
  accent-color: var(--terracotta);
}

/*
 * A visible focus ring matters more here than usual: the range is the only
 * way to operate this by keyboard, and it sits under a photograph where a
 * default outline is easy to lose.
 */
.ba__range:focus-visible {
  outline: 2px solid var(--terracotta-deep);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .ba.is-live .ba__stage { cursor: default; }
}

/* --- the upload fields --- */

.comment-form-ba {
  margin: 18px 0;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 2px;
}

.comment-form-ba__legend {
  padding: 0 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.comment-form-ba__hint {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--espresso-soft);
}

.comment-form-ba__field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 10px;
}

.comment-form-ba__field:last-child { margin-bottom: 0; }

.comment-form-ba__field label {
  flex: 0 0 4.5rem;
  font-size: 13px;
  font-weight: 600;
}

.comment-form-ba__field input[type="file"] {
  flex: 1 1 12rem;
  min-width: 0;   /* file inputs refuse to shrink otherwise, and overflow */
  font-size: 13px;
}

/* =========================================================================
 * Customer account
 *
 * The My Account pages carried no theme CSS at all until 2026-09-02: they
 * rendered in WooCommerce's default stylesheet, which meant a bulleted list of
 * underlined links and a plain table inside an otherwise designed site.
 *
 * Everything here is scoped under .woocommerce-account so it cannot leak into
 * the cart or checkout, which are block based and styled elsewhere.
 * ====================================================================== */

.woocommerce-account .entry__body,
.woocommerce-account .wrap > .woocommerce {
  max-width: none;
}

/*
 * Logged-out forms: login, register, reset password.
 *
 * .woocommerce-account .entry__body drops the reading measure just above,
 * because the logged-IN dashboard is a two column nav plus content layout that
 * needs the full 1240px. The side effect is that the logged-OUT pages, which
 * are a single small form, inherit that width too. Measured on production
 * 2026-09-04: the username and password fields rendered 1062px wide inside a
 * 1128px form. Nothing was broken, it just looked unfinished, and a text input
 * a metre long is genuinely harder to use than a short one.
 *
 * Capped near the intro's own 46ch so the form and the text above it line up
 * as one column rather than two unrelated widths.
 *
 * Scoped to .woocommerce-account, so the "Returning customer?" login that
 * appears on the checkout page is untouched.
 */
.woocommerce-account form.woocommerce-form-login,
.woocommerce-account form.woocommerce-form-register,
.woocommerce-account form.woocommerce-ResetPassword {
  max-width: 460px;
}

/* --- logged-out intro --- */
.acct-intro { max-width: 46ch; margin-bottom: clamp(24px, 3vw, 36px); }
.acct-intro__lede {
  margin-top: 12px;
  font-size: 15px;
  font-weight: 300;
  line-height: 1.7;
  color: var(--espresso-soft);
}

/* --- navigation: a pill row, matching the shop toolbar --- */
.acct-nav { margin-bottom: clamp(24px, 3.5vw, 40px); }
.acct-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0 0 18px;
  list-style: none;
  border-bottom: 1px solid var(--line);
}
.acct-nav li { margin: 0; }
.acct-nav a {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--espresso-soft);
  text-decoration: none;
  background: transparent;
  transition: color .25s var(--ease), border-color .25s var(--ease), background .25s var(--ease);
}
.acct-nav a:hover { color: var(--espresso); border-color: var(--espresso); }
.acct-nav .is-active > a {
  background: var(--espresso);
  border-color: var(--espresso);
  color: var(--cream);
}

/* Log out leaves rather than navigates, so it sits apart from the row. */
.acct-nav__logout { margin-left: auto; }
.acct-nav__logout a { border-color: transparent; color: var(--espresso-soft); }
.acct-nav__logout a:hover { color: var(--terracotta-deep); border-color: var(--terracotta); }

/* --- orders as cards --- */
.acct-orders { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.acct-order {
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: clamp(16px, 2.2vw, 24px);
  background: var(--cream);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.acct-order:hover { border-color: var(--sand-deep); box-shadow: var(--shadow); }
.acct-order--cancelled,
.acct-order--refunded,
.acct-order--failed { opacity: .72; }

.acct-order__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.acct-order__no { font-size: 15px; font-weight: 700; color: var(--espresso); margin-right: 10px; }
.acct-order__date {
  font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--espresso-soft);
}

.acct-badge {
  display: inline-block; padding: 5px 12px; border-radius: 999px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  white-space: nowrap;
}
.acct-badge--good { background: var(--olive); color: #fff; }
.acct-badge--live { background: var(--terracotta); color: #fff; }
.acct-badge--wait { background: var(--sand-deep); color: var(--espresso-soft); }
.acct-badge--dead { background: transparent; color: var(--espresso-soft); border: 1px solid var(--line); }

.acct-order__thumbs { display: flex; gap: 8px; margin-top: 16px; }
.acct-order__thumbs img {
  width: 56px; height: 70px; object-fit: cover; border-radius: 2px; background: var(--sand);
}

.acct-order__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-top: 18px;
}
.acct-order__total { font-size: 14px; color: var(--espresso-soft); }
.acct-order__total .amount { font-weight: 700; color: var(--espresso); }
.acct-order__actions { display: flex; gap: 8px; flex-wrap: wrap; }

.acct-pager { display: flex; gap: 8px; margin-top: 22px; }

/* --- empty state --- */
.acct-empty {
  border: 1px solid var(--line); border-radius: 2px;
  padding: clamp(32px, 5vw, 56px); text-align: center; background: var(--sand);
}
.acct-empty__title { font-size: clamp(19px, 2vw, 24px); font-weight: 700; color: var(--espresso); }
.acct-empty__body {
  margin: 10px auto 22px; max-width: 44ch;
  font-size: 14px; font-weight: 300; line-height: 1.7; color: var(--espresso-soft);
}

/* --- forms: addresses, account details, login, lost password --- */
.woocommerce-account .woocommerce form .form-row { margin-bottom: 18px; padding: 0; }
.woocommerce-account label {
  display: block; margin-bottom: 7px;
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--espresso);
}
.woocommerce-account input[type="text"],
.woocommerce-account input[type="email"],
.woocommerce-account input[type="tel"],
.woocommerce-account input[type="password"],
.woocommerce-account select,
.woocommerce-account .select2-container .select2-selection--single {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--cream);
  font-family: inherit;
  font-size: 14px;
  color: var(--espresso);
  transition: border-color .25s var(--ease);
}
.woocommerce-account input:focus,
.woocommerce-account select:focus { outline: none; border-color: var(--terracotta); }

.woocommerce-account fieldset {
  border: 1px solid var(--line); border-radius: 2px; padding: 20px; margin-top: 24px;
}
.woocommerce-account fieldset legend {
  padding: 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--espresso);
}

/* Core's own button classes, restyled to the theme's button. */
.woocommerce-account .woocommerce-Button,
.woocommerce-account button.button,
.woocommerce-account input.button,
.woocommerce-account a.button {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 14px 26px; border: 1px solid var(--espresso); border-radius: 2px;
  background: var(--espresso); color: var(--cream);
  font-family: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; cursor: pointer;
  transition: background .25s var(--ease), transform .25s var(--ease);
}
.woocommerce-account .woocommerce-Button:hover,
.woocommerce-account button.button:hover,
.woocommerce-account input.button:hover,
.woocommerce-account a.button:hover {
  background: var(--terracotta-deep); border-color: var(--terracotta-deep); transform: translateY(-2px);
}

/* --- addresses --- */
.woocommerce-account .woocommerce-Addresses {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap);
}
.woocommerce-account .woocommerce-Address {
  border: 1px solid var(--line); border-radius: 2px; padding: clamp(18px, 2.2vw, 26px);
}
.woocommerce-account .woocommerce-Address-title {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.woocommerce-account .woocommerce-Address-title h2,
.woocommerce-account .woocommerce-Address-title h3 {
  font-size: 15px; font-weight: 700; color: var(--espresso);
}
.woocommerce-account .woocommerce-Address address {
  margin-top: 12px; font-style: normal; font-size: 14px; font-weight: 300;
  line-height: 1.75; color: var(--espresso-soft);
}

/* --- dashboard and generic account copy --- */
.woocommerce-account .woocommerce-MyAccount-content p {
  font-size: 15px; font-weight: 300; line-height: 1.75; color: var(--espresso-soft);
}
.woocommerce-account .woocommerce-MyAccount-content a { color: var(--terracotta-deep); }

/* --- notices --- */
.woocommerce-account .woocommerce-message,
.woocommerce-account .woocommerce-info,
.woocommerce-account .woocommerce-error {
  border-left: 3px solid var(--terracotta);
  background: var(--sand);
  padding: 16px 18px;
  border-radius: 2px;
  font-size: 14px;
  color: var(--espresso);
  list-style: none;
}
.woocommerce-account .woocommerce-error { border-left-color: #b3402f; }

@media (max-width: 640px) {
  .woocommerce-account .woocommerce-Addresses { grid-template-columns: 1fr; }
  .acct-nav ul { gap: 6px; }
  .acct-nav a { padding: 9px 14px; font-size: 10px; letter-spacing: .1em; }
  /* On a phone the row would push Log out onto its own line at the far right,
     which reads as a stray element. Let it sit in the flow instead. */
  .acct-nav__logout { margin-left: 0; }
  .acct-order__thumbs img { width: 48px; height: 60px; }
  .acct-order__foot { align-items: flex-start; flex-direction: column; }
}

/* -------------------------------------------------------------------------
 * Account: beating WooCommerce's own specificity.
 *
 * The rules above lost to core's `.woocommerce form .form-row input.input-text`
 * (0,3,2), which left inputs on 7px padding and a near-black border in the
 * middle of a cream page. These selectors match core's shape and add the
 * page-level class, so they win without resorting to !important.
 * ---------------------------------------------------------------------- */
.woocommerce-account .woocommerce form .form-row input.input-text,
.woocommerce-account .woocommerce form .form-row textarea,
.woocommerce-account .woocommerce form .form-row select,
.woocommerce-account .woocommerce form input.input-text,
.woocommerce-account .woocommerce form input[type="password"] {
  width: 100%;
  box-sizing: border-box;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--cream);
  font-family: inherit;
  font-size: 14px;
  line-height: 1.4;
  color: var(--espresso);
  transition: border-color .25s var(--ease);
}

.woocommerce-account .woocommerce form .form-row input.input-text:focus,
.woocommerce-account .woocommerce form .form-row textarea:focus,
.woocommerce-account .woocommerce form .form-row select:focus {
  outline: none;
  border-color: var(--terracotta);
}

/*
 * The password field wraps its input in a relative container with a reveal
 * button. Core absolutely positions that button against its own padding, so
 * the input needs room for it at the new padding.
 */
.woocommerce-account .woocommerce-password-input { position: relative; display: block; }
.woocommerce-account .show-password-input {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  cursor: pointer;
}
.woocommerce-account .woocommerce form .form-row .woocommerce-password-input input.input-text {
  padding-right: 46px;
}

/*
 * Core wraps the login form in a bordered card with a lilac border that belongs
 * to no palette on this site. The intro block above it already frames the form,
 * so the box is unnecessary chrome.
 */
.woocommerce-account .woocommerce form.login,
.woocommerce-account .woocommerce form.register,
.woocommerce-account .woocommerce form.lost_reset_password {
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: clamp(20px, 3vw, 32px);
  margin: 0;
  background: var(--cream);
}

/*
 * Core prints its own "Login" heading immediately under the intro block, which
 * gave the page three stacked headings: the H1, "Welcome back", and "Login".
 * Hidden visually rather than removed, so the form keeps an accessible name
 * and a template update cannot silently strip it.
 */
.woocommerce-account #customer_login > .u-column1 > h2,
.woocommerce-account #customer_login > .u-column2 > h2,
body:not(.logged-in).woocommerce-account .woocommerce > h2 {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Remember me sits inline with the button on core's markup; give it room. */
.woocommerce-account .woocommerce-form-login__rememberme {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: 14px;
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--espresso-soft);
}
.woocommerce-account .woocommerce-form-login__rememberme input { margin: 0; }

/* The lost-password link should not read as body copy. */
.woocommerce-account .woocommerce-LostPassword {
  margin-top: 16px;
  font-size: 12px;
}
.woocommerce-account .woocommerce-LostPassword a {
  color: var(--espresso-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.woocommerce-account .woocommerce-LostPassword a:hover {
  color: var(--terracotta-deep);
  border-color: var(--terracotta);
}

/* Required-field asterisk in the brand accent rather than pure red. */
.woocommerce-account .required { color: var(--terracotta-deep); border: 0; }

/* -------------------------------------------------------------------------
 * Account: undoing WooCommerce's two-column float.
 *
 * Core floats the navigation into a 30% left column and the content into a 68%
 * right one. That is written for the default sidebar; it squeezed the pill row
 * into a narrow column so the pills stacked vertically, which is the sidebar
 * this design was chosen to replace.
 *
 * Nav on top, content beneath, both full width.
 * ---------------------------------------------------------------------- */
.woocommerce-account .woocommerce-MyAccount-navigation,
.woocommerce-account .woocommerce-MyAccount-content {
  float: none;
  width: 100%;
  margin: 0;
}

.woocommerce-account .woocommerce-MyAccount-content { margin-top: 0; }

/* Core's clearfix on the wrapper is no longer needed, but harmless if present. */
.woocommerce-account .woocommerce::after { content: ""; display: table; clear: both; }

/* -------------------------------------------------------------------------
 * Notices with an icon.
 *
 * Core positions the icon absolutely and reserves room with a large left
 * padding. The notice styling above replaced that padding, so the icon landed
 * on top of the first line of text.
 * ---------------------------------------------------------------------- */
.woocommerce-account .woocommerce-message,
.woocommerce-account .woocommerce-info,
.woocommerce-account .woocommerce-error {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 20px;
  margin: 0 0 22px;
}

/* Core draws its icon with a ::before; this design does not need it. */
.woocommerce-account .woocommerce-message::before,
.woocommerce-account .woocommerce-info::before,
.woocommerce-account .woocommerce-error::before {
  content: none;
}

/* Push any button in a notice to the end of the row rather than inline mid-sentence. */
.woocommerce-account .woocommerce-message .button,
.woocommerce-account .woocommerce-info .button {
  margin-left: auto;
  flex: 0 0 auto;
}

/*
 * A cancelled or refunded card is dimmed as a whole, which was also dimming its
 * button into looking disabled. Bring the actions back to full strength: the
 * order is over, but its links still work.
 */
.acct-order--cancelled .acct-order__actions,
.acct-order--refunded .acct-order__actions,
.acct-order--failed .acct-order__actions { opacity: 1; }

/* -------------------------------------------------------------------------
 * Account polish, from looking at the rendered pages.
 * ---------------------------------------------------------------------- */

/*
 * Log out was pushed to the far end with margin-left:auto. Once the row wraps
 * that lands it alone on a second line, which reads as a mistake rather than as
 * separation. It now flows with the row and is set apart by weight and a rule
 * instead of by position, which holds at every width.
 */
.acct-nav__logout { margin-left: 0; }
.acct-nav__logout a {
  border-color: transparent;
  color: var(--espresso-soft);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: var(--line);
}
.acct-nav__logout a:hover {
  color: var(--terracotta-deep);
  border-color: transparent;
  text-decoration-color: var(--terracotta);
}

/*
 * WooCommerce puts the action button BEFORE the message text in notice markup.
 * As a flex row that rendered the button on the first line and the sentence
 * underneath it, so the page told you to act before saying why.
 */
.woocommerce-account .woocommerce-message .button,
.woocommerce-account .woocommerce-info .button,
.woocommerce-account .woocommerce-error .button {
  order: 2;
  margin-left: auto;
}

/*
 * Links inside account content are terracotta, which is right for body copy and
 * wrong for buttons: it was repainting the label on the dark VIEW button. Any
 * element carrying a button class keeps its own colour.
 */
.woocommerce-account .woocommerce-MyAccount-content a.btn,
.woocommerce-account .woocommerce-MyAccount-content a.button,
.woocommerce-account .woocommerce-MyAccount-content a.woocommerce-Button {
  color: var(--cream);
}
.woocommerce-account .woocommerce-MyAccount-content a.btn--ghost {
  color: var(--espresso);
}
.woocommerce-account .woocommerce-MyAccount-content a.btn--ghost:hover {
  color: var(--cream);
}

/* -------------------------------------------------------------------------
 * Addresses: neutralising core's column classes inside the grid.
 *
 * The wrapper carries `u-columns col2-set` and each card carries
 * `u-column1 col-1`, which WooCommerce styles as `float:left; width:48%`.
 * Inside a grid container the float is ignored but the width is not, so both
 * cards collapsed to about 180px and "Add Billing address" wrapped one word per
 * line. The grid owns the columns; the cards just need to fill their cell.
 * ---------------------------------------------------------------------- */
.woocommerce-account .woocommerce-Addresses .woocommerce-Address,
.woocommerce-account .woocommerce-Addresses .col-1,
.woocommerce-account .woocommerce-Addresses .col-2,
.woocommerce-account .woocommerce-Addresses .u-column1,
.woocommerce-account .woocommerce-Addresses .u-column2 {
  float: none;
  width: auto;
  max-width: none;
  margin: 0;
}

.woocommerce-account .woocommerce-Address-title {
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}

/* The edit link is an action, not prose: keep it on one line and out of the
   terracotta body-link treatment. */
.woocommerce-account .woocommerce-Address-title .edit {
  white-space: nowrap;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--espresso-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.woocommerce-account .woocommerce-Address-title .edit:hover {
  color: var(--terracotta-deep);
  border-color: var(--terracotta);
}

/* The edit-address FORM is a single column, so it must not inherit the grid. */
.woocommerce-account .woocommerce-address-fields { max-width: 34rem; }
.woocommerce-account .woocommerce-address-fields__field-wrapper { margin-bottom: 8px; }

/*
 * Core clears its float layout with `.col2-set::before/::after { content:" " }`.
 * Those pseudo-elements are harmless in a block container and become GRID ITEMS
 * in a grid one: ::before took cell 1, so Billing landed in cell 2 and Shipping
 * in cell 3, rendering the two cards diagonally across two rows instead of side
 * by side. The grid needs no clearfix, so the pseudo-elements are removed.
 */
.woocommerce-account .woocommerce-Addresses::before,
.woocommerce-account .woocommerce-Addresses::after {
  content: none;
}

/* The address card heading carries a default margin that dropped it below the
   edit link sitting opposite it. */
.woocommerce-account .woocommerce-Address-title h2,
.woocommerce-account .woocommerce-Address-title h3 { margin: 0; }

/* Buttons are buttons, not links: core's link underline was showing through on
   VIEW and on the notice actions. */
.woocommerce-account .woocommerce-MyAccount-content a.btn,
.woocommerce-account .woocommerce-MyAccount-content a.button,
.woocommerce-account .woocommerce-MyAccount-content a.woocommerce-Button,
.woocommerce-account .acct-empty a.btn { text-decoration: none; }

/* -------------------------------------------------------------------------
 * Header search panel and results page.
 *
 * The header control was a decorative icon with no handler until 2026-09-02,
 * and there was no search template at all, so results rendered through the
 * blog index with nothing saying what had been searched for.
 * ---------------------------------------------------------------------- */
.head-search {
  border-top: 1px solid rgba(250, 246, 240, .12);
  background: var(--espresso);
  padding: 16px var(--pad);
}
.head-search__form {
  display: flex;
  gap: 10px;
  align-items: center;
  max-width: var(--wrap);
  margin-inline: auto;
}
.head-search__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 12px 15px;
  border: 1px solid rgba(250, 246, 240, .22);
  border-radius: 2px;
  background: var(--cream);
  font-family: inherit;
  font-size: 14px;
  color: var(--espresso);
}
.head-search__input:focus { outline: none; border-color: var(--terracotta); }

.search-again {
  display: flex;
  gap: 10px;
  align-items: center;
  max-width: 30rem;
  margin-bottom: clamp(24px, 3vw, 36px);
}
.search-again .head-search__input { border-color: var(--line); }

.search-group { margin: clamp(24px, 3vw, 36px) 0 16px; color: var(--espresso); }

.search-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.search-list a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 2px;
  text-decoration: none;
  transition: border-color .25s var(--ease);
}
.search-list a:hover { border-color: var(--sand-deep); }
.search-list__title { font-size: 15px; font-weight: 600; color: var(--espresso); }
.search-list__type {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--espresso-soft); white-space: nowrap;
}

.search-wider { margin-top: 22px; font-size: 13px; }
.search-wider a {
  color: var(--espresso-soft);
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  text-decoration: none;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.search-wider a:hover { color: var(--terracotta-deep); border-color: var(--terracotta); }

@media (max-width: 640px) {
  .head-search__form, .search-again { flex-wrap: wrap; }
  .head-search__input { flex: 1 1 100%; }
}

/* On a phone the input takes the full row and the button dropped beneath it
   left aligned, reading as a stray control. Let it fill the row too. */
@media (max-width: 640px) {
  .head-search__form .btn,
  .search-again .btn { flex: 1 1 100%; }
}

/* Highlighted rule inside a legal page. Used for the "opened products cannot be
   returned" line, which the client asked to have stand out because it is the
   term customers are most likely to miss and most likely to argue about. */
.policy-callout {
  border-left: 3px solid var(--terracotta);
  background: var(--sand);
  padding: 18px 20px;
  border-radius: 2px;
  margin: 22px 0;
}

/*
 * Order received, and the other WooCommerce pages that render through page.php.
 *
 * page.php exists for the legal and About pages, where .entry__head and
 * .entry__body carry a reading measure (68ch and 72ch) so a wall of text does
 * not run the full 1240px. That is right for prose and wrong here: the order
 * received page is a summary table, and a table capped at 72ch inside a 1240px
 * wrap reads as shoved against the left edge with a third of the screen empty.
 *
 * So the measure comes off and the wrap narrows instead. The content is then
 * centred by .wrap's own margin-inline:auto rather than by adding a second
 * centring rule that would fight it.
 *
 * Scoped to the order received and order pay body classes only. Deliberately
 * NOT view-order: that one renders inside My Account, which has its own
 * layout, and narrowing the wrap there would make a single account tab
 * narrower than every other tab beside it.
 */
.woocommerce-order-received .wrap,
.woocommerce-order-pay .wrap {
  max-width: 880px;
}

.woocommerce-order-received .entry__head,
.woocommerce-order-received .entry__body,
.woocommerce-order-pay .entry__head,
.woocommerce-order-pay .entry__body {
  max-width: none;
}
