/* ============================================================
   Wedbox Studio — modular page builder
   Every module is a band: it owns its vertical rhythm and its ground.
   Four grounds only (parchment, linen, espresso, espresso-deep); tan is an
   accent, never a field. Squared corners, hairline rules, no shadows.
   ============================================================ */

.wb-mod { padding: var(--space-section) var(--gutter); }
.wb-mod + .wb-mod { padding-top: var(--space-section); }

/* ---------- A band that continues the one above ----------
   A Text only row with no eyebrow and no title is not a new section: it is the
   same piece of writing, split across two rows because one of them needed an
   image. Two full section paddings between them (240px, and 322px from the last
   line to the next on a real post) made it read as something else entirely.

   The seam closes from both sides — the band above gives up most of its bottom
   padding, the continuation all of its top — so the two read as one column of
   prose. :has() is what lets the band above know what follows it. */
.wb-mod + .wb-mod.wb-mod-text--continues { padding-top: 0; }
.wb-mod:has(+ .wb-mod-text--continues) { padding-bottom: var(--space-xl); }

/* Consecutive bands on the same ground share one seam instead of two. */
.wb-mod--linen + .wb-mod--linen,
.wb-mod--espresso + .wb-mod--espresso,
.wb-mod--espresso-deep + .wb-mod--espresso-deep { padding-top: 0; }

.wb-mod--linen         { background: var(--color-linen); }
.wb-mod--espresso      { background: var(--color-espresso); }
.wb-mod--espresso-deep { background: var(--color-espresso-deep); }

.wb-mod-header { margin-bottom: var(--space-xxl); max-width: 720px; }
/* Some bands want the intro to run the full row rather than sit in the 720px
   reading column — the Design Grid asks for it explicitly. */
.wb-mod-header--wide { max-width: none; }
.wb-mod-header h2 { margin-bottom: var(--space-md); }
.wb-mod-header p { margin: 0; }

/* ---------- Image frame ----------
   A passe-partout, not a border: a hairline rule in the band's text colour with
   a 6px gap between it and the photograph. The gap is left open, so the mat is
   whatever ground the module sits on — parchment, linen or one of the espressos.
   Used by Text + Image, Call-out and Grid. */
.wb-imgframe {
  border: 1px solid var(--color-ink);
  padding: 6px;
  background: transparent;
}
.wb-imgframe img { display: block; }
.wb-on-dark .wb-imgframe { border-color: var(--color-on-dark); }

/* A circular image takes a circular mount. */
.wb-mod-split--img-round .wb-imgframe,
.wb-mod-callout-inner--img-round .wb-imgframe { border-radius: var(--radius-pill); }

/* ---------- Parallax bleed ----------
   A parallax layer is moved by `progress x host height x factor`, and progress
   reaches +/-1 exactly as the band enters and leaves the viewport. So the bleed
   a layer needs on each side is not a fixed 8% — it *is* the factor. Anything
   less drags the layer's own edge into the frame at the ends of the scroll,
   which is the grey band the Full-width image was showing.

   Percentages here are deliberately spent on `top`/`bottom` of an absolutely
   positioned layer: on those they resolve against the container's HEIGHT. A
   percentage margin would resolve against its WIDTH — the trap the old
   `margin-top: -7.5%` fell into on a band far wider than it is tall. */
.wb-px-host                { --wb-px-bleed: 0%; }
.wb-px-host--subtle        { --wb-px-bleed: 10%; }
.wb-px-host--normal        { --wb-px-bleed: 20%; }
.wb-px-host--strong        { --wb-px-bleed: 30%; }

/* ---------- Tables ----------
   Written in the editor with the bundled TinyMCE table plugin, which tags them
   .wb-table. Hairline rules and a tracked header, like everything else here:
   no zebra fill, no boxed cells, no shadow. Wide tables scroll inside their own
   frame rather than pushing the page sideways. */
.wb-content table,
.wb-mod-body table,
.wb-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-lg) 0;
  font-size: var(--type-body-sm-size);
  line-height: var(--type-body-sm-lh);
}
.wb-content table th,
.wb-mod-body table th,
.wb-table th {
  text-align: left;
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--type-label-size); letter-spacing: var(--type-label-track);
  text-transform: uppercase; color: var(--color-ink);
  padding: var(--space-sm) var(--space-md) var(--space-sm) 0;
  border-bottom: 1px solid var(--color-ink);
}
.wb-content table td,
.wb-mod-body table td,
.wb-table td {
  padding: var(--space-sm) var(--space-md) var(--space-sm) 0;
  border-bottom: 1px solid var(--color-hairline);
  vertical-align: top; color: var(--color-body);
}
.wb-content table tr:last-child td,
.wb-mod-body table tr:last-child td,
.wb-table tr:last-child td { border-bottom: 0; }
.wb-on-dark table th { color: var(--color-on-dark); border-bottom-color: var(--color-on-dark); }
.wb-on-dark table td { color: var(--color-on-dark-soft); border-bottom-color: var(--color-hairline-dark); }
/* The plugin wraps a wide table in this; without a scroll it would widen the page. */
.wb-content .mce-item-table-wrap,
.wb-mod-body .mce-item-table-wrap { overflow-x: auto; }

/* Editor prose inside a module. */
.wb-mod-body > * + * { margin-top: var(--space-lg); }
.wb-mod-body h2, .wb-mod-body h3 { font-size: var(--type-display-sm-size); line-height: var(--type-display-sm-lh); letter-spacing: var(--type-display-sm-track); }
.wb-mod-body ul, .wb-mod-body ol { padding-left: 1.2em; }
.wb-mod-body li + li { margin-top: var(--space-xs); }
.wb-mod-body img { display: block; }
.wb-on-dark .wb-mod-body { color: var(--color-on-dark-soft); }

/* ---------- Text only ---------- */
.wb-mod-text-inner > * + * { margin-top: var(--space-lg); }
.wb-mod-text-inner .t-label { display: block; }
.wb-mod-text--narrow .wb-mod-text-inner { max-width: 720px; }
/* Full width breaks the container, not just its reading column: the band keeps
   only the page gutter, so the text runs as wide as the Full-width image does. */
.wb-mod-text--full .wb-container { max-width: none; }
.wb-mod-text--center .wb-mod-text-inner { margin-left: auto; margin-right: auto; text-align: center; }
.wb-mod-text--right .wb-mod-text-inner { margin-left: auto; text-align: right; }

/* ---------- Text + image / text + video ---------- */
/* Top by default, not centre. Centring reads well when the two columns are a
   similar height, but against a long article it pushes the picture halfway down
   the page — measured 807px below the first line on a real post, well off the
   opening screen, so a reader met the piece with an empty column beside it. */
.wb-mod-split-grid {
  display: grid; align-items: start;
  grid-template-columns: 1fr 1fr; gap: 80px;
}
.wb-mod-split--vcenter .wb-mod-split-grid { align-items: center; }
.wb-mod-split--img-xsmall .wb-mod-split-grid { grid-template-columns: 3fr 1fr; }
.wb-mod-split--img-small .wb-mod-split-grid  { grid-template-columns: 2fr 1fr; }
.wb-mod-split--img-large .wb-mod-split-grid  { grid-template-columns: 1fr 2fr; }
.wb-mod-split--media-left .wb-mod-split-text  { order: 2; }
.wb-mod-split--media-left .wb-mod-split-media { order: 1; }
.wb-mod-split--media-left.wb-mod-split--img-xsmall .wb-mod-split-grid { grid-template-columns: 1fr 3fr; }
.wb-mod-split--media-left.wb-mod-split--img-small .wb-mod-split-grid  { grid-template-columns: 1fr 2fr; }
.wb-mod-split--media-left.wb-mod-split--img-large .wb-mod-split-grid  { grid-template-columns: 2fr 1fr; }

.wb-mod-split-text > * + * { margin-top: var(--space-lg); }
.wb-mod-split-text .t-label { display: block; }
.wb-mod-split-media img { display: block; width: 100%; height: auto; border-radius: var(--radius-none); }
.wb-mod-split--img-round .wb-mod-split-media img { aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-pill); }

.wb-mod-split-media--video video { width: 100%; display: block; background: var(--color-espresso-deep); }
.wb-mod-embed { position: relative; }
/* The iframe's own chrome is not the module's frame — the passe-partout is on
   the wrapper, so this stays 0 and does not fight .wb-imgframe. */
.wb-mod-embed iframe { width: 100%; aspect-ratio: 16/9; height: auto; display: block; border: 0; }
.wb-mod-split-media--video .wb-imgframe { display: block; }

/* ---------- Call-out ---------- */
.wb-mod-callout-inner { display: grid; gap: var(--space-xl); align-items: center; }
.wb-mod-callout-inner.has-image { grid-template-columns: 1fr 1fr; gap: 80px; }
.wb-mod-callout-inner--img-xsmall.has-image { grid-template-columns: 1fr 3fr; }
.wb-mod-callout-inner--img-small.has-image  { grid-template-columns: 1fr 2fr; }
.wb-mod-callout-inner--img-large.has-image  { grid-template-columns: 2fr 1fr; }
.wb-mod-callout-media img { display: block; width: 100%; height: auto; }
.wb-mod-callout-inner--img-round .wb-mod-callout-media img { aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-pill); }
.wb-mod-callout-text > * + * { margin-top: var(--space-lg); }
.wb-mod-callout-text p { margin: 0; max-width: 560px; }

/* ---------- Full-width image ---------- */
.wb-mod-fullimage { margin: 0; padding: 0; }
/* The ground behind the photograph. The hero never showed its bleed because
   espresso sat behind it; this band had nothing, so any gap was the page. */
.wb-mod-fullimage-media {
  position: relative; overflow: hidden; height: 60vh;
  background: var(--color-espresso);
}
.wb-mod-fullimage--tall .wb-mod-fullimage-media { height: 88vh; }
/* Absolute, so the bleed resolves against the height (see "Parallax bleed").

   The height is stated, not left to `auto` between top and bottom: an <img> is
   a REPLACED element, and an absolutely positioned replaced box with height
   auto takes its INTRINSIC height and ignores `bottom` entirely (CSS 2.1
   §10.6.5). The layer was coming out 233% taller than its frame instead of the
   40% asked for, and object-fit had nothing to crop against. A <div> with a
   background — the hero — does stretch, which is why it never showed this. */
.wb-mod-fullimage-media img {
  position: absolute;
  top: calc(-1 * var(--wb-px-bleed, 0%));
  left: 0; right: 0;
  width: 100%;
  height: calc(100% + (2 * var(--wb-px-bleed, 0%)));
  object-fit: cover; object-position: center; display: block; will-change: transform;
}
.wb-mod-fullimage figcaption {
  padding: var(--space-md) var(--gutter) 0; margin: 0 auto; max-width: var(--container-max);
  font-family: var(--font-body); font-size: var(--type-label-size);
  letter-spacing: var(--type-label-track); text-transform: uppercase; color: var(--color-label);
}
/* Optional headline + prose laid OVER the image, on a scrim that exists only to
   keep them readable. Centred and mid-height by default. */
.wb-mod-fullimage-scrim {
  position: absolute; inset: 0;
  background: rgba(37, 31, 25, var(--wb-fi-scrim, 0.45));
}
.wb-mod-fullimage-copy {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  padding: var(--space-xxl) var(--gutter);
}
.wb-mod-fullimage-copy--top    { justify-content: flex-start; }
.wb-mod-fullimage-copy--middle { justify-content: center; }
.wb-mod-fullimage-copy--bottom { justify-content: flex-end; }
.wb-mod-fullimage-copy h2 { color: var(--color-on-dark); margin-bottom: var(--space-md); }
.wb-mod-fullimage-copy .wb-mod-body { max-width: 640px; color: var(--color-on-dark-soft); }
.wb-mod-fullimage-copy--center { text-align: center; }
.wb-mod-fullimage-copy--center .wb-mod-body { margin-left: auto; margin-right: auto; }
.wb-mod-fullimage-copy--right { text-align: right; }
.wb-mod-fullimage-copy--right .wb-mod-body { margin-left: auto; }

/* ---------- Grid (image + text) ---------- */
.wb-mod-grid-items { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px 24px; }
.wb-mod-grid-card { display: block; text-decoration: none; color: inherit; }
.wb-mod-grid-card.is-link:hover .wb-mod-grid-media img { opacity: 0.92; }
.wb-mod-grid-media img { display: block; width: 100%; aspect-ratio: 4/5; object-fit: cover; transition: opacity 200ms ease-out; }
.wb-mod-grid-text { margin-top: 18px; }
.wb-mod-grid-text h3 { font-size: 26px; line-height: 1.2; letter-spacing: -0.4px; margin-bottom: var(--space-xs); }
.wb-on-dark .wb-mod-grid-text h3 { color: var(--color-on-dark); }
.wb-mod-grid-body { font-size: var(--type-body-sm-size); }
.wb-mod-grid-card.is-link:hover h3 { color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-grid-card.is-link:hover h3 { color: var(--color-gold); }

/* ---------- "This is a link" affordance (icon cards + grid cards) ----------
   Nothing in this system lifts or casts a shadow, so the signal is a rule that
   draws itself in under the label and an arrow that slides. Both are inert
   until the card is hovered or focused, which is what makes it read as a link
   rather than as decoration. */
.wb-mod-more {
  display: inline-flex; align-items: baseline; gap: var(--space-xs);
  margin-top: var(--space-md);
  font-family: var(--font-body); font-size: var(--type-label-size);
  letter-spacing: var(--type-label-track); text-transform: uppercase;
  color: var(--color-gold-ink); position: relative; padding-bottom: 4px;
}
.wb-on-dark .wb-mod-more { color: var(--color-gold); }
.wb-mod-more::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform 260ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.wb-mod-more .arrow { transition: transform 260ms cubic-bezier(0.22, 0.61, 0.36, 1); }
.is-link:hover .wb-mod-more::after,
.is-link:focus-visible .wb-mod-more::after { transform: scaleX(1); }
.is-link:hover .wb-mod-more .arrow,
.is-link:focus-visible .wb-mod-more .arrow { transform: translateX(4px); }
.wb-mod-grid-card.is-link,
.wb-mod-icon-card.is-link { cursor: pointer; }
@media (prefers-reduced-motion: reduce) {
  .wb-mod-more::after { transition: none; }
  .wb-mod-more .arrow { transition: none; }
}

/* ---------- Products (Collection) ----------
   Every published product in one grid, four per row by default. The card is
   the whole link: the framed image, the title under it, and the label with the
   arrow that says where it goes. */
.wb-mod-products-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(var(--wb-mod-cols, 4), 1fr);
  gap: var(--space-xxl) var(--space-lg);
}
.wb-mod-product-link { display: block; text-decoration: none; color: inherit; }
.wb-mod-products-grid .wb-imgframe { display: block; }

/* The frame clips the zoom, so the image grows inside the mat and never over
   the hairline. */
.wb-mod-product-media {
  display: block; overflow: hidden; background: var(--color-linen);
}
.wb-mod-product-media img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: transform 600ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Format — every card is cropped to the same box whatever was uploaded. */
.wb-mod-products--portrait  .wb-mod-product-media { aspect-ratio: 4/5; }
.wb-mod-products--tall      .wb-mod-product-media { aspect-ratio: 3/4; }
.wb-mod-products--square    .wb-mod-product-media { aspect-ratio: 1/1; }
.wb-mod-products--landscape .wb-mod-product-media { aspect-ratio: 3/2; }

/* Shape — the mount and the image take the same corner. A circle overrides the
   format above: it can only be a circle in a square box. */
.wb-mod-products--img-rounded .wb-imgframe,
.wb-mod-products--img-rounded .wb-mod-product-media { border-radius: 10px; }
.wb-mod-products--img-circle .wb-imgframe,
.wb-mod-products--img-circle .wb-mod-product-media { border-radius: var(--radius-pill); }
.wb-mod-products--img-circle .wb-mod-product-media { aspect-ratio: 1/1; }

/* The mouse-over: a slight push-in, nothing lifts and nothing casts a shadow. */
.wb-mod-products--zoom .wb-mod-product-link:hover .wb-mod-product-media img,
.wb-mod-products--zoom .wb-mod-product-link:focus-visible .wb-mod-product-media img { transform: scale(1.06); }

.wb-mod-product-title {
  display: block; margin-top: 18px;
  font-family: var(--font-display); font-weight: 400;
  font-size: 24px; line-height: 1.2; letter-spacing: -0.3px;
  color: var(--color-ink);
}
.wb-on-dark .wb-mod-product-title { color: var(--color-on-dark); }
.wb-mod-product-link:hover .wb-mod-product-title { color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-product-link:hover .wb-mod-product-title { color: var(--color-gold); }

.wb-mod-product-price {
  display: block; margin-top: var(--space-xxs);
  font-size: var(--type-body-sm-size); color: var(--color-gold-ink);
}
.wb-on-dark .wb-mod-product-price { color: var(--color-gold); }

.wb-mod-product-excerpt {
  display: block; margin-top: var(--space-sm);
  font-size: var(--type-body-sm-size); line-height: var(--type-body-sm-lh);
  color: var(--color-body);
}
.wb-on-dark .wb-mod-product-excerpt { color: var(--color-on-dark-soft); }

.wb-mod-product-link .wb-mod-more { margin-top: var(--space-sm); }
.wb-mod-product-link:focus-visible { outline: 1px solid var(--color-gold-ink); outline-offset: 6px; }
.wb-mod-products-cta { margin: var(--space-xxl) 0 0; }

@media (prefers-reduced-motion: reduce) {
  .wb-mod-product-media img { transition: none; }
  .wb-mod-products--zoom .wb-mod-product-link:hover .wb-mod-product-media img,
  .wb-mod-products--zoom .wb-mod-product-link:focus-visible .wb-mod-product-media img { transform: none; }
}

/* ---------- Design Grid ----------
   A plate of images, up to six per row. The click enlarges rather than
   navigates, so there is no link affordance here — the zoom cursor from
   css/lightbox.css is the whole signal. */
.wb-mod-dgrid-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(var(--wb-mod-cols, 4), 1fr);
  gap: var(--space-xl) var(--space-lg);
}
.wb-mod-dgrid-button {
  display: block; width: 100%;
  background: transparent; text-align: left;
}
/* The button reset only applies where there is no frame to reset away: a bare
   `border: 0` here sits after .wb-imgframe at equal specificity and silently
   erased the passe-partout it is supposed to be wearing. */
.wb-mod-dgrid-grid:not(.wb-mod-dgrid--framed) .wb-mod-dgrid-button { padding: 0; border: 0; }
button.wb-mod-dgrid-button { cursor: zoom-in; }

/* The frame clips the zoom, so the image grows inside the mat. */
.wb-mod-dgrid-media { display: block; overflow: hidden; background: var(--color-linen); }
.wb-mod-dgrid-media img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: transform 600ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* Natural keeps whatever shape the file has, so the row is deliberately ragged. */
.wb-mod-dgrid--natural   .wb-mod-dgrid-media img { height: auto; object-fit: fill; }
.wb-mod-dgrid--portrait  .wb-mod-dgrid-media { aspect-ratio: 4/5; }
.wb-mod-dgrid--tall      .wb-mod-dgrid-media { aspect-ratio: 3/4; }
.wb-mod-dgrid--square    .wb-mod-dgrid-media { aspect-ratio: 1/1; }
.wb-mod-dgrid--landscape .wb-mod-dgrid-media { aspect-ratio: 3/2; }

.wb-mod-dgrid--img-rounded .wb-mod-dgrid-button,
.wb-mod-dgrid--img-rounded .wb-mod-dgrid-media { border-radius: 10px; }
.wb-mod-dgrid--img-circle .wb-mod-dgrid-button,
.wb-mod-dgrid--img-circle .wb-mod-dgrid-media { border-radius: var(--radius-pill); }
.wb-mod-dgrid--img-circle .wb-mod-dgrid-media { aspect-ratio: 1/1; }

.wb-mod-dgrid--zoom .wb-mod-dgrid-button:hover .wb-mod-dgrid-media img,
.wb-mod-dgrid--zoom .wb-mod-dgrid-button:focus-visible .wb-mod-dgrid-media img { transform: scale(1.06); }
.wb-mod-dgrid-button:focus-visible { outline: 1px solid var(--color-gold-ink); outline-offset: 4px; }
.wb-on-dark .wb-mod-dgrid-button:focus-visible { outline-color: var(--color-gold); }

.wb-mod-dgrid-title {
  display: block; margin-top: var(--space-sm);
  font-family: var(--font-display); font-weight: 400;
  font-size: 20px; line-height: 1.25; color: var(--color-ink);
}
.wb-on-dark .wb-mod-dgrid-title { color: var(--color-on-dark); }
.wb-mod-dgrid-caption {
  display: block; margin-top: var(--space-xxs);
  font-size: var(--type-body-sm-size); color: var(--color-label);
}
.wb-on-dark .wb-mod-dgrid-caption { color: var(--color-on-dark-soft); }

@media (prefers-reduced-motion: reduce) {
  .wb-mod-dgrid-media img { transition: none; }
  .wb-mod-dgrid--zoom .wb-mod-dgrid-button:hover .wb-mod-dgrid-media img,
  .wb-mod-dgrid--zoom .wb-mod-dgrid-button:focus-visible .wb-mod-dgrid-media img { transform: none; }
}

/* ---------- Posts (Journal) ----------
   One entry per row, image beside the text, a hairline between entries. The
   image and the read-more both lead to the piece; the title is the accessible
   link, and the image is marked aria-hidden so a screen reader is not handed
   the same destination three times. */
.wb-mod-journal-list > * + * {
  margin-top: var(--space-xxl);
  padding-top: var(--space-xxl);
  border-top: 1px solid var(--color-hairline);
}
.wb-on-dark .wb-mod-journal-list > * + * { border-top-color: var(--color-hairline-dark); }

/* How much of the row the image column takes — one setting for the whole list,
   so the entries line up instead of each finding its own edge. */
.wb-mod-journal--img-xsmall { --wb-journal-media: 220px; }
.wb-mod-journal--img-small  { --wb-journal-media: 300px; }
.wb-mod-journal--img-medium { --wb-journal-media: 380px; }
.wb-mod-journal--img-large  { --wb-journal-media: 480px; }

.wb-mod-journal-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, var(--wb-journal-media, 380px));
  gap: var(--space-xxl);
  align-items: start;
}
/* Columns swap, markup order does not — stacked, the text still comes first. */
.wb-mod-journal--media-left .wb-mod-journal-item { grid-template-columns: minmax(0, var(--wb-journal-media, 380px)) minmax(0, 1fr); }
.wb-mod-journal--media-left .wb-mod-journal-text  { grid-column: 2; grid-row: 1; }
.wb-mod-journal--media-left .wb-mod-journal-media { grid-column: 1; grid-row: 1; }
.wb-mod-journal--media-none .wb-mod-journal-item { grid-template-columns: 1fr; }
/* An entry with no featured image lets the text run the full row. */
.wb-mod-journal-item:not(:has(.wb-mod-journal-media)) { grid-template-columns: 1fr; }

.wb-mod-journal-title {
  font-family: var(--font-display); font-weight: 300;
  font-size: var(--type-display-sm-size); line-height: var(--type-display-sm-lh);
  letter-spacing: var(--type-display-sm-track);
  margin: 0; color: var(--color-ink);
}
.wb-on-dark .wb-mod-journal-title { color: var(--color-on-dark); }
.wb-mod-journal-title a { color: inherit; text-decoration: none; }
.wb-mod-journal-title a:hover { color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-journal-title a:hover { color: var(--color-gold); }

.wb-mod-journal-meta {
  display: flex; flex-wrap: wrap; gap: var(--space-xs);
  margin: var(--space-sm) 0 0; color: var(--color-label);
}
.wb-on-dark .wb-mod-journal-meta { color: var(--color-on-dark-soft); }

.wb-mod-journal-opening {
  margin: var(--space-md) 0 0;
  font-size: var(--type-body-size); line-height: var(--type-body-lh);
  color: var(--color-body); max-width: 68ch;
}
.wb-on-dark .wb-mod-journal-opening { color: var(--color-on-dark-soft); }

/* Sits inline at the end of the paragraph, the way "… Ler mais" reads. */
.wb-mod-journal-more {
  white-space: nowrap; margin-left: var(--space-xxs);
  color: var(--color-gold-ink); text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 260ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.wb-on-dark .wb-mod-journal-more { color: var(--color-gold); }
.wb-mod-journal-more:hover { border-bottom-color: currentColor; }
/* Hovering the title or the image animates the read-more too: all three lead to
   the same piece, so all three should say so at once. The affordance is the
   shared one — a rule that draws in, an arrow that slides, nothing lifts. */
.wb-mod-journal-item.is-link:hover .wb-mod-journal-more { border-bottom-color: currentColor; }
.wb-mod-journal-item.is-link:hover .wb-mod-journal-more .arrow,
.wb-mod-journal-item.is-link:focus-within .wb-mod-journal-more .arrow { transform: translateX(4px); }
.wb-mod-journal-item.is-link:focus-within .wb-mod-journal-more { border-bottom-color: currentColor; }
.wb-mod-journal-item.is-link:hover .wb-mod-journal-title a { color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-journal-item.is-link:hover .wb-mod-journal-title a { color: var(--color-gold); }
.wb-mod-journal-more .arrow {
  display: inline-block; margin-left: var(--space-xxs);
  transition: transform 260ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.wb-mod-journal-more:hover .arrow { transform: translateX(4px); }

.wb-mod-journal-media a { display: block; }
.wb-mod-journal-thumb { display: block; overflow: hidden; background: var(--color-linen); }
.wb-mod-journal-thumb img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: opacity 200ms ease-out;
}
.wb-mod-journal-media a:hover .wb-mod-journal-thumb img { opacity: 0.92; }
.wb-mod-journal--natural   .wb-mod-journal-thumb img { height: auto; }
.wb-mod-journal--landscape .wb-mod-journal-thumb { aspect-ratio: 3/2; }
.wb-mod-journal--square    .wb-mod-journal-thumb { aspect-ratio: 1/1; }
.wb-mod-journal--portrait  .wb-mod-journal-thumb { aspect-ratio: 4/5; }

.wb-mod-journal-cta { margin: var(--space-xxl) 0 0; }

@media (prefers-reduced-motion: reduce) {
  .wb-mod-journal-more,
  .wb-mod-journal-more .arrow { transition: none; }
}

/* Pictures a reader can enlarge. The class marks the region, and js/overlays.js
   picks up the images inside it — the cursor is the whole affordance, as in the
   Design Grid. An image already wrapped in a link keeps its link. */
.wb-zoomable img { cursor: zoom-in; }
.wb-zoomable a img,
.wb-zoomable button img { cursor: inherit; }

/* ---------- Journal entry (single post) ----------
   The featured image sits in the same passe-partout the modules use, and opens
   the shared lightbox — the reading column is narrow, and these are pictures of
   fabric that reward being looked at properly. */
/* The featured image, when a post has one. It crops to 3:2 from the TOP: left
   at its own height a portrait file filled the screen and pushed the opening
   line below the fold, so a reader met a photograph and no article. There is no
   per-post control — the size and shape of the pictures *inside* a post are the
   module fields' business, and a second set of controls for the one image most
   of these posts never set was a box that did nothing. */
.wb-journal-figure { margin: 0 0 var(--space-xxl); }
.wb-journal-figure-media { aspect-ratio: 3/2; }
.wb-journal-figure-media img { height: 100%; object-fit: cover; object-position: center top; }
.wb-journal-figure-button {
  display: block; width: 100%;
  background: transparent; text-align: left;
}
button.wb-journal-figure-button { cursor: zoom-in; }
.wb-journal-figure-media { display: block; overflow: hidden; background: var(--color-linen); }
.wb-journal-figure-media img { display: block; width: 100%; height: auto; transition: opacity 200ms ease-out; }
button.wb-journal-figure-button:hover .wb-journal-figure-media img { opacity: 0.92; }
button.wb-journal-figure-button:focus-visible { outline: 1px solid var(--color-gold-ink); outline-offset: 4px; }
.wb-journal-figure figcaption { margin-top: var(--space-sm); color: var(--color-label); }

/* ---------- Icons + text ---------- */
.wb-mod-icons-grid { display: grid; grid-template-columns: repeat(var(--wb-mod-cols, 3), 1fr); gap: var(--space-xl) var(--space-lg); }
.wb-mod-icon-card {
  display: block; text-decoration: none; color: inherit;
  border-top: 1px solid var(--color-hairline); padding-top: var(--space-lg);
}
.wb-on-dark .wb-mod-icon-card { border-top-color: var(--color-hairline-dark); }
.wb-mod-icons-grid--center { text-align: center; }
.wb-mod-icons-grid--center .wb-mod-icon-card { border-top: 0; padding-top: 0; }
.wb-mod-icons-grid--card-circle .wb-mod-icon-card {
  border: 1px solid var(--color-hairline); border-radius: var(--radius-pill);
  aspect-ratio: 1; padding: var(--space-xl);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.wb-mod-icon-card .icon { display: inline-flex; margin-bottom: var(--space-md); color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-icon-card .icon { color: var(--color-gold); }
.wb-mod-icon-card .icon svg { width: calc(32px * var(--wb-icon-scale, 1)); height: calc(32px * var(--wb-icon-scale, 1)); fill: currentColor; }
.wb-mod-icon-card .icon--rounded,
.wb-mod-icon-card .icon--circle {
  padding: var(--space-md); border: 1px solid var(--color-gold-deep);
  border-radius: var(--radius-none);
}
.wb-mod-icon-card .icon--circle { border-radius: var(--radius-pill); }
.wb-mod-icon-card h3 { font-size: var(--type-title-lg-size); font-family: var(--font-body); font-weight: 400; letter-spacing: 0; line-height: var(--type-title-lg-lh); margin-bottom: var(--space-xs); }
.wb-on-dark .wb-mod-icon-card h3 { color: var(--color-on-dark); }
.wb-mod-icon-card p { font-size: var(--type-body-sm-size); margin: 0; }
.wb-mod-icon-card.is-link:hover h3 { color: var(--color-gold-ink); }
.wb-on-dark .wb-mod-icon-card.is-link:hover h3 { color: var(--color-gold); }

/* ---------- Client marks ----------
   A quiet band: label on the left, marks on the right. The track only carries
   .is-marquee — and therefore only animates — when wedbox.js finds the marks
   wider than the viewport, so a short list is spread out and still. */
/* Doubled class on purpose. `.wb-mod + .wb-mod { padding-top: section }` is
   0,0,2,0 and out-specifies a single `.wb-mod-marks`, so whenever this band
   followed another module its top padding sprang back to 120px while the
   bottom stayed at 64 — the logos sat low in their own band. */
.wb-mod.wb-mod-marks { padding-top: var(--space-xxl); padding-bottom: var(--space-xxl); }
.wb-mod-marks-inner { display: flex; align-items: center; gap: var(--space-xxl); }
.wb-mod-marks-label { flex-shrink: 0; color: var(--color-label); }
.wb-on-dark .wb-mod-marks-label { color: var(--color-on-dark-soft); }
.wb-mod-marks-viewport { flex: 1; min-width: 0; overflow: hidden; }
.wb-mod-marks-track { display: flex; align-items: center; gap: 56px; width: max-content; }
.wb-mod-marks-track:not(.is-marquee) { width: 100%; justify-content: space-between; }
.wb-mod-marks-track.is-marquee {
  animation: wb-marquee var(--wb-marquee-duration, 40s) linear infinite;
}
.wb-mod-marks-track.is-marquee:hover { animation-play-state: paused; }
@keyframes wb-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1 * var(--wb-marquee-shift, 0px))); }
}
@media (prefers-reduced-motion: reduce) {
  .wb-mod-marks-track.is-marquee { animation: none; }
}
.wb-mod-mark { flex-shrink: 0; display: inline-flex; align-items: center; text-decoration: none; }
.wb-mod-mark img {
  display: block; height: var(--wb-mark-height, 36px); width: auto;
  /* The marks are a texture at this size, not a logo wall — they settle to the
     band's ink and come up to full colour on hover. */
  filter: grayscale(1); opacity: 0.7; transition: opacity 200ms ease-out, filter 200ms ease-out;
}
a.wb-mod-mark:hover img { filter: none; opacity: 1; }
.wb-mod-mark--word {
  font-family: var(--font-display); font-weight: 300; font-size: var(--type-serif-lead-size);
  line-height: 1.2; color: var(--color-label); white-space: nowrap;
  transition: color 200ms ease-out;
}
.wb-on-dark .wb-mod-mark--word { color: var(--color-on-dark-soft); }
a.wb-mod-mark--word:hover { color: var(--color-gold-ink); }
.wb-on-dark a.wb-mod-mark--word:hover { color: var(--color-gold); }

/* ---------- Sliders (carousel + quotes) ---------- */
.wb-mod-stage { position: relative; }
.wb-mod-slide { display: none; }
.wb-mod-slide[data-active="true"] { display: block; }
.wb-mod-nav { margin-top: var(--space-xl); }
.wb-mod-dots { display: flex; gap: var(--space-sm); }
.wb-mod-dot {
  width: 28px; height: 2px; padding: 0; border: 0; cursor: pointer;
  background: var(--color-hairline); border-radius: var(--radius-none);
}
.wb-mod-dot[data-active="true"] { background: var(--color-gold-deep); }
.wb-on-dark .wb-mod-dot { background: var(--color-hairline-dark); }
.wb-on-dark .wb-mod-dot[data-active="true"] { background: var(--color-gold); }

.wb-mod-carousel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: center; }
.wb-mod-carousel--img-xsmall .wb-mod-carousel-grid { grid-template-columns: 1fr 3fr; }
.wb-mod-carousel--img-small .wb-mod-carousel-grid  { grid-template-columns: 1fr 2fr; }
.wb-mod-carousel--img-large .wb-mod-carousel-grid  { grid-template-columns: 2fr 1fr; }
.wb-mod-carousel-media .wb-imgframe { display: block; }
.wb-mod-carousel-media img { display: block; width: 100%; height: auto; }
.wb-mod-carousel--img-round .wb-mod-carousel-media img { aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-pill); }
/* A circular portrait takes a circular mount, as everywhere else. */
.wb-mod-carousel--img-round .wb-mod-carousel-media .wb-imgframe { border-radius: var(--radius-pill); }
.wb-mod-carousel-text > * + * { margin-top: var(--space-md); }
.wb-mod-carousel-text h3 { font-size: var(--type-display-sm-size); line-height: var(--type-display-sm-lh); letter-spacing: var(--type-display-sm-track); }
.wb-on-dark .wb-mod-carousel-text h3 { color: var(--color-on-dark); }

/* ---------- Quotes ---------- */
.wb-mod-quote-grid { display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-xl); align-items: start; }
.wb-mod-quote-grid .glyph {
  font-family: var(--font-display); font-weight: 300; font-size: 96px;
  line-height: 0.8; color: var(--color-gold); user-select: none;
}
.wb-mod-quote-grid blockquote {
  margin: 0; font-family: var(--font-display); font-weight: 300;
  font-size: var(--type-display-sm-size); line-height: 1.35; letter-spacing: -0.4px;
  color: var(--color-ink);
}
.wb-on-dark .wb-mod-quote-grid blockquote { color: var(--color-on-dark); }
.wb-mod-quote-grid .attrib { margin-top: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-xxs); }
.wb-mod-quote-grid .name,
.wb-mod-quote-grid .role {
  font-family: var(--font-body); font-size: var(--type-label-size);
  letter-spacing: var(--type-label-track); text-transform: uppercase;
}
.wb-mod-quote-grid .name { color: var(--color-ink); }
.wb-mod-quote-grid .role { color: var(--color-label); }
.wb-on-dark .wb-mod-quote-grid .name { color: var(--color-on-dark); }
.wb-on-dark .wb-mod-quote-grid .role { color: var(--color-on-dark-soft); }
/* The portrait is a background-image box, not an <img>, so the mount wraps it
   rather than the frame being put on the box itself — otherwise the 6px gap
   would be filled by the photograph instead of the ground. */
.wb-mod-quote-grid .portrait-mount { display: block; width: fit-content; }
.wb-mod-quote-grid .portrait { width: 160px; aspect-ratio: 1; background: var(--color-greige) center/cover no-repeat; }
.wb-mod-quotes--img-xsmall .wb-mod-quote-grid .portrait { width: 88px; }
.wb-mod-quotes--img-small .wb-mod-quote-grid .portrait  { width: 120px; }
.wb-mod-quotes--img-large .wb-mod-quote-grid .portrait  { width: 220px; }
.wb-mod-quotes--img-round .wb-mod-quote-grid .portrait  { border-radius: var(--radius-pill); }
.wb-mod-quotes--img-round .wb-mod-quote-grid .portrait-mount { border-radius: var(--radius-pill); }

/* ---------- Top hero ----------
   Three heights: the homepage hero is the tall one, every other page opens on
   the shorter cut, and Extra short is roughly two thirds of Short for a band
   that only has to announce a page. module-hero.php picks per page; the row
   can override. */
.wb-mod-hero { position: relative; overflow: hidden; background: var(--color-espresso); padding: 96px var(--gutter); }
.wb-mod-hero--xshort { padding: 64px var(--gutter); }
.wb-mod-hero--tall { padding: 140px var(--gutter); }
.wb-mod-hero--xshort .wb-mod-hero-grid { min-height: 240px; }
.wb-mod-hero--normal .wb-mod-hero-grid { min-height: 360px; }
.wb-mod-hero--tall .wb-mod-hero-grid { min-height: 520px; }

/* Nothing above the headline — no kicker, no eyebrow — so the band closes in
   rather than centring a short block inside a tall box and opening a hole
   above the title. Tall feels it most, which is where the cut is deepest. */
.wb-mod-hero--lean.wb-mod-hero--xshort { padding: 52px var(--gutter); }
.wb-mod-hero--lean.wb-mod-hero--normal { padding: 72px var(--gutter); }
.wb-mod-hero--lean.wb-mod-hero--tall   { padding: 96px var(--gutter); }
.wb-mod-hero--lean.wb-mod-hero--xshort .wb-mod-hero-grid { min-height: 180px; }
.wb-mod-hero--lean.wb-mod-hero--normal .wb-mod-hero-grid { min-height: 260px; }
.wb-mod-hero--lean.wb-mod-hero--tall   .wb-mod-hero-grid { min-height: 380px; }
.wb-mod-hero-bg {
  position: absolute;
  top: calc(-1 * var(--wb-px-bleed, 0%));
  bottom: calc(-1 * var(--wb-px-bleed, 0%));
  left: 0; right: 0;
  background-size: cover; background-position: var(--wb-hero-focus, center top);
  will-change: transform;
}
.wb-mod-hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(100deg,
    rgba(37, 31, 25, var(--wb-hero-scrim, 0.92)) 0%,
    rgba(37, 31, 25, var(--wb-hero-scrim-2, 0.55)) 100%);
}
.wb-mod-hero-inner { position: relative; }
.wb-mod-hero-kicker { color: var(--color-gold); margin-bottom: var(--space-xxl); }

/* ---------- Hero type over a photograph ----------
   Most of these images are pale — dresses, ivory, daylight — and the scrim is
   not allowed to go darker to compensate. So the small type stops being tinted
   and takes the headline's own colour, which already reads: tan (#b8a98a) and
   the muted body grey are the two that disappear, white does not.

   The tracked labels are thin at this size, so they also get a tight shadow —
   the one place this system uses one. It is a legibility device over
   photography, not decoration: the flat espresso hero below has none. */
.wb-mod-hero--photo .wb-mod-hero-kicker,
.wb-mod-hero--photo .wb-mod-hero-content .t-label-lg { color: var(--color-on-dark); }
.wb-mod-hero--photo .wb-mod-hero-content p { color: var(--color-on-dark); }
.wb-mod-hero--photo .wb-mod-hero-kicker,
.wb-mod-hero--photo .wb-mod-hero-content .t-label-lg {
  text-shadow: 0 1px 3px rgba(37, 31, 25, 0.55);
}
.wb-mod-hero--photo .wb-mod-hero-content h1,
.wb-mod-hero--photo .wb-mod-hero-content p {
  text-shadow: 0 1px 2px rgba(37, 31, 25, 0.35);
}
.wb-mod-hero-grid { display: grid; grid-template-columns: 1fr; gap: 80px; align-items: center; }
.wb-mod-hero--panel .wb-mod-hero-grid { grid-template-columns: 3fr 2fr; }
.wb-mod-hero-content > * + * { margin-top: var(--space-lg); }
.wb-mod-hero-content .t-label-lg { display: block; }
.wb-mod-hero-content h1 { color: var(--color-on-dark); max-width: 16ch; }
.wb-mod-hero-content h1 em { font-style: italic; color: var(--color-gold); }
.wb-mod-hero-content p { max-width: 60ch; color: var(--color-on-dark-soft); }
.wb-mod-hero-actions { display: flex; gap: var(--space-md); flex-wrap: wrap; margin-top: var(--space-xl); }
.wb-mod-hero-panel img { display: block; width: 100%; aspect-ratio: 4/5; object-fit: cover; }

/* ---------- Author block ---------- */
/* The block that closes a journal entry. The three identity lines — name, role,
   email — are set tight so they read as one stack rather than three separate
   statements; the bio keeps its air below them. */
.wb-mod-author {
  display: grid; grid-template-columns: 120px 1fr; gap: var(--space-lg);
  /* Left, not centred: it closes a piece whose every line starts at the same
     rule, and an auto-centred block drifts off that edge. */
  max-width: 720px; margin: var(--space-xxl) 0 0;
  padding: var(--space-xl) 0 0; border-top: 1px solid var(--color-hairline);
  align-items: start;
}
/* Its wrapper already carries the page gutter, and the block brings its own
   margin — the band would otherwise open on 96px of nothing. */
.wb-page--author { padding-top: 0; }

/* The author closes a post that has just ended on a module band, and that band
   already spent its own bottom padding. The block's own margin on top of that
   was 224px of nothing between the last line and the byline. */
.wb-page--author .wb-mod-author { margin-top: 0; }

/* A post that opens on a hero: the body follows the band closely, the way a
   module follows one on a page. The 96px of .wb-page is the rhythm for a page
   that starts on its own title, and after a hero it reads as a hole. */
.wb-page--after-hero { padding-top: var(--space-xxl); }

/* No editor body, and modules following: the column ends on its last line and
   the module below brings its own opening rhythm. Left in, .wb-page's bottom
   padding and the module's top padding stack around a zero-height div. */
.wb-page--lean-bottom { padding-bottom: 0; }
.wb-mod-author-photo img {
  display: block; width: 120px; height: 120px;
  object-fit: cover; border-radius: var(--radius-pill);
}
.wb-mod-author-info .t-label { display: block; margin-bottom: var(--space-xxs); }
.wb-mod-author-info h3 { font-size: var(--type-display-sm-size); line-height: 1.15; margin: 0; }
.wb-mod-author-info .role { display: block; font-size: var(--type-body-sm-size); color: var(--color-label); margin-top: 1px; }
.wb-mod-author-info p { margin: var(--space-md) 0 0; font-size: var(--type-body-sm-size); }
.wb-mod-author-info .email { display: inline-block; margin-top: 1px; font-size: var(--type-body-sm-size); }

/* ---------- Responsive ---------- */
@media (max-width: 1080px) {
  .wb-mod-grid-items { grid-template-columns: repeat(2, 1fr); }
  .wb-mod-products-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-xl) var(--space-lg); }
  .wb-mod-dgrid-grid { grid-template-columns: repeat(min(var(--wb-mod-cols, 4), 3), 1fr); }
  .wb-mod-icons-grid { grid-template-columns: repeat(min(var(--wb-mod-cols, 3), 3), 1fr); }
  .wb-mod-split-grid,
  .wb-mod-carousel-grid,
  .wb-mod-callout-inner.has-image { gap: var(--space-xl); }
  .wb-mod-hero { padding: 96px var(--gutter); }
  .wb-mod-hero--xshort { padding: 56px var(--gutter); }
  .wb-mod-hero--tall .wb-mod-hero-grid { min-height: 420px; }
  .wb-mod-hero--lean.wb-mod-hero--tall .wb-mod-hero-grid { min-height: 320px; }
}

@media (max-width: 760px) {
  .wb-mod-split-grid,
  .wb-mod-carousel-grid,
  .wb-mod-callout-inner.has-image,
  .wb-mod-hero--panel .wb-mod-hero-grid { grid-template-columns: 1fr !important; }
  .wb-mod-split--media-left .wb-mod-split-text,
  .wb-mod-split--media-left .wb-mod-split-media { order: initial; }
  .wb-mod-grid-items,
  .wb-mod-products-grid,
  .wb-mod-icons-grid { grid-template-columns: 1fr; }
  /* A plate of images survives two-up on a phone where a text card would not. */
  .wb-mod-dgrid-grid { grid-template-columns: repeat(min(var(--wb-mod-cols, 4), 2), 1fr); }
  .wb-mod-journal-item,
  .wb-mod-journal--media-left .wb-mod-journal-item { grid-template-columns: 1fr !important; gap: var(--space-lg); }
  .wb-mod-journal--media-left .wb-mod-journal-text  { grid-column: 1; grid-row: 1; }
  .wb-mod-journal--media-left .wb-mod-journal-media { grid-column: 1; grid-row: 2; }
  .wb-mod-quote-grid { grid-template-columns: 1fr; gap: var(--space-lg); }
  .wb-mod-quote-grid .portrait { width: 120px !important; }
  .wb-mod-fullimage-media { height: 48vh; }
  .wb-mod-icons-grid--card-circle .wb-mod-icon-card { aspect-ratio: auto; border-radius: var(--radius-none); padding: var(--space-lg); }
  .wb-mod-author { grid-template-columns: 1fr; }
  .wb-mod-marks-inner { flex-direction: column; align-items: flex-start; gap: var(--space-lg); }
  .wb-mod-marks-viewport { width: 100%; }
}
