/* ============================================================================
   THEME — Design Lab skin  (cms_themes #5 "Design Lab Redesign")
   ============================================================================

   WHAT THIS IS
   The design language from /_design-lab/, applied to the REAL site's markup.
   The lab is 16 hand-written HTML pages whose CSS targets lab classes (.act,
   .statement, .hero__inner). The live site renders from the CMS through 27
   WebElement renderers and emits an entirely different vocabulary (.container,
   .section-title, .card-standard, .grid-container). Nothing in lab.css matches
   anything here, so this is a re-skin against the real classes rather than a
   copy of that file.

   HOW IT LOADS
   page_generator.php swaps the site's ONE stylesheet for whatever
   cmsActiveThemeStylesheet() returns, so a theme sheet REPLACES styles.css
   rather than adding to it. This file therefore imports the real stylesheet
   first and overrides afterwards, which keeps every layout, carousel,
   checkout and payment screen working exactly as before and leaves this file
   responsible only for the look.

   The import carries its own version token because the link tag's ?v= applies
   to this file, not to what it imports, and this server sends a 30-day
   max-age for CSS. BUMP THE TOKEN IF styles.css CHANGES, or staging will
   render new markup against an old base sheet.

   REVERTING
   Nothing here is destructive and no markup, renderer or content row is
   touched. Point staging back at theme 1 to undo it completely:
     UPDATE cms_theme_activation SET theme_id = 1 WHERE env = 'staging';
   ============================================================================ */

@import url("styles.css?v=lab-1");

/* Self-hosted, and deliberately NOT from fonts.googleapis.com: Chord Hero runs
   events in Guangzhou and Shanghai where that host is blocked, and a CDN link
   would drop the headline face for exactly those visitors. Served from
   /assets/fonts/ rather than /_design-lab/assets/fonts/ because the lab folder
   is on the never-copy-to-production list, and a skin that might one day ship
   cannot depend on a staging-only path. */
@font-face {
  font-family: "Nunito Sans";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("assets/fonts/NunitoSans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Nunito Sans";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("assets/fonts/NunitoSans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --lab-ink:       #1b1512;   /* warm dark — body text and dark grounds */
  --lab-ink-soft:  #5f5349;   /* warm taupe for secondary text */
  --lab-paper:     #ffffff;
  --lab-paper-dim: #f5f5f7;

  /* The brand orange, undiluted. It was previously darkened for fills so a
     white label could pass AA, and that darkening is what read as red. The fix
     is to leave the orange alone and darken the LABEL: ink on #ff5733 is
     5.73:1, where white on it is 3.15:1 and fails. */
  --lab-flame:     #ff5733;
  --lab-flame-deep:#e8431f;
  /* The one place the brand orange cannot go: small text on white needs 4.5:1
     and #ff5733 is 3.15:1. Chosen to stay as ORANGE as possible while passing
     rather than as close to the brand hue as possible, which at this lightness
     reads red-brown. */
  --lab-flame-ink: #c94200;

  --lab-display: "Nunito Sans", "Segoe UI", "Lucida Grande", system-ui, sans-serif;
  --lab-ease:    cubic-bezier(0.23, 1, 0.32, 1);
  --lab-measure: 78rem;
  --lab-gutter:  clamp(1.25rem, 5vw, 6rem);
  --lab-stack:   clamp(3.25rem, 8vh, 6.5rem);
}

/* ---- Type ------------------------------------------------------------------
   The site sets Arial and #333 — a cold grey that fights every warm photograph
   on the page. Body copy moves to the system UI stack for text and Nunito Sans
   for anything that behaves as a headline. */
body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Helvetica Neue", Arial, sans-serif;
  color: var(--lab-ink);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* NOTE THE ABSENT `color`, and do not add it back. Headings inherit, so they
   pick up --lab-ink from body on ordinary sections while any context that sets
   its own colour keeps it. An earlier draft set colour on the bare h1-h4
   selector and that beat the inherited white in .carousel-caption — which sets
   `color: white` on the wrapper and lets its headings inherit — turning the
   homepage hero headline into dark ink over a photograph. Typography here,
   colour by inheritance. */
h1, h2, h3, h4,
.section-title, .section-subtitle,
.card-title, .accordion-section-title,
.nft-title, .testimonial-slide-speaker {
  font-family: var(--lab-display);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* Explicit belt-and-braces for text that sits over imagery: these must stay
   light regardless of what the type layer above does. */
.carousel-caption, .carousel-caption h2, .carousel-caption h3,
.carousel-caption p, .carousel-item-overlay { color: #fff; }

/* clamp() rather than a fixed 2.5rem: the old size is nearly as large on a
   390px phone as on a 1440px monitor, which is what makes section headings
   wrap to four lines on mobile. */
.section-title {
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 3.1rem);
  font-weight: 680;
  line-height: 1.05;
  margin-bottom: 0.75rem;
}
.section-subtitle {
  font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.3rem);
  font-weight: 500;
  line-height: 1.45;
  color: var(--lab-ink-soft);
  letter-spacing: -0.01em;
}
.section-description, .card-description, .section-content p {
  color: var(--lab-ink-soft);
  line-height: 1.6;
}

/* ---- Rhythm ----------------------------------------------------------------
   1200px/20px is a 2015 container. The lab runs a wider measure with a much
   larger vertical step, and the vertical step is most of what reads as
   "designed" rather than "assembled". */
/* WHY THESE CARRY !important, when nothing else here does.
   Every Container element is rendered with its settings baked into a style
   attribute: `max-width:1200px; text-align:center; background-color:white;
   padding:40px 20px`. An inline declaration outranks any selector, so the
   whole rhythm layer of this theme was being silently discarded — which is
   most of why the skin read as "same page, new font". !important is the only
   thing that outranks a normal inline declaration, so it is used HERE ONLY,
   and only for the four properties the renderer inlines.

   Card backgrounds are inlined as `#F5F5F5 !important`, which not even this
   can beat — an important inline declaration wins outright. No matter: that
   grey is within a hair of --lab-paper-dim, so the cards already sit on the
   right colour by accident. */
.page-content .container {
  max-width: var(--lab-measure) !important;
  padding: var(--lab-stack) var(--lab-gutter) !important;
}
.page-content > .content-position { padding-block: 0; }
.card-container.grid-container { gap: 1.25rem; }

/* Alternating grounds — REMOVED, on purpose. The rule predates the ActBlock
   and Statement elements: back when every section was a white container, any
   rhythm beat none. Now the acts and statements carry their own grounds, and
   parity alternation was landing on the wrong nodes — a heading Container and
   the card Grid that belongs to it are separate DOM children, so one went dim
   and the other stayed white, splitting single sections in half visually. The
   lab's own pages keep non-act sections on plain paper, so plain paper is
   also the faithful choice. */

/* Card grids ran edge to edge while the headings above them sat inside the
   measure — the grid is a separate top-level row, so the container override
   never reached it. Same ceiling, same gutters, and a bottom step so a
   heading + grid pair reads as one section. */
.page-content .card-container.grid-container,
.page-content .grid-container {
  max-width: var(--lab-measure);
  margin-inline: auto;
  padding-inline: var(--lab-gutter);
  padding-block-end: var(--lab-stack);
}

/* An act's second button goes ghost, same as the statement's — primary plus
   quiet secondary is the lab's only button pairing. */
.act .button-container .button:not(:first-child) {
  background: transparent;
  color: var(--lab-ink);
  border: 1px solid rgba(27, 21, 18, 0.35);
}
.act--dark .button-container .button:not(:first-child) {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.44);
}

/* THE STRUCTURAL MOVE — media beside text, alternating sides.
   This is what actually makes a page read as the lab rather than as a stack of
   centred blocks, and it needs no content change: a Container that holds both
   a .section-image and a .section-content already has exactly the parts of a
   lab .act block, just stacked and centred by the author's "image above"
   choice. Turning the flex row back on and left-aligning the text is the whole
   transformation.

   :has() is doing the work — it fires only for containers that really hold an
   image, so text-only sections such as the awards heading stay centred, which
   is right for them. Below 900px everything returns to a single column, which
   is what the stacked layout was already doing. */
@media (min-width: 900px) {
  .page-content .container.image-above:has(> .section-image):has(> .section-content),
  .page-content .container.image-below:has(> .section-image):has(> .section-content) {
    flex-direction: row !important;
    align-items: center !important;
    text-align: left !important;
    gap: clamp(2rem, 5vw, 4.5rem);
  }
  .page-content .content-position > div:nth-child(even).container.image-above:has(> .section-image),
  .page-content .content-position > div:nth-child(even).container.image-below:has(> .section-image) {
    flex-direction: row-reverse !important;
  }
  /* .section-content carries its OWN inline text-align:center, separate from
     the container's, so left-aligning the container alone leaves the copy
     centred inside a left-hand column — which looks like a mistake rather than
     a choice. Both have to be overridden. */
  .page-content .container.image-above:has(> .section-image) > .section-content,
  .page-content .container.image-below:has(> .section-image) > .section-content {
    flex: 1 1 42%;
    text-align: left !important;
  }
  /* styles.css declares .section-title TWICE — once around line 1070 and again
     at 2879 under a "Featured Products" heading, and the second one adds
     text-align:center. Being later, it wins everywhere, so the heading stayed
     centred above left-aligned body copy. This selector is specific enough to
     beat it on its own; no !important needed, because the centring comes from
     a stylesheet rather than from a style attribute. */
  .page-content .container.image-above:has(> .section-image) .section-title,
  .page-content .container.image-below:has(> .section-image) .section-title,
  .page-content .container.image-above:has(> .section-image) .section-subtitle,
  .page-content .container.image-below:has(> .section-image) .section-subtitle {
    text-align: left;
    width: auto;
    margin-top: 0;
  }
  .page-content .container.image-above:has(> .section-image) > .section-image,
  .page-content .container.image-below:has(> .section-image) > .section-image { flex: 1 1 55%; }
  /* The buttons inherit the centring from the container's inline style. */
  .page-content .container.image-above:has(> .section-image) .container-buttons,
  .page-content .container.image-below:has(> .section-image) .container-buttons {
    justify-content: flex-start;
  }
}

/* Section imagery gets the same softened corner as the cards. The lab never
   shows a hard-cornered photograph, and this is most of why its sections read
   as composed rather than as an image dropped above some text. */
.container .section-image img,
.container .section-image video {
  border-radius: 15px;
  width: 100%;
  height: auto;
}

/* The two-column section layouts the CMS already supports get the lab's
   proportions and a real gap. These are the closest thing in the live markup
   to the lab's .act blocks — same parts, media beside text — so the layout is
   already there whenever an author picks image-left or image-right. */
@media (min-width: 900px) {
  .container.image-left,
  .container.image-right {
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: center;
  }
  .container.image-left > .section-content,
  .container.image-right > .section-content { flex: 1 1 42%; }
  .container.image-left > .section-image,
  .container.image-right > .section-image { flex: 1 1 55%; }
}

/* ---- Cards -----------------------------------------------------------------
   8px radius and a flat 2px shadow read as Bootstrap. The lab uses a softer
   corner and a hairline instead of a drop shadow, with the lift saved for
   hover so a static grid sits flat and quiet. */
.card, .nft-card {
  border-radius: 15px;
  border: 1px solid rgba(27, 21, 18, 0.12);
  box-shadow: none;
  background: var(--lab-paper);
  transition: transform 240ms var(--lab-ease), border-color 240ms var(--lab-ease);
}
@media (hover: hover) and (pointer: fine) {
  .card:hover, .nft-card:hover {
    transform: translateY(-4px);
    border-color: rgba(27, 21, 18, 0.28);
    box-shadow: none;
  }
}
.card-title, .nft-title {
  font-size: 1.15rem;
  font-weight: 640;
  line-height: 1.25;
}
.card-description { font-size: 0.94rem; }
.card-media img { border-radius: 0; }

/* ---- Buttons ---------------------------------------------------------------
   White on the brand orange, matching the lab's own .btn rule — which accepts
   the 3.15:1 contrast as a documented decision, with hover DARKENING to
   #e8431f (4.00:1) because a white label needs the fill to go down, not up.
   An earlier draft used ink labels here, misreading the palette note; the
   lab's actual buttons are white-labelled, and the owner confirmed white. */
.button, .card .button, .container-buttons a, .container-buttons button,
.btn-primary, input[type="submit"] {
  font-family: var(--lab-display);
  font-weight: 640;
  letter-spacing: -0.01em;
  border-radius: 999px;
  background: var(--lab-flame);
  color: #fff;
  border: none;
  padding: 0.8rem 1.5rem;
  transition: background 200ms var(--lab-ease), transform 200ms var(--lab-ease);
}
.button:hover, .card .button:hover, .container-buttons a:hover,
.btn-primary:hover, input[type="submit"]:hover {
  background: var(--lab-flame-deep);
  color: #fff;
  transform: translateY(-1px);
}

/* ---- Navigation ------------------------------------------------------------
   The lab nav is a dark translucent bar that the page scrolls under. Applied
   here without touching the navbar's mechanics — the hamburger, language
   selector and cart keep their existing behaviour and only the surface
   changes. */
.site-header .navbar,
header.site-header {
  background: rgba(27, 21, 18, 0.62);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.navbar .main-menu > li > a,
.navbar-links a,
.cls-trigger {
  font-family: var(--lab-display);
  font-weight: 560;
  letter-spacing: -0.005em;
  color: #fff;
}

/* THE NAV HAS TO FIT, not merely avoid wrapping.
   Nunito Sans sets wider than the Arial it replaced, and staging carries a
   seventh menu item ("Events & Promotions") that production does not, so the
   row ran out of space: labels broke onto a second line, and once that was
   stopped with nowrap they pushed the language pill and cart off the edge
   instead — the same failure wearing a different hat.

   Worth recording, since it is NOT caused by this theme: production wraps
   these same labels at ~1100px in its own font. The bar has never had a
   strategy between the desktop layout and the 768px hamburger; it simply had
   more slack. This scales the gap and label size with the viewport across that
   dead zone so all seven items, the language selector and the cart stay on one
   row down to the hamburger breakpoint. The clamps are what hold it: the
   middle terms are linear fits through 22px/16px at 1440 and 8px/13.5px at
   1100, and the bounds stop it growing or shrinking past either end. */
.navbar-links .main-menu {
  gap: clamp(0.5rem, calc(4.12vw - 2.33rem), 1.4rem);
}
.navbar-links .main-menu > li > a,
.navbar .main-menu > li > a {
  white-space: nowrap;
  font-size: clamp(0.82rem, calc(0.735vw + 0.34rem), 1rem);
}
/* The right-hand cluster must never be the thing that gets squeezed out — it
   holds the language selector and the cart, which are the only two controls in
   the bar a visitor can actually need. */
.navbar-right { flex: 0 0 auto; }

/* The linear fit above still leaves the cart clipped in the tightest part of
   the range, where seven labels, the selector and the cart are competing for
   about 1150px. Below 1280 the labels give up more than the fit would, because
   a slightly smaller menu label costs the visitor nothing and a missing cart
   costs them the purchase. */
@media (max-width: 1280px) {
  .navbar-links .main-menu { gap: 0.5rem; }
  .navbar-links .main-menu > li > a,
  .navbar .main-menu > li > a { font-size: 0.78rem; }
}

/* Around iPad-landscape width the labels have given all they can, so the space
   comes from the chrome instead: the logo and its margin. Both already step
   down at the mobile breakpoint, this just brings that forward to where the
   row actually runs out rather than where the hamburger happens to appear.

   KNOWN CEILING, deliberately not papered over: below roughly 950px seven
   labels plus the selector and cart do not fit at any readable size, and the
   real fix is to hand over to the hamburger earlier than 768px. That lives in
   navigation.php, which production shares — and production, with six shorter
   labels, does not need it. So it is a decision about the nav rather than a
   side effect of a design trial, and it stays out of this file. */
@media (max-width: 1120px) {
  .navbar-logo { height: 30px; }
  .logo-container { margin-right: 1rem; }
  .navbar-links .main-menu { gap: 0.4rem; }
  .navbar-links .main-menu > li > a,
  .navbar .main-menu > li > a { font-size: 0.74rem; }

  /* The language pill drops its word and keeps its globe, which is worth about
     55px — the difference between the cart being on the bar and off it. The
     label is CLIPPED, not display:none, so it stays in the accessibility tree
     and a screen reader still announces the current language; the trigger also
     carries title="English" already. */
  .cls-trigger { padding: 6px 10px; }
  .cls-trigger-label {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip-path: inset(50%); overflow: hidden; white-space: nowrap;
  }
}
.navbar .main-menu > li > a:hover,
.navbar-links a:hover { color: var(--lab-flame); }

/* ---- Dropdowns — the lab's panel ------------------------------------------
   navigation.php centres the panel under its trigger on a mid-grey ground with
   centred labels, which is why a two-word entry like "Van Gogh Explorer Set"
   wrapped into a ragged stack. The lab's panel is warm dark, left-aligned and
   wide enough for a title plus the one-line description beneath it.

   !important on background and alignment only: those rules ship in an inline
   <style> emitted after this stylesheet, so source order hands them the win at
   equal specificity. */
/* THE PANEL MUST CLEAR THE BAR, NOT START INSIDE IT.
   .submenu is positioned against its <li>, and that <li> is only 38px tall
   sitting 12px down inside an 80px bar — so `top: 100%` opened the panel at
   y=50, thirty pixels up inside the bar, covering the menu it belongs to.

   Fixed at the source rather than by nudging `top`: the row now stretches to
   the full height of the bar (as the lab's nav group does), so 100% genuinely
   means "below the bar", and the small offset after it is a deliberate gap.
   Stretching the row also enlarges the hover target, which is why the trigger
   pill now fills the bar height instead of floating in the middle of it. */
/* The height chain, top to bottom. .navbar is a fixed 80px block but
   .navbar-container only occupied its top 62px, and .navbar-left was a
   centred flex item inside that — so the trigger row bottomed out at y=50
   with thirty pixels of bar still below it, and no amount of stretching
   further down could reach the edge. Each level is given the height it was
   missing; the logo, language pill and cart are re-centred explicitly so
   stretching the container does not smear them down the bar. */
.navbar-container { height: 100% !important; align-items: stretch !important; }
.logo-container, .navbar-right, .hamburger-menu { align-self: center; }
/* DESKTOP ONLY — and this one was doing real damage on phones.
   Under 768px .navbar-links is not a row inside the bar, it is the open menu
   panel: position: fixed, top: 60px, sized by its content. Two things then
   collide. theme-lab gives .navbar a backdrop-filter, and an ancestor with a
   backdrop-filter becomes the containing block for its position: fixed
   descendants — so the panel's containing block is the 60px bar, not the
   viewport. height: 100% !important therefore resolved to SIXTY PIXELS.
   The panel painted its background across those 60px and every menu item
   below the first overflowed out of it and onto the hero photograph.
   That is the "transparent menu" — the background was never missing, the box
   was sixty pixels tall.
   The stretch and flex rules below it are the same story: a vertical list of
   full-width rows must not be stretched or centred like a horizontal bar. */
@media (min-width: 769px) {
  .navbar-left, .navbar-links, .navbar-links .main-menu { height: 100% !important; }
  .navbar-links .main-menu { align-items: stretch !important; }
  .navbar-links .main-menu > li { display: flex; align-items: center; }
}
/* REVERTED: the pill briefly filled the whole row. That made the hover state a
   heavy block and put the trigger's underline hard against the panel edge, so
   a 2px gap read as cramped even though it matched the lab numerically — the
   lab's bar is 54px, ours is 80px, and the same gap under a much taller pill
   does not feel the same. Back to the compact pill, with the panel offset
   below tuned to the eye instead: roughly 8px of daylight, between the 15px
   that felt detached and the 2px that felt stuck. */
.navbar-links .main-menu > li > a { align-self: center; }
/* The invisible bridge that keeps hover alive across the gap has to match it,
   or the menu closes when the pointer crosses the space. */
/* Must span from the row's bottom to the panel's top, or hover dies as the
   pointer crosses the gap. Sized a little over the offset above. */
@media (min-width: 769px) {
.has-submenu::before { height: 0.8rem !important; }
}

/* Desktop only. Under 769px the submenu is not a floating panel at all — it is
   an in-flow accordion that navigation.php collapses with max-height:0. Every
   declaration below describes a floating panel, and nine of them are
   !important, so unscoped they beat the accordion's own rules on a phone:
   the collapsed submenu kept this background and this padding, and since
   max-height does not clip padding it drew a rounded near-black bar under
   every parent item. Third unscoped desktop rule in this file to do that —
   the height chain and the submenu greys were the first two. */
@media (min-width: 769px) {
.submenu {
  /* Negative, and deliberately so: the row is stretched to the bar's height
     while the pill inside it is compact, so 100% lands well below the pill.
     Pulling back up puts the panel about 8px under the pill — clear of the
     trigger's underline, clear of the menu text, and neither detached nor
     stuck to it. */
  top: calc(100% - 5px) !important;
  background-color: rgba(27, 21, 18, 0.97) !important;
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px !important;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.42) !important;
  min-width: 17rem !important;
  left: 0 !important;
  transform: none !important;
  padding: 0.5rem !important;
  text-align: left !important;
}
}
.submenu li { text-align: left !important; width: 100%; }
.submenu a {
  display: grid !important;
  gap: 0.12rem;
  padding: 0.6rem 0.75rem !important;
  border-radius: 9px;
  text-align: left !important;
  white-space: normal !important;
  transition: background 180ms var(--lab-ease);
}
.submenu a:hover { background: rgba(255, 255, 255, 0.08); }
.submenu__label {
  font-family: var(--lab-display);
  font-size: 0.95rem; font-weight: 620; letter-spacing: -0.012em;
  color: #fff; line-height: 1.25;
}
.submenu__desc {
  font-size: 0.8rem; line-height: 1.3;
  color: rgba(255, 255, 255, 0.58);
  letter-spacing: -0.004em;
}
.submenu a:hover .submenu__label { color: var(--lab-flame); }

/* The hover underline spans the whole row. navigation.php sizes it at 80% and
   centres it, which suited the old centred dropdown; against left-aligned rows
   it stopped short at both ends and read as a mis-measured rule rather than a
   deliberate one. Full width of the row, wiping in from the left and out to
   the right — the same direction as the top-level nav underline, so the two
   levels behave alike. Percentages on an absolutely positioned box resolve
   against the padding box, so 100% covers the row's padding too. */
.submenu a::after {
  width: 100% !important;
  left: 0 !important;
  bottom: 0.1rem;
  transform: scaleX(0) !important;
  transform-origin: bottom right;
}
.submenu a:hover::after {
  transform: scaleX(1) !important;
  transform-origin: bottom left;
}

/* Top-level items get the lab's quiet hover pill rather than only a colour
   change, so the trigger reads as a target before it is clicked. */
.navbar-links .main-menu > li > a {
  padding: 0.45rem 0.7rem !important;
  border-radius: 9px;
  transition: background 180ms var(--lab-ease), color 180ms var(--lab-ease);
}
.navbar-links .main-menu > li > a:hover { background: rgba(255, 255, 255, 0.09); }

/* THE SCROLLED BAR STAYS DARK, and the hover stays legible.
   navigation.php turns the bar brand-orange on scroll (.navbar.scrolled) and
   on hero-less pages (body.no-hero .navbar), while its hover rule sets link
   text to #FF5733 — the SAME orange. Hovered text therefore vanished into the
   bar. The lab's nav is dark translucent at every scroll position, so keeping
   it dark fixes the appearance and the bug in one move: flame-on-dark reads,
   flame-on-flame does not.

   !important because those rules live in an inline <style> that page_generator
   emits AFTER the stylesheet link, so source order hands them the win at equal
   specificity. */
.navbar.scrolled,
body.no-hero .navbar,
body.no-hero .site-header .navbar {
  background-color: rgba(27, 21, 18, 0.62) !important;
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}
.navbar.scrolled .navbar-links a,
body.no-hero .navbar .navbar-links a { color: #fff; }
.navbar-links a:hover,
.navbar.scrolled .navbar-links a:hover,
body.no-hero .navbar .navbar-links a:hover,
.navbar .main-menu > li > a:hover { color: var(--lab-flame) !important; }

/* Opaque fallback where the blur is unavailable or unwanted — a translucent
   bar with no blur behind it is unreadable, not merely plainer. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header .navbar, header.site-header,
  .navbar.scrolled, body.no-hero .navbar { background-color: var(--lab-ink) !important; }
}
@media (prefers-reduced-transparency: reduce) {
  .site-header .navbar, header.site-header,
  .navbar.scrolled, body.no-hero .navbar {
    background-color: var(--lab-ink) !important;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* ---- Partners --------------------------------------------------------------
   Logos belong to other companies and arrive in a dozen colours and weights;
   holding them at one visual weight stops the strip reading as a ransom note.
   Colour returns on hover, so the brand is never permanently altered. */
.partner-logo {
  filter: grayscale(1);
  opacity: 0.62;
  transition: filter 220ms var(--lab-ease), opacity 220ms var(--lab-ease);
}
.partner-logo:hover { filter: none; opacity: 1; }

/* ---- Alternating grounds ---------------------------------------------------
   A run of identical white bands is what makes a long page read as one
   undifferentiated scroll. This gives every other section a warm off-white,
   which is the cheapest way to get the lab's sense of separate movements. */
.page-content .container:nth-of-type(even) { background: var(--lab-paper-dim); }

/* ============================================================================
   LAB ELEMENTS — .act and .statement, emitted by the ActBlock and Statement
   WebElements. Ported from _design-lab/assets/lab.css and retokenised onto
   --lab-*; the class names are the lab's own (verified against styles.css:
   zero collisions), so a lab mockup and a CMS page built from these elements
   are the same design by construction rather than by imitation.
   ============================================================================ */
.display {
  font-family: var(--lab-display);
  font-size: clamp(2.1rem, 6.5vw, 5.5rem);
  font-weight: 700;
  line-height: 0.96;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
/* SCOPED TO THE CLASS, NOT TO EACH SECTION — deliberately.
   These three were enumerated section by section (.act, .statement, .news,
   .paths, .proof, .comparison-section...) and every new lab element then
   rendered at browser-default size until someone remembered to extend the
   list. That happened four times. .eyebrow/.headline/.lede/.display appear
   nowhere in styles.css (checked), so they are safe to own outright: any
   element emitting them now inherits the scale for free. */
.eyebrow {
  font-size: 0.75rem; font-weight: 620;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--lab-flame-ink);
}
.headline {
  font-family: var(--lab-display);
  font-size: clamp(1.9rem, 4.2vw, 3.4rem);
  font-weight: 680; line-height: 1.03; letter-spacing: -0.023em;
  text-wrap: balance;
}
.lede {
  font-size: clamp(1.02rem, 1.3vw, 1.3rem);
  line-height: 1.5; letter-spacing: -0.006em;
  color: var(--lab-ink-soft);
  max-width: 46ch; text-wrap: pretty;
}

.act {
  display: grid; gap: clamp(2rem, 5vw, 4.5rem);
  grid-template-columns: 1fr;
  align-items: center;
  padding-block: var(--lab-stack);
  padding-inline: max(var(--lab-gutter), calc((100% - var(--lab-measure)) / 2));
  background: var(--lab-paper);
}
@media (min-width: 900px) {
  .act { grid-template-columns: 1fr 1fr; }
  .act--flip .act__media { order: -1; }
}
.act__text { display: grid; gap: 1.25rem; justify-items: start; text-align: left; }

/* A brand mark in the eyebrow slot. Sized above the ChordSpheres kit's stated
   120px screen minimum — below that its letterforms break up — and given the
   clear space its guidelines ask for (the height of the C on every side),
   which is why this carries its own margin rather than relying on the act's
   row gap. */
.eyebrow--logo { line-height: 0; }
.eyebrow--logo img {
  width: clamp(140px, 15vw, 178px);
  height: auto;
  display: block;
  margin-block: 0.15rem 0.55rem;
}
.act__media img { width: 100%; border-radius: 13px; aspect-ratio: 3/2; object-fit: cover; }
/* fit:contain — show the whole artwork instead of cropping it to 3:2. The
   fixed ratio is released rather than kept-and-letterboxed, so a 2.4:1 product
   shot renders at its own proportions instead of sitting in bars; max-height
   stops a near-square flat-lay from towering over its own copy. */
.act--contain .act__media img {
  aspect-ratio: auto;
  object-fit: contain;
  height: auto;
  max-height: 32rem;
}
.act--dark { background: var(--lab-ink); color: #fff; }
.act--dark .headline { color: #fff; }
.act--dark .lede { color: rgba(255, 255, 255, 0.72); }
.act--dark .eyebrow { color: var(--lab-flame); }
.act--tint { background: var(--lab-paper-dim); }
/* The act renders its own buttons through the site's generateButtons(), which
   emits .button-container — left-aligned here, centred everywhere else. */
.act .button-container { justify-content: flex-start; margin-top: 0.25rem; }

.statement {
  position: relative; min-height: min(78svh, 700px);
  display: grid; place-items: center; text-align: center;
  padding: var(--lab-stack) var(--lab-gutter);
  background-color: var(--lab-ink);
  color: #fff; overflow: hidden; isolation: isolate;
}
.statement--hero { min-height: min(92svh, 820px); align-items: end; }
/* ---- Two dark bands in a row -------------------------------------------
   A full-bleed statement after a hero (or after a dark act) has no boundary
   against the one above it: both are photographs under a dark wash, so they
   read as a single undifferentiated mass. Occurs on 7 pages.

   A margin would be worse, not better — the page ground is white, so a gap
   between two dark bands shows as a hard seam.

   The hero and the band under it live in different `.content-position`
   wrappers, so a plain `+` between them never matches — the selector has to
   cross the wrapper.

   Instead the second band takes a step down in tone, an explicit top edge,
   and an inset shadow that reads as a lift. It also gives up height, because
   two things claiming 80-90svh in a row means neither is the arrival. */
.content-position.position-hero + .content-position > .statement:first-child,
.statement--hero + .statement,
.statement + .statement,
.act--dark + .statement {
    min-height: min(66svh, 580px);
    border-top: 1px solid rgba(255, 255, 255, 0.16);
    box-shadow: inset 0 26px 52px -26px rgba(0, 0, 0, 0.8);
}
.content-position.position-hero + .content-position > .statement:first-child::after,
.statement--hero + .statement::after,
.statement + .statement::after,
.act--dark + .statement::after {
    background: rgba(27, 21, 18, 0.78);
}

.statement__bg {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
}
/* Scrim, so white type stays readable over any frame of the media. The hero
   variant grades it toward the bottom where its text sits; the closer holds
   an even wash. */
.statement::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(27, 21, 18, 0.62);
}
/* TWO gradients, because the hero text is left-aligned but the vertical scrim
   is weakest (0.15) at exactly the height the text sits. Over a dark photo that
   is fine; over a bright one — a lit phone screen, a pale wall — the copy sits
   on its own worst case. The horizontal layer darkens the text column and
   fades out by 70%, so the artwork on the right keeps its contrast while the
   words keep theirs. The vertical layer still anchors the nav and the lower
   edge. */
.statement--hero::after {
  background:
    linear-gradient(90deg,
      rgba(27, 21, 18, 0.74) 0%, rgba(27, 21, 18, 0.55) 38%,
      rgba(27, 21, 18, 0.18) 68%, rgba(27, 21, 18, 0.06) 100%),
    linear-gradient(180deg,
      rgba(27, 21, 18, 0.52) 0%, rgba(27, 21, 18, 0.12) 38%, rgba(27, 21, 18, 0.78) 100%);
}
/* Centred hero copy has no "text side" to darken, so it keeps the plain
   vertical wash rather than an off-centre one. */
.statement--hero:not(:has(.statement__inner)) ::after,
.statement--hero.statement--centred::after {
  background: linear-gradient(180deg,
    rgba(27, 21, 18, 0.55) 0%, rgba(27, 21, 18, 0.15) 35%, rgba(27, 21, 18, 0.85) 100%);
}
.statement__inner {
  display: grid; gap: 1.5rem; justify-items: center;
  max-width: min(100%, 46rem);
}
.statement--hero .statement__inner {
  justify-items: start; text-align: left;
  max-width: min(100%, 44rem);
  margin-right: auto; padding-block-end: clamp(1rem, 6vh, 4rem);
}
.statement .display { color: #fff; max-width: 18ch; }
.statement .lede { color: rgba(255, 255, 255, 0.82); max-width: 52ch; }
.statement .eyebrow { color: var(--lab-flame); }
.statement__kicker {
  font-family: var(--lab-display);
  font-size: clamp(1.25rem, 2.4vw, 2rem);
  font-weight: 660; letter-spacing: -0.025em; line-height: 1.15;
}
.statement .button-container { margin-top: 0.5rem; }
.statement--hero .button-container { justify-content: flex-start; }
/* Ghost/secondary buttons on a statement's dark ground go light to stay
   visible; the primary is already white-on-flame. */
.statement .button-container .button:not(:first-child) {
  background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.44);
}

/* ---- Recognition strip -----------------------------------------------------
   The lab renders the five badges as a quiet borderless row (.proof__row);
   the CMS renders them as bordered Cards in a Grid. A ProofStrip element that
   collects children was considered and rejected: child-gathering requires a
   case in the legacy dispatch switch (see the Marquee pattern) or the cards
   double-render at top level. Two selectors do the whole job instead.

   The hooks are the theme-5 row ids (badge grid 3448, its heading 3437) —
   content-coupled by design: this stylesheet is already specific to theme 5,
   and rows keep their ids for the life of the theme. */
[data-cms-id="3448"] {
  display: flex !important;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  max-width: var(--lab-measure);
  margin-inline: auto;
  padding-block: 0 var(--lab-stack);
}
[data-cms-id="3448"] .card {
  background: transparent !important;
  border: none;
  box-shadow: none;
  border-radius: 0;
  flex: 0 1 150px;
  text-align: center;
}
[data-cms-id="3448"] .card:hover { transform: none; }
/* !important against the per-card inline <style> blocks the Grid renderer
   emits (#card-<id> .card-media img { width:100% } etc.) — an ID selector
   beats this attribute selector, and those blocks crop the badges to 3/2. */
[data-cms-id="3448"] .card-media {
  aspect-ratio: auto !important;
  width: auto !important;
  height: auto !important;
  flex: 0 0 auto !important;
}
[data-cms-id="3448"] .card-content { padding: 0 !important; }
[data-cms-id="3448"] .card-media img {
  height: 72px !important;
  width: auto !important;
  margin-inline: auto;
  object-fit: contain !important;
  aspect-ratio: auto !important;
}
[data-cms-id="3448"] .card-title {
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 520;
  color: var(--lab-ink-soft);
  line-height: 1.35;
  margin-top: 0.6rem;
}
[data-cms-id="3437"] { padding-block-end: 1rem !important; }

/* ---- Newsletter — the lab's signup band ------------------------------------
   Ported from the lab's .signup: a centred single column with a pill input
   and button on the warm dim ground, replacing the boxed section. */
.newsletter-section {
  background: var(--lab-paper-dim);
  padding: var(--lab-stack) var(--lab-gutter);
}
.newsletter-content {
  max-width: 34rem;
  margin-inline: auto;
  text-align: center;
  display: grid;
  gap: 0.9rem;
  justify-items: center;
}
.newsletter-content h2 {
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  font-weight: 680;
  letter-spacing: -0.02em;
}
.newsletter-content p { color: var(--lab-ink-soft); line-height: 1.55; }
.newsletter-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
  width: 100%;
}
.newsletter-form input[type="email"] {
  /* NOT `flex: 1 1 16rem`. styles.css turns this form to flex-direction:column
     below 768px, and a flex-basis applies to the MAIN axis — so in column mode
     16rem became the input's HEIGHT and the field rendered 256px tall. Basis
     auto with a min-width keeps the intent (roughly 16rem before wrapping) in
     row mode without touching height in column mode. */
  flex: 1 1 auto;
  min-width: min(16rem, 100%);
  min-height: 44px;
  padding: 0.6rem 1.1rem;
  border: 1px solid rgba(27, 21, 18, 0.25);
  border-radius: 999px;
  font-size: 1rem;
  background: var(--lab-paper);
}
.newsletter-form input[type="email"]:focus-visible {
  outline: 2px solid var(--lab-flame-ink);
  outline-offset: 1px;
}
.newsletter-form button {
  min-height: 44px;
  padding: 0.6rem 1.5rem;
  border: none;
  border-radius: 999px;
  background: var(--lab-flame);
  color: #fff;
  font-weight: 640;
  font-size: 1rem;
  cursor: pointer;
  transition: background 200ms var(--lab-ease);
}
.newsletter-form button:hover { background: var(--lab-flame-deep); color: #fff; }

/* ---- Partners marquee ------------------------------------------------------
   styles.css gives each .partner-logo a 140px-tall flex box; an earlier
   version of this theme then shrank the IMAGE inside it to 44px, which
   produced tiny marks floating in a tall empty band — the worst of both.
   This is the lab's own sizing rule (its comment on a flat 46px: "too
   small"), nudged larger per the owner: a shared visible height with width
   guards, because these logos run from near-square to very wide and height
   alone lets the wide ones hog the track while square ones vanish. The box
   collapses to its content, which is what removes the dead vertical space. */
.partner-marquee { padding-block: 0.5rem var(--lab-stack); }
.partner-logo {
  height: auto !important;
  display: flex; align-items: center;
}
.partner-logo img {
  height: clamp(64px, 8vw, 110px);
  min-width: 90px; max-width: 260px;
  width: auto; object-fit: contain;
}

/* ---- Testimonials — a quiet band, not a white gap --------------------------
   Given a ground so it separates the sections either side of it. Without this
   a testimonial carousel between two white sections leaves one long
   undifferentiated field, and any section that genuinely needs white (a photo
   shot on white, like the parts flat-lay) has nothing to stand out from. */
.testimonial-carousel-container {
  /* styles.css caps this at 1200px with a 3rem auto margin, so a background
     on it paints a floating BOX rather than a band across the page — which is
     why the first attempt at this changed nothing visible. Released to full
     width here, with the slides put back on the measure inside, so the ground
     actually separates the sections either side. */
  max-width: none !important;
  margin: 0 !important;
  background: var(--lab-paper-dim);
  padding: var(--lab-stack) var(--lab-gutter);
  border-radius: 0;
}
.testimonial-carousel-slides,
.testimonial-carousel-controls,
.testimonial-carousel-indicators {
  max-width: var(--lab-measure);
  margin-inline: auto;
}
.testimonial-carousel-container .testimonial-slide-quote { color: var(--lab-ink); }

/* ---- Image comparison — the paint stage, made draggable --------------------
   The lab illustrates "your design, your story" with a STATIC before/after
   photograph. The live site has something better for the same beat: a real
   before/after slider the reader drags. Kept, and dressed in the lab's
   clothes rather than left looking like a widget borrowed from another site —
   section rhythm and measure, a centred head, a softened frame, and label
   pills in the palette instead of black boxes.

   The head classes are shared with every other lab section, so the type scale
   above already covers them. */
/* Selector matches the renderer's OWN inline rule exactly.
   ImageComparison.php emits a per-page <style> containing
   `.content-section.comparison-section { max-width:1200px; margin:0 auto 30px }`,
   and a one-class selector — even an !important one — is the wrong tool
   against it when the goal is simply to out-rank a two-class rule reliably in
   every browser. Matching its shape and adding !important removes all doubt.
   Without this the dim ground paints a centred column with white gutters
   instead of a band, which is the same box-not-a-band trap the testimonial
   container had. */
.content-section.comparison-section {
  max-width: none !important;
  margin: 0 !important;
  padding: var(--lab-stack) var(--lab-gutter) !important;
  background: var(--lab-paper);
}
.content-section.comparison-section.comparison--tint { background: var(--lab-paper-dim); }
.content-section.comparison-section.comparison--dark { background: var(--lab-ink); color: #fff; }
.comparison--dark .headline { color: #fff; }
.comparison--dark .lede { color: rgba(255,255,255,.72); }
.comparison--dark .eyebrow { color: var(--lab-flame); }

.comparison__head {
  display: grid; gap: 1rem; max-width: 44rem;
  margin: 0 auto clamp(2.5rem, 6vh, 4rem);
  text-align: center; justify-items: center;
}
.comparison__head .eyebrow { color: var(--lab-flame-ink); }

/* The frame. The renderer inlines width/aspect-ratio/margin on the container,
   so only the surface treatment is set here — nothing that would fight it. */
.comparison-section .comparison-container { max-width: var(--lab-measure) !important; }
.comparison-section .image-comparison {
  border-radius: 15px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(27,21,18,.06), 0 18px 44px rgba(27,21,18,.08);
}
/* ACT LAYOUT — copy beside the slider, the same shape as every other section
   on these pages. The emitted markup is already exactly two children (the head
   and the frame), so this is a layout change rather than a re-render: the
   section becomes the same two-column grid .act uses, with the same gap and
   measure, and the head unwinds from centred to left-aligned.

   Only above 900px. Below that it stays stacked, which is what .act does too. */
@media (min-width: 900px) {
  .content-section.comparison--act {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: clamp(2rem, 5vw, 4.5rem);
    padding-inline: max(var(--lab-gutter), calc((100% - var(--lab-measure)) / 2)) !important;
  }
  .comparison--act .comparison__head {
    text-align: left;
    justify-items: start;
    margin: 0;
    max-width: none;
  }
  .comparison--act .comparison-container {
    margin: 0 !important;
    width: 100% !important;
  }
  /* Slider on the left, copy on the right. Order rather than DOM sequence, so
     reading order stays claim-then-illustration for screen readers. */
  .comparison--act-flip .comparison-container { order: -1; }
  /* In this layout the frame fills its column instead of being capped for a
     centred page, so the portrait cap below is relaxed here. */
  .comparison--act.comparison--portrait .image-comparison {
    max-width: min(430px, 100%) !important;
  }
  .comparison--act-flip.comparison--portrait .image-comparison { margin-inline: 0 auto !important; }
}

/* A portrait frame has to be capped by WIDTH or the aspect ratio makes it
   thousands of pixels tall. The renderer sets max-width on an ID selector,
   which outranks any class, hence !important — the one place in this block
   that needs it. min() keeps it from overflowing a phone. */
.comparison--portrait .image-comparison {
  max-width: min(430px, 82vw) !important;
  margin-inline: auto !important;
}
.comparison--dark .image-comparison { box-shadow: 0 18px 44px rgba(0,0,0,.4); }

/* Labels: the site's black boxes become the lab's quiet pills. Ink on white
   at 0.78rem clears AA comfortably, where white-on-black-70% was heavier than
   anything else on these pages. */
.comparison-section .comparison-label {
  background: rgba(255,255,255,.92) !important;
  color: var(--lab-ink) !important;
  border-radius: 999px !important;
  padding: .4rem .9rem !important;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .01em;
  box-shadow: 0 1px 3px rgba(27,21,18,.12);
}
/* The handle picks up the brand orange so the one draggable thing on the page
   looks draggable. */
.comparison-section .slider-handle {
  background: var(--lab-flame) !important;
  box-shadow: 0 2px 10px rgba(27,21,18,.28) !important;
  border: none !important;
}
.comparison-section .slider-handle::before,
.comparison-section .slider-handle::after { color: #fff !important; }
.comparison-section .comparison-slider { background: rgba(255,255,255,.9) !important; }

/* ---- Spec sheet (.specs, emitted by SpecSheet) -----------------------------
   Ported from lab.css and retokenised. The lab hardcodes two spec columns plus
   the media; here the element decides how many columns it actually filled, so
   the grid is declared for the three-part case and collapses cleanly when a
   sheet has only one column of groups. */
.specs { padding: var(--lab-stack) var(--lab-gutter); background: var(--lab-paper); }
.specs__head {
  display: grid; gap: 1rem; max-width: var(--lab-measure);
  margin: 0 auto clamp(2.5rem, 6vh, 4rem);
}
.specs__head .eyebrow { color: var(--lab-ink-soft); }
.specs__grid {
  display: grid; gap: clamp(2rem, 4vw, 3.5rem);
  max-width: var(--lab-measure); margin: 0 auto;
}
@media (min-width: 900px) {
  .specs__grid { grid-template-columns: 1fr 1fr 1.15fr; align-items: start; }
  /* One column of groups + media: let the media keep its share rather than
     stretching a lone spec column across two thirds of the measure. */
  .specs__grid:has(> .specs__col:only-of-type) { grid-template-columns: 1.4fr 1.15fr; }
}
.specs__col h3 {
  font-family: inherit;
  font-size: 0.78rem; font-weight: 620; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--lab-flame-ink);
  margin-bottom: 1rem;
}
.specs__col h3 + dl { margin-bottom: 2rem; }
.specs__col dl { display: grid; grid-template-columns: auto 1fr; gap: 0.55rem 1.25rem; }
.specs__col dt { font-size: 0.9rem; color: var(--lab-ink-soft); }
.specs__col dd { font-size: 0.9rem; font-weight: 520; text-align: right; margin: 0; }
.specs__media img { width: 100%; border-radius: 13px; object-fit: contain; }
.specs__note {
  margin-top: 0.9rem; text-align: center;
  font-size: 0.72rem; color: var(--lab-ink-soft);
}

/* ---- Paths (.paths, emitted by PathGrid) -----------------------------------
   Parallel routes, not a sequence. Ported from lab.css and retokenised. */
.paths { padding: var(--lab-stack) var(--lab-gutter); background: var(--lab-paper-dim); }
.paths__head {
  display: grid; gap: 1rem; max-width: 44rem;
  margin: 0 auto clamp(3rem, 7vh, 4.5rem);
  text-align: center; justify-items: center;
}
.paths__head .eyebrow { color: var(--lab-flame-ink); }
.paths__head .lede { color: var(--lab-ink-soft); max-width: 46ch; }
.paths__grid {
  display: grid; gap: 1.5rem; max-width: var(--lab-measure); margin: 0 auto;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}
.path {
  background: var(--lab-paper); border-radius: 15px; overflow: hidden;
  display: grid; grid-template-rows: auto 1fr;
  box-shadow: 0 1px 3px rgba(27,21,18,.06), 0 12px 32px rgba(27,21,18,.05);
  transition: transform 260ms var(--lab-ease), box-shadow 260ms var(--lab-ease);
}
@media (hover: hover) and (pointer: fine) {
  .path:hover {
    transform: translateY(-4px);
    box-shadow: 0 2px 6px rgba(27,21,18,.08), 0 20px 48px rgba(27,21,18,.09);
  }
}
.path img { width: 100%; aspect-ratio: 3/2; object-fit: cover; }
.path__body {
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: grid; gap: 0.6rem; align-content: start;
}
.path__body h3 {
  font-family: var(--lab-display);
  font-size: 1.25rem; font-weight: 660; letter-spacing: -0.018em;
}
.path__hook {
  font-weight: 600; color: var(--lab-flame-ink);
  font-size: 1rem; letter-spacing: -0.01em;
}
.path__body p { color: var(--lab-ink-soft); font-size: 0.95rem; line-height: 1.55; }
.path__body .button-container { justify-content: flex-start; margin-top: .4rem; }

/* ---- Proof (.proof, emitted by ProofStrip) ---------------------------------
   Award marks, borderless. Replaces the homepage's row-id-pinned version, which
   could not travel to the other pages carrying the same strip. */
.proof { padding: var(--lab-stack) var(--lab-gutter); text-align: center; background: var(--lab-paper); }
.proof__head { display: grid; gap: 1rem; justify-items: center; margin-bottom: clamp(2.5rem, 6vh, 4rem); }
.proof__head .eyebrow { color: var(--lab-ink-soft); }
.proof__row {
  display: grid; gap: clamp(1.5rem, 4vw, 3.5rem);
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  max-width: var(--lab-measure); margin: 0 auto; align-items: start;
}
.proof__item { display: grid; gap: 0.85rem; justify-items: center; text-decoration: none; color: inherit; }
/* Was clamp(64px, 8vw, 92px) — too small to read. These marks carry fine print
   ("CERTIFIED PEDAGOGICAL QUALITY", "FINALIST 2021") that is the whole point of
   showing them, and at 92px it was illegible. The sources are 600x400 with
   generous internal padding, so the badge itself occupies well under half that;
   160px of frame still renders inside the source resolution rather than
   upscaling. */
.proof__item img { height: clamp(96px, 12vw, 160px); width: auto; object-fit: contain; }
.proof__item span {
  font-size: 0.78rem; line-height: 1.35; color: var(--lab-ink-soft);
  letter-spacing: 0.005em; max-width: 18ch;
}

/* ---- Newsroom (.news, emitted by the Newsroom element) ---------------------
   Ported from the lab: not a uniform card grid — the lead story gets the
   room, everything after it is a dated list. Dark ground, same as the lab. */
.news {
  padding: var(--lab-stack) var(--lab-gutter);
  background-color: var(--lab-ink);
  color: #fff;
}
.news__head {
  display: grid; gap: 1rem; max-width: var(--lab-measure);
  margin: 0 auto clamp(3rem, 7vh, 4.5rem);
}
.news__head .eyebrow { color: var(--lab-flame); }
.news__head .headline { color: #fff; }
.news__head .lede { color: rgba(255, 255, 255, 0.66); }

.news__lead {
  display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center;
  max-width: var(--lab-measure); margin: 0 auto clamp(3rem, 7vh, 4.5rem);
}
@media (min-width: 900px) { .news__lead { grid-template-columns: 1.05fr 1fr; } }
.news__lead img { width: 100%; aspect-ratio: 3/2; object-fit: cover; border-radius: 13px; }
.news__meta {
  display: flex; gap: 0.6rem; align-items: center;
  font-size: 0.76rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}
.news__lead h3 {
  font-family: var(--lab-display);
  font-size: clamp(1.6rem, 3.1vw, 2.6rem); font-weight: 660;
  letter-spacing: -0.026em; line-height: 1.12; margin: 0.85rem 0 0;
  text-wrap: balance; color: #fff;
}
.news__excerpt {
  margin-top: 1rem; font-size: clamp(1rem, 1.4vw, 1.15rem);
  line-height: 1.5; color: rgba(255, 255, 255, 0.72); text-wrap: pretty;
}
.news__link {
  display: inline-block; margin-top: 1.4rem; text-decoration: none;
  font-weight: 560; font-size: 0.95rem; color: var(--lab-flame);
  border-bottom: 1px solid currentColor; padding-bottom: 2px;
}

.news__second {
  display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: center;
  max-width: var(--lab-measure); margin: 0 auto clamp(3rem, 7vh, 4.5rem);
  padding-top: clamp(2rem, 5vh, 3rem);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
@media (min-width: 900px) { .news__second { grid-template-columns: 1fr 1.4fr; } }
.news__second img { width: 100%; aspect-ratio: 16/10; object-fit: cover; border-radius: 14px; }
.news__second h3 {
  font-family: var(--lab-display);
  font-size: clamp(1.15rem, 1.9vw, 1.55rem); font-weight: 640;
  letter-spacing: -0.018em; line-height: 1.2; margin: 0.7rem 0 0.6rem; color: #fff;
}
.news__second p { color: rgba(255, 255, 255, 0.66); font-size: 0.95rem; line-height: 1.55; }

.news__list { max-width: var(--lab-measure); margin: 0 auto; border-top: 1px solid rgba(255, 255, 255, 0.12); }
.news__row {
  display: grid; gap: 0.35rem 1.5rem; align-items: baseline;
  grid-template-columns: 1fr; padding: 1.15rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
  text-decoration: none; color: inherit;
  transition: background 220ms var(--lab-ease), padding-inline 220ms var(--lab-ease);
}
@media (min-width: 760px) { .news__row { grid-template-columns: 7.5rem 1fr; } }
@media (hover: hover) and (pointer: fine) {
  .news__row:hover { background: rgba(255, 255, 255, 0.045); padding-inline: 1rem; }
  .news__row:hover .news__row-title { color: var(--lab-flame); }
}
.news__row-date {
  font-size: 0.78rem; letter-spacing: 0.07em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42); font-variant-numeric: tabular-nums;
}
.news__row-title {
  font-size: clamp(0.98rem, 1.25vw, 1.1rem); font-weight: 520;
  line-height: 1.35; letter-spacing: -0.008em; color: #fff;
  transition: color 200ms var(--lab-ease);
}
.news__more { display: flex; justify-content: center; margin-top: clamp(2.5rem, 6vh, 3.5rem); }

/* ---- Motion ----------------------------------------------------------------
   Scroll-linked reveal, native and JS-free, matching the lab's. Guarded by
   @supports so browsers without scroll-driven animations simply show the
   content — the animation is progressive enhancement, never a prerequisite
   for seeing the page. */
@keyframes lab-rise {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .page-content .container,
    .card-container.grid-container > .card {
      animation: lab-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 220px;
    }
  }
}
@media (prefers-reduced-motion: reduce) {
  .page-content .container,
  .card-container.grid-container > .card,
  .card, .nft-card {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    transition: none;
  }
}

/* ============================================================================
   ARCH — the full newsroom archive. Same dark ground and accent as .news, but
   a different job: .news leads with the featured story, an archive weights
   nothing above anything else and stays strictly chronological.

   Cards rather than the rows .news uses, because a plain list made every story
   look identical, and workshops and exhibitions are the most photographed
   thing the company does. The picture is the reason to click.

   No topic chips and no place line: cms_news has neither a category nor a
   location column, so the concept's versions of those were describing fields
   that do not exist. The chips here are years, which are real.
   ========================================================================== */
.arch {
  padding: var(--lab-stack) var(--lab-gutter);
  background-color: var(--lab-ink);
  color: #fff;
}
.arch__head {
  display: grid; gap: 1rem; max-width: var(--lab-measure);
  margin: 0 auto clamp(2.5rem, 6vh, 4rem);
}
.arch__head .eyebrow { color: var(--lab-flame); }
.arch__head .lede { color: rgba(255, 255, 255, 0.66); max-width: 52ch; }

.arch__filter {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  max-width: var(--lab-measure); margin: 0 auto 1.5rem;
}
.arch__chip {
  font-size: 0.82rem; font-weight: 600; letter-spacing: 0.01em;
  padding: 0.45rem 1rem; border-radius: 980px;
  text-decoration: none;
  background: transparent; color: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.22);
  transition: background 200ms var(--lab-ease), color 200ms var(--lab-ease);
}
.arch__chip:hover { background: #fff; color: var(--lab-ink); border-color: #fff; }

.arch__year {
  max-width: var(--lab-measure); margin: clamp(2rem, 5vh, 3rem) auto 1rem;
  font-family: inherit;
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--lab-flame);
  /* The jump links land here; without this the heading hides under the fixed
     site header. */
  scroll-margin-top: 6rem;
}
.arch__year:first-of-type { margin-top: 0; }

.arch__grid {
  max-width: var(--lab-measure); margin: 0 auto;
  display: grid; gap: 1.5rem 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr));
}
.arch__card {
  display: grid; gap: 0.6rem; align-content: start;
  text-decoration: none; color: inherit;
}
.arch__thumb {
  display: block; overflow: hidden; border-radius: 14px;
  background: rgba(255, 255, 255, 0.06); aspect-ratio: 16 / 10;
}
.arch__thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: saturate(0.92);
  transition: transform 500ms var(--lab-ease), filter 300ms var(--lab-ease);
}
.arch__card:hover .arch__thumb img { transform: scale(1.04); filter: saturate(1.05); }
.arch__meta {
  display: flex; gap: 0.5rem; align-items: baseline;
  font-size: 0.74rem; letter-spacing: 0.06em; text-transform: uppercase;
}
.arch__date { color: var(--lab-flame); font-weight: 700; font-variant-numeric: tabular-nums; }
.arch__title {
  font-size: 1.02rem; font-weight: 620; line-height: 1.32;
  letter-spacing: -0.015em; color: #fff; text-wrap: pretty;
}
.arch__card:hover .arch__title { color: var(--lab-flame); }
.arch__count {
  max-width: var(--lab-measure); margin: 1.25rem auto 0;
  font-size: 0.82rem; color: rgba(255, 255, 255, 0.5);
}

@media (prefers-reduced-motion: reduce) {
  .arch__thumb img { transition: none; }
  .arch__card:hover .arch__thumb img { transform: none; }
}

/* ============================================================================
   EV — the events page: one upcoming event given the room, everything else a
   scannable row. Replaces the <style> block LiveEventsList used to emit, which
   sat later in the document than this file and so could never be themed.
   ========================================================================== */
.ev { padding: var(--lab-stack) var(--lab-gutter); background: var(--lab-paper); }
.ev.act--surface { background: var(--lab-paper-dim); }
.ev__head {
  display: grid; gap: 0.75rem; max-width: var(--lab-measure);
  margin: 0 auto clamp(2rem, 5vh, 3rem);
}

/* The date is the fact people came for, so it gets its own column and its own
   scale rather than being a caption under a title. */
.ev__next {
  display: grid; gap: clamp(1.5rem, 4vw, 3rem); align-items: start;
  max-width: var(--lab-measure); margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 2.5rem);
  border: 1px solid rgba(27, 21, 18, 0.12); border-radius: 18px;
  background: var(--lab-paper);
}
@media (min-width: 720px) { .ev__next { grid-template-columns: auto 1fr; } }
.ev__next + .ev__next { margin-top: 1.25rem; }
.ev__date {
  text-align: center; padding: 1rem 1.4rem; border-radius: 14px;
  background: var(--lab-ink); color: #fff; min-width: 7.5rem;
}
.ev__date-day {
  font-family: var(--lab-display); font-size: clamp(2.4rem, 6vw, 3.4rem);
  font-weight: 680; line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.ev__date-month { margin-top: 0.35rem; font-size: 0.9rem; font-weight: 600; }
.ev__date-year {
  font-size: 0.74rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.ev__body { display: grid; gap: 1rem; justify-items: start; }
.ev__body .headline { font-size: clamp(1.5rem, 3vw, 2.2rem); margin: 0; }
.ev__facts { display: flex; flex-wrap: wrap; gap: 0.5rem 1.75rem; font-size: 0.92rem; }
.ev__facts strong {
  display: block; font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--lab-flame-ink);
}
.ev__empty {
  max-width: var(--lab-measure); margin: 0 auto;
  text-align: center; color: var(--lab-ink-soft);
}

/* Past events: rows, because a past event is looked up rather than browsed. */
.ev__past {
  max-width: var(--lab-measure); margin: 0 auto;
  border-top: 1px solid rgba(27, 21, 18, 0.12);
}
.ev__row {
  display: grid; gap: 0.3rem 1.5rem; align-items: baseline;
  grid-template-columns: 1fr; padding: 1.15rem 0;
  border-bottom: 1px solid rgba(27, 21, 18, 0.12);
  text-decoration: none; color: inherit;
}
@media (min-width: 760px) { .ev__row { grid-template-columns: 10rem 1fr auto; } }
.ev__row-date {
  font-size: 0.8rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--lab-flame-ink);
  font-variant-numeric: tabular-nums;
}
.ev__row-title { font-weight: 560; line-height: 1.4; text-wrap: pretty; }
.ev__row-go { font-size: 0.85rem; font-weight: 560; color: var(--lab-flame-ink); }
.ev__row:hover .ev__row-title { color: var(--lab-flame-ink); }

/* ============================================================================
   FU — the follow-us account list. Ported from the design concept.

   The whole card is the link, so there is no "Visit Now" pill repeating the
   same two words once per account. What distinguishes the cards is the handle,
   so the handle is the second line rather than body copy under a heading.
   ========================================================================== */
.fu { padding: var(--lab-stack) var(--lab-gutter); background: var(--lab-paper); }
.fu__head {
  max-width: var(--lab-measure); margin: 0 auto clamp(2rem, 5vh, 3rem);
  display: grid; gap: 0.9rem;
}
.fu__grid {
  max-width: var(--lab-measure); margin: 0 auto;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
}
.fu__card {
  display: grid; gap: 0.35rem; align-content: start;
  padding: 1.3rem 1.4rem; border-radius: 14px;
  border: 1px solid rgba(27, 21, 18, 0.12);
  background: var(--lab-paper);
  text-decoration: none; color: inherit;
  transition: transform 160ms var(--lab-ease), box-shadow 160ms var(--lab-ease);
}
.fu__card:hover { transform: translateY(-3px); box-shadow: 0 12px 28px rgba(27, 21, 18, 0.10); }
.fu__platform { font-size: 1.05rem; font-weight: 660; letter-spacing: -0.015em; }
.fu__card:hover .fu__platform { color: var(--lab-flame-ink); }
.fu__handle { font-size: 0.92rem; color: var(--lab-ink-soft); }
.fu__region {
  justify-self: start; margin-top: 0.5rem;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 0.2rem 0.55rem; border-radius: 980px;
  background: rgba(27, 21, 18, 0.06); color: var(--lab-ink-soft);
}
@media (prefers-reduced-motion: reduce) {
  .fu__card { transition: none; }
  .fu__card:hover { transform: none; }
}
