/* Built by tools/build_rebrand_css.py. Edit the parts in assets/css/rebrand/, never this file. */

/* ===== about.css ===== */
/* =====================================================================
   UBORA - About page, rebuilt on the coastal set.

   Owns .ubora-about* and nothing else. The orchestrator concatenates
   assets/css/rebrand/*.css after extra.css and day.css, so the brand tokens
   (--wp--preset--*, --u-*) are already in scope here.

   Every rule below is scoped under a .ubora-about* ancestor on purpose: other
   agents are writing sibling files in this directory and may style the shared
   .ubora-shot image class globally.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Photographs
   ---------------------------------------------------------------------
   Each one arrives from ubora_site_image() carrying width/height, the srcset
   ladder and the art director's focal point as an inline object-position.
   Nothing here may set object-position or a file path.
   --------------------------------------------------------------------- */
.ubora-about figure { margin: 0; }
.ubora-about picture { display: block; }
.ubora-about figure img { display: block; width: 100%; height: auto; }

/* ---------------------------------------------------------------------
   2. Morning - the hero
   ---------------------------------------------------------------------
   A-01 runs full bleed and the copy plate rides up over its lower edge, so the
   page opens on the room rather than on a headline. The plate is solid
   parchment: the type never sits on the photograph, so contrast is fixed.
   --------------------------------------------------------------------- */
.ubora-about-hero { position: relative; }

.ubora-about-hero__shot img {
  aspect-ratio: 2.35 / 1;
  max-height: 74vh;
  object-fit: cover;
}

.ubora-about-hero__plate {
  position: relative;
  z-index: 1;
  max-width: min(44rem, 92vw);
  margin-inline: auto;
  box-shadow: 0 22px 60px rgba(74, 55, 38, 0.14);
}

.ubora-about-hero--shot .ubora-about-hero__plate {
  margin-block-start: clamp(-5rem, -4vw, -2rem);
}

/* ---------------------------------------------------------------------
   3. Full-bleed bands (A-02 founder story, A-06 closing standard)
   --------------------------------------------------------------------- */
.ubora-about-band img {
  aspect-ratio: 2.4 / 1;
  max-height: 58vh;
  object-fit: cover;
}

/* ---------------------------------------------------------------------
   4. Midday - the quote plate
   ---------------------------------------------------------------------
   A-03 is the only place on this page where type sits on a photograph, so it
   carries a scrim. Parchment washed over the frame, never a switch to white
   type: at 0.62 alpha the plate stays above 5:1 for Anchor even if the crop
   underneath goes black, and the wash reads as light rather than as a panel.
   --------------------------------------------------------------------- */
.ubora-about-quote {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  align-content: center;
  min-height: clamp(20rem, 44vh, 30rem);
}

.ubora-about-quote__plate {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* The only image on the page that fills a box rather than setting one, so it
   has to outrank the height:auto in section 1 above. */
.ubora-about .ubora-about-quote__plate picture,
.ubora-about .ubora-about-quote__plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ubora-about-quote__plate::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    82% 74% at 50% 48%,
    rgba(242, 237, 227, 0.88) 0%,
    rgba(242, 237, 227, 0.72) 58%,
    rgba(242, 237, 227, 0.62) 100%
  );
}

.ubora-about-quote__copy {
  position: relative;
  z-index: 1;
}

/* ---------------------------------------------------------------------
   5. Midday - the four pillars (A-05a..d)
   ---------------------------------------------------------------------
   The grid is declared here as well as on the block so the row holds four up
   whether or not the grid layout support renders, and so the phone cut is a
   plain override rather than a fight with generated layout CSS.
   --------------------------------------------------------------------- */
.ubora-about-pillars .ubora-about-pillar-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1rem, 2.2vw, 1.75rem);
}

.ubora-about-pillar__shot img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 2px;
}

.ubora-about-pillar h3 {
  text-wrap: balance;
}

/* ---------------------------------------------------------------------
   6. Sunset - the close
   ---------------------------------------------------------------------
   Sand ground with a clay wash falling off the top edge, so the last screen
   reads as late light instead of the dark void the old page ended on.
   --------------------------------------------------------------------- */
.ubora-about-close {
  background-image: linear-gradient(
    180deg,
    rgba(139, 94, 60, 0.12) 0%,
    rgba(220, 201, 178, 0) 58%
  );
}

.ubora-about-caption {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}

/* ---------------------------------------------------------------------
   7. Phone. Desktop above this line is untouched by anything below it.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  .ubora-about-hero__shot img {
    aspect-ratio: 5 / 4;
    max-height: 62vh;
  }

  .ubora-about-hero__plate {
    max-width: 94vw;
  }

  .ubora-about-hero--shot .ubora-about-hero__plate {
    margin-block-start: -1.75rem;
  }

  .ubora-about-band img {
    aspect-ratio: 3 / 2;
    max-height: 52vh;
  }

  .ubora-about-quote {
    min-height: clamp(17rem, 64vw, 24rem);
  }

  /* On a phone the quote runs the full width of the band, so it leaves the
     centre of the radial and lands on the weak edge of it. Flatten the wash
     and hold it at 0.80: that keeps Slate above 6:1 even if the crop under
     the words is the darkest part of the frame. */
  .ubora-about-quote__plate::after {
    background: radial-gradient(
      130% 105% at 50% 50%,
      rgba(242, 237, 227, 0.9) 0%,
      rgba(242, 237, 227, 0.8) 100%
    );
  }

  .ubora-about-pillars .ubora-about-pillar-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.4rem 0.9rem;
  }
}

/* ===== bundle.css ===== */
/* ===========================================================================
   Trio bundle band (patterns/bundle-teaser.php)

   Every rule is scoped under .ubora-trio-band, so nothing here can reach
   another section. The one class borrowed from elsewhere is .ubora-checks,
   which is styled in extra.css and only tuned here, never redefined.

   The band carries no background of its own: it rides over the day canvas like
   the rest of the homepage close, and only the offer card lifts off the
   plaster. Shadows read from --u-cast (the sun's real position that frame) with
   a static fallback for any page that never boots the day engine.
   =========================================================================== */

.ubora-trio-band {
	position: relative;
}

/* --- The intro, over plaster ---------------------------------------------
   .u-copy is the day layer's overlaid-type utility: it caps the measure to the
   clean half of the frame and carries the hour-driven sand scrim. Here the
   block is centred rather than held left, so both are re-stated. day.css binds
   the portrait rule at :root[data-day-variant] .u-copy (0,3,0), which a plain
   class pair would lose to, hence the :root prefix.
   ------------------------------------------------------------------------- */

:root .ubora-trio-band .ubora-trio__intro.u-copy {
	max-width: min(46rem, 100%);
	margin-left: auto;
	margin-right: auto;
	text-align: center;
}

/* Same sand-to-transparent gradient as day.css, re-centred. Oversized and
   fully feathered on purpose: a bounded scrim shows its own edge as a faint
   rectangle on plaster, which is worse than no scrim at all. */
:root .ubora-trio-band .ubora-trio__intro.u-copy::before {
	inset: -80% -45%;
	background: radial-gradient(
		54% 48% at 50% 50%,
		rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 1.6)) 0%,
		rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 0.7)) 40%,
		rgb(var(--u-ground) / 0) 70%
	);
}

/* --- The photograph mosaic ------------------------------------------------
   B-01 leads at double height on the left, B-03 (the open box) sits top right,
   B-02 fills beneath it. All three slots are 4:5, so the lead keeps close to
   its native crop and the two smaller cells land near square. The container
   owns the height via aspect-ratio, which gives every cell a definite box and
   keeps the mosaic from reflowing as the images decode.
   ------------------------------------------------------------------------- */

.ubora-trio-band .ubora-trio__media {
	display: grid;
	grid-template-columns: 1.62fr 1fr;
	grid-template-rows: 1fr 1fr;
	gap: 0.7rem;
	aspect-ratio: 100 / 76;
}

.ubora-trio-band .ubora-trio__media.is-3 .ubora-trio__img--lead {
	grid-row: span 2;
}

/* Degraded sets: the manifest can be missing a cut without the section
   collapsing into a stack of stretched cells. */
.ubora-trio-band .ubora-trio__media.is-2 {
	grid-template-columns: 1fr 1fr;
	grid-template-rows: 1fr;
	aspect-ratio: 8 / 5;
}

.ubora-trio-band .ubora-trio__media.is-1 {
	grid-template-columns: 1fr;
	grid-template-rows: 1fr;
	aspect-ratio: 4 / 5;
}

.ubora-trio-band .ubora-trio__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: 12px;
	box-shadow: var(--u-cast, -1.6px 2.4px 22px rgb(74 55 38 / 0.1));
}

/* --- The offer card -------------------------------------------------------
   Panel tone comes from the day layer so the card sits in the same light as
   the room; the literal fallback is the late-morning frame day.css defaults to.
   ------------------------------------------------------------------------- */

.ubora-trio-band .ubora-trio__offer {
	background: rgb(var(--u-panel, 246 242 234) / 0.92);
	border: 1px solid var(--wp--preset--color--concrete);
	border-radius: 14px;
	padding: clamp(1.4rem, 3vw, 2.2rem);
	box-shadow: var(--u-cast, -1.6px 2.4px 22px rgb(74 55 38 / 0.1));
}

.ubora-trio-band .ubora-trio__label {
	margin: 0 0 0.55rem;
	font-family: var(--wp--preset--font-family--outfit);
	font-weight: 700;
	font-size: 0.68rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--clay);
}

/* Price row: the figure the customer pays leads, the list price follows it
   struck through, and the saving is stated as the dollar figure rather than a
   percentage. Baseline alignment so the three sit on one line optically. */
.ubora-trio-band .ubora-trio__prices {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.5rem 0.8rem;
	margin: 0;
}

.ubora-trio-band .ubora-trio__now {
	font-family: var(--wp--preset--font-family--gloock);
	font-size: clamp(2.5rem, 5.2vw, 3.4rem);
	line-height: 0.95;
	color: var(--wp--preset--color--black);
}

.ubora-trio-band .ubora-trio__was {
	font-family: var(--wp--preset--font-family--outfit);
	font-size: 1.1rem;
	color: var(--wp--preset--color--silver);
	text-decoration-thickness: 1px;
}

.ubora-trio-band .ubora-trio__save {
	padding: 0.34rem 0.7rem;
	border: 1px solid var(--wp--preset--color--concrete);
	border-radius: 999px;
	background: var(--wp--preset--color--white);
	font-family: var(--wp--preset--font-family--outfit);
	font-weight: 600;
	font-size: 0.72rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--clay-deep);
	white-space: nowrap;
}

.ubora-trio-band .ubora-trio__math {
	margin: 0.55rem 0 0;
	font-family: var(--wp--preset--font-family--crimson);
	font-style: italic;
	font-size: 1.05rem;
}

.ubora-trio-band .ubora-trio__checks {
	margin-top: 0.95rem;
}

.ubora-trio-band .ubora-trio__checks li {
	font-size: 0.98rem;
	line-height: 1.5;
}

.ubora-trio-band .ubora-trio__cta {
	margin-top: 1.3rem;
}

.ubora-trio-band .ubora-trio__fine {
	margin: 0.9rem 0 0;
	font-family: var(--wp--preset--font-family--outfit);
	font-size: 0.8rem;
	letter-spacing: 0.02em;
	color: var(--wp--preset--color--silver);
}

/* No photography deployed: the surviving column is the offer card, so let it
   off its 46% basis and centre it. The width is an inline style from the block,
   hence the override. */
.ubora-trio-band .ubora-trio__layout.is-solo .wp-block-column {
	flex-basis: 100% !important;
	max-width: 34rem;
	margin-inline: auto;
}

/* --- Phone ---------------------------------------------------------------
   wp:columns already stacks at this width, so the only work left is closing up
   the mosaic and the card. Desktop is untouched below this line.
   ------------------------------------------------------------------------- */

@media (max-width: 781px) {
	.ubora-trio-band .ubora-trio__media {
		gap: 0.5rem;
	}

	.ubora-trio-band .ubora-trio__img {
		border-radius: 10px;
	}

	.ubora-trio-band .ubora-trio__offer {
		padding: 1.3rem 1.15rem;
	}

	.ubora-trio-band .ubora-trio__now {
		font-size: 2.6rem;
	}

	.ubora-trio-band .ubora-trio__checks li {
		font-size: 0.94rem;
	}
}

/* ===== cart.css ===== */
/* =====================================================================
   UBORA rebrand — cart + order confirmation.

   This file owns exactly three new elements: the cart letterhead strip
   (X-01), the empty-bag ledge (X-02) and the thank-you band (X-04).
   Every other rule for these two pages already lives in extra.css and is
   deliberately left alone.

   Cart is a conversion page, not a showcase, so the photography here is
   restrained: one thin height-capped band, never full-bleed.

   Selectors that pin a band height carry two classes on purpose. The
   rebrand bundle is concatenated, so a later generic `.ubora-shot` rule
   from another file must not be able to flatten these.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Cart letterhead strip — X-01
   The plate is only 1200x300. Capped to the wide column and cropped to a
   band; stretched full-bleed across a 1920 screen it would go soft.
   --------------------------------------------------------------------- */
.ubora-cart-strip {
  position: relative;
  overflow: hidden;
  border-radius: 10px;
  background: var(--wp--preset--color--oat);
}
.ubora-cart-strip p { margin: 0; }
.ubora-cart-strip img.ubora-shot {
  display: block;
  width: 100%;
  height: 92px;
  object-fit: cover;
}

/* Warm veil on the lower edge so the band settles into the sand page
   ground rather than reading as a hero photograph. */
.ubora-cart-strip::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(220, 201, 178, 0) 45%, rgba(220, 201, 178, 0.5) 100%);
}

/* ---------------------------------------------------------------------
   2. Empty bag — X-02, the travertine ledge
   Small and centred. The empty state is a nudge back to the shop, so the
   picture must not push the tiles below the fold.
   --------------------------------------------------------------------- */
.ubora-empty-shot { margin: 0 0 1.5rem; }
.ubora-empty-shot p { margin: 0; }
.ubora-empty-shot img.ubora-shot {
  display: block;
  width: min(240px, 62vw);
  height: auto;
  margin: 0 auto;
  border-radius: 12px;
  border: 1px solid var(--ubora-edge);
}

/* ---------------------------------------------------------------------
   3. Thank-you band — X-04
   Post-purchase, so this one is allowed to be the picture on the page. It
   fades into the parchment confirmation panel directly beneath it.
   --------------------------------------------------------------------- */
.ubora-confirm-band {
  position: relative;
  overflow: hidden;
  line-height: 0;
}
.ubora-confirm-band p { margin: 0; }
.ubora-confirm-band img.ubora-shot {
  display: block;
  width: 100%;
  height: clamp(220px, 24vw, 360px);
  object-fit: cover;
}
.ubora-confirm-band::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 42%;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(242, 237, 227, 0) 0%, rgba(242, 237, 227, 0.92) 100%);
}

/* ---------------------------------------------------------------------
   4. Phone. Same markup is served to both, so this is the only place the
   sizes change; desktop above is untouched.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  .ubora-cart-strip { border-radius: 8px; }
  .ubora-cart-strip img.ubora-shot { height: 56px; }
  .ubora-empty-shot { margin-bottom: 1.2rem; }
  .ubora-empty-shot img.ubora-shot { width: min(200px, 56vw); }
  .ubora-confirm-band img.ubora-shot { height: 180px; }
}

/* ===== collection.css ===== */
/* =====================================================================
   UBORA: the collection (shop archive) frame.

   Owned by the collection pass: templates/archive-product.html and
   patterns/shop-filters.php. This file is the page AROUND the products:
   banner, collection entries, filter rail, grid rhythm. The product cards
   themselves are already rebranded in extra.css (.ubora-pcard) and are not
   touched here.

   Loads after extra.css, so equal-specificity rules here win on purpose.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Collection banner (slot C-01)
   --------------------------------------------------------------------- */
.ubora-collection .ubora-cbanner {
  /* Bleed through the root padding even where the constrained-layout
     alignfull rule does not reach. The custom property is core's; the 0px
     fallback makes this a no-op if root padding is ever switched off, and
     the values are identical to core's, so this can never double-shift. */
  margin-left: calc(var(--wp--style--root--padding-left, 0px) * -1);
  margin-right: calc(var(--wp--style--root--padding-right, 0px) * -1);
  max-width: none;
}
/* core/shortcode can hand its content back inside a paragraph. */
.ubora-cbanner p { margin: 0; }
.ubora-cbanner__img, .ubora-cbanner img {
  display: block;
  width: 100%;
  /* Uncropped, 2.4:1 is a 600px wall on a laptop and pushes every product
     under the fold. Cap the band and crop into it instead. */
  height: clamp(150px, 30vw, 500px);
  object-fit: cover;
  /* The nine bottles sit LOW in the frame (plaster and palm shadow above,
     wood ledge below). Holding the crop below centre keeps every cap and
     every base in shot at any width. If the art director ever ships a focal
     point for C-01, ubora_site_image() writes it inline and wins over this. */
  object-position: 50% 70%;
  background-color: var(--wp--preset--color--oat);
}

/* ---------------------------------------------------------------------
   2. Page head
   --------------------------------------------------------------------- */
.ubora-shop-head { text-align: center; }
.ubora-shop-head .wp-block-term-description { color: var(--wp--preset--color--steel); }
.ubora-shop-facts {
  margin-top: 0.35rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.84rem;
  letter-spacing: 0.05em;
  color: var(--wp--preset--color--steel);
}
.ubora-shop-facts a {
  color: var(--wp--preset--color--clay-deep);
  text-underline-offset: 3px;
}

/* ---------------------------------------------------------------------
   3. Collection entries (slots C-02a / C-02b / C-02c)
   The three doors into the nine: him, her, unisex. Each links to the real
   filtered archive (?ubora_gender=), so they are navigation, not decoration.
   --------------------------------------------------------------------- */
.ubora-centries {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.2rem;
}
.ubora-centry {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
  border-radius: 12px;
  overflow: hidden;
  background: var(--wp--preset--color--oat);
  box-shadow: var(--wp--preset--shadow--lift);
  transition: transform var(--ubora-dur) var(--ubora-ease), box-shadow var(--ubora-dur) var(--ubora-ease);
}
.ubora-centry:hover {
  transform: translateY(-4px);
  box-shadow: var(--wp--preset--shadow--soft);
}
.ubora-centry:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay-deep);
  outline-offset: 3px;
}
.ubora-centry__frame {
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--wp--preset--color--oat);
}
.ubora-centry__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--ubora-dur-slow) var(--ubora-ease-out);
}
.ubora-centry:hover .ubora-centry__frame img { transform: scale(1.04); }
.ubora-centry__plate {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 2.8rem 1rem 1rem;
  text-align: center;
  /* Warm veil, not a dark scrim: the label sits on the travertine ledge at
     the foot of every C-02 frame, so parchment keeps the daylight. */
  background: linear-gradient(180deg, rgba(242, 237, 227, 0) 0%, rgba(242, 237, 227, 0.88) 52%, rgba(242, 237, 227, 0.97) 100%);
}
.ubora-centry__label {
  display: block;
  font-family: var(--wp--preset--font-family--gloock);
  font-size: 1.3rem;
  line-height: 1.1;
  color: var(--wp--preset--color--black);
}
.ubora-centry__meta {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.76rem;
  letter-spacing: 0.05em;
  color: var(--wp--preset--color--steel);
}
.ubora-centry.is-active {
  outline: 2px solid var(--wp--preset--color--clay);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------
   4. Filter rail
   --------------------------------------------------------------------- */
.ubora-shop-nav .ubora-filters { margin: 0; }
.ubora-shop-nav .ubora-chip {
  background: rgba(255, 255, 255, 0.62);
  border-color: rgba(28, 26, 23, 0.12);
}
.ubora-shop-nav .ubora-chip:hover {
  background: var(--wp--preset--color--white);
  border-color: var(--wp--preset--color--clay);
}
.ubora-shop-nav .ubora-chip.is-active {
  background: var(--wp--preset--color--black);
  color: var(--wp--preset--color--parchment);
  border-color: var(--wp--preset--color--black);
}
.ubora-filters__clear {
  margin: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.78rem;
}
.ubora-filters__clear a {
  color: var(--wp--preset--color--clay-deep);
  text-underline-offset: 3px;
}

/* Desktop only: the sticky sidebar already carries Collections and By mood,
   and the three entries above carry the same three categories, so the chip
   rail would be a third copy of one navigation. It is NOT redundant below
   901px, where extra.css hides every sidebar box except Sort and the chips
   become the only filter control on the page. If the sidebar shortcode ever
   goes away, delete this rule with it. */
@media (min-width: 901px) {
  .ubora-shop-nav .ubora-filters { display: none; }
}

/* ---------------------------------------------------------------------
   5. Grid rhythm
   --------------------------------------------------------------------- */
@media (min-width: 901px) {
  /* Nothing sits above the grid inside the main column any more, so its own
     top margin would drop it below the top of the sticky sidebar. */
  .ubora-shop-main .ubora-shop-grid { margin-top: 0; }
}
@media (min-width: 1180px) {
  /* Nine scents, three per collection. A fixed 3-up grid makes the archive
     read as three complete rows instead of a ragged auto-fill, and matches
     the three entry cards above it. */
  .ubora-shop-main .ubora-shop-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---------------------------------------------------------------------
   6. Phone. Same markup as desktop; everything below is media-query scoped.
   --------------------------------------------------------------------- */
@media (max-width: 900px) {
  .ubora-shop-nav { margin-bottom: 0.2rem; }
  /* Tapping a collection has to land on products. Once a filter is on, the
     entries are only the door the shopper already walked through, and on a
     phone they cost a screen before the first bottle. The body class comes
     from inc/shop.php. */
  body.ubora-filtered .ubora-shop-nav .ubora-centries { display: none; }
  /* The two controls are the same three destinations, so only one of them is
     ever on screen: the photographs while browsing everything, the chips once
     a filter is on and the entries have stepped aside. Mood chips always
     show, because the sidebar's mood list is hidden at this width. */
  body:not(.ubora-filtered) .ubora-shop-nav .ubora-filters__row[data-filter="gender"] { display: none; }
  .ubora-filters { gap: 0.55rem; }
  .ubora-filters__row { gap: 0.45rem; }
}
@media (max-width: 600px) {
  /* Too narrow for a label inside the frame: the plate drops out of the
     photograph and becomes a caption under it. */
  .ubora-centries { gap: 0.7rem; }
  .ubora-centry {
    background: transparent;
    box-shadow: none;
    border-radius: 0;
    overflow: visible;
  }
  .ubora-centry:hover { transform: none; box-shadow: none; }
  .ubora-centry__frame {
    border-radius: 10px;
    box-shadow: var(--wp--preset--shadow--lift);
  }
  .ubora-centry__plate {
    position: static;
    padding: 0.45rem 0 0;
    background: none;
  }
  .ubora-centry__label { font-size: 1rem; }
  .ubora-centry__meta { font-size: 0.72rem; letter-spacing: 0.02em; }
  .ubora-centry.is-active { outline: 0; }
  .ubora-centry.is-active .ubora-centry__frame { outline: 2px solid var(--wp--preset--color--clay); }
  .ubora-shop-facts { font-size: 0.78rem; }
}

/* ---------------------------------------------------------------------
   7. Reduced motion
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ubora-centry,
  .ubora-centry__frame img { transition: none; }
  .ubora-centry:hover { transform: none; }
  .ubora-centry:hover .ubora-centry__frame img { transform: none; }
}

/* ===== faq.css ===== */
/* ===========================================================================
   FAQ page (patterns/page-faq.php)

   Everything here is scoped to .ubora-faqhead / .ubora-faqbody / .ubora-faqclose
   and to the .ubora-faq--page modifier, so the shared accordion rules in
   extra.css keep behaving exactly as they do wherever else .ubora-faq is used.
   =========================================================================== */

/* --- Header band: F-01 photograph with the title plate lapping over it ----- */

.ubora-faqhead {
	position: relative;
	background-color: var(--wp--preset--color--sand);
	padding-bottom: clamp(1.5rem, 3vw, 2.5rem);
}

.ubora-faqhead__media {
	position: relative;
	overflow: hidden;
	line-height: 0;
}

.ubora-faqhead__media img {
	display: block;
	width: 100%;
	height: clamp(13rem, 24vw, 20rem);
	object-fit: cover;
}

/* A short fade to sand at the foot of the photograph, so the plate lands on it
   softly rather than cutting a hard line across the plaster. The first stop is
   sand at zero alpha, not a new colour. */
.ubora-faqhead__media::after {
	content: "";
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	height: 45%;
	background: linear-gradient(to bottom, rgba(220, 201, 178, 0), var(--wp--preset--color--sand));
	pointer-events: none;
}

.ubora-faqhead__plate {
	position: relative;
	z-index: 1;
	max-width: min(760px, calc(100% - 2.5rem));
	margin: 0 auto;
	padding: 2.2rem clamp(1.4rem, 4vw, 2.8rem) 2rem;
	background-color: var(--wp--preset--color--parchment);
	border: 1px solid var(--ubora-edge);
	border-radius: 12px;
	text-align: center;
}

/* Only lap upward when the photograph is actually there. If F-01 is not
   deployed the media div is never printed and the plate simply sits on sand. */
.ubora-faqhead__media + .ubora-faqhead__plate {
	margin-top: -3.5rem;
}

.ubora-faqhead__eyebrow {
	margin: 0 0 0.55rem;
	font-family: var(--wp--preset--font-family--outfit);
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--wp--preset--color--clay);
}

.ubora-faqhead__title {
	margin: 0;
	font-family: var(--wp--preset--font-family--gloock);
	font-size: clamp(2.1rem, 4.6vw, 3.1rem);
	line-height: 1.06;
	color: var(--wp--preset--color--black);
	text-wrap: balance;
}

.ubora-faqhead__sub {
	margin: 0.85rem auto 0;
	max-width: 44ch;
	font-family: var(--wp--preset--font-family--crimson);
	font-size: 1.06rem;
	line-height: 1.6;
	color: var(--wp--preset--color--steel);
	text-wrap: pretty;
}

/* --- The list itself: one oat plate on the sand ground -------------------- */

.ubora-faq--page {
	background-color: var(--wp--preset--color--oat);
	border: 1px solid var(--ubora-edge);
	border-radius: 12px;
	padding: 0.35rem clamp(1.15rem, 3.6vw, 2.25rem);
}

.ubora-faq--page .ubora-faq__item:last-child {
	border-bottom: 0;
}

.ubora-faq--page .ubora-faq__q {
	list-style: none;
	padding: 1.2rem 0;
	font-size: 1.05rem;
	line-height: 1.4;
	color: var(--wp--preset--color--black);
	transition: color var(--ubora-dur-fast) var(--ubora-ease);
}

/* Safari still paints its own disclosure triangle on a summary. */
.ubora-faq--page .ubora-faq__q::-webkit-details-marker {
	display: none;
}

.ubora-faq--page .ubora-faq__q:hover,
.ubora-faq--page .ubora-faq__item[open] > .ubora-faq__q {
	color: var(--wp--preset--color--clay);
}

.ubora-faq--page .ubora-faq__q:focus-visible {
	outline: 2px solid var(--wp--preset--color--clay);
	outline-offset: 5px;
	border-radius: 4px;
}

.ubora-faq--page .ubora-faq__a {
	padding: 0 0 1.35rem;
	max-width: 62ch;
	font-family: var(--wp--preset--font-family--crimson);
	font-size: 1.1rem;
	line-height: 1.72;
	color: var(--wp--preset--color--steel);
}

.ubora-faq--page .ubora-faq__a a {
	color: var(--wp--preset--color--clay);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.18em;
}

.ubora-faq--page .ubora-faq__item[open] .ubora-faq__a {
	animation: ubora-faq-reveal 300ms var(--ubora-ease-out) both;
}

@keyframes ubora-faq-reveal {
	from { opacity: 0; transform: translateY(-5px); }
	to   { opacity: 1; transform: none; }
}

/* --- Closing plate -------------------------------------------------------- */

.ubora-faqclose__title {
	font-family: var(--wp--preset--font-family--gloock);
	font-size: clamp(1.6rem, 3.2vw, 2.15rem);
	line-height: 1.15;
	margin-bottom: 0.2rem;
}

.ubora-faqclose__facts {
	margin-top: 1.4rem;
	font-family: var(--wp--preset--font-family--outfit);
	font-size: 0.8rem;
	letter-spacing: 0.03em;
	color: var(--wp--preset--color--silver);
}

.ubora-faqclose__contact {
	font-size: 0.95rem;
}

.ubora-faqclose__contact a {
	color: var(--wp--preset--color--clay);
	text-underline-offset: 0.18em;
}

/* --- Phone ---------------------------------------------------------------- */

@media (max-width: 781px) {
	.ubora-faqhead__media img {
		height: clamp(10.5rem, 38vw, 14rem);
	}

	.ubora-faqhead__media + .ubora-faqhead__plate {
		margin-top: -2.25rem;
	}

	.ubora-faqhead__plate {
		max-width: calc(100% - 1.6rem);
		padding: 1.6rem 1.25rem 1.4rem;
		border-radius: 10px;
	}

	.ubora-faqhead__title {
		font-size: clamp(1.9rem, 8vw, 2.4rem);
	}

	.ubora-faqhead__sub {
		font-size: 1rem;
	}

	.ubora-faq--page {
		padding: 0.2rem 1.05rem;
		border-radius: 10px;
	}

	.ubora-faq--page .ubora-faq__q {
		font-size: 1rem;
		padding: 1.05rem 0;
	}

	.ubora-faq--page .ubora-faq__a {
		font-size: 1.04rem;
		padding-bottom: 1.15rem;
	}

	.ubora-faqclose__facts {
		font-size: 0.76rem;
		line-height: 1.7;
	}
}

@media (prefers-reduced-motion: reduce) {
	.ubora-faq--page .ubora-faq__item[open] .ubora-faq__a {
		animation: none;
	}

	.ubora-faq--page .ubora-faq__q {
		transition: none;
	}
}

/* ===== global.css ===== */
/* =====================================================================
   UBORA rebrand - global chrome (the footer).

   The old footer was the tail of the dark art direction: a black slab with
   silver and steel type on it. Those two tokens are now dark warm greys
   (#514840, #574D44), so that footer was also failing contrast on every
   secondary line. It is now the same limewashed plaster the rest of the
   site sits in, with G-03 as the band that divides the page from it.

   Slot G-03 is DECORATIVE. The manifest ships it with an empty alt on
   purpose, so nothing here may place copy over it: the band is its own
   full-bleed strip above the footer content, and the footer ground below
   is solid. Contrast can never depend on where the plaster happens to be
   light or dark.

   This file is concatenated after extra.css, so every rule below is a
   deliberate override of a dark-footer default. Selectors stay scoped to
   .ubora-footer / .ubora-footer-band and touch nothing else.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. The plaster band - G-03
   2400x600 limewash, cropped to a shallow strip. Full-bleed is right here
   (it is a wall, not a photograph) and the plate is wide enough to hold up
   across a 2560 screen. The lower edge dissolves into the footer ground
   with a veil rather than a mask, so the join survives on any browser.
   --------------------------------------------------------------------- */
.ubora-footer-band {
  position: relative;
  overflow: hidden;
  line-height: 0; /* no descender gap under the inline image */
  background-color: var(--wp--preset--color--parchment);
  border-top: 1px solid var(--ubora-edge);
}

/* The shortcode block can hand back its output inside a paragraph. */
.ubora-footer-band p { margin: 0; line-height: 0; }

.ubora-footer-band img.ubora-shot {
  display: block;
  width: 100%;
  height: clamp(86px, 10vw, 150px);
  object-fit: cover;
  /* object-position is left alone on purpose: the resolver writes the art
     director's focal point inline when the slot carries one. */
}

.ubora-footer-band::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(242, 237, 227, 0) 38%,
    rgba(242, 237, 227, 0.72) 82%,
    rgba(242, 237, 227, 1) 100%
  );
}

/* ---------------------------------------------------------------------
   2. The footer ground
   Parchment, matching the header, so the page is bracketed by the same
   material top and bottom. position:relative keeps both blocks in the
   positioned layer, above the fixed day room.
   --------------------------------------------------------------------- */
.ubora-footer,
.ubora-footer-band {
  position: relative;
}

.ubora-footer__wordmark { margin-bottom: 0.1rem; }

.ubora-footer h4 { margin-bottom: 0.4rem; }

/* Links: quiet espresso in the lists, clay on hover. The global link colour
   is clay, which is correct on a page but far too loud for four stacked
   columns of navigation. */
.ubora-footer a {
  color: var(--wp--preset--color--espresso);
  text-decoration: none;
  transition: color var(--ubora-dur-fast, 140ms) ease;
}
.ubora-footer a:hover,
.ubora-footer a:focus-visible {
  color: var(--wp--preset--color--clay);
  text-decoration: underline;
  text-underline-offset: 0.22em;
}
.ubora-footer a:focus-visible,
.ubora-footer .ubora-newsletter__input:focus-visible,
.ubora-footer .ubora-newsletter__btn:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------
   3. The newsletter form on a light ground
   extra.css styles this form for the old black footer: transparent field,
   parchment text, steel hairline. On plaster that is an invisible input.
   The exit-intent modal already carries the same kind of override, so this
   follows that pattern rather than changing the base.
   --------------------------------------------------------------------- */
.ubora-footer .ubora-newsletter__label {
  color: var(--wp--preset--color--steel);
}
.ubora-footer .ubora-newsletter__input {
  background-color: var(--wp--preset--color--white);
  color: var(--wp--preset--color--black);
  border-color: var(--wp--preset--color--concrete);
}
.ubora-footer .ubora-newsletter__input::placeholder {
  color: var(--wp--preset--color--steel);
}
.ubora-footer .ubora-newsletter__btn:hover,
.ubora-footer .ubora-newsletter__btn:focus-visible {
  background-color: var(--wp--preset--color--clay-deep);
  color: var(--wp--preset--color--parchment);
}
.ubora-footer .ubora-newsletter__note {
  color: var(--wp--preset--color--clay-deep);
}

/* ---------------------------------------------------------------------
   4. Fine print
   The locked FTC block is rendered by a pattern this pass does not own.
   Its own layout attribute is nested under "style" and so never reaches
   the markup, which leaves the disclaimer running the full content width.
   Centred here instead of editing that file. Its silver type reads at
   7.4:1 on parchment, so the colour is left as it is.
   --------------------------------------------------------------------- */
.ubora-footer .ubora-ftc {
  max-width: 940px;
  margin-inline: auto;
}

/* ---------------------------------------------------------------------
   5. Phone
   Same markup as desktop, so everything phone-specific lives here. The
   band gets shorter: on a 390px screen a 150px wall is a whole thumb of
   scrolling before the first link.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  .ubora-footer-band img.ubora-shot {
    height: 68px;
  }
  .ubora-footer-band::after {
    background: linear-gradient(
      180deg,
      rgba(242, 237, 227, 0) 30%,
      rgba(242, 237, 227, 0.75) 80%,
      rgba(242, 237, 227, 1) 100%
    );
  }
  .ubora-footer .wp-block-columns {
    gap: var(--wp--preset--spacing--40, 1.5rem);
  }
  .ubora-footer .ubora-newsletter {
    max-width: none;
  }
}

/* The footer needed NO measure fix.
   Its .wp-block-columns is already alignwide at max-width 1240px, sitting on
   exactly the same x as every other section. The earlier reading of "footer is
   full bleed" was measuring the outer .ubora-footer wrapper, which is alignfull
   by design like every other band on the page. Adding a gutter here put 244px
   of padding on a container that was already correct and squeezed the columns
   until "contact@uborafr / agrances.com" wrapped mid-word. Left alone. */

/* The disclaimer is the one paragraph on the site with legal weight and it was
   set at 11.5px, CENTRED, 528 characters across 940px. Centred long-form text
   has no consistent left edge for the eye to return to, which is exactly the
   wrong setting for something anyone might actually need to read. Left ranged,
   a real measure, and a size that does not require leaning in. */
.ubora-footer .ubora-ftc,
.ubora-footer .ubora-ftc p {
  /* rem, not ch: 68ch measured at the disclaimer's own tiny font resolved to
     556px, which is not a reading measure, it is a column. */
  max-width: 46rem;
  margin-inline: auto;
  text-align: left;
  font-size: 0.8rem;
  line-height: 1.65;
}

/* ===== grid.css ===== */
/* =====================================================================
   grid.css — the product card and the founding-tester review card.

   Owned by the grid pass. Pairs with mu-plugins/ubora-pdp-modules.php:
   [ubora_shop_grid] and [ubora_home_reviews]. Nothing else in the theme
   is touched from here.

   Loads after extra.css (functions.php enqueues rebrand.css second), so
   equal-specificity rules here win on purpose. Where extra.css used a
   class+element selector (.ubora-pcard__img img, 0-1-1) a bare class
   would LOSE regardless of order, so those overrides carry a second
   class deliberately. Do not "simplify" them back down.
   ===================================================================== */

/* ---------------------------------------------------------------------
   0. Utility
   --------------------------------------------------------------------- */

/* Screen-reader-only text. The theme had no such helper and core's
   .screen-reader-text is not guaranteed to be enqueued on every view. */
.ubora-sr {
  position: absolute;
  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;
}

/* ---------------------------------------------------------------------
   1. The card action opens the PDP.

   The card used to end in an ajax add-to-cart button. Buying a fragrance
   from a grid means buying it without ever seeing the notes, the roller
   macro or the 30-day guarantee, which for a scent nobody can smell is a
   returns problem, not a conversion win. The CTA is now a span inside the
   card link: same visual weight, one target, one tab stop.
   --------------------------------------------------------------------- */
.ubora-pcard__cta {
  display: block;
  margin-top: 0.85rem;
  text-align: center;
  background: transparent;
  color: var(--wp--preset--color--black);
  border: 1px solid var(--wp--preset--color--black);
  border-radius: 6px;
  padding: 0.7rem 1rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.72rem;
  line-height: 1.2;
  transition: background var(--ubora-dur) var(--ubora-ease),
              color var(--ubora-dur) var(--ubora-ease),
              border-color var(--ubora-dur) var(--ubora-ease);
}
.ubora-pcard:hover .ubora-pcard__cta,
.ubora-pcard:focus-within .ubora-pcard__cta {
  background: var(--wp--preset--color--clay);
  color: var(--wp--preset--color--parchment);
  border-color: var(--wp--preset--color--clay);
}
/* Touch: hover never fires, so give the tap its own confirmation. */
.ubora-pcard__link:active .ubora-pcard__cta {
  background: var(--wp--preset--color--clay);
  color: var(--wp--preset--color--parchment);
  border-color: var(--wp--preset--color--clay);
}
/* The whole card is now one focus target, so it needs a visible ring. */
.ubora-pcard__link:focus-visible {
  outline: 2px solid var(--wp--preset--color--black);
  outline-offset: 4px;
  border-radius: 10px;
}

/* ---------------------------------------------------------------------
   2. Hover / focus swaps the card image to the environment shot.

   Shots 01, 02 and 05 are held identical across all nine SKUs, so a grid
   of hero frames reads as one bottle nine times (measured mean per-channel
   delta between SKUs: 5.6 on shot 01, 75.3 on shot 04). Shot 04 is the
   scent's own setting and until now only appeared at position four of the
   PDP gallery. The back image stacks above the front and fades in, so
   there is no luminance dip mid-transition.
   --------------------------------------------------------------------- */
.ubora-pcard__img--swap {
  position: relative;
}
.ubora-pcard__img--swap .ubora-pcard__shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Two classes on purpose: extra.css sets the transform transition at
   .ubora-pcard__img img (0-1-1), which outranks a bare class. */
.ubora-pcard__img .ubora-pcard__shot {
  transition: opacity var(--ubora-dur) var(--ubora-ease-out),
              transform var(--ubora-dur-slow) var(--ubora-ease-out);
}
.ubora-pcard__shot--back {
  opacity: 0;
}
/* Gated on a real hover pointer: on touch, :hover can latch after a tap
   and leave the wrong frame showing behind the page transition. */
@media (hover: hover) {
  .ubora-pcard:hover .ubora-pcard__shot--back {
    opacity: 1;
  }
}
.ubora-pcard:focus-within .ubora-pcard__shot--back {
  opacity: 1;
}

/* ---------------------------------------------------------------------
   3. The scent on the card: top notes only.

   One line, ellipsised rather than wrapped, so every card in a row stays
   the same height and the grid keeps its rhythm. The full pyramid (top /
   heart / base) stays on the PDP. The hairline reads as the card's own
   divider between who it is and what is in it.
   --------------------------------------------------------------------- */
.ubora-pcard__notes {
  display: flex;
  align-items: baseline;
  gap: 0.45em;
  min-width: 0;
  margin: 0.1rem 0 0.45rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--ubora-edge);
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.76rem;
  line-height: 1.45;
}
.ubora-pcard__notes-key {
  flex: 0 0 auto;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wp--preset--color--steel);
}
/* min-width:0 is what actually lets a flex child ellipsise. */
.ubora-pcard__notes-val {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--wp--preset--color--slate);
}

/* ---------------------------------------------------------------------
   4. Founding-tester review cards: the words lead, the stars follow.

   All ten are five stars, so the star row carries no information and a
   fifth identical row of gold at the top of a card reads as manufactured.
   The quote now opens the card and the rating sits in the caption at
   footnote size. No review text is edited, hidden or reordered within
   itself; only the card's internal order and the carousel order change.
   --------------------------------------------------------------------- */
.ubora-review--words-first {
  display: flex;
  flex-direction: column;
}
.ubora-review--words-first blockquote {
  flex: 1 1 auto;
  margin: 0 0 1rem;
  font-size: 1.12rem;
  line-height: 1.55;
  color: var(--wp--preset--color--slate);
}
.ubora-review--words-first figcaption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  padding-top: 0.7rem;
  border-top: 1px solid var(--ubora-edge);
}
.ubora-review--words-first .ubora-review__who {
  min-width: 0;
}
.ubora-review__rating {
  flex: 0 0 auto;
  line-height: 1;
}
/* Three classes: extra.css sets .ubora-review .ubora-rating__stars (0-2-0). */
.ubora-review--words-first .ubora-review__rating .ubora-rating__stars {
  font-size: 0.82rem;
  letter-spacing: 0.5px;
  opacity: 0.7;
}

/* The carousel label sits directly on the day canvas, where Clay measures
   only 3.2:1 against the frames. Anchor is the only text colour that
   passes over those photographs. */
.ubora-home-reviews__label {
  color: var(--wp--preset--color--black);
}

/* ---------------------------------------------------------------------
   5. Reduced motion. The image swap still happens, it just happens at
      once: the information is the point, the cross-fade is not.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ubora-pcard__img .ubora-pcard__shot {
    transition: none;
  }
  .ubora-pcard:hover .ubora-pcard__img img,
  .ubora-pcard:focus-within .ubora-pcard__img img {
    transform: none;
  }
  .ubora-pcard__cta {
    transition: none;
  }
}

/* ---------------------------------------------------------------------
   6. Phone. Media-query scoped, so desktop is untouched.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  .ubora-pcard__notes {
    font-size: 0.7rem;
    margin-bottom: 0.35rem;
    padding-top: 0.4rem;
  }
  .ubora-pcard__notes-key {
    font-size: 0.55rem;
    letter-spacing: 0.1em;
  }
  .ubora-pcard__cta {
    margin-top: 0.65rem;
    padding: 0.62rem 0.6rem;
    font-size: 0.66rem;
    letter-spacing: 0.08em;
  }
  .ubora-review--words-first blockquote {
    font-size: 1.02rem;
  }
}

/* "I am" belongs to the name under it. At 36px away it read as an orphaned
   label floating above the card rather than the opening of "I am Legendary". */
/* wpautop leaves an EMPTY <p> between the label and the name: zero height, but
   18px of margin top and bottom, which is the whole 38px gap. An
   adjacent-sibling rule on the label never matched because that empty
   paragraph sits between the two. "I am" and the name are one phrase and
   should sit like one. */
.ubora-pcard__link > p:empty,
.ubora-pcard > p:empty {
  display: none;
}
.ubora-pcard__iam {
  display: block;
  margin-bottom: 0.1rem;
}

/* ===== heroline.css ===== */
/* =====================================================================
   UBORA rebrand — the hero value line.

   Owns: patterns/hero.php only, and within it exactly two things that did
   not exist before: .ubora-hero-value (the price argument) and the small
   .ubora-hero-caption__more link at the end of the fine print.

   Nothing here restyles the eyebrow, the h1, the checklist, the rating
   line or the buttons. Those live in extra.css and are shared.

   Context that shapes every rule below: this column is .u-copy, which on
   the landscape day variants is capped at min(46ch, 46%) and sits over a
   photograph of a travertine ledge. So the line has to survive a narrow
   measure and it has to be Anchor ink. Measured against the real frames,
   Steel manages 2.3:1 and Clay 3.2:1, and the theme's own default body
   colour is Espresso, which is no better. No plate opacity rescues any of
   them, so this line names its colour instead of inheriting one.
   ===================================================================== */

/* ---------------------------------------------------------------------
   The line itself
   ---------------------------------------------------------------------
   A left rule rather than a box. A panel here would read as a second
   surface stacked inside .u-copy's own dusk plate, and two nested
   surfaces on a photograph look like a mistake. A 2px rule reads as
   "this is the argument" and costs no background.

   The rule is Clay at partial alpha. That is fine where Clay text is not:
   it carries no information, so the 3:1 non-text floor is the bar it has
   to clear, not 4.5:1. Anything a reader must actually read in this block
   is Anchor.
   --------------------------------------------------------------------- */

.ubora-hero-value {
  margin: 1.2rem 0 0;
  /* No rule down the left edge. A border-inline-start is the blockquote
     device, and it framed the page's primary price claim as something quoted
     from elsewhere rather than as the brand's own statement. The line earns its
     separation from space and from the serif figures inside it. */
  padding-inline-start: 0;
  border-inline-start: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: clamp(0.98rem, 1.05vw, 1.06rem);
  line-height: 1.5;
  letter-spacing: 0.005em;

  /* Anchor, stated rather than inherited. theme.json sets body text to
     Espresso (#4A3F35); over these frames that lands in the same failing
     band as Steel. The .u-copy remap in day.css only rewrites the preset
     variables an element already asks for, so a paragraph that names no
     colour at all slips past it. This one names Anchor directly and is
     therefore correct on the canvas and off it. */
  color: var(--wp--preset--color--black, #1c1a17);
}

/* ---------------------------------------------------------------------
   The two figures
   ---------------------------------------------------------------------
   The whole line is one sentence; the emphasis has to come from the face
   and the size, not from a second colour, because there is no second
   colour available over these frames that passes.

   Gloock is the display serif already used for headings, so lifting the
   numerals into it makes them read as figures without introducing a new
   voice. It ships as a single weight: <b> would otherwise ask the browser
   to synthesise a bold and the numerals would smear, hence the explicit
   font-weight.
   --------------------------------------------------------------------- */

.ubora-hero-value__fig {
  /* The figures sit INSIDE a running sentence, so they take the sentence's
     size. They were 1.14em and 1.42em in Gloock against an Outfit body, which
     put two faces and three sizes in one line and read as a webfont failing to
     load rather than as emphasis. The contrast now comes from the face and a
     tighter tracking, which is enough for a serif dropped into a grotesque. */
  font-family: var(--wp--preset--font-family--gloock);
  font-weight: 400;
  font-size: 1.02em;
  line-height: inherit;
  letter-spacing: 0.01em;
  color: inherit;

  /* "$130 to $500" is one figure and must never break across two lines.
     At a 46ch measure it otherwise wraps on the word "to" roughly half
     the time, and a range split over a line break reads as two prices. */
  white-space: nowrap;
}

/* Our own price is the payoff of the sentence, so it gets the larger
   step. Kept to 1.42em: much past that and it starts competing with the
   h1 two elements above it, which is not the job. */
/* Ours is the number that matters, so it gets weight and colour rather than a
   size jump: the size jump is what broke the line. */
.ubora-hero-value__fig--ours {
  font-size: 1.02em;
  color: var(--wp--preset--color--clay-deep, #6F4A2E);
}

/* ---------------------------------------------------------------------
   "See the comparison" in the fine print
   ---------------------------------------------------------------------
   The value line asserts a price range; this is the only above-the-fold
   route to the page that actually lays that range out side by side.
   Deliberately a quiet text link at the end of the caption and NOT a
   third button: the column already carries two CTAs and a tertiary
   target above them would just bleed clicks off the primary.

   Two classes deep on purpose. The <a> is excluded from day.css's Anchor
   remap (that exclusion is what keeps the Clay CTA buttons Clay), so this
   link would otherwise take the theme's global link colour over the
   photograph. The parent class also puts this above any element-level
   link rule theme.json emits.
   --------------------------------------------------------------------- */

.ubora-hero-caption .ubora-hero-caption__more {
  color: var(--wp--preset--color--black, #1c1a17);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  white-space: nowrap;
}

.ubora-hero-caption .ubora-hero-caption__more:hover,
.ubora-hero-caption .ubora-hero-caption__more:focus-visible {
  text-decoration-thickness: 2px;
}

/* ---------------------------------------------------------------------
   Phone
   ---------------------------------------------------------------------
   781px is the site's phone breakpoint. Below it the day engine drops to
   the portrait variant and .u-copy goes full width, so the line is no
   longer fighting a 46% column: it can lose the fluid upper bound and
   sit a touch tighter under the supporting line. Desktop is untouched by
   everything in this block.
   --------------------------------------------------------------------- */

@media (max-width: 781px) {
  .ubora-hero-value {
    margin-top: 1rem;
    font-size: 1rem;
    padding-inline-start: 0.8rem;
  }

  /* One step down so the payoff figure cannot push the sentence to a
     fourth line on a 360px screen. */
  .ubora-hero-value__fig--ours {
    font-size: 1.32em;
  }
}

/* ===== homepage.css ===== */
/* =====================================================================
   UBORA: the homepage day, beats 2 and 4.

   Owned by the homepage pass: templates/front-page.html only. Everything
   here is scoped to a beat class, so nothing in this file can reach the
   shop archive, the PDP or any pattern that another pass owns. The product
   card itself is still defined in extra.css (.ubora-pcard); this file only
   changes how a card is proportioned INSIDE the collection beat.

   Loads after extra.css (rebrand.css is enqueued with ubora-extra as a
   dependency), so equal-specificity rules here win on purpose.

   Two problems, both structural:

     ITEM 8  the collection beat cost 1.84 viewports where every other beat
             costs 1.0, and that is where the scroll dragged.
     ITEM 4  the proof beat had no exit at all.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Collection beat: the intro becomes a rail
   ---------------------------------------------------------------------
   Measured on the live stack at 1240px wide (wideSize), the beat was:

     intro plate       ~260px
     pills             ~74px
     two rows of 4:5   ~1492px   (398px columns, so 498px of photograph
                                  per card before a word of type)
     trailing line     ~46px
     padding 70/70     ~108px
     ------------------------------
     ~2063px, i.e. 1.84 screens on the 1121px viewport it was measured on.

   Shrinking the cards was not an option, so the stack was rebuilt as a
   layout instead. The intro leaves the vertical budget entirely (it sits
   beside the cards, where the rail has slack to spare), and the cards get
   a narrower column, which is where most of the height actually goes:

     pills + grid      ~1148px   (287px columns, square shot)
     rail              ~400px    (parallel, so it costs nothing)
     padding 60/60     ~72px
     ------------------------------
     ~1220px, i.e. ~1.09 screens. Six cards, nothing hidden, no copy cut.

   1180px is the floor for two columns: below it the cards column drops
   under about 250px per card and they stop being readable, which the
   brief rules out. Under 1180 the beat stacks exactly as it does today.
   --------------------------------------------------------------------- */
@media (min-width: 1180px) {
  /* The rail heading has to come down with the column.
     theme.json sets this h2 for a full-width measure, and at 54px inside a
     221px rail it wrapped to SIX lines of about eight characters, which is
     narrower than any readable measure and made the rail shout over the cards
     it is introducing. Scoped to the two-column layout only: below 1180px the
     rail is full width again and the display size is correct there. */
  .ubora-collection-rail h2 {
    /* Second pass. 2.15rem still set five lines of about thirteen characters in
       a 221px column, which is under any readable measure for a display face.
       The rail is an introduction, not the headline of the page. */
    font-size: clamp(1.35rem, 1.7vw, 1.6rem);
    line-height: 1.12;
    letter-spacing: -0.01em;
  }

  .ubora-collection-layout {
    display: grid;
    /* The rail holds a 46ch-ish measure at its widest and never eats the
       cards; minmax(0,1fr) stops a wide card row from blowing the track
       out (grid items default to min-content, and a product title is a
       long word). */
    grid-template-columns: clamp(228px, 24%, 300px) minmax(0, 1fr);
    gap: clamp(1.6rem, 2.6vw, 2.6rem);
    align-items: start;
  }

  /* Belt and braces on core's flow margins. The block already carries
     blockGap 0, but core prints its layout rules in a <style> whose order
     against an enqueued stylesheet is not something to bet a layout on.
     (0,2,0) clears the generated .wp-container-* rules either way. */
  .ubora-collection-layout > .wp-block-group {
    margin-block-start: 0;
    margin-block-end: 0;
  }

  /* A grid item is min-content wide by default; the card grid must be
     allowed to be narrower than its widest card. */
  .ubora-collection-cards {
    min-width: 0;
  }

  /* Centred type is right when it sits over the middle of the frame. In a
     left-hand rail it reads as a ragged column, so the rail aligns left.
     :is() takes the specificity of its most specific argument, so this is
     (0,1,1) and clears core's .has-text-align-center at (0,1,0) without
     touching the markup, which the phone still wants centred. */
  .ubora-collection-rail :is(h2, h3, h4, p) {
    text-align: left;
  }

  /* The plate is bounded and centred for a full-width band; in a rail it
     should simply be the rail. */
  :root .wp-block-group.u-plate.ubora-collection-rail {
    max-width: none;
    margin-inline: 0;
  }
}

/* ---------------------------------------------------------------------
   2. Collection beat: card density
   ---------------------------------------------------------------------
   The card shot is a 4:5 frame (1200x1500). In this beat only, it is
   cropped square, which takes 20% off the tallest element on the page and
   is the single biggest saving available without shrinking the card.

   Safe against the delivered set: the bottle occupies roughly 20%-82% of
   the frame height, and a centred cover crop of 4:5 into 1:1 keeps the
   middle 80% (10%-90%). Cap and base both stay in shot; what goes is
   plaster above and ledge foreground below. Anything flatter than 1:1
   starts cutting the cap, so 1:1 is the floor, not a starting point.

   Scoped to .ubora-beat--collection: the shop archive, the order
   confirmation grid and the bestsellers pattern keep the 4:5 card.
   --------------------------------------------------------------------- */
.ubora-beat--collection .ubora-pcard__img--shot {
  aspect-ratio: 1 / 1;
}

/* The grid's own 1.6rem top margin doubled up with the pills' 1.6rem
   bottom margin for 51px of nothing between the filter and the thing it
   filters. */
.ubora-beat--collection .ubora-shop-grid {
  margin: 0.9rem 0 0;
}
.ubora-beat--collection .ubora-home-pills {
  margin-bottom: 1rem;
}

/* ---------------------------------------------------------------------
   3. The rail's exit
   ---------------------------------------------------------------------
   The All nine pill reveals the other three in place, so this link is not
   a substitute for them: it is the route to the page where the nine can
   be filtered by gender, energy and family, and sorted. Deliberately a
   text link and not a button, because the cards beside it are the beat's
   real call to action and a second button would compete with them.

   Colour: this sits inside .u-plate--solid, a fixed 0.92 panel, so Clay
   Deep is on parchment here rather than on the photograph and holds well
   past AA. day.css remaps clay/steel/concrete/silver/slate/espresso to
   Anchor inside a plate but deliberately leaves links their accent, and
   clay-deep is not in that remap list, so this is the intended colour and
   not an accident of the cascade.
   --------------------------------------------------------------------- */
.ubora-collection-link {
  margin-top: 0.9rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.84rem;
  letter-spacing: 0.04em;
}
.ubora-collection-link a {
  color: var(--wp--preset--color--clay-deep);
  text-underline-offset: 3px;
}
.ubora-collection-link a:hover,
.ubora-collection-link a:focus-visible {
  color: var(--wp--preset--color--black);
}

/* The trio line moved inside the plate, where it is on a panel instead of
   on the photograph. It is the offer, so it carries a little more weight
   than the fine print above it. */
.ubora-collection-rail .ubora-trio-line {
  margin-top: 0.7rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   4. Proof beat: the one exit
   ---------------------------------------------------------------------
   The button is a solid clay fill with parchment type, so its contrast is
   its own and does not depend on the plate or the hour behind it. Only
   the spacing is set here; the button treatment itself is theme.json's.
   --------------------------------------------------------------------- */
.ubora-proof-cta {
  justify-content: center;
}

/* ---------------------------------------------------------------------
   5. Phone (<=781px)
   ---------------------------------------------------------------------
   The two-column rail never runs here, so the beat stacks as it always
   has: plate, pills, one card per row. The square crop from section 2 is
   deliberately NOT undone on phone - a full-bleed 4:5 card shot is the
   single tallest thing on the small-screen homepage, and squaring it
   takes roughly a fifth off each of six cards.

   Nothing in this block may change a desktop rule.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  .ubora-collection-link {
    margin-top: 0.7rem;
  }
  /* Full-width tap target for the proof exit, in line with the rest of
     the phone CTAs. */
  .ubora-proof-cta .wp-block-button,
  .ubora-proof-cta .wp-block-button__link {
    width: 100%;
  }
}

/* ---------------------------------------------------------------------
   The close beat: let the foreground win
   ---------------------------------------------------------------------
   The close sits over the nine-bottle lineup, and the lineup renders those
   bottles at roughly the same size as the trio photographs in front of it. The
   same object at two scales, fighting: background bottles, photographed
   bottles, and an offer card cutting through both. It read as clutter rather
   than as the last argument on the page.

   The fix is not to move anything, it is to push the room back. A warm veil
   scoped to this beat only drops the lineup to texture so the photographs and
   the offer card read as the subject. The lineup is still legible behind it,
   which is the point of putting it here.

   Painted on the beat itself rather than the shared canvas so no other beat
   dims: the canvas is one fixed element behind the whole page. */
:root[data-day-variant] .ubora-beat--close {
  position: relative;
  isolation: isolate;
}
:root[data-day-variant] .ubora-beat--close::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: rgb(var(--u-ground, 205 175 146) / 0.52);
}

/* ---------------------------------------------------------------------
   Below the day
   ---------------------------------------------------------------------
   The subscribe band sits AFTER the last beat, so it is explicitly not part of
   the arc, but it was still floating on the fixed canvas: at 19:30 that is a
   near-dark room, so a sparse two-column block with Steel text read as a
   0.94-screen void and broke the contrast rule at the same time.

   Giving it its own ground does both jobs. It stops being "in the room", it
   lands the page on a real surface before the footer, and it lets the copy use
   Steel legitimately again. */
.ubora-after-day {
  position: relative;
  isolation: isolate;
  background: var(--wp--preset--color--parchment, #F2EDE3);
  padding-block: clamp(2.5rem, 5vw, 4rem);
}

/* ===== nav.css ===== */
/* =====================================================================
   UBORA rebrand - navigation (header bar, Shop mega-menu, mobile drawer).

   Item 9. The old nav was seven items, three of which (For Him / For Her /
   Unisex) were gender filters doing a worse job than the two-question
   finder, while /find-your-scent/ and /why-roll-on-oil/ appeared nowhere.
   parts/header.html now ships five: Shop, Find Your Scent, 3 for $88,
   Why Oil, Our Story. The gender filters moved into the Shop mega-menu and
   the mobile drawer's chip rows; Subscribe & Save (a waitlist for something
   that is not selling yet) moved under Offers.

   This file is concatenated AFTER extra.css, so the rules below deliberately
   override pre-rebrand header defaults. Everything is scoped to .ubora-nav,
   .ubora-mega or .ubora-menu and touches nothing else. Nothing here sits over
   the day canvas, so no hour tokens and no Anchor-contrast concerns: this
   chrome is dark type on parchment throughout.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Narrow desktop (782-1023px): UNDO the old collapse.

   extra.css hides nav items 2, 3 and 4 in this range on the assumption that
   they are the three gender shortcuts and one hover away inside the
   mega-menu. Both halves of that assumption are now wrong: items 2-4 are
   Find Your Scent / 3 for $88 / Why Oil, and the mega-menu is display:none
   below 1024px (menu.js also refuses to bind it), so hiding them would leave
   a tablet with Shop and Our Story and no route to either new page.

   The row still has to fit, so instead of dropping items we tighten the gap
   and the tracking. Selectors carry one extra class over extra.css's (0,3,0)
   so the restore wins on specificity, not just on load order.
   --------------------------------------------------------------------- */
@media (min-width: 782px) and (max-width: 1023px) {
  .ubora-header .ubora-header__bar .ubora-nav .wp-block-navigation-item:nth-child(2),
  .ubora-header .ubora-header__bar .ubora-nav .wp-block-navigation-item:nth-child(3),
  .ubora-header .ubora-header__bar .ubora-nav .wp-block-navigation-item:nth-child(4) {
    display: flex;
  }

  /* The flex gap lives on the generated layout class on the <ul> (0,1,0), so
     a two-class selector takes it without !important. Both elements are
     targeted because which one carries the layout class depends on the core
     navigation markup of the day. */
  .ubora-header .ubora-nav,
  .ubora-header .ubora-nav .wp-block-navigation__container {
    column-gap: 1.15rem;
    row-gap: 0.4rem;
  }

  /* font-size and letter-spacing are inline on the <nav> from the block's
     typography attributes, so they can only be beaten on a descendant that
     merely inherits them. The link element is exactly that. */
  .ubora-header .ubora-nav .wp-block-navigation-item__content {
    font-size: 0.68rem;
    letter-spacing: 0.1em;
  }

  .ubora-header .ubora-header__bar {
    gap: 1.25rem;
  }
}

/* ---------------------------------------------------------------------
   2. The offer item. "3 for $88" is a price, not a category, so it gets the
   clay-deep accent to read as one. clay-deep (#6F4A2E) on the parchment
   header measures ~6.4:1, comfortably past AA, and the existing hover
   underline in extra.css already draws in clay.
   --------------------------------------------------------------------- */
.ubora-nav .ubora-nav__offer .wp-block-navigation-item__content,
.ubora-nav .wp-block-navigation-item__content[href$="/bundles/"] {
  color: var(--wp--preset--color--clay-deep);
}
/* The href selector is the belt to the className's braces: core/navigation-link
   renders custom classes through get_block_wrapper_attributes today, but the
   attribute match holds either way and costs nothing. */

/* ---------------------------------------------------------------------
   3. Mega-menu additions.
   --------------------------------------------------------------------- */

/* The finder is the reason the gender links could be demoted, so it leads the
   Collections column as a two-line feature rather than another flat link. */
.ubora-mega__col a.ubora-mega__feature {
  display: block;
  /* Capped so the two-line subtitle cannot widen the Collections column and
     squeeze the four-across product grid on the right. */
  max-width: 15rem;
  padding: 0.55rem 0.7rem;
  margin: 0 0 0.55rem;
  border: 1px solid var(--wp--preset--color--concrete);
  border-radius: 10px;
  background: var(--wp--preset--color--oat);
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--wp--preset--color--clay-deep);
  transition: background-color 0.22s ease, border-color 0.22s ease;
}
.ubora-mega__col a.ubora-mega__feature:hover,
.ubora-mega__col a.ubora-mega__feature:focus-visible {
  background: var(--wp--preset--color--sand);
  border-color: var(--wp--preset--color--clay);
  color: var(--wp--preset--color--clay-deep);
}
.ubora-mega__col a.ubora-mega__feature span {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.76rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--wp--preset--color--steel);
}

/* Subscribe & Save is not buyable yet. Label it honestly in the menu so the
   click is a choice rather than a surprise. */
.ubora-mega__col a.ubora-mega__soon {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
}
.ubora-mega__col a.ubora-mega__soon span {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--wp--preset--color--clay);
  border: 1px solid var(--wp--preset--color--concrete);
  border-radius: 999px;
  padding: 0.1rem 0.42rem;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
   4. Mobile drawer additions. Phone-only by construction: .ubora-menu is only
   reachable from the hamburger, which extra.css shows below 782px. No rule
   here can reach the desktop layout.
   --------------------------------------------------------------------- */

/* Group headings above the two chip rows. .ubora-menu__nav is a flex column
   whose <a> children carry the divider borders; these <p> labels sit between
   them and must not inherit that treatment. */
.ubora-menu__nav .ubora-menu__label {
  margin: 0.9rem 0 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--clay);
}

/* Same accent as the desktop offer item. */
.ubora-menu__nav a.ubora-menu__offer {
  color: var(--wp--preset--color--clay-deep);
}

/* The waitlist link sits below the primary rows, quieter than them, above the
   account/cart utility line. */
.ubora-menu__soon {
  margin: 0;
}
.ubora-menu__soon a {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-decoration: none;
  color: var(--wp--preset--color--steel);
}
.ubora-menu__soon a span {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--wp--preset--color--clay);
  border: 1px solid var(--ubora-edge, var(--wp--preset--color--concrete));
  border-radius: 999px;
  padding: 0.1rem 0.42rem;
}

/* Chip rows already carry a bottom rule from extra.css. The second one (who
   it is for) sits directly under the first, so drop the doubled divider that
   the mood row leaves behind it and keep the labels doing the separating. */
.ubora-menu__nav .ubora-menu__label + .ubora-menu__moods {
  padding-top: 0.45rem;
}

/* ---------------------------------------------------------------------
   The announcement rail
   ---------------------------------------------------------------------
   It was pure Anchor (#1C1A17) full bleed: the only hard-black element on a
   site built entirely from warm plaster and travertine, so it read as a
   third-party bar bolted above the design rather than part of it. Clay Deep
   keeps the contrast a top bar needs while belonging to the palette. */
.ubora-announce,
.wp-block-group.ubora-announce {
  background-color: var(--wp--preset--color--clay-deep, #6F4A2E);
}

/* ===== pdpday.css ===== */
/* =====================================================================
   UBORA — the held hour on a product page.
   ---------------------------------------------------------------------
   The homepage scrubs 24 frames of the ledge across the scroll. A product
   page gets ONE of those frames, at the hour that matches that scent's
   _ubora_energy, and it never moves. inc/day.php prints it at wp_body_open:

     <div class="ubora-held" style="--u-held-ground:222 201 173">
       <picture>…<img class="ubora-held__frame"></picture>
     </div>

   There is no JavaScript on this path at all: no engine module, no canvas,
   no scroll listener, no requestAnimationFrame, no frame prefetching. The
   browser resolves one <picture> and that is the whole feature.

   =====================================================================
   WHY THIS IS RESTRAINED RATHER THAN DRAMATIC
   =====================================================================

   The obvious version of this idea is the homepage treatment: the room at
   full strength with the content floating over it on plates. That is the
   wrong answer on a PDP, and the reason is measurable rather than a matter
   of taste.

   In every landscape cut of the ledge the product mass sits right of about
   58% of the frame. On the PDP the buy column is the right 48% of the page.
   So the busiest, highest-variance region of the photograph lands exactly
   under the price, the quantity field, the add-to-cart button and the fine
   print, which are the four things on this page that must not lose a single
   point of contrast. Plating the buy column would fix that, but a plate is a
   visible redesign of an area other work is already tuning, and the brief
   for this slice is atmosphere, not a new above-the-fold.

   So the frame is veiled hard, and the alpha is SOLVED, not eyeballed.

   Every held frame (6 hours x 3 orientation cuts) was downsampled to about
   40 patches across — roughly the width of a few words of body copy at
   full-bleed — and the 2nd-percentile patch taken as the realistic worst
   case a line of type could land on. Composited under the veil below, at
   the most transparent end of its gradient:

                       on this veil     on today's flat Sand
     Espresso #4A3F35     6.01:1              6.35:1
     Steel    #574D44     4.84:1              5.11:1
     Anchor   #1C1A17    10.20:1             10.78:1
     Clay     #8B5E3C     3.28:1              3.46:1
     ClayDeep #6F4A2E     4.58:1              4.84:1

   Everything that passes AA today still passes AA, with headroom. The one
   ink that drops below its old figure into genuinely risky territory is
   Clay, which was ALREADY under AA on flat Sand before any of this existed;
   the single Clay ink in single-product.html has been moved to Clay Deep,
   which takes that heading from 3.46:1 (fail) to 4.58:1 (pass). Anything
   else on the page that inks small type in Clay was failing before this
   change and is not this slice to fix.

   =====================================================================
   WHY THE VEIL IS THIS COLOUR
   =====================================================================

   rgb(228 214 196) is Sand and Parchment mixed 62/38 and resolved here at
   author time, so there is no color-mix() dependency and no new colour in
   the palette.

   Veiling with pure Parchment was tried first and rejected. It reads better
   on paper — it lightens the ground, so every contrast ratio improves — but
   it lifts the PDP's ground to roughly #E8E4DC while /shop/ and the homepage
   stay on Sand #DCC9B2, and it flattens the two Oat #F8F5EE bands on this
   page down to almost nothing. A product page that is visibly a different
   colour from the rest of the site reads as a bug, not as a decision.

   With this mix at these alphas the resulting ground lands, per hour, at:

     fresh  07:32  rgb(223 211 194)   cool and light, first light on the ledge
     woody  11:26  rgb(226 210 189)   the brightest hour, sun on stone
     bold   18:51  rgb(218 203 184)   deepest and warmest, the room after sun

   Sand itself is rgb(220 201 178). So the page ground hovers around the
   brand ground and breathes with the hour rather than replacing it, and the
   Oat bands keep their full separation.

   The effect is deliberately close to the threshold of perception: about ten
   levels of drift between the earliest and latest hour, plus whatever
   spatial structure survives at 9% — the fall of the window light and the
   soft line of the ledge. That is the honest ceiling for a photograph behind
   this much small type. Anything stronger needs plates, and plates are a
   different brief.

   Nothing here animates, so prefers-reduced-motion has nothing to switch
   off. prefers-contrast and forced-colors do, and they switch it off
   completely.
   ===================================================================== */

.ubora-held {
	position: fixed;
	inset: 0;
	/* Beneath everything in flow, above the canvas that <body>'s Sand
	   background paints. Negative z-index, rather than the homepage's
	   z-index:0 plus lifted content, on purpose: on a PDP the room is
	   GROUND, not a room the page floats in. Nothing else on the page needs
	   a stacking rule, and no existing element changes its paint order. */
	z-index: -1;
	overflow: hidden;
	pointer-events: none;
	/* Visible only for the instant before the frame decodes. inc/day.php
	   writes the hour's own ground here, taken from the same CSS_GROUND ramp
	   in assets/js/day/sun.js that the engine writes on the homepage, so
	   there is no flash of the wrong daylight. */
	background-color: rgb(var(--u-held-ground, 225 212 191));
	contain: strict;
}

/* The <picture> is a wrapper with no box of its own to contribute. */
.ubora-held picture {
	display: contents;
}

/* The frame, blurred.

   The blur is the most important line in this file and it was found by
   looking, not by reasoning. Unblurred, at exactly these veil alphas, the
   ledge shot is legible enough that the BOTTLE STANDING IN IT reads as a
   second, ghosted product floating behind the buy column, a few hundred
   pixels from the real one in the gallery. On the morning frames the label
   is faintly readable. That does not look like atmosphere, it looks like a
   rendering bug.

   Blurring destroys exactly the wrong half of the photograph and keeps
   exactly the right half. Gone: the bottle as an object, the label, the edge
   of the ledge, the leaf shadow. Kept: the hour's colour, the direction the
   window light falls, the soft warm pool where the sun lands. That residue is
   the whole point of the feature.

   It is also free contrast headroom. A blur is a local average, so it pulls
   the darkest patches up toward the mean, which is the exact direction the
   contrast table above needs. The measured numbers were taken on frames
   downsampled to roughly this radius, so they hold with the blur and are
   conservative without it.

   Sized in vw so the radius is proportional to the frame on screen rather
   than being heavy on a phone and weak on a monitor.

   scale() is not decoration: a blurred image feathers to transparent at its
   own edges, which would show as a pale border inside the fixed layer. The
   overscale pushes those edges outside the parent's overflow clip. */
.ubora-held__frame {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* The manifest's own focal point for the ledge, so the composition sits
	   where the engine puts it rather than drifting to frame centre. */
	object-position: 68% 60%;
	filter: blur(clamp(14px, 1.8vw, 30px));
	transform: scale(1.08);
}

/* The veil.

   Weighted so the thinnest part sits over the upper left of the viewport,
   which is the gallery column on desktop and therefore the half of the page
   carrying the least small type, and thickens toward the right and the
   bottom where the buy column and the long-form copy live.

   0.91 is the floor everywhere, because a gradient is only as safe as its
   most transparent stop and the contrast table above is computed at it. */
.ubora-held::after {
	content: "";
	position: absolute;
	inset: 0;
	background: radial-gradient(
		132% 104% at 22% 28%,
		rgb(228 214 196 / 0.91) 0%,
		rgb(228 214 196 / 0.94) 52%,
		rgb(228 214 196 / 0.96) 100%
	);
}

/* Belt and braces, and a note for whoever hits this next.

   The frame is visible at all only because <body>'s background propagates to
   the canvas and paints beneath a z-index:-1 child. If the ground ever stops
   showing through, the cause will be a background that has landed on the
   block wrapper instead of on <body>. Saying so explicitly costs nothing,
   since the wrapper is transparent by default. */
body.ubora-has-held-day .wp-site-blocks {
	background-color: transparent;
}

/* ---------------------------------------------------------------------
   Phones
   ---------------------------------------------------------------------
   The PDP collapses to a single column below 781px, so body copy runs the
   full width and a left-weighted gradient no longer maps to anything. The
   veil goes flatter and a little heavier and the frame becomes a warm cast
   rather than a picture.

   The portrait cut is also the one most likely to be paid for by the
   megabyte. inc/day.php serves the 720px file here rather than the 1080:
   about 60KB, lazily, at low priority, blurred, behind a veil that keeps 93%
   of the ground. At that radius the two files are the same image.
   --------------------------------------------------------------------- */

@media (max-width: 781px) {
	.ubora-held::after {
		background: radial-gradient(
			150% 100% at 50% 22%,
			rgb(228 214 196 / 0.93) 0%,
			rgb(228 214 196 / 0.95) 60%,
			rgb(228 214 196 / 0.96) 100%
		);
	}
}

/* ---------------------------------------------------------------------
   Opt outs
   --------------------------------------------------------------------- */

/* Asked for more contrast: drop the photograph entirely. What is left is a
   flat ground one shade lighter than Sand, which is strictly better for
   every ink on the page than the page's own default. */
@media (prefers-contrast: more) {
	.ubora-held__frame {
		display: none;
	}

	.ubora-held::after {
		background: rgb(228 214 196);
	}
}

@media (forced-colors: active) {
	.ubora-held {
		display: none;
	}
}

@media print {
	.ubora-held {
		display: none;
	}
}

/* ===== quiz.css ===== */
/* =====================================================================
   UBORA rebrand — the scent finder (patterns/scent-finder.php)

   Two layouts in one stylesheet:
     default        every panel stacked and readable (no JS, or JS failed)
     .is-enhanced   set by assets/js/scent-finder.js once it has taken over,
                    which turns the same panels into a one-at-a-time stepper

   Nothing here hides content unless the script that can show it again has
   already run.
   ===================================================================== */

/* ---------------------------------------------------------------------
   Entry hero — Q-01 (plaster left, three bottles right)
   The photograph leaves its whole left third as empty limewash, so the
   headline sits there in anchor ink with a parchment scrim behind it
   rather than a dark veil over the room.
   --------------------------------------------------------------------- */
.ubora-qhero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--wp--preset--color--parchment);
  min-height: clamp(420px, 45vw, 620px);
  display: flex;
  align-items: center;
}

.ubora-qhero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.ubora-qhero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  display: block;
}

.ubora-qhero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* The stops are anchored to the SAME centring math as the copy, not to a
     percentage of the viewport. The copy is capped at 36rem inside a 1240px
     centred container, so a percentage-based veil drifts out of register in
     both directions: below about 1030px the copy runs past the 66% stop and
     the end of the headline sits on bare photograph, and above 1240px the
     container starts indenting while the veil stays pinned to the left edge. */
  --q-lead: max(0px, calc((100% - 1240px) / 2));
  background: linear-gradient(
    90deg,
    rgba(242, 237, 227, 0.94) 0,
    rgba(242, 237, 227, 0.90) calc(var(--q-lead) + 40rem),
    rgba(242, 237, 227, 0.45) calc(var(--q-lead) + 50rem),
    rgba(242, 237, 227, 0) calc(var(--q-lead) + 62rem)
  );
}

.ubora-qhero__copy {
  position: relative;
  z-index: 1;
  width: min(100%, 1240px);
  margin-inline: auto;
  padding-block: clamp(3rem, 6vw, 5rem);
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
}

/* The copy column stops well short of the bottles, which start around 58% of
   the frame. 36rem is the widest it can run before the headline overlaps them
   on a 1440 screen. */
.ubora-qhero__copy > * {
  max-width: 36rem;
}

.ubora-qhero__fine {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.84rem;
  letter-spacing: 0.03em;
  margin-top: 1.1rem;
}

/* ---------------------------------------------------------------------
   The finder itself
   --------------------------------------------------------------------- */
.ubora-sf-band {
  background: var(--wp--preset--color--parchment);
}

.ubora-sf {
  --ubora-sf-line: rgba(28, 26, 23, 0.1);
  display: block;
  scroll-margin-top: 5rem;
}

/* Progress rail: meaningless while every panel is on screen at once, so it
   only appears once the stepper is live. */
.ubora-sf__rail {
  display: none;
}

.ubora-sf.is-enhanced .ubora-sf__rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.6rem;
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--wp--preset--color--silver);
}

.ubora-sf__rail-step {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.ubora-sf__rail-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.55rem;
  height: 1.55rem;
  border-radius: 999px;
  border: 1px solid var(--wp--preset--color--concrete);
  font-size: 0.72rem;
  letter-spacing: 0;
  line-height: 1;
}

.ubora-sf__rail-step.is-current {
  color: var(--wp--preset--color--black);
}

.ubora-sf__rail-step.is-current .ubora-sf__rail-num {
  background: var(--wp--preset--color--black);
  border-color: var(--wp--preset--color--black);
  color: var(--wp--preset--color--parchment);
}

.ubora-sf__rail-step.is-done {
  color: var(--wp--preset--color--clay-deep);
}

.ubora-sf__rail-step.is-done .ubora-sf__rail-num {
  border-color: var(--wp--preset--color--clay);
  color: var(--wp--preset--color--clay-deep);
}

/* Panels. Stacked by default; one at a time once the script is in charge. */
.ubora-sf__panel {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  align-items: stretch;
  overflow: hidden;
  border: 1px solid var(--ubora-sf-line);
  border-radius: 14px;
  background: var(--wp--preset--color--oat);
  box-shadow: var(--wp--preset--shadow--lift);
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.ubora-sf__panel:last-child {
  margin-bottom: 0;
}

.ubora-sf.is-enhanced .ubora-sf__panel {
  display: none;
  margin-bottom: 0;
}

.ubora-sf.is-enhanced .ubora-sf__panel.is-current {
  display: grid;
}

.ubora-sf__plate {
  position: relative;
  min-height: 15rem;
  background: var(--wp--preset--color--sand);
}

.ubora-sf__plate img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ubora-sf__body {
  padding: clamp(1.6rem, 3.4vw, 2.9rem);
}

/* The result screen's evening light (Q-02c), set as a custom property on the
   panel by the pattern. The scrim sits at 0.86 so the palm shadow reads as a
   warm diagonal while the plate stays within a few percent of oat, which
   leaves every text contrast ratio on this panel where it was. */
.ubora-sf__panel.has-evening .ubora-sf__body {
  background-image:
    linear-gradient(rgba(248, 245, 238, 0.86), rgba(248, 245, 238, 0.92)),
    var(--ubora-sf-evening);
  background-size: cover;
  background-position: 50% 35%;
  background-repeat: no-repeat;
}

.ubora-sf__step {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--wp--preset--color--clay-deep);
  margin: 0 0 0.5rem;
}

.ubora-sf__q {
  font-family: var(--wp--preset--font-family--gloock);
  font-weight: 400;
  font-size: clamp(1.75rem, 3vw, 2.35rem);
  line-height: 1.12;
  letter-spacing: -0.012em;
  color: var(--wp--preset--color--black);
  margin: 0;
}

/* The script moves focus here on every step so the new question is announced.
   That makes the heading a visible focus target too, so it keeps a real ring,
   softened with an offset and a radius so it reads as a frame around the
   question rather than a box drawn on it. */
.ubora-sf__q:focus {
  outline: none;
}

.ubora-sf__q:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay);
  outline-offset: 8px;
  border-radius: 6px;
}

.ubora-sf__note {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--wp--preset--color--steel);
  margin: 0.7rem 0 1.4rem;
  max-width: 34rem;
}

/* Option list — real links, styled as cards. */
.ubora-sf__opts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.ubora-sf__opt {
  display: grid;
  gap: 0.16rem;
  text-decoration: none;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--wp--preset--color--concrete);
  border-radius: 10px;
  background: var(--wp--preset--color--white);
  transition:
    border-color var(--ubora-dur, 280ms) var(--ubora-ease, ease),
    box-shadow var(--ubora-dur, 280ms) var(--ubora-ease, ease),
    transform var(--ubora-dur-fast, 140ms) var(--ubora-ease, ease);
}

.ubora-sf__opt:hover {
  border-color: var(--wp--preset--color--clay);
  box-shadow: var(--wp--preset--shadow--lift);
  transform: translateY(-1px);
  text-decoration: none;
}

.ubora-sf__opt:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay-deep);
  outline-offset: 2px;
  border-color: var(--wp--preset--color--clay);
  text-decoration: none;
}

.ubora-sf__opt:active {
  transform: translateY(0);
}

.ubora-sf__opt-label {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--wp--preset--color--black);
}

.ubora-sf__opt-sub {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--wp--preset--color--steel);
}

.ubora-sf__opt-hint {
  font-family: var(--wp--preset--font-family--crimson);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--wp--preset--color--clay-deep);
  margin-top: 0.1rem;
}

.ubora-sf__opt--quiet {
  background: transparent;
  border-style: dashed;
}

.ubora-sf__opt--quiet .ubora-sf__opt-label {
  color: var(--wp--preset--color--steel);
  font-weight: 500;
}

/* Question 2 is grouped by who the visitor picked. With the stepper live only
   one group is on screen, and its title doubles as a reminder of answer 1. */
.ubora-sf__set + .ubora-sf__set {
  margin-top: 1.7rem;
}

.ubora-sf__set[hidden] {
  display: none;
}

.ubora-sf__set-title {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--wp--preset--color--silver);
  margin: 0 0 0.65rem;
}

/* Result panel */
.ubora-sf__names {
  font-family: var(--wp--preset--font-family--gloock);
  font-size: clamp(1.15rem, 2vw, 1.45rem);
  line-height: 1.3;
  color: var(--wp--preset--color--clay-deep);
  margin: 0.75rem 0 0;
}

.ubora-sf__cta {
  margin: 1.5rem 0 0;
}

.ubora-sf__go {
  display: inline-block;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--wp--preset--color--parchment);
  background: var(--wp--preset--color--clay);
  border-radius: 4px;
  padding: 1rem 2.1rem;
  transition: background-color var(--ubora-dur, 280ms) var(--ubora-ease, ease);
}

.ubora-sf__go:hover,
.ubora-sf__go:focus-visible {
  background: var(--wp--preset--color--black);
  color: var(--wp--preset--color--parchment);
  text-decoration: none;
}

.ubora-sf__go:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay-deep);
  outline-offset: 3px;
}

.ubora-sf__alt {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.88rem;
  color: var(--wp--preset--color--steel);
  margin: 1rem 0 0;
}

.ubora-sf__alt a {
  color: var(--wp--preset--color--clay-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ubora-sf__alt a:hover {
  color: var(--wp--preset--color--black);
}

.ubora-sf__alt-sep {
  margin: 0 0.45rem;
  color: var(--wp--preset--color--concrete);
}

/* Back / start over do nothing without the script, so they only exist with it. */
.ubora-sf__back {
  display: none;
}

.ubora-sf.is-enhanced .ubora-sf__back {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 1.5rem;
  padding: 0.35rem 0;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  color: var(--wp--preset--color--steel);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ubora-sf.is-enhanced .ubora-sf__back:hover {
  color: var(--wp--preset--color--black);
}

.ubora-sf.is-enhanced .ubora-sf__back:focus-visible {
  outline: 2px solid var(--wp--preset--color--clay-deep);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------------
   Phone. Desktop is untouched below this line.
   --------------------------------------------------------------------- */
@media (max-width: 781px) {
  /* Hero stacks: the photograph reads on its own, the copy sits under it on
     parchment, so no scrim and no text over a busy crop. */
  .ubora-qhero {
    display: block;
    min-height: 0;
  }

  .ubora-qhero__media {
    position: relative;
    inset: auto;
    aspect-ratio: 16 / 10;
  }

  .ubora-qhero__media img {
    object-position: 62% 50%;
  }

  .ubora-qhero__media::after {
    display: none;
  }

  .ubora-qhero__copy {
    padding-block: 2rem 2.5rem;
  }

  .ubora-qhero__copy > * {
    max-width: none;
  }

  .ubora-sf__panel {
    grid-template-columns: 1fr;
  }

  .ubora-sf__plate {
    min-height: 0;
    /* Q-02a/b/c are 2400x1400 (1.71), so 16/9 barely crops them. Q-03 is a
       PANEL cut at 1400x1750 (0.8) — forcing it to 16/9 throws away about 55%
       of a portrait composition, and it is the result screen the whole finder
       builds toward. Each plate gets the aspect of the frame it holds. */
    aspect-ratio: 12 / 7;
  }

  .ubora-sf__plate--tall {
    aspect-ratio: 4 / 5;
  }

  .ubora-sf__body {
    padding: 1.5rem 1.25rem 1.75rem;
  }

  .ubora-sf__opt {
    padding: 0.95rem 1rem;
  }

  /* Thumb-sized target on the primary CTA. */
  .ubora-sf__go {
    display: block;
    text-align: center;
    padding: 1.05rem 1.5rem;
  }

  .ubora-sf.is-enhanced .ubora-sf__rail {
    gap: 0.4rem 1rem;
    font-size: 0.68rem;
    letter-spacing: 0.14em;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ubora-sf__opt {
    transition: none;
  }

  .ubora-sf__opt:hover {
    transform: none;
  }
}

/* ===== utility.css ===== */
/* =====================================================================
   UBORA — utility pages on the coastal set.

   Owns: templates/404.html and templates/search.html only.
   Tokens come from theme.json; nothing new is invented here.

   The 404 photograph (U-01) is an empty limewashed room with a lit arch
   on the right, so the copy sits on the shaded left wall under a
   parchment veil and the way out stays in frame. On phones the veil is
   dropped and the plate moves below the image, which is the one crop
   where overlaid text would have been a gamble.
   ===================================================================== */


/* ---------------------------------------------------------------------
   1. 404 — the stage

   Image and copy share a single grid cell rather than the copy being
   absolutely positioned, so if the type ever outgrows the band (large
   default font size, failed webfont) the band grows with it instead of
   clipping the CTA.
   --------------------------------------------------------------------- */

.ubora-404-stage {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  background: var(--wp--preset--color--sand);
}

.ubora-404-stage > * {
  grid-area: 1 / 1;
  min-width: 0;
}

/* The shortcode block can hand back the <img> bare or inside a stray <p>.
   Either way it has to fill the cell, not sit in it with a margin. */
.ubora-404-stage > p {
  display: grid;
  margin: 0;
}

.ubora-404-stage .ubora-shot {
  display: block;
  width: 100%;
  height: 100%;
  min-height: clamp(24rem, 46vw, 40rem);
  object-fit: cover;
  object-position: 50% 50%;
}

/* Warm haze over the left third: it gives the copy a near-parchment ground
   at every crop, and reads as light rather than as a scrim. Absolutely
   positioned, so it is out of flow and never becomes a grid item. */
.ubora-404-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Stops anchored to the plate's own gutter maths rather than to a percentage
     of the stage. .ubora-404-plate indents by max(gutter, (100% - 1240px)/2)
     and caps its children at 32rem, so a percentage veil falls short of the
     copy on every viewport between about 782px and 1240px. */
  --u404-lead: max(1rem, calc((100% - 1240px) / 2));
  background: linear-gradient(
    97deg,
    rgba(242, 237, 227, 0.94) 0,
    rgba(242, 237, 227, 0.88) calc(var(--u404-lead) + 34rem),
    rgba(242, 237, 227, 0.45) calc(var(--u404-lead) + 44rem),
    rgba(242, 237, 227, 0) calc(var(--u404-lead) + 56rem)
  );
}

.ubora-404-plate {
  z-index: 2;
  align-self: center;
  padding-block: var(--wp--preset--spacing--40);
  /* Line the copy up with the wide container gutter on big screens, and
     fall back to the standard page gutter below that. */
  padding-inline: max(var(--wp--preset--spacing--40), calc((100% - 1240px) / 2));
}

.ubora-404-plate > * {
  max-width: 32rem;
}

.ubora-404 .ubora-404-eyebrow {
  margin: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.ubora-404 .ubora-404-title {
  margin: 0.55rem 0 0;
  font-family: var(--wp--preset--font-family--gloock);
  font-size: clamp(2.3rem, 5vw, 3.9rem);
  line-height: 1.03;
  letter-spacing: -0.01em;
  color: var(--wp--preset--color--black);
}

.ubora-404 .ubora-404-line {
  margin: 0.85rem 0 0;
  max-width: 30rem;
  font-family: var(--wp--preset--font-family--crimson);
  font-size: clamp(1.05rem, 1.4vw, 1.2rem);
  line-height: 1.55;
  color: var(--wp--preset--color--slate);
}

.ubora-404-cta {
  flex-wrap: wrap;
}


/* ---------------------------------------------------------------------
   2. 404 — the routes back
   --------------------------------------------------------------------- */

.ubora-404 .ubora-404-sub {
  margin: 0;
  font-family: var(--wp--preset--font-family--gloock);
  font-size: clamp(1.55rem, 3.2vw, 2.05rem);
  line-height: 1.15;
}

.ubora-404-recover .ubora-search {
  margin-top: 0.4rem;
}

.ubora-404-doors .ubora-tiles {
  margin-bottom: 0;
}

/* House fine-print register, matching .ubora-grid-meta. Colour is left to
   the block's preset class, which carries !important. */
.ubora-404 .ubora-404-fine,
.ubora-search-page .ubora-search-fine {
  margin: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  line-height: 2;
  text-transform: uppercase;
}


/* ---------------------------------------------------------------------
   3. Search — the frame
   --------------------------------------------------------------------- */

.ubora-search-page .ubora-search-eyebrow {
  margin: 0;
  font-family: var(--wp--preset--font-family--outfit);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.ubora-search-page .wp-block-query-title {
  margin: 0.4rem 0 0;
  font-family: var(--wp--preset--font-family--gloock);
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  line-height: 1.1;
  color: var(--wp--preset--color--black);
}

/* wpautop can leave an empty paragraph beside a shortcode's output. */
.ubora-search-frame > p:empty {
  display: none;
}


/* ---------------------------------------------------------------------
   4. Search — the empty state

   [ubora_search_results] owns the empty-state markup (it lives in the
   mu-plugin), so the U-02 plate is printed on every search and revealed
   only when that markup is on the page. Two independent gates, written as
   separate rules on purpose so a browser without :has() still gets the
   body-class one instead of dropping both:

     a) body.search-no-results — exact here, because inc/shop.php forces the
        main search query to post_type=product, the same condition the
        shortcode tests.
     b) :has(.ubora-search-empty) — covers the empty-term case, where the
        main query still returns products but the shortcode does not.

   Hidden is the safe default: an empty ledge above a wall of results would
   read as a bug.
   --------------------------------------------------------------------- */

.ubora-search-plate {
  display: none;
  max-width: 340px;
  margin: 0 auto var(--wp--preset--spacing--30);
}

body.search-no-results .ubora-search-plate {
  display: block;
}

.ubora-search-frame:has(.ubora-search-empty) .ubora-search-plate {
  display: block;
}

.ubora-search-plate .ubora-shot {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
  box-shadow: 0 18px 44px rgba(28, 26, 23, 0.1);
}

/* The shortcode's own empty-state line, lifted into the serif body face. */
.ubora-search-frame .ubora-search-empty__line {
  max-width: 34rem;
  margin-inline: auto;
  font-family: var(--wp--preset--font-family--crimson);
  font-size: clamp(1.05rem, 1.4vw, 1.2rem);
  line-height: 1.55;
}

/* Four doors across the full wide container would be four slivers of
   whitespace either side of a centred column of copy. */
.ubora-search-frame .ubora-search-empty .ubora-tiles {
  max-width: 900px;
  margin-inline: auto;
}


/* ---------------------------------------------------------------------
   5. Phone. Everything above this line is desktop and stays untouched.
   --------------------------------------------------------------------- */

@media (max-width: 781px) {

  /* No overlaid text on a hard crop: the image becomes a band and the
     plate sits under it on parchment. */
  .ubora-404-stage {
    display: block;
    background: var(--wp--preset--color--parchment);
  }

  .ubora-404-stage > * {
    grid-area: auto;
  }

  .ubora-404-stage > p {
    display: block;
  }

  .ubora-404-stage::after {
    display: none;
  }

  .ubora-404-stage .ubora-shot {
    height: min(46vh, 19rem);
    min-height: 0;
    /* Hold the lit archway in frame once the sides are cropped away. */
    object-position: 72% 50%;
  }

  .ubora-404-plate {
    text-align: center;
    padding-inline: var(--wp--preset--spacing--40);
    padding-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
  }

  .ubora-404-plate > * {
    max-width: none;
  }

  .ubora-404 .ubora-404-line {
    max-width: none;
    margin-inline: auto;
  }

  .ubora-404-cta {
    justify-content: center;
  }

  .ubora-404 .ubora-404-fine,
  .ubora-search-page .ubora-search-fine {
    line-height: 1.8;
    letter-spacing: 0.1em;
  }

  .ubora-search-plate {
    max-width: 76vw;
  }
}

/* ===== whyoil.css ===== */
/* =====================================================================
   UBORA rebrand — homepage beat 4: "Why we skip the spray."

   Owns: patterns/why-oil.php only. Three delivered photographs (H-05a
   lasts, H-05b holds, H-05c travels) on the plaster, one per claim.

   The beat rides over the scroll-driven day canvas, so nothing here paints
   a section background. The photographs are the only opaque surfaces, they
   cast the sun's own shadow via .u-lift, and the type takes the same
   hour-driven sand scrim the other beats use (--u-copy-scrim, written to
   :root every frame by assets/js/day/sun.js).
   ===================================================================== */

.ubora-whyoil {
  position: relative;
  background: none;
}

/* ---------------------------------------------------------------------
   Intro copy
   ---------------------------------------------------------------------
   .u-copy is a narrow left-hand column on the landscape day variants,
   because on those frames the bottle owns the right of the room. This beat
   is a centred three-up with no single mass to dodge, so the column rules
   are re-centred here and only the scrim is kept.

   Specificity is deliberate: :root[data-day-variant='wide'] .u-copy is
   0,3,0, so a plain class pair would lose to it.
   --------------------------------------------------------------------- */

:root .ubora-whyoil .ubora-whyoil__intro.u-copy {
  max-width: min(46rem, 100%);
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* Same sand-to-transparent gradient as day.css, re-centred under a centred
   block. Oversized and fully feathered on purpose: a bounded scrim shows
   its own edge as a faint rectangle on plaster, which is worse than none. */
:root .ubora-whyoil .ubora-whyoil__intro.u-copy::before {
  inset: -80% -45%;
  background: radial-gradient(
    54% 48% at 50% 50%,
    rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 1.6)) 0%,
    rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 0.7)) 40%,
    rgb(var(--u-ground) / 0) 70%
  );
}

/* ---------------------------------------------------------------------
   The three panels
   --------------------------------------------------------------------- */

.ubora-whyoil__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.1rem, 2.4vw, 2.2rem);
  align-items: start;
}

.ubora-whyoil__panel {
  margin: 0;
}

/* The photograph is the only opaque surface in the beat. .u-lift gives it
   the composed --u-cast shadow, so it agrees with the sun in the render
   behind it rather than floating on a generic drop shadow. */
.ubora-whyoil__shot {
  border-radius: 12px;
  overflow: hidden;
}

/* The delivered crop is 1400x1750. Pinning the ratio keeps the three tops
   and bottoms on the same line whatever the column rounds to, and it is
   deterministic, so it costs nothing in layout shift. */
.ubora-whyoil__shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

.ubora-whyoil__body {
  position: relative;
  z-index: 1;
  padding: 0.95rem 0.15rem 0;
}

/* Caption legibility on plaster, on the same hour ramp as .u-copy. Near
   invisible through the middle of the day, rising as the room goes amber. */
.ubora-whyoil__body::before {
  content: "";
  position: absolute;
  inset: -35% -30% -45% -30%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    58% 52% at 42% 46%,
    rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 1.5)) 0%,
    rgb(var(--u-ground) / calc(var(--u-copy-scrim, 0.1) * 0.65)) 42%,
    rgb(var(--u-ground) / 0) 72%
  );
}

.ubora-whyoil__label {
  margin: 0 0 0.35rem;
  font-family: var(--wp--preset--font-family--gloock);
  font-size: clamp(1.25rem, 1.6vw, 1.55rem);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.005em;
  color: var(--wp--preset--color--black);
}

.ubora-whyoil__copy {
  margin: 0;
  max-width: 34ch;
  font-family: var(--wp--preset--font-family--outfit);
  font-size: clamp(0.88rem, 1vw, 0.98rem);
  line-height: 1.62;
  color: var(--wp--preset--color--steel);
}

/* ---------------------------------------------------------------------
   Phone. Desktop is untouched below this line.
   --------------------------------------------------------------------- */

@media (max-width: 781px) {
  .ubora-whyoil__grid {
    grid-template-columns: 1fr;
    gap: 1.9rem;
    /* Capped so a frame does not balloon on a large phone or a small tablet.
       The delivered 4:5 crop is kept at every width on purpose: all three are
       full-height bottle compositions, and a shallower crop takes the cap off
       one of them and the roller ball off another. */
    max-width: 26rem;
    margin-left: auto;
    margin-right: auto;
  }

  .ubora-whyoil__body {
    padding-top: 0.85rem;
  }

  .ubora-whyoil__copy {
    max-width: none;
  }
}

/* ===== whyoilpage.css ===== */
/* /why-roll-on-oil/ — the comparison page the homepage beat links into.
   Ordinary page furniture: this route is NOT part of the day arc, so nothing
   here reads the hour tokens. */

.ubora-whyoil-page__band {
  margin: 0;
  display: block;
  max-height: 44vh;
  overflow: hidden;
}
.ubora-whyoil-page__band img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The intro plate laps up over the band so the band reads as a header rather
   than a stray image sitting above the page. */
.ubora-whyoil-page__intro {
  position: relative;
  margin-top: -3.5rem;
  border-radius: 16px 16px 0 0;
}

.ubora-whyoil-page__table .ubora-hc-wrap {
  max-width: 900px;
  margin-inline: auto;
}

.ubora-whyoil-page__split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
  max-width: 1100px;
  margin-inline: auto;
}
.ubora-whyoil-page__shot {
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--wp--preset--shadow--lift);
}
.ubora-whyoil-page__shot img {
  width: 100%;
  height: auto;
  display: block;
}
.ubora-whyoil-page__note h2 {
  margin: 0.2rem 0 0.8rem;
}
.ubora-whyoil-page__note p {
  color: var(--wp--preset--color--steel);
}

@media (max-width: 781px) {
  .ubora-whyoil-page__band { max-height: 34vh; }
  .ubora-whyoil-page__intro { margin-top: -2rem; }
  /* Single column on a phone, photograph first so the page still opens on an
     image rather than a wall of argument. */
  .ubora-whyoil-page__split { grid-template-columns: 1fr; }
}
