/*
Theme Name: Hello Elementor Child
Theme URI: https://elementor.com/hello-theme/
Description: Child theme for 19th & 7th. Home for custom CSS and any template overrides. Business logic (shortcodes, Elementor query filters) lives in WPCode, NOT here, so it survives theme updates.
Author: 19th & 7th
Template: hello-elementor
Version: 3.28.3
Text Domain: hello-elementor-child
*/

/* ============================================================
   Custom CSS below this line.
   ============================================================ */

/* ------------------------------------------------------------
   NEWSREADER — self-hosted variable font (paragraph voice)
   Replaces Le Monde Livre Std as the serif. Self-hosted rather than
   pulled from fonts.googleapis.com: the site deliberately runs
   Adobe-only (elementor_google_font = 0) and did a render-blocking
   cleanup, so a third font host would be a step backwards.
   ONE file per style covers weight 200-800 continuously (variable
   wght axis) plus an opsz axis. "Optical Size: Auto" in the design
   reference = font-optical-sizing: auto, the CSS default, which is
   why opsz needs no explicit value anywhere.
   Files: fonts/newsreader-latin[-italic].woff2 (Google Fonts, OFL).
   Weights in use: 200 body/lead/pullquote, 300 caption.
   ------------------------------------------------------------ */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-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: "Newsreader";
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-latin-italic.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;
}
/* opsz is the default behaviour, but declare it so a future reset
   can't quietly switch it off and flatten the optical sizing. */
body { font-optical-sizing: auto; }

/* ------------------------------------------------------------
   Header navigation
   Elementor Nav Menu widget .elementor-element-6734fff,
   inside header template (post 99) sticky wrapper .elementor-element-6ee74c8
   Spacing uses the global --gap-width (13px, defined on :root in the
   kit CSS) to stay consistent with the rest of the site.
   ------------------------------------------------------------ */

/* Desktop dropdown items: keep them flush-left (aligned with the top-level
   items) with just a small right gap so long labels never touch the panel edge.
   !important beats Elementor's post-99.css "a { padding:4px 0 }" (0,4,1). */
.elementor-element-6734fff .elementor-nav-menu--dropdown a.elementor-sub-item {
	padding-right: 10px !important;
}

/* Submenu indicator: typographic "+" in the menu font, not the FA SVG. */
.elementor-element-6734fff .sub-arrow svg {
	display: none;
}
.elementor-element-6734fff .sub-arrow::after {
	content: "+";
	font-family: inherit;
	font-weight: 500;
	font-size: 0.82em;
	line-height: 1;
	margin-left: 1px;
	display: inline-block;
}

/* Tablet / mobile flyout: full-width overlay with large, tap-friendly
   rows (Elementor left it position:static + ~123px wide, inflating the
   fixed header). Anchored to the full-width header wrapper 6ee74c8. */
@media (max-width: 1024px) {
	.elementor-element-6ee74c8 {
		position: relative;
	}
	.elementor-element-6734fff {
		position: static;
	}
	.elementor-element-6734fff nav.elementor-nav-menu--dropdown {
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		width: auto;
		z-index: 9999;
		background-color: var(--e-global-color-grnd7a1); /* Ink slot: flips to Parchment in light mode */
		padding: calc(var(--gap-width) / 2) 0;
	}
	.elementor-element-6734fff nav.elementor-nav-menu--dropdown a.elementor-item {
		font-size: 16.5px !important;
		line-height: 1.1 !important;
		padding: var(--gap-width) calc(var(--gap-width) * 2) !important;
	}
	.elementor-element-6734fff nav.elementor-nav-menu--dropdown a.elementor-sub-item {
		font-size: 13.5px !important;
		padding: var(--gap-width) calc(var(--gap-width) * 2) var(--gap-width) calc(var(--gap-width) * 3) !important;
	}
}

/* NB: don't zero the last top-level item's right padding.
   It was tried so the menu's right edge would mirror the logo's 10px left
   inset, but the nav uses the "framed" hover pointer, which draws a box around
   each item using its own padding. With 0 right / 8px left, the frame around
   CONTACT renders lopsided on hover. Symmetric item padding (the Elementor
   default 8px) wins over exact edge alignment here. */

/* Light mode: dropdown hover/active text tones (2026-08-10).
   Elementor (post-99.css) authors hover/highlighted text as the Ink slot
   (c8af26f) and active text as the Eggshell slot (316eccb). The theme flip
   turns those into Parchment-on-Voltage (washed) and Ink-on-Cobalt (murky).
   Re-pin for light only: hover = ink on Voltage, active = cream on Cobalt.
   Dark mode stays 100% Elementor-authored. */
html.theme-light .elementor-element-6734fff .elementor-nav-menu--dropdown a:hover,
html.theme-light .elementor-element-6734fff .elementor-nav-menu--dropdown a:focus,
html.theme-light .elementor-element-6734fff .elementor-nav-menu--dropdown a.highlighted {
	color: var(--e-global-color-fgur7b2) !important; /* = Ink in light */
}
html.theme-light .elementor-element-6734fff .elementor-nav-menu--dropdown a.elementor-item-active {
	color: var(--e-global-color-42a1059) !important; /* Cream: stays light on Cobalt */
}

/* ============================================================
   svc-print-rule — THE services card (canon since Aug 2026)
   The six demo variants (brutal/soft/print/print-light/print-bar/
   print-lockup) were deleted 2026-08-07 along with their hidden demo
   sections on page 90; history lives in
   uploads/_style.css.before-unify-20260807.css.
   Shared structure below; colors ride the t7 token table (end of
   file). DARK (default) = glass over particles; LIGHT (.svc-light)
   = cream print sheet. Hover = mode-relative CTA flood (Ember wipe
   family). Type baselines to the ramp: h2 = H3 slot, body = P3.
   ============================================================ */
/* t7-baton — the icon-column + title-bar + rule anatomy. Extracted from
   svc-print-rule 2026-08-20 (Paul: "the positioning of the icon and the
   rule that sits under it... that stuff is good I like that"). The svc
   selectors are GROUPED IN so the cascade is byte-identical; the inner
   plumbing is still Elementor-element-ID anchored and cannot be portable
   until 76dc37e/e129150/ff59f8d/be52190 become elected classes on loop
   template 123. That is the remaining step, not a blocker for this one. */
.t7-baton,
.svc-print-rule { --bar-h: 96px; --rule-w: 6px; }

/* --- shared structure --- */
.svc-print-rule .e-loop-item.type-services > a.e-con {
  position: relative;
  overflow: hidden;
  border-style: solid;
  border-width: var(--gap-width, 13px);
  border-radius: 0;
  padding: 26px 30px 24px;
  gap: 0 !important;
  transform: none;
  transition: transform .25s ease, box-shadow .25s ease;
}
.svc-print-rule .e-loop-item.type-services > a.e-con > * { position: relative; z-index: 1; }
.svc-print-rule .e-loop-item.type-services > a.e-con::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--t7-grain);
}
.svc-print-rule .e-loop-item.type-services .elementor-element-be52190 {
  gap: 0 !important;
  align-items: stretch !important;
  width: 100%;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-76dc37e {
  background: none !important;
  border: none;
  box-shadow: none;
  padding: 0;
  margin: 0;
  width: var(--bar-h) !important;
  transform: none;
  flex-shrink: 0;
  display: flex;
  align-items: center;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-76dc37e img {
  display: block;
  width: 100%;
  height: auto;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-e129150 {
  flex-grow: 1;
  align-self: stretch;
  display: flex;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-e129150 .elementor-widget-container { width: 100%; height: 100%; display: flex; }
.t7-baton h2,
.svc-print-rule .e-loop-item.type-services h2 {
  background: none;
  width: 100%;
  min-height: var(--bar-h);
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0 8px 0 16px;
  border-bottom-style: solid;
  border-bottom-width: var(--rule-w);
  /* type = the H3 ramp custom (6d3761b), capped at 6.75vw so zoomed phones
     never overflow. Was wired to the `text` SYSTEM slot until 2026-08-20 —
     that slot is now the UNSET tripwire, never reference it. */
  font-family: var(--e-global-typography-6d3761b-font-family), sans-serif !important;
  font-weight: var(--e-global-typography-6d3761b-font-weight) !important;
  letter-spacing: var(--e-global-typography-6d3761b-letter-spacing) !important;
  font-size: min(var(--e-global-typography-6d3761b-font-size), 6.75vw) !important;
  min-width: 0; max-width: 100%;
  overflow-wrap: anywhere; hyphens: auto; -webkit-hyphens: auto;
  line-height: 1;
  text-shadow: none;
  transition: color .25s ease .08s, border-bottom-color .25s ease .08s;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-ff59f8d {
  transform: none !important;
  width: 100%;
  max-width: none;
  margin: 0;
  background: none;
  border: none;
  padding: 20px 10px 12px 16px;
  /* body voice = P3 (serif) from the kit ramp */
  font-family: var(--e-global-typography-b55f19d-font-family), serif !important;
  font-size: var(--e-global-typography-b55f19d-font-size) !important;
  font-weight: var(--e-global-typography-b55f19d-font-weight) !important;
  line-height: var(--e-global-typography-b55f19d-line-height) !important;
  letter-spacing: var(--e-global-typography-b55f19d-letter-spacing) !important;
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
  -webkit-hyphens: auto;
  text-shadow: none;
  transition: color .25s ease .08s;
}
.svc-print-rule .e-loop-item.type-services .elementor-element-76dc37e img { transition: filter .25s ease .08s; }

/* ---- SERVICE ICON: unified onto the knockout MASK, 2026-08-20 (Paul) ----
   Was a plain <img> + a hand-tuned CSS filter chain; the other 25 icons
   were already masked. Now identical mechanism: a t7-icon-box wrapper
   paints what shows through the cut-outs, the masked widget paints the
   field. Colour is now TWO TOKENS instead of filter math.

   --t7-icon-box  is scoped TRANSPARENT here on purpose: in an svc card
   the cut-outs must keep showing the CARD surface (cream on svc-light,
   glass on dark), which is exactly how the filtered <img> read. Flip it
   to Ink if you ever want these to become true tiles like the offerings.

   ADDITIVE: the .elementor-element-76dc37e img rules above still stand,
   so this renders correctly both before and after tmpl 123 is converted. */
.svc-print-rule .e-loop-item.type-services .t7-icon-box {
  --t7-icon-box: transparent;
  --width: var(--bar-h) !important;
  width: var(--bar-h) !important;
  flex: 0 0 auto;
  align-self: center;
  background-color: transparent !important;
  /* Elementor gives a nested e-con-full 10px of its own padding, which
     inset the masked widget to 76px inside a 96px box and broke the icon's
     connection to the rule under the title. The tile is the box — it can
     never have padding. Also written defensively: this holds no matter how
     many t7-icon-box levels the template ends up with. */
  padding: 0 !important;
  --padding-block-start: 0px; --padding-block-end: 0px;
  --padding-inline-start: 0px; --padding-inline-end: 0px;
}
/* The whole icon chain is pinned to the BOX, not to Elementor's emitted
   sizes. The widget carries the mask, so it must be exactly --bar-h or the
   knockout stops meeting the rule under the title (Paul, 2026-08-20: "this
   element is showing padding, breaking the connection between the icon
   block and the stroke"). It was never padding — the widget was sizing to
   76px inside a 96px box while .elementor-icon rendered at its authored
   100px and overflowed. Elementor's icon `size` setting is IRRELEVANT here;
   do not try to fix this in the widget. */
.svc-print-rule .e-loop-item.type-services .t7-icon-box .elementor-widget-icon {
  width: 100% !important;
  height: 100% !important;
  align-self: stretch !important;
  flex: 1 0 auto !important;
  margin: 0 !important;
  padding: 0 !important;
  transition: background-color .25s ease .08s;
}
.svc-print-rule .e-loop-item.type-services .t7-icon-box .elementor-icon-wrapper,
.svc-print-rule .e-loop-item.type-services .t7-icon-box .elementor-icon {
  width: 100% !important;
  height: 100% !important;
  line-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
}
.svc-print-rule .e-loop-item.type-services .t7-icon-box .elementor-icon > svg {
  width: 100% !important;
  height: 100% !important;
  display: block;
}
.svc-print-rule .e-loop-item.type-services .t7-icon-box .elementor-icon > svg {
  transition: fill .25s ease .08s;
}
/* field colour = the same pair the filter chain used to fake */
.svc-print-rule:not(.svc-light) .e-loop-item.type-services .t7-icon-box { --t7-icon-tile: var(--t7-frost-text); }
.svc-print-rule.svc-light .e-loop-item.type-services .t7-icon-box { --t7-icon-tile: var(--e-global-color-c8af26f); }
/* hover: field flips to the CTA flood text tone, matching h2 + body */
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con:hover .t7-icon-box { --t7-icon-tile: var(--t7-cta-flood-text); }
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con:hover .t7-icon-box { --t7-icon-tile: var(--e-global-color-42a1059); }
/* hover lift lives on the WRAPPER (which owns the gloss ::after) — card and
   glare move as one object; the inner link never translates on its own */
.svc-print-rule .e-loop-item.type-services { position: relative; transition: transform .25s ease; }
.svc-print-rule .e-loop-item.type-services:hover { transform: translate(-2px, -3px); }
.svc-print-rule .e-loop-item.type-services > a.e-con:hover { transform: none !important; }

/* --- DARK (default): glass card over the particle field.
   Frame/text/flood all ride t7 tokens so both theme modes come free. --- */
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con {
  background-color: var(--t7-frost-tint) !important;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  border-color: var(--t7-frost-frame) !important;
  box-shadow: none !important;
}
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con::after {
  opacity: .07;
  mix-blend-mode: soft-light;
}
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con::before { background: var(--t7-cta-flood) !important; }
.svc-print-rule:not(.svc-light) .e-loop-item.type-services h2 {
  color: var(--t7-frost-text) !important;
  border-bottom-color: var(--t7-frost-frame) !important;
}
.svc-print-rule:not(.svc-light) .e-loop-item.type-services .elementor-element-ff59f8d { color: var(--t7-frost-text) !important; }
.svc-print-rule:not(.svc-light) .e-loop-item.type-services .elementor-element-76dc37e img { filter: var(--t7-cta-icon) !important; }
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con:hover { box-shadow: none !important; }
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con:hover h2 {
  color: var(--t7-cta-flood-text) !important;
  border-bottom-color: var(--t7-cta-flood-text) !important;
}
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con:hover .elementor-element-ff59f8d { color: var(--t7-cta-flood-text) !important; }
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con:hover .elementor-element-76dc37e img { filter: var(--t7-cta-flood-icon) !important; }

/* --- LIGHT (.svc-light): cream sheet, black ink (approved shape) --- */
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con {
  background-color: var(--e-global-color-42a1059) !important; /* Marshmallow (was Cream pre-2026-08-11) */
  border-color: var(--e-global-color-c8af26f);
  box-shadow: 5px 7px 14px color-mix(in srgb, var(--e-global-color-c8af26f) 16%, transparent), inset 0 -14px 26px color-mix(in srgb, var(--e-global-color-c8af26f) 5%, transparent);
}
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con::after {
  opacity: .08;
  mix-blend-mode: multiply;
}
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con::before { background: var(--e-global-color-c8af26f); }
.svc-print-rule.svc-light .e-loop-item.type-services h2 {
  color: var(--e-global-color-c8af26f) !important;
  border-bottom-color: var(--e-global-color-c8af26f);
}
.svc-print-rule.svc-light .e-loop-item.type-services .elementor-element-ff59f8d { color: color-mix(in srgb, var(--e-global-color-c8af26f) 85%, transparent) !important; }
.svc-print-rule.svc-light .e-loop-item.type-services .elementor-element-76dc37e img { filter: brightness(0) !important; }
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con:hover {
  box-shadow: 8px 10px 20px color-mix(in srgb, var(--e-global-color-c8af26f) 20%, transparent), inset 0 -14px 26px color-mix(in srgb, var(--e-global-color-c8af26f) 5%, transparent);
}
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con:hover h2 {
  color: var(--e-global-color-42a1059) !important;
  border-bottom-color: var(--e-global-color-42a1059);
}
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con:hover .elementor-element-ff59f8d { color: color-mix(in srgb, var(--e-global-color-316eccb) 85%, transparent) !important; }
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con:hover .elementor-element-76dc37e img {
  filter: var(--t7-filter-eggshell) !important;
}

@media (max-width: 767px) {
  .t7-baton,
  .svc-print-rule { --bar-h: 56px; --rule-w: 4px; }
  .svc-print-rule .e-loop-item.type-services > a.e-con {
    border-width: 5px;
    padding: 16px 16px 14px;
  }
  .t7-baton h2,
  .svc-print-rule .e-loop-item.type-services h2 {
    font-size: clamp(1.125rem, 4.125vw, 1.575rem) !important;
    padding: 0 6px 0 10px;
  }
  .svc-print-rule .e-loop-item.type-services .elementor-element-ff59f8d { padding: 12px 6px 8px 10px; }
}

/* === Mobile drift/overflow fixes (Aug 2026) ===
   Homepage contact form gutter (-5px margins) poked past the viewport,
   enabling horizontal panning on mobile -> diagonal drift + fixed-header lag. */
.elementor-element-389d061 { overflow-x: clip; }
/* safety net: forbid horizontal document panning site-wide.
   `clip` (not `hidden`) so no scroll container is created and sticky keeps working */
html, body { overflow-x: clip; }
/* Elementor sticky stamps a stale inline px width on resize/rotation */
.elementor-element-6ee74c8.elementor-sticky--active { width: 100% !important; }

/* ---------- MODIFIER: svc-light (light ground for any demo section) ---------- */
.elementor-element.svc-light,
.svc-light .e-con {
  background-color: transparent !important;
  background-image: none !important;
}
.elementor-element.svc-light { background-color: var(--e-global-color-316eccb) !important; }
.elementor-element.svc-light::before { display: none !important; }
/* softer paper shadow on light ground */

/* dark rule section: ground fully transparent so the site-wide particle
   canvas (fixed, z-index -1) shows through behind the ink cards */
.elementor-element.svc-print-rule:not(.svc-light),
.svc-print-rule:not(.svc-light) .e-con {
  background-color: transparent !important;
  background-image: none !important;
}
.elementor-element.svc-print-rule:not(.svc-light)::before { display: none !important; }

/* ---------- INTRO ----------
   UNIFIED 2026-08-10: the bespoke ink-card block (8px cream frame, 50%
   frost, grain) is GONE. The intro container (f5c322c on page 90) now
   elects `fx-lift` in Elementor; its statement copy elects `t7-display`
   so the card type contract respects its authored display type. */

/* ---------- services loop: single column at every breakpoint,
   card width relative to the grid rail (Paul 2026-08-07, widened 2026-08-11):
   phone 100% -> tablet 75% -> desktop 80%.
   NOTE the old desktop value was min(60%, 720px); the 720px cap was DEAD
   CODE — the grid rail maxes at ~1008px inside the boxed container, so 60%
   resolved to 605px and the cap never fired. Percentages are of the rail,
   so 100% here is the widest possible without touching the container. --- */
/* t7-rail — single column, card width relative to the grid rail:
   phone 100% -> tablet 75% -> desktop 80%. Extracted 2026-08-20; svc
   grouped in so nothing moves. */
.t7-rail .elementor-loop-container.elementor-grid,
.svc-print-rule .elementor-loop-container.elementor-grid {
  grid-template-columns: 1fr !important;
  justify-items: center;
}
.t7-rail > .elementor-loop-container > .e-loop-item,
.svc-print-rule .e-loop-item.type-services { width: 100%; }
@media (min-width: 768px) {
  .t7-rail > .elementor-loop-container > .e-loop-item,
  .svc-print-rule .e-loop-item.type-services { width: 75%; }
}
@media (min-width: 1025px) {
  .t7-rail > .elementor-loop-container > .e-loop-item,
  .svc-print-rule .e-loop-item.type-services { width: 80%; }
}

/* ---------- LIGHT SERVICES: faint Unsplash wash (luminosity) ---------- */
.elementor-element.svc-print-rule.svc-light {
  background-color: var(--e-global-color-316eccb) !important;
  background-image:
    linear-gradient(color-mix(in srgb, var(--e-global-color-316eccb) 90%, transparent), color-mix(in srgb, var(--e-global-color-316eccb) 90%, transparent)),
    url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/georgie-cobbs-muOHbrFGEQY-unsplash.jpg") !important;
  background-size: auto, cover !important;
  background-position: center, center !important;
  background-blend-mode: normal, luminosity !important;
}

/* ---------- TRUST ZONE (testimonials ea0a21a + industries cd347e3) ----------
   Transparent: the page ground (the elected pattern - sheet on home, the
   site default since 2026-10-13) shows through under the veil; elements get
   the tactile print language.
   JUDGEMENT PASS - flag anything to dial back. */
/* section bg cleared so the ground pattern shows through */
.elementor-element-ea0a21a,
.elementor-element-cd347e3 {
  background-color: transparent !important;
  background-image: none !important;
  position: relative;
  overflow: hidden;
}
/* (angled ampersand tile dropped; particle engine retired 2026-08-19) */
.elementor-element-ea0a21a > *,
.elementor-element-cd347e3 > * { position: relative; z-index: 1; }
/* faint grain over both sections */
.elementor-element-ea0a21a::after,
.elementor-element-cd347e3::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .05;
  mix-blend-mode: soft-light;
  z-index: 2;
  background-image: var(--t7-grain);
}
/* "Trusted by" heading floats free, clean (letterpress smudged the tight line overlap) */
.elementor-element-7015ffc .elementor-heading-title { text-shadow: none; }
/* the yellow divider: bright color gets the sticker treatment */
.elementor-element-1b5bdbf .elementor-divider-separator {
  border-top-width: 14px !important;
  border-top-color: var(--e-global-color-230debb, #FFDE00) !important;
}
/* client marquee: UNIFIED 2026-08-10 — slides are `t7-card` (elected on the
   Client Carousel loop template 117 root); the old bespoke frosted-ink rule
   is gone. Logos ride the mode-aware icon filter: eggshell dark / ink light
   (moved from tmpl-117 widget 908bc9f custom_css; breadcrumb left there). */
.elementor-117 .elementor-element-908bc9f img {
  filter: var(--t7-cta-icon);
}
/* logo box (2026-10-18, Gidget 618/694, re-enabled after the 2b trims): every logo sits in the
   same ~150x80 box and is fitted inside it (contain), so wide wordmarks and
   square marks read at a similar size instead of being sized by width only.
   max-width keeps it inside narrow cards. [src] lifts it over Elementor's
   own width:80% rule for this widget. Also /logos/ tiles (template 3053,
   widget 6dc66c4), Gidget 696. */
.elementor-117 .elementor-element.elementor-element-908bc9f img[src],
.elementor-3053 .elementor-element.elementor-element-6dc66c4 img[src] {
  width: 150px;
  max-width: 100%;
  height: 80px;
  object-fit: contain;
}
/* (cd347e3 carousel slides are tmpl-104 t7-cards — the glass system owns
   their chrome; the old 3px-border rule here was dead weight, removed.) */
/* testimonial text: letterpress whisper */
.elementor-element-e04c3b1 .swiper-slide {
  text-shadow: 0 -1px 1px rgba(0,0,0,.45);
}
/* light flip (2026-08-10): quote stays ink on the Ember bubble. The widget
   authors its text as the Ink slot (c8af26f), which the flip turns
   Parchment = washed on orange. */
html.theme-light .elementor-element-e04c3b1 .elementor-testimonial__text {
  color: var(--e-global-color-fgur7b2) !important; /* = Ink in light */
}

/* ============================================================
   THE SURFACE SYSTEM  (v1.6.0, 2026-08-18)

   Everything visible answers TWO questions, each from a small menu.
   Declare answers as classes (Elementor > Advanced > CSS Classes);
   axes combine freely, nesting recurses, and NOTHING in this file
   restates a color table — a new variant is a combination, never
   a new recipe.

   1. TONE — dark thing or light thing?
        (nothing)   follows the site mode
        .t7-dark    ABSOLUTE: always a dark ground
        .t7-light   ABSOLUTE: always a light ground
        .t7-invert  RELATIVE: opposite of the current mode
      Declared in the two TONE TABLES at the end of this file.
      Each re-declares the six semantic slots for its subtree, so
      every descendant — bare headings included — inherits.

   2. SURFACE — what does it paint?
        (nothing)   clear: whatever is behind shows through
        .t7-solid   opaque Ground fill, Figure marks
        .t7-clear   forced transparency (over an authored fill)
      ...plus two finishes, each on or off:
        .t7-frame   gap-width border in the frame material

   PRESETS (grouped into the axis rules — same declarations, not
   copies; retuning an axis retunes its presets):
        .t7-card   = glass + frame + gloss   (loop cards)
                     tone — the per-card escape hatch
   "Glass is the legibility layer; the frame and the gloss are
   what make a card a card."

   PALETTE (kit 89, rebuilt 2026-08-11). Marks | canyon | paper:
     Ink   c8af26f   Ash         3bc915a
     Slate 27abaeb   Eggshell    316eccb
     Smoke 07f7664   Marshmallow 42a1059
     Ember bc74cd9 | Cobalt d3cd34a | Voltage 230debb
   ZERO-HEX DOCTRINE: every color below references a kit global
   (directly or via color-mix). Re-read hexes from the kit, never
   from old comments. Legacy names fx-frost/fx-sheet/fx-soft and
   ui-ground-* survive lower down for playground 759 only.
   ============================================================ */
/* ui-kit body default: parent theme wires body to the "text" slot (= H3 on
   this site) so bare text inherited H3 scale. Re-point body at P3 vars —
   tracks the P3 global and the theme flip automatically. */
body {
  font-family: var(--e-global-typography-b55f19d-font-family), serif;
  font-size: var(--e-global-typography-b55f19d-font-size);
  font-weight: var(--e-global-typography-b55f19d-font-weight, 200);
  line-height: var(--e-global-typography-b55f19d-line-height);
  letter-spacing: var(--e-global-typography-b55f19d-letter-spacing);
  color: var(--e-global-color-fgur7b2);
}

body {
  /* shared texture + filter tokens (deduped 2026-08-07) */
  --t7-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
  --t7-filter-eggshell: brightness(0) saturate(100%) invert(94%) sepia(6%) saturate(180%) hue-rotate(20deg) brightness(99%) contrast(94%);
  /* GLOSS RECIPES — the only two in the file, and deliberately OPTICAL
     (alpha white/black knows no palette). The A/B/C/D shadow verdict
     (wpcode966 demo on 759) lands HERE when Paul makes it — one edit.
     WHICH recipe a scope uses is that scope's own --t7-card-light line in
     the TONE TABLES at the end of this file: highlights whisper on a
     dark ground and shout on a light one.
     2026-08-20 (Paul): ONE LIGHT SOURCE. Sheen straightened 118deg ->
     180deg so it shares the vertical axis the shade layer was already
     on — light from above, shade at the bottom, one physics. Same pass
     halved every alpha; subtlety is the point. The front-loaded 4-stop
     falloff is DELIBERATE — it is what keeps the middle of the card
     clean. Do not "simplify" it to a 2-stop ramp without re-halving. */
  --t7-card-light-on-dark: linear-gradient(180deg, rgba(255,255,255,.045) 0%, rgba(255,255,255,.025) 25%, rgba(255,255,255,.01) 45%, rgba(255,255,255,0) 65%), linear-gradient(0deg, rgba(0,0,0,.105) 0%, rgba(0,0,0,.04) 18%, rgba(0,0,0,0) 38%);
  --t7-card-light-on-light: linear-gradient(180deg, rgba(255,255,255,.165) 0%, rgba(255,255,255,.09) 25%, rgba(255,255,255,.035) 45%, rgba(255,255,255,0) 65%), linear-gradient(0deg, rgba(23,24,15,.05) 0%, rgba(23,24,15,.025) 18%, rgba(23,24,15,0) 38%);
}
/* (the old per-mode frost/shadow/glow tables and t7 ROLE TOKENS lived
   here through v1.5.3 — they are now single formulas in THE DERIVED
   TOKENS rule at the end of this file, resolved per tone scope) */

/* ---- THE FOUR AXES (presets grouped in — same declarations, no copies) ---- */

/* SURFACE: glass — tint only since blur was retired 2026-09-10.
   The svc services card keeps its own backdrop-filter: that one is
   element-scoped to the services loop and is not part of this axis. */
/* (the .t7-glass axis retired 2026-08-20 — blur and wash are separate
   classes now, and t7-column elects t7-solid like anything else) */
/* the presets predate the axis and force their fill over Elementor-
   authored backgrounds; that contract stays on the presets only */
.t7-card .elementor-widget-heading .elementor-heading-title { color: var(--t7-frost-text); }

/* SURFACE: solid — opaque Ground, Figure marks */
.t7-solid { background-color: var(--e-global-color-grnd7a1); color: var(--e-global-color-fgur7b2); }
.t7-solid .elementor-widget-heading .elementor-heading-title { color: var(--e-global-color-fgur7b2); }

/* SURFACE: clear — forced transparency where Elementor authored a fill */
.t7-clear { background-color: transparent !important; background-image: none !important; }

/* ------------------------------------------------------------
   THE DECOMPOSED AXES (Paul's re-cut, 2026-08-19). Canon:
   specs/2026-08-19-surface-axes-canon.md + page 759 §02.
   Each does ONE thing and stacks with the others. ADDITIVE for now:
   t7-glass / t7-film / t7-gloss are untouched until Paul verifies
   these in the lab, then production migrates onto them.
   ------------------------------------------------------------ */

/* BLUR IS RETIRED (Paul, 2026-09-10). The class and its dial are gone:

     --t7-blur-radius: 4px;
     .t7-blur {
       -webkit-backdrop-filter: blur(var(--t7-blur-radius, 4px));
       backdrop-filter: blur(var(--t7-blur-radius, 4px));
     }

   Why it went. Blur was the one treatment in this group that could not
   compose on a single element. Paint stacks on itself — duotone blends
   against the element's own image, wash draws in ::after above it — but
   backdrop-filter reaches BEHIND, so its subject always had to live on an
   ancestor. Every blurred band therefore cost an extra container, and that
   extra container was the reason the case-study section shape kept drifting.
   Retiring blur lets a band be one Shell for a flat ground and two only when
   it genuinely bleeds.

   Class elections were deliberately LEFT IN THE DATA (42 of them across
   posts 62, 63, 78, 82, 204, 205, 206, 759) rather than swept. Dead class
   text renders nothing; a bulk _elementor_data sweep across live pages is
   where the revision-clobber trap lives. They come out with the
   spacing-unify comb. Story Blocks 1564 and 1569 WERE stripped the same
   day, so newly stamped bands stop carrying the dead class.

   If it ever comes back, it comes back as paint in a pseudo-element, not as
   a filter — that keeps it on one element and out of the grammar. */

/* DUOTONE — maps an authored background IMAGE into the tone's
   materials. `luminosity` keeps the image's light and takes colour
   from the layer beneath, so the picture survives as tone.
   Sets background-COLOR only, never background-image, so it composes
   with whatever Elementor authored on the element. This is exactly
   what t7-film does minus the scrim — the scrim is t7-wash now. */
.t7-duotone {
  background-color: var(--t7-duotone-base, var(--t7-duo-shadow));
  background-blend-mode: luminosity;
}
/* Elementor's per-element overlay would fight the map — neutralise it
   (same reason .t7-film does; beats Elementor's (0,3,0) rule) */
.e-con.t7-duotone { --overlay-opacity: 0 !important; }

/* WASH — a translucent layer ON TOP OF DUOTONE and UNDER content.
   z-index -1 is what keeps it off the foreground; `isolation` traps it
   in this element's stacking context. Uses ::after because Elementor
   owns .e-con::before for its own overlays. Strength is one number. */
.t7-wash { position: relative; isolation: isolate; }
.t7-wash::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background: var(--t7-wash);
}

/* ============================================================
   PATTERN — the tiled two-tone fill axis.            (2026-08-21)
   ------------------------------------------------------------
   Split in two on purpose, so new artwork NEVER touches colour:

     .t7-pattern          GEOMETRY + PAINT. Tile size, repeat,
                          position, opacity. Paints --t7-bg as the
                          field and --t7-fg through the mask.
     .t7-pattern--<name>  ARTWORK ONLY. One --t7-pattern-src, plus
                          its natural tile size. Nothing else, ever.

   ADDING A PATTERN IS ONE RULE, TWO DECLARATIONS. That is the
   whole point of the split — the colour logic is written once.

   ONE LABEL IS A FULL ELECTION (2026-08-26). Every machinery rule
   below also matches [class*="t7-pattern--"], so typing a single
   `t7-pattern--<name>` into a container's CSS Classes field is the
   whole contract — any pattern, any container, any page. Bare
   `t7-pattern` (no artwork named) stays legal and draws the empty
   tile. The per-industry CSS defaults that used to fill that gap
   were retired the same day: the page->artwork mapping now lives
   ON the five industry containers as explicit labels, not here.

   MASK, NOT A COLOURED SVG. The artwork ships as pure geometry
   (opaque black on transparent = an alpha mask) and CSS owns the
   paint, so every pattern re-resolves per tone scope (t7-dark /
   t7-light / t7-invert) with zero per-pattern colour rules. Same
   mechanism as the service-icon and plate knockouts below.

   ::before, not ::after, because .t7-wash owns ::after and the
   order law is fill -> duotone -> wash. Elementor also owns
   .e-con::before for its overlay, so the container arm carries a
   `body…` element to out-specify Elementor's (0,3,0) WITHOUT
   !important, and the overlay itself is zeroed the same way
   .t7-duotone does it.
   ============================================================ */
.t7-pattern,
[class*="t7-pattern--"] {
  position: relative;
  isolation: isolate;
  /* WHICH TOKEN LANDS ON WHICH REGION is a property of the ARTWORK, not of
     the system, so it is one indirection the modifier can flip.
       ground = what shows where the mask is TRANSPARENT
       paint  = what the mask paints where it is OPAQUE
     Default is the positive reading: mark painted in --t7-fg on a --t7-bg
     field. Art authored as a NEGATIVE (the drawing is the surround, the mark
     is the hole) swaps these two in its own rule, which flips the coverage
     ratio without touching the artwork or any colour logic. */
  --t7-pattern-ground: var(--t7-bg);
  --t7-pattern-paint:  var(--t7-fg);
  background-color: var(--t7-pattern-ground);
  /* no modifier elected = a bare field, never a solid slab of --t7-fg.
     An undefined --t7-pattern-src would compute mask-image to `none`,
     which paints the mark colour edge to edge. Default to an empty
     tile so the failure mode is "nothing drawn", not "everything". */
  --t7-pattern-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'/%3E");
}
.e-con.t7-pattern,
.e-con[class*="t7-pattern--"] { --overlay-opacity: 0 !important; }

.t7-pattern::before,
[class*="t7-pattern--"]::before,
body.elementor-kit-89 .e-con.t7-pattern::before,
body.elementor-kit-89 .e-con[class*="t7-pattern--"]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--t7-pattern-paint);
  background-image: none;
  /* WEIGHT, matched to the shipped ground (2026-08-21). The city looks
     soft because a 75% veil sits on it; an ELECTED pattern has no veil at
     all, so at opacity 1 it rendered at 4x the ground's contrast and read
     as a different, louder material. Paul spotted it as "two washes
     fighting" - it was one wash MISSING.
     The default is derived, not a magic number: what survives the ground's
     veil is exactly 100% minus its strength, so retuning
     --t7-veil-strength carries every pattern with it instead of silently
     desyncing them. --t7-pattern-opacity overrides per element. */
  opacity: var(--t7-pattern-opacity, calc(100% - var(--t7-veil-strength)));
  -webkit-mask-image: var(--t7-pattern-src);
          mask-image: var(--t7-pattern-src);
  -webkit-mask-size: var(--t7-pattern-size, 64px);
          mask-size: var(--t7-pattern-size, 64px);
  -webkit-mask-repeat: var(--t7-pattern-repeat, repeat);
          mask-repeat: var(--t7-pattern-repeat, repeat);
  -webkit-mask-position: var(--t7-pattern-position, 0 0);
          mask-position: var(--t7-pattern-position, 0 0);
}

/* ARTWORK REGISTRY + per-name modifiers moved to DATA 2026-08-27:
   `patterns` CPT + WPCode "t7: pattern registry emitter" (id 1526) now
   declare every --t7-art-<slug>, its ruled size, and each
   .t7-pattern--<slug> modifier. Ruled sizes and density rulings live on
   the pattern posts (notes field). Tile intake still goes through
   ~/projects/19th/t7-patterns/ (make-tile.py - white-rect, fill:none and
   double-hyphen traps live there). The machinery above stays here; the
   artwork never will again. See specs/2026-08-27-pattern-cpt-design.md. */

/* GROUND ELECTION (2026-08-27) - the page's assigned pattern replaces the
   city INSIDE the ground layer, so the veil (.t7g-veil, a later sibling)
   still sits on top and an elected ground reads as the same material as
   the city. Resolver: WPCode "t7: pattern resolver" (id 1527) emits the
   body class + --t7-ground-src/-size vars from the page's assignment
   (explicit pick -> its industry's pick -> nothing = the city, untouched).
   Machinery only - which artwork is a data question, never answered here. */
body.t7-ground-elected #t7-drafted-ground svg { display: none; }
body.t7-ground-elected #t7-drafted-ground::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--t7-pattern-line);
  -webkit-mask-image: var(--t7-ground-src);
          mask-image: var(--t7-ground-src);
  -webkit-mask-size: var(--t7-ground-size, 64px);
          mask-size: var(--t7-ground-size, 64px);
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
}

/* ============================================================
   HERE — the scroll-position axis.                  (2026-08-26)
   ------------------------------------------------------------
   The first axis on this sheet that does NOT resolve from a class
   you type. Fill, duotone, wash, frame, lift and pattern are all
   elections — you name them and they hold. This one resolves from
   WHERE THE READER IS, so it needs a partner in JS: WPCode
   "t7-here observer" does exactly one thing — put `is-here` on one
   child of .t7-here and take it off the others. Nothing else in
   this stylesheet reads scroll position.

   ONE LABEL IS A FULL ELECTION, same contract as t7-pattern:
   `t7-here` goes on the RAIL container. Its element children ARE
   the rail items — there is no per-item class, the machinery
   selects them. `t7-here-group` marks the matching content groups.
   Rail item N pairs with group N by DOCUMENT ORDER, so adding a
   fourth section is one heading plus one group and nothing here.

   DIM IS THE DEFAULT (Paul, 2026-08-26). Voltage is already doing
   the work on every card header; three lit headlines at once makes
   the band shout. The accent reading is kept as a modifier so
   changing your mind is a class in a field, not an edit here.

   DESKTOP ONLY, and deliberately. Below 1025px the rail is gone
   and each heading rides above its own group instead (Paul's call
   the same day) — a vertical rail at phone width eats the screen.
   The mobile labels live in the page data, not here, so they still
   read with JS off.
   ============================================================ */
.t7-here > * {
  transition:
    opacity 260ms cubic-bezier(.4, 0, .2, 1),
    color   260ms cubic-bezier(.4, 0, .2, 1);
}

/* the dim reading — the default */
.t7-here > *          { opacity: .22; }
.t7-here > .is-here   { opacity: 1; }

/* the accent reading — artwork, not machinery. Elect with
   `t7-here t7-here--voltage` on the rail. */
.t7-here--voltage > *        { opacity: .3; }
.t7-here--voltage > .is-here { opacity: 1; color: var(--t7-accent); }
.t7-here--voltage > .is-here .elementor-heading-title { color: var(--t7-accent); }

/* THE PIN. Works because the row container is already
   align-items:flex-start — a stretched flex child cannot stick.
   --t7-here-top clears the sticky header (template 99); it is the
   one dial, so retuning the header retunes this with it. */
@media (min-width: 1025px) {
  .t7-here {
    position: sticky;
    top: var(--t7-here-top, 144px);
    align-self: flex-start;
    /* drops the titles down the band so they read against the card
       headers instead of the card's top edge (Paul, 2026-08-26).
       Padding, not margin: it moves them in BOTH the flowing and the
       pinned state, where a top offset would only move one. */
    padding-top: var(--t7-here-drop, 60px);
  }
  .t7-here-label { display: none; }
}

/* Below desktop: no rail, the heading rides above its own group. */
@media (max-width: 1024px) {
  .t7-here { display: none; }
  .t7-here-label { display: block; }
  .t7-here-group + .t7-here-group { margin-top: calc(var(--gap-width, 13px) * 2); }
}

/* clicking a rail item scrolls to its group; without this the group
   lands underneath the sticky header. Same dial as the pin. */
.t7-here-group { scroll-margin-top: var(--t7-here-top, 144px); }

/* THE CARDS ANSWER THE RAIL (Paul, 2026-08-26). Only the FRAME moves.
   Dimming the whole card would take the copy down with it, and the copy
   is the thing you are trying to read. Recedes toward the ground rather
   than to transparent, so a receded frame still separates the card from
   the photograph behind it.

   Needs `is-here` on the GROUP as well as on the rail item - snippet
   1293 sets both. Desktop only, same as the rail: on a phone you can
   only see one group anyway, and a dimmed frame around the card you are
   reading looks like it is disabled. */
@media (min-width: 1025px) {
  .t7-here-group .t7-frame {
    border-color: color-mix(in srgb, var(--t7-frost-frame) var(--t7-here-recede, 40%), var(--t7-ground));
    transition: border-color 260ms cubic-bezier(.4, 0, .2, 1);
  }
  .t7-here-group.is-here .t7-frame { border-color: var(--t7-frost-frame); }

  /* one election, both expressions: electing the accent reading on the
     rail takes the frames with it. */
  .e-con:has(> .t7-here--voltage) .t7-here-group.is-here .t7-frame {
    border-color: var(--t7-accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .t7-here > * { transition-duration: 1ms; }
}

/* ============================================================
   RUNIN - a list set as running text.      (2026-08-26, barred 08-27)
   ------------------------------------------------------------
   Entries flow and wrap like prose, each one whole - "Anthony
   Bourdain: Parts Unknown" never breaks across two lines - and
   each sits on a bar.

   THE BAR IS A PIECE OF THE FRAME (Paul, 2026-08-18) and this
   obeys that law: background is --t7-bar-bg, the frame material,
   and the type on it is the GROUND. White-figure-on-ground in
   dark mode, and it inverts with everything else in light mode
   for free. No literal colours here.

   CENTRED, NOT JUSTIFIED, and that is not a preference - it is
   the only stable option (found 2026-08-27). Real justification
   needs `text-align: justify` plus a collapsible SPACE between
   the items for the browser to stretch. Elementor strips the
   whitespace between </li> and <li> when the widget is saved, so
   the spaces vanish, and inline-blocks with no space between them
   fuse into one continuous white slab. A layout that breaks when
   you open the page in the editor is not a layout. Flex `gap`
   owns the spacing instead: nothing to strip, nothing to fuse.
   (`space-between` is not the answer either - it justifies the
   LAST row too and flings three stragglers across the full width.)

   The bar is what separates entries, so the bullet the first
   version drew - and the overflow:hidden that clipped it at line
   ends - is gone.
   ============================================================ */
.t7-runin ul,
ul.t7-runin {
  display: flex;
  flex-wrap: wrap;
  justify-content: var(--t7-runin-align, center);
  align-items: baseline;
  column-gap: var(--t7-runin-gap, .5em);
  row-gap: var(--t7-runin-leading, .42em);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* A bar may never exceed its frame. Sports 205's two sentence-length
   credits measured 639.72px in a 223px box at 375 (2.87x) and 887.95px
   at 1280 - and because html/body are `overflow-x: clip` there is no
   scrollbar, so 41% of that bar was CUT, not scrollable. nowrap was
   written for Music's one-to-five-word credits and holds for them:
   short bars are provably unmoved by this (HBO/Showtime/ESPN/WWE rects
   byte-identical with it on and off). This is a SAFETY NET, not a
   design change - the real fix for those two entries is editorial,
   and once the copy is trimmed this rule goes inert on its own. */
.t7-runin li,
ul.t7-runin > li {
  white-space: normal;
  max-width: 100%;
  list-style: none;
}

@media (max-width: 767px) {
  .t7-runin ul,
  ul.t7-runin { --t7-runin-gap: .34em; --t7-runin-leading: .34em; }
}

/* ============================================================
   RUNIN LINKS - bars that are links.               (2026-08-28)
   ------------------------------------------------------------
   First linked host: the FOOTER (template 100) sets the top menu
   and the copyright/socials row as runin bars. An anchor in a bar
   wears the bar's own ink - without this it falls back to the
   theme link colour and the bar reads two-toned. On hover the
   whole bar floods, using the same mode-relative pair the service
   cards flood with, so every hot surface answers hover alike.
   Breadcrumb: footer 100 widgets a1f0071 (links) / a1f0072
   (copyright + socials), in containers aa6ca46 / 9117efa. */
.t7-runin li a {
  color: inherit;
  text-decoration: none;
}
/* the light-mode link law (html.theme-light a, Cobalt) ties the rule
   above at (0,1,2) and wins on source order - the bar keeps its own
   ink in both modes, so the light scope re-states the inherit. */
html.theme-light .t7-runin li a { color: inherit; }
/* HOVER RETIRED 2026-08-31 (Paul): these bars used to swap background
   instantly on hover (--t7-cta-flood). He wants them to "behave like the
   social buttons" - white at rest, Ember WIPE on hover - so the linked
   bars joined t7-wipe's selector lists below instead. Nothing here should
   set a hover background; the wipe owns it. The bar's resting face is
   still THE BAR FACE rule above (frame material + ground ink). */
.t7-runin li:has(> a) { --t7-wipe-fg: var(--t7-btn-fg-flip); }
.t7-runin li:has(> a) > a { position: relative; z-index: 1; transition: color .25s ease .08s; }
.t7-runin li:has(> a):hover { color: var(--t7-wipe-fg); }

/* footer 100: both runin lists sit in a bottom-left composition -
   centred bars would fight it. Dial only; the layout law stays in
   the runin rule above. */
.elementor-element.elementor-element-a1f0071,
.elementor-element.elementor-element-a1f0072 {
  --t7-runin-align: flex-start;
}

/* ============================================================
   THE BAR FACE - one definition, four hosts.        (2026-08-27)
   ------------------------------------------------------------
   "THE BAR IS A PIECE OF THE FRAME" (Paul, 2026-08-18): background
   is the frame material, type is the ground. That means it inverts
   with the mode for free and carries no literal colour.

   It is wanted in more than one place now - the run-in credits
   list, carousel captions on both carousel hosts - so the face is
   declared ONCE here and each host elects it. A FIFTH host is a
   line in this selector list, never a second copy of the colours.
   Hosts own their LAYOUT below; this rule owns only what a bar
   looks like.

   The caption arm is (0,3,0) on purpose: Elementor's own
   per-post CSS colours captions at (0,2,0) and loads AFTER this
   file, so a matching-specificity rule would lose the tie.
   ============================================================ */
.t7-runin li,
ul.t7-runin > li,
.t7-capbar.elementor-widget-image-carousel .elementor-image-carousel-caption,
.t7-photoslide .t7-capbar.t7-capbar--item {
  background: var(--t7-bar-bg);
  color: var(--t7-ground);
  padding: var(--t7-bar-pad, .16em .45em .24em);
  /* the bar hugs its type instead of inheriting the block's leading -
     without this it stands about 2.5x the cap height */
  line-height: var(--t7-bar-leading, 1.2);
}

/* ============================================================
   CAPBAR - a carousel caption worn as a bar.        (2026-08-27)
   ------------------------------------------------------------
   The label sits ON the image, flush into its bottom-left corner,
   instead of underneath it.

   Elementor renders each slide as figure.swiper-slide-inner >
   img + figcaption, and that figure ships position:static, so it
   has to become the positioning context first or the caption
   would anchor to the whole carousel. Going absolute also lifts
   the caption out of flow, which is what stops it adding a strip
   of height below every image.
   ============================================================ */
.t7-capbar .swiper-slide-inner { position: relative; display: block; }
.t7-capbar .swiper-slide-image { display: block; }
.t7-capbar.elementor-widget-image-carousel .elementor-image-carousel-caption {
  position: absolute;
  left: var(--t7-capbar-inset, 0);
  bottom: var(--t7-capbar-inset, 0);
  margin: 0;
  max-width: 100%;
  text-align: left;
  /* Elementor's reset.css carries a bare `figcaption { font-style:
     italic }`. That is (0,0,1) and this rule is (0,3,0), so plain
     specificity puts it back upright - no !important needed, and
     none wanted: an !important here is exactly the kind of hidden
     override that makes an Elementor edit silently lose later. */
  font-style: normal;
  /* a long name rides the bar rather than stacking it two lines high */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============================================================
   CAPBAR, LOOP-ITEM REGISTER.                       (2026-09-07)
   ------------------------------------------------------------
   Same bar, different anchor. The register above is welded to the
   IMAGE carousel: it hangs off Elementor's own
   figure.swiper-slide-inner > img + figcaption. A LOOP carousel
   has no figcaption at all - each slide is a loop-item template -
   so t7-capbar alone would store fine on that host and paint
   nothing.

   So the loop item supplies both halves itself: the item's root
   container (t7-photoslide) becomes the positioning context, and
   the caption is a real heading widget wearing
   t7-capbar t7-capbar--item. Live on Music post 62 widget
   4d55064, loop item template 2218 (client `photo` -> post title).

   Two registers of one idea, the way t7-tab carries a heading
   register and a caption register - NOT two capbar classes.

   Stacked to (0,3,0) for the same reason the image register is:
   the heading carries no colour global on purpose, so Elementor's
   widget-default colour rule sits at (0,2,0) and loads after this
   file. A matching-specificity rule would lose the tie and the
   caption would arrive stock Elementor blue.
   ============================================================ */
.t7-photoslide { position: relative; }
.t7-photoslide .t7-capbar.t7-capbar--item {
  position: absolute;
  left:   var(--t7-capbar-inset, 0);
  bottom: var(--t7-capbar-inset, 0);
  margin: 0;
  max-width: 100%;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============================================================
   PAGINATION - the dots are squares.                (2026-08-27)
   ------------------------------------------------------------
   Swiper ships its bullets at border-radius:50%. Nothing else on
   this site is round - frames, bars, cards, plates and patterns
   are all square - so the dots were the last curved thing in a
   square language (Paul, 2026-08-27: "since everything is so
   blocky").

   SITE-WIDE ON PURPOSE. This is a shape decision about the
   system, not about one carousel, and round dots on the homepage
   next to square dots on Music would read as a bug rather than a
   choice. It touches every swiper pagination on the site: home
   90, templates 116/148/195, Entertainment 63, Music 62, UI Test
   759. If one carousel ever has to differ, scope it by moving
   this rule under that widget's own class - do not add a second
   radius somewhere else.

   Set --t7-dot-radius on any scope to bring curves back.
   ============================================================ */
.swiper-pagination-bullet { border-radius: var(--t7-dot-radius, 0); }

/* ============================================================
   CHROME - the carousel's own controls.             (2026-08-27)
   ------------------------------------------------------------
   Arrows and dot placement. SEPARABLE FROM t7-capbar ON PURPOSE:
   a carousel may want barred captions and stock controls, or the
   reverse. The surface canon's standing complaint about glass and
   film is that two independent ideas got welded into one class
   and neither could be understood alone - not repeating that.

   THE DOTS CLEAR THE ARTWORK. Swiper parks .swiper-pagination
   absolutely at bottom:5px inside a wrapper with only 30px of
   bottom padding, so the dots land on the bottom of the images.
   The wrapper gets real padding to sit them in, and is made
   position:relative so `bottom` actually resolves against it
   rather than some ancestor. --t7-dots-drop is the one dial.
   (The wrapper is overflow:hidden, so the dots have to live
   INSIDE that padding - a negative offset would be clipped.)

   THE ARROWS are an accent glyph on a ground square, Ember on
   hover, per Paul 2026-08-27. Ember is written the way the rest
   of this file writes it - the global id with a literal fallback.

   EVERY SELECTOR HERE IS (0,3,0), for the same reason as the
   caption: Elementor emits .elementor-element-<id> .elementor-
   swiper-button at (0,2,0) and its per-post CSS loads AFTER this
   file, so a matching-specificity rule loses the tie. Proved the
   hard way - at (0,2,0) the arrow BACKGROUND applied (nothing
   competing for it) while colour, glyph size and the dot offset
   all silently lost. If a chrome rule ever "does nothing", this
   is the first thing to check.

   SECOND HOST: loop-carousel (2026-08-30, homepage 90 client
   marquee d0f969b; house style per Paul - this chrome is THE
   carousel chrome for any logo/photo carousel). The two widget
   types build different scaffolding, so the shared rules carry
   both hosts in ONE selector list (bar-face law) and each host
   keeps a small block for the vendor CSS only it has to beat:
   - image-carousel: arrows are children of the overflow-hidden
     wrapper, BUT Elementor ships that wrapper position:static
     (widget-image-carousel.css) so absolute children resolve
     against the WIDGET (.elementor-widget is relative) and an
     overflow clip does not reach them. The widget takes the side
     padding, exactly like the loop host. CORRECTED 2026-09-22:
     this file used to force the wrapper relative and pad it
     instead, which dragged the arrows inside the clip and left
     the neighbouring slides peeking in the 25px gutters (seen on
     Slim Jim 70 on Paul's phone). Never make the wrapper relative.
   - loop-carousel: arrows+dots are SIBLINGS of .swiper, outside
     the clip; the WIDGET takes the side padding and the widget
     (position:relative) is the arrows' containing block. Its
     vendor sheet (widget-loop-carousel.min.css) drives geometry
     through custom properties, so where a var exists we DIAL it
     (--swiper-padding-bottom: 0) instead of out-specifying.
   - loop host arrow colours are NOT in this file AT ALL: the
     loop-carousel exposes arrow_normal_color AND arrow_hover_
     color (image-carousel lacks hover - its one warranted
     !important below). Both wired in Elementor: normal to the
     Accent slot, hover to Ember. Same for dots_normal/hover.
   - loop host vertical centring is a % MARGIN, same as the image
     host. SUPERSEDED 2026-08-30: this note used to read that
     tmpl-117 pins a CONSTANT 240px strip at every width and so
     needed a fixed --t7-strip-mid and no --t7-cols. That was true
     of the homepage's old column counts only. Re-measured on two
     hosts at six widths once every tmpl-117 host was pinned 4/3/2,
     the strip is width-PROPORTIONAL with a floor:
       strip_h = max(0.8 * stripWidth / cols, 165px)
     which is exactly the case the old note predicted would be
     "the day this host learns the % margin instead." That day
     arrived; it did. --t7-strip-mid survives as an optional
     per-widget override, no longer the mechanism.
   ============================================================ */
/* Side padding = half the nav block, on the WIDGET: the wrapper (the
   overflow clip) insets by nav/2, so an arrow at left/right 0 - absolute
   against the widget, outside the clip - straddles the strip edge with
   its CENTRE on the edge (Paul, 2026-08-28), and the clip lands exactly
   on the strip so no neighbour slide shows in the gutter. Both hosts
   now carry the same geometry. The widget's Advanced > Padding must
   stay EMPTY - an authored 0 beats this at (0,3,0)+ (Music 62 lesson). */
.t7-chrome.elementor-widget-image-carousel {
  padding-inline: calc(var(--t7-nav-size, 50px) / 2);
}
.t7-chrome.elementor-widget-image-carousel .elementor-image-carousel-wrapper {
  /* NO position here - Elementor's static is what lets the arrows out.
     Elementor bakes 30px bottom padding into this wrapper for absolute
     pagination; the dots are static flow now, so it is dead height that
     skews the arrow centring math. */
  padding-bottom: 0;
}
/* Loop host: same side inset, but on the WIDGET - the arrows are
   siblings of .swiper, positioned against the widget box (already
   position:relative from Elementor), and offsets resolve against its
   PADDING box, so left/right 0 (the vendor default - no rule needed)
   puts the block's CENTRE exactly on each strip edge. The vendor
   pagination reserve is a var, so it is dialled off, not fought
   (its own rule sits at (0,4,0) behind a :not(:has()) chain). */
.t7-chrome.elementor-widget-loop-carousel {
  padding-inline: calc(var(--t7-nav-size, 50px) / 2);
  --swiper-padding-bottom: 0px; /* dots are static flow below, not absolute */
}
/* Dots flow BELOW the strip in normal flow - never absolute. 14 bullets
   wrap to two rows on phones, and an absolute bottom:0 block grows UPWARD
   into the photo (seen on Paul's phone 2026-08-28). Static flow makes the
   overlap geometrically impossible at any row count. Replaces the old
   --t7-dots-drop padding scheme; that dial is retired. */
.t7-chrome.elementor-widget-image-carousel .swiper-pagination,
.t7-chrome.elementor-widget-loop-carousel .swiper-pagination {
  position: static;
  margin-top: var(--t7-dots-gap-y, 16px);
}
/* Loop host only: its vendor sheet parks the bullets with inset/
   translate/width:max-content at (0,2,0); static flow ignores the
   offsets but a transform still moves a static box, so both are
   neutralised here. */
.t7-chrome.elementor-widget-loop-carousel .swiper-pagination {
  transform: none;
  width: 100%;
}
/* Loop host active dot = Accent, matching the Music roster's
   dots_color -> accntsl. The loop-carousel exposes NO active-dot
   control (only normal/hover, wired in Elementor), so this is the
   legitimate child-sheet case - kit global, no literal.
   IMAGE host joined 2026-09-22 (Slim Jim 70 influencer carousel
   3398560): the image-carousel exposes ONE dots_color for every
   bullet, so with dots_color -> Ash the active dot needs this line
   to read Accent like the Music roster. One more host, one more
   line - not a second copy of the colour. */
/* Image host at (0,5,0) via stacked class, NOT a typo: dots_color emits
   .elementor-70 .elementor-element.elementor-element-<id>
   .swiper-pagination-bullet at (0,4,0) from the per-post sheet, which
   loads after this file - measured 2026-09-22, the (0,3,0) form lost
   and the active dot stayed Ash. Same stacking the arrow rules use. */
.t7-chrome.t7-chrome.t7-chrome.elementor-widget-image-carousel .swiper-pagination-bullet-active,
.t7-chrome.elementor-widget-loop-carousel .swiper-pagination-bullet-active {
  background: var(--e-global-color-accntsl);
}
/* ...and inactive dots stand SOLID, like the Music roster's. The
   image host gets opacity:1 from e-swiper.min.css; the loop host's
   vendor sheet leaves swiper's 0.2 default standing (measured
   2026-08-30: Ash@1 on Music vs Ash@.2 here). State reads by COLOUR
   (Ash vs Accent), not by fade - one language on both hosts. */
.t7-chrome.elementor-widget-loop-carousel .swiper-pagination-bullet {
  opacity: 1;
}
/* CORRECTION 2026-09-22: the image host does NOT get opacity:1 from
   e-swiper - measured on Slim Jim 70 (3398560) the inactive bullets
   sat at swiper's .2 default, and the bullet gap arrived 6px against
   the roster's 8px (loop dots_gap; the image-carousel exposes no gap
   control). Both are swiper custom properties, so they are DIALLED
   on the pagination box, not out-specified. --t7-dots-gap-x is the
   one dial; 8px mirrors Music 62 4d55064. */
.t7-chrome.elementor-widget-image-carousel .swiper-pagination {
  --swiper-pagination-bullet-inactive-opacity: 1;
  --swiper-pagination-bullet-horizontal-gap: var(--t7-dots-gap-x, 8px);
}
/* Arrows: horizontally the centre rides each strip edge (left/right 0
   inside the padded box). Vertically 50% centres on strip + dots, so
   --t7-arrow-lift pulls up by roughly half the dots block to sit mid-
   photo; the phone media query below re-dials it for two dot rows. */
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button-prev { left: 0; }
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button-next { right: 0; }
/* Vertical centring, exact at ANY width: the slides are square, so the
   photo is (100% - nav padding) / columns tall, and a percentage MARGIN
   on an absolute box resolves against the containing block's WIDTH -
   that anchors the arrow to the photo's midline with the dots block
   (whose row count varies with width) out of the equation entirely.
   Dial: --t7-cols MUST mirror the widget's slides_to_show per
   breakpoint (Elementor: 4 desktop here, 2 tablet, 1 phone defaults).
   At (0,5,0) via stacked class - NOT a typo: this widget uses
   Elementor's "pagination position: outside", which emits its own
   top calc at (0,4,0) from e-swiper.min.css; three classes lose. */
.t7-chrome.t7-chrome.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button {
  top: 0;
  margin-top: calc((100% - var(--t7-nav-size, 50px)) / var(--t7-cols, 4) / 2 - var(--t7-nav-size, 50px) / 2);
}
/* Loop host centring, like the image host's, is a PERCENTAGE MARGIN -
   see the correction inside the rule. Stacked to (0,5,0) because the
   vendor sheet's own top calc (50% of widget INCLUDING the dots, minus
   a 15px "caption spacing") comes in at (0,4,0). */
.t7-chrome.t7-chrome.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button {
  /* CORRECTION 2026-08-30: the header above recorded tmpl-117 as pinning
     a CONSTANT 240px strip at every width. That held for the homepage's
     old column counts, not universally. Measured on two hosts, six
     widths, after the slide counts were pinned 4/3/2 everywhere:
       strip_h = max(0.8 * stripWidth / cols, 165px)
     - 0.8 because tmpl-117 renders its art at 80% of the slide
     - 165px is a min-height on the tmpl-117 loop-item container; it
       binds below roughly a 1025px viewport
     Verified: home 90 165/165/208.7 and music 62 165/187.5/217.9 at
     375/768/1280. A fixed --t7-strip-mid could not serve both, and left
     arrows 37.5px low on the homepage the moment its strip became 165px.
     So the loop host finally learns the image host's trick, exactly as
     this block's header predicted: a PERCENTAGE MARGIN resolves against
     the containing block's WIDTH, so the arrow tracks the strip at every
     width with no media queries and no per-widget dial. (100% - nav) is
     the strip width - the widget carries padding-inline of nav/2 a side.
     --t7-strip-mid KEEPS its original meaning - the distance from the
     strip top to the arrow centre - and is now merely OPTIONAL: unset,
     it is computed; set on a widget whose art is not tmpl-117's 80%,
     that literal still wins exactly as it always did. */
  top: 0;
  margin-top: calc(
      var(--t7-strip-mid,
          max(0.8 * (100% - var(--t7-nav-size, 50px)) / var(--t7-cols, 4),
              var(--t7-strip-floor, 165px)) / 2)
      - var(--t7-nav-size, 50px) / 2);
}
/* CARD carousels are not logo strips.                    (2026-08-31)
   The formula above describes tmpl-117 art (80% of the slide, floored
   at 165px). A tmpl-104 CASE-STUDY CARD carousel is a different animal:
   its height is the card's, set by --t7-carousel-h under
   t7-carousel-fixed. Feeding it the strip formula put the arrows near
   the top of a tall card - measured on the services template, arrow
   centre 111px into a 569px strip, 174px high. --t7-strip-mid is the
   documented per-host override, so the card hosts simply declare their
   own midline and the formula above never runs for them. */
.t7-chrome.t7-carousel-fixed.elementor-widget-loop-carousel {
  --t7-strip-mid: calc(var(--t7-carousel-h, 620px) / 2);
}
.t7-chrome.t7-chrome.t7-chrome.t7-carousel-fixed.elementor-widget-loop-carousel .elementor-swiper-button {
  /* re-stated so the card midline beats the strip formula's (0,5,0) */
  margin-top: calc(var(--t7-strip-mid) - var(--t7-nav-size, 50px) / 2);
}
/* PHOTO carousels fill their slide.                     (2026-09-07)
   The strip formula's 0.8 is tmpl-117's logo art, which renders at
   80% of the slide, and its 165px floor is that template's own
   min-height. Template 2218 has neither: the photo is width:100%
   with no min-height, so the slide is as tall as the art is wide
   (the roster photos are square). Fed the logo formula the arrows
   ride 10% of a slide-width too high.

   Same remedy the card hosts take - declare the midline instead of
   fighting the formula - but the PERCENTAGE trick is kept rather
   than a literal: a percentage margin resolves against the
   containing block's WIDTH, so this tracks every breakpoint with
   no media queries. (100% - nav) is the strip width, because the
   widget carries padding-inline of nav/2 a side; --t7-cols already
   mirrors slides_to_show 4/3/2 further down this file. */
.t7-chrome.t7-carousel-photo.elementor-widget-loop-carousel {
  --t7-strip-mid: calc((100% - var(--t7-nav-size, 50px)) / var(--t7-cols, 4) / 2);
}
.t7-chrome.t7-chrome.t7-chrome.t7-carousel-photo.elementor-widget-loop-carousel .elementor-swiper-button {
  /* re-stated so the photo midline beats the strip formula's (0,5,0) */
  margin-top: calc(var(--t7-strip-mid) - var(--t7-nav-size, 50px) / 2);
}
.t7-chrome.t7-chrome.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button {
  /* post-90.css emits --arrow-*-translate-y:-50% on the wrapper and the
     vendor transform rule consumes it at (0,4,0), so the shared
     transform:none at (0,3,0) loses HERE (it held on the image host,
     whose translateY came in lower). Pinned again at (0,5,0). */
  transform: none;
}

.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button,
.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button {
  width: var(--t7-nav-size, 50px);
  height: var(--t7-nav-size, 50px);
  /* Centre on the PHOTO, not the wrapper: 50% of the wrapper minus half
     the below-photo block (dots gap + dot rows) minus half the arrow
     itself. translateY(-50%) does not survive here (verified live), so
     transform is pinned to none; the top rule further down carries the
     actual centring math (and the specificity war story). */
  transform: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--t7-ground);
  /* NO base colour here on purpose. Elementor owns the arrow colour
     (Advanced > Arrows > Color, wired to the Accent global) and emits
     it at (0,5,0) from post-62.css, which loads after this file - a
     colour declared here would be dead code that LOOKS like it works.
     Change the resting arrow colour in Elementor, not in this rule.
     (Loop host likewise: arrow_normal_color/arrow_hover_color wired
     to Accent/Ember in the widget, emitted from post-90.css.) */
  border-radius: 0;
  transition: color .2s ease;
}
/* the glyph ships as an inline SVG here, not an icon font, so it
   takes a box rather than a font-size. Sizing is shared by both
   hosts; the fill story is not (below). */
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button svg,
.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button svg {
  width: var(--t7-nav-glyph, 54%);
  height: var(--t7-nav-glyph, 54%);
}
/* IMAGE HOST ONLY: Elementor paints this fill LITERALLY from
   arrows_color at (0,5,0), not via currentColor, so without forcing it
   the glyph ignores the hover entirely - the button went Ember while
   the chevron stayed Accent. Tying fill to currentColor keeps the
   resting colour under Elementor's control AND lets the hover reach
   the glyph. The LOOP host must NOT get this: its vendor sheet drives
   the svg fill from arrow_normal/hover_color directly, so currentColor
   there would re-break what Elementor already does right. */
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button svg {
  fill: currentColor !important;
}
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button i,
.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button i { font-size: var(--t7-nav-glyph-size, 1.5rem); }

.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button:hover,
.t7-chrome.elementor-widget-image-carousel .elementor-swiper-button:focus-visible {
  /* !important is warranted HERE and nowhere else in this block:
     Elementor's image-carousel exposes arrows_color but NO hover
     colour, so there is no Elementor setting for this to silently
     beat - the usual objection does not apply. The alternative was
     out-specifying (0,5,0) with a selector chain nobody could read.
     The LOOP host is deliberately absent: it HAS a hover control
     (arrow_hover_color), wired to Ember in the widget itself. */
  color: var(--e-global-color-bc74cd9, #F2452E) !important;
}
/* Loop host keyboard parity: the vendor sheet only styles :hover, so
   Ember on focus rides the same var the Elementor control emits. */
.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button:focus-visible {
  color: var(--arrow-hover-color, var(--e-global-color-bc74cd9, #F2452E));
}
.t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button:focus-visible svg {
  fill: var(--arrow-hover-color, var(--e-global-color-bc74cd9, #F2452E));
}

@media (prefers-reduced-motion: reduce) {
  .t7-chrome.elementor-widget-image-carousel .elementor-swiper-button,
  .t7-chrome.elementor-widget-loop-carousel .elementor-swiper-button { transition-duration: 1ms; }
}
/* --t7-cols mirrors slides_to_show per breakpoint. BOTH hosts read it
   now (2026-08-30): the image host for its own % margin, and the loop
   host since it learned the same trick above. The two hosts run
   different counts - image 4/2/1, loop 4/3/2 - which is why they are
   dialled separately rather than sharing one rule. Every tmpl-117 loop
   host (home 90 d0f969b, music 62 a3f07e2, entertainment 63 0308446 +
   bc6738d) is pinned 4/3/2 in Elementor; if one ever diverges, set
   --t7-cols on that widget rather than editing these. */
@media (max-width: 1024px) {
  .t7-chrome.elementor-widget-image-carousel { --t7-cols: 2; }
  .t7-chrome.elementor-widget-loop-carousel  { --t7-cols: 3; }
}
@media (max-width: 767px) {
  .t7-chrome.elementor-widget-image-carousel { --t7-cols: 1; }
  .t7-chrome.elementor-widget-loop-carousel  { --t7-cols: 2; }
}
/* PHOTO register on the IMAGE host (2026-09-22). A photo carousel is
   pinned 4/3/2 whichever widget builds it - the Music roster (loop,
   62 4d55064) and the Slim Jim influencer strip (image-carousel,
   70 3398560) must read as the same object. The image host's stock
   2/1 above is the logo-strip count; t7-carousel-photo re-dials it
   to the roster's 3/2 so the arrow midline math stays exact. (0,3,0)
   beats the (0,2,0) defaults above; Elementor's slides_to_show on
   the widget must be pinned 4/3/2 to match. */
@media (max-width: 1024px) {
  .t7-chrome.t7-carousel-photo.elementor-widget-image-carousel { --t7-cols: 3; }
}
@media (max-width: 767px) {
  .t7-chrome.t7-carousel-photo.elementor-widget-image-carousel { --t7-cols: 2; }
}

/* ============================================================
   CARDED STRIP - a t7-card wrapping a carousel yields on phones.
   ------------------------------------------------------------
   (2026-08-30) THE bug Paul reported: "the brands carousel is
   still falling out of the viewport on mobile." It was not
   falling out, it was being CUT - the bands are `overflow:hidden`
   and html/body are `overflow-x: clip`, so there is no scrollbar
   and the clipped part is simply lost.
   Measured at 375, home 90 d0f969b sat 73 -> 448 (a 73px overhang
   from three stacked insets: band ea0a21a padding 50 + inner
   299243e padding 10 + frame b499c54 border 13); entertainment 63
   `0308446` and `bc6738d` sat 50 -> 425 (+50). In every case the
   NEXT ARROW landed outside the viewport and was unhittable -
   elementFromPoint null on 90, 37px clipped on 63.
   NOT fixable from widget settings. All three widths measured live
   on BOTH pages, and the two non-vw values are catastrophic
   because the wrapping frame is `--width:auto` (shrink-to-fit,
   from .e-con.t7-card) so a percentage is circular and resolves
   to max-content:
                 home 90        entertainment 63
     100vw       375px  (+73)   375px  (+50)    <- kept, least bad
     100%        5467px         3167px (+2842)
     unset/auto  16777216px     3167px
   So the widget keeps 100vw and THE FRAME yields instead, on
   phones only. Matched by shape rather than by id: any t7-card
   whose direct child is a chromed carousel. Three hosts today
   (90 b499c54, 63 19378e6 + cdfa13c); a fourth gets it for free.
   Verified at 375 per host: home widget 73 -> 302 with the next
   arrow 252 -> 302 in view; entertainment strips 375 -> 275px,
   overhang +50 -> 0, body.scrollWidth 425 -> 375, next arrow
   262 -> 312 in view, slides_to_show_mobile 2 preserved, zero
   elements past the viewport. Desktop geometry byte-identical
   with the rule on and off.
   ============================================================ */
@media (max-width: 767px) {
  .e-con.t7-card:has(> .elementor-widget-loop-carousel.t7-chrome),
  .e-con.t7-card:has(> .elementor-widget-image-carousel.t7-chrome) { --width: 100%; }
}

/* ============================================================
   MARK - one-colour artwork painted as the current figure.
   ------------------------------------------------------------
   (2026-08-30) Elect `t7-mark` on an image WIDGET whose asset is
   single-colour black artwork (the intro-block company logo) and
   it rides --t7-cta-icon: eggshell on dark, ink on light - the
   SAME mechanism the client-marquee logos (tmpl 117, 908bc9f)
   and the services icons already use, generalised to an
   electable class instead of another one-off scoped rule.
   Tone-scope aware for free: --t7-cta-icon re-resolves under
   t7-dark / t7-light / t7-invert (theme engine section C), so a
   marked logo inside an inverted band flips with its band.
   NO literal colours here - the token IS the rule.
   First host: homepage 90 intro logo (image widget 25ff73f). */
.t7-mark img { filter: var(--t7-cta-icon); }

/* FRAME — gap-width border in the frame material */
.t7-frame { border: var(--gap-width, 13px) solid var(--t7-frost-frame); }

/* FRAME: rule — a single-edge frame piece. Production hand-rolled this
   five times (case studies 70/79/656/658/660: top var(--gap-width),
   other sides 0, some still carrying the dead #EAEBDC) before it had a
   name. Same material as the full frame; --t7-rule-edge picks the side. */
.t7-rule {
  border-style: solid;
  border-color: var(--t7-frost-frame);
  border-width: 0;
  border-top-width: var(--gap-width, 13px);
}
.t7-rule--bottom { border-top-width: 0; border-bottom-width: var(--gap-width, 13px); }
.t7-rule--left   { border-top-width: 0; border-left-width: var(--gap-width, 13px); }
.t7-rule--right  { border-top-width: 0; border-right-width: var(--gap-width, 13px); }

/* TONE: inherit — a documented no-op. "No class" is the default, but an
   operator cannot SEE the absence of a class in Elementor's UI, so the
   one state nobody can read is the one everything starts in. This class
   says it out loud and does nothing else. Do not give it declarations. */
.t7-inherit { /* intentionally empty — see comment above */ }

/* GLOSS — topmost like real lamination; reaches over the frame when
   there is one (negative inset), hugs the box when there isn't */
.t7-card { position: relative; }
.t7-card::after,
.svc-print-rule .e-loop-item.type-services::after {
  content: ""; position: absolute; inset: 0;
  pointer-events: none; z-index: 99;
  background: var(--t7-card-light);
}
.t7-frame.t7-card::after { inset: calc(-1 * var(--gap-width, 13px)); }

/* ---- preset extras ---- */
/* CARD IS ONLY THE OVERLAY (Paul, 2026-08-20). It owns `position:
   relative` and the ::after lighting pass above. NOT spacing, NOT
   margin, NOT hover. fx-lift's old margin/padding/hover was rehomed
   here during the purge and that was wrong — a card says "this reads
   as raised," nothing else. Spacing belongs to the layout. */

/* fx-invert modifier: solid fill in the card's OPPOSITE tone. The six
   slots are flipped by the tone tables (this class is grouped into the
   .t7-invert selector lists at the end of the file); here it only goes
   solid in its own — now flipped — materials. Element level beats any
   inherited scope, so the escape hatch works anywhere. */
/* (its three solid-fill lines live at the very END of the file — the
   element level must out-cascade THE DERIVED TOKENS rule, and file
   order is the tiebreaker) */

/* ============================================================
   t7 card system — rebuilt loop cards (104 case study, 569 staff)
   Anatomy: .t7-card = frame root (bg = frame material, padding =
   var(--gap-width), gloss ::after over everything) > .t7-panel
   surface, .t7-bar tag bar, .t7-plate icon plate, .t7-accent
   accent heading. All colors from THE DERIVED TOKENS rule (end of
   this file).
   ACCENT LAW: Cobalt on light surfaces, Voltage on dark, Ember
   = active/hover.
   ============================================================ */
/* (the old per-mode --t7-bar-accent/--t7-cta-* tables lived here — they
   are now one formula each in THE DERIVED TOKENS rule: the bar and the
   CTA flood carry the accent of the OPPOSITE ground, --t7-accent-flip,
   because a bar/flood is itself a Figure-material surface) */

/* (.t7-card itself is painted by the axis rules above: glass+frame+gloss) */
.t7-panel { color: var(--t7-frost-text); }
.t7-panel .elementor-widget-heading .elementor-heading-title { color: var(--t7-frost-text); }
.t7-accent.elementor-widget-heading .elementor-heading-title { color: var(--t7-accent); }
.t7-accent.elementor-widget-heading .elementor-heading-title:hover { color: var(--e-global-color-bc74cd9, #F2452E); }
/* THE BAR IS A PIECE OF THE FRAME (Paul, 2026-08-18): its bg is the
   frame material in every state — glass, solid, either mode — so it
   reads as the frame passing behind the tags. The TAGS ARE NOT
   ACCENTED (Paul, 2026-09-01): they carry Ground, the readable mark
   on a Figure field (--t7-bar-mark), so a tag reads as type cut out
   of the frame instead of a coloured link.
   !important on both: tmpl-104 authors a bar bg + Voltage links, and
   post-104.css loads after this file — the tokens must own the bar
   or it never flips (that was the "bar doesn't invert" bug). */
.t7-bar { background: var(--t7-bar-bg) !important; }
.t7-bar a { color: var(--t7-bar-mark) !important; }
.elementor-widget-icon.t7-plate .elementor-icon { background-color: var(--t7-plate-bg) !important; box-shadow: none !important; }
.elementor-widget-icon.t7-plate .elementor-icon, .elementor-widget-icon.t7-plate .elementor-icon svg, .elementor-widget-icon.t7-plate .elementor-icon svg * { color: var(--t7-plate-glyph) !important; fill: var(--t7-plate-glyph) !important; }
.elementor-widget-icon.t7-plate .elementor-icon svg { position: relative; z-index: 1; transition: fill .25s ease .08s; }
.elementor-widget-icon.t7-plate .elementor-icon:hover svg,
.elementor-widget-icon.t7-plate .elementor-icon:hover svg * { fill: var(--t7-plate-glyph-flip) !important; color: var(--t7-plate-glyph-flip) !important; }

/* ============================================================
   t7-wipe — THE hover flood. ONE mechanism, previously copied
   four times (plate / social / button / svc card) with identical
   geometry and timing and only the colour differing.
   Consolidated 2026-08-20 (Paul: "it should be its own CSS
   property... then I can apply that hover animation to other
   things like buttons").

   TWO DIALS:
     --t7-wipe-flood  what floods in        (default Ember)
     --t7-wipe-fg     what the content becomes while flooded

   The existing consumers are GROUPED INTO the selector
   lists below (five since 2026-08-31: the footer's linked runin
   bars joined, replacing their own instant-flood hover) — no class election needed, nothing to migrate.
   Anything NEW just wears `t7-wipe` and sets the two vars.
   Do not re-add a per-consumer ::before; change it here.
   ============================================================ */
.t7-wipe,
.elementor-widget-icon.t7-plate .elementor-icon,
.elementor-social-icon,
.elementor-button,
.svc-print-rule .e-loop-item.type-services > a.e-con,
.t7-runin li:has(> a) { position: relative; overflow: hidden; }

.t7-wipe::before,
.elementor-widget-icon.t7-plate .elementor-icon::before,
.elementor-social-icon::before,
.elementor-button::before,
.svc-print-rule .e-loop-item.type-services > a.e-con::before,
.t7-runin li:has(> a)::before {
  content: ""; display: block; position: absolute; inset: 0; z-index: 0;
  background: var(--t7-wipe-flood, var(--e-global-color-bc74cd9, #F2452E));
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s cubic-bezier(.65,0,.35,1);
}
.t7-wipe:hover::before,
.elementor-widget-icon.t7-plate .elementor-icon:hover::before,
.elementor-social-icon:hover::before,
.elementor-button:hover::before,
.svc-print-rule .e-loop-item.type-services > a.e-con:hover::before,
.t7-runin li:has(> a):hover::before { transform: scaleX(1); }

/* flood colour per consumer — everything else takes the Ember default */
.svc-print-rule:not(.svc-light) .e-loop-item.type-services > a.e-con { --t7-wipe-flood: var(--t7-cta-flood); }
.svc-print-rule.svc-light .e-loop-item.type-services > a.e-con { --t7-wipe-flood: var(--e-global-color-c8af26f); }

/* self-sufficient defaults for anything NEW wearing t7-wipe. The four
   legacy consumers keep their own inner-element flip rules because each
   flips a different thing (svg fill / nested wrapper / plain colour). */
.t7-wipe { --t7-wipe-fg: var(--t7-btn-fg-flip); }
.t7-wipe > * { position: relative; z-index: 1; transition: color .25s ease .08s, fill .25s ease .08s; }
.t7-wipe:hover { color: var(--t7-wipe-fg); }

/* social icons wear THE BAR FACE, not the button face.   (2026-08-31)
   They used to take --t7-btn-* (the Accent plate), which put Cobalt
   squares next to Ink runin bars in light mode - Paul: "just make those
   icons match the other blocks at rest". The socials sit in the same
   footer composition as the bars, so they take the bar's pair:
   --t7-bar-bg (frame material) behind, --t7-ground glyph on top,
   exactly like `.t7-runin li`. Hover is unchanged - the Ember wipe and
   the glyph fade are inherited from t7-wipe's lists above, so the ONLY
   difference from a bar is that transition, which is the point. */
.elementor-social-icon {
  background-color: var(--t7-bar-bg) !important;
  border-radius: 0 !important;
  position: relative; overflow: hidden;
}
.elementor-social-icon i, .elementor-social-icon svg, .elementor-social-icon svg * {
  color: var(--t7-ground) !important; fill: var(--t7-ground) !important;
}
.elementor-social-icon i, .elementor-social-icon svg { position: relative; z-index: 1; transition: color .25s ease .08s, fill .25s ease .08s; }
.elementor-social-icon:hover i, .elementor-social-icon:hover svg, .elementor-social-icon:hover svg * { color: var(--t7-btn-fg-flip) !important; fill: var(--t7-btn-fg-flip) !important; }

/* icon-list glyph + counter number: mode-following defaults (explicit widget
   colors still win — no !important on purpose) */
.elementor-icon-list-icon i, .elementor-icon-list-icon svg, .elementor-icon-list-icon svg * {
  color: var(--e-global-color-fgur7b2); fill: var(--e-global-color-fgur7b2);
}
.elementor-counter .elementor-counter-number-wrapper, .elementor-counter .elementor-counter-title {
  color: var(--e-global-color-fgur7b2);
}

/* t7-cards inside carousels: legacy slide chrome must yield to the glass
   system - MATERIAL only, never GEOMETRY.             (revised 2026-09-03)

   WHAT CHANGED AND WHY. This rule used to be a `border:` SHORTHAND carrying
   !important, so it pinned WIDTH, STYLE and COLOUR in one stroke - and the
   card container's Border control in the loop template was dead on arrival.
   Elementor compiles a container border to a real `border-width` at (0,3,0)
   in per-template CSS that loads AFTER this sheet, which normally wins; an
   !important here beat it every time, silently. Paul hunted it on template
   2069, the testimonial card: the band around a quote reads as padding, and
   nothing in the panel would move it. That is exactly the failure mode the
   governance rule exists to prevent.

   WIDTH AND STYLE NOW BELONG TO ELEMENTOR. Template 2069's card container
   t7qeeb6 carries Border = solid / var(--gap-width) on all four sides, which
   computes to the same 13px this shorthand used to force. Template 104's card
   (c91bf0a) authors no border at all and falls through to .t7-frame at
   (0,1,0) - also 13px, also unchanged. Nothing moved; the dial just moved
   into the panel. A card in a slide that wears neither gets no width, which
   is correct: an unframed card should not grow a frame from a carousel rule.

   COLOUR STAYS SEALED, and that is not a preference. --t7-frost-frame and
   --t7-frost-tint are DERIVED tokens: they re-resolve under t7-dark /
   t7-light / t7-invert so a card inverts with its band for free. An Elementor
   colour control can hold a kit global or a literal and nothing else, so
   authoring the frame colour in the panel freezes the card in one mode. The
   per-template sheet loads after this file, so the tokens need !important or
   the flip dies - the same reasoning as THE BAR FACE above. box-shadow keeps
   its pin for the reason this rule was written in the first place: it is the
   legacy slide chrome being suppressed, and no Elementor control is asking
   for it back.

   Breadcrumb lives in t7qeeb6's Elementor Custom CSS box. */
.swiper-slide > .e-con.t7-card {
  border-style: solid;                              /* default only - Elementor may change it */
  border-color: var(--t7-frost-frame) !important;   /* token contract - sealed, mode-aware */
  background: var(--t7-frost-tint) !important;      /* token contract - sealed, mode-aware */
  box-shadow: none !important;
}

/* buttons: one interaction language site-wide — Ember is active, brief rise */
.elementor-button, .t7-bar a, .elementor-widget-icon.t7-plate .elementor-icon {
  transition: background-color .15s ease, color .15s ease, transform .12s ease;
}
/* buttons = surface-relative accent (the plate token pair: Cobalt/eggshell on
   light surfaces, Voltage/ink on dark, inverted under SOLID) + the svc-card
   Ember wipe on hover. */
.elementor-button {
  background-color: var(--t7-btn-bg) !important;
  color: var(--t7-btn-fg) !important;
  border-radius: 0 !important;
  position: relative; overflow: hidden;
}
.elementor-button .elementor-button-content-wrapper { position: relative; z-index: 1; transition: color .25s ease .08s; }
.elementor-button:hover { color: var(--t7-btn-fg-flip) !important; transform: translateY(-1px); }
.elementor-button:active { transform: translateY(0); }
/* buttons ON cards ride the card's surface (invert-aware plate pair) */
.t7-card .elementor-button { background-color: var(--t7-plate-bg) !important; color: var(--t7-plate-glyph) !important; }
.t7-card .elementor-button:hover { color: var(--t7-plate-glyph-flip) !important; }
/* plate placement is a CSS contract, not an Elementor setting: every t7-plate
   pins to its card's inner bottom-right corner, one gap unit off the frame.
   !important on purpose — widget-level positioning must never move this. */
.t7-card .elementor-widget-icon.t7-plate {
  position: absolute !important;
  right: var(--gap-width, 13px) !important;
  bottom: var(--gap-width, 13px) !important;
  top: auto !important; left: auto !important;
  margin: 0 !important;
  width: auto !important;
  z-index: 10;
}
/* plate box: fixed 79x79, pinned to the WIDGET'S bottom-right corner — that
   corner is (gap,gap) from the card's inner corner on every card, regardless
   of whatever width Elementor compiles for the widget box itself. */
.t7-card .elementor-widget-icon.t7-plate .elementor-icon {
  position: absolute !important;
  right: 0 !important; bottom: 0 !important; left: auto !important; top: auto !important;
  width: 79px !important; height: 79px !important;
  box-sizing: border-box; padding: 14px !important;
  display: flex; align-items: center; justify-content: center;
}
.t7-card .elementor-widget-icon.t7-plate .elementor-icon svg { width: 100% !important; height: 100% !important; }
/* reserved plate zone: any card WITH a plate pads its panels' bottoms by the
   plate's full footprint — text and plate are separate rows by construction,
   they can never collide at any width. */
.t7-card:has(.t7-plate) .t7-panel { padding-bottom: calc(79px + 2 * var(--gap-width, 13px)) !important; }

.t7-plate { box-shadow: none !important; }

/* PLAYGROUND LEGACY (759 demos only). fx-sheet/fx-soft = the print-sheet
   era, kept for reference; ui-ground-* predate the tone axis:
     ui-ground-dark  = t7-dark  + t7-solid      (use those instead)
     ui-ground-light = t7-light + t7-solid
     ui-ground-inverse = t7-invert + t7-solid   (grouped into the tone
                         tables, so its slots flip; painted here) */


.ui-ground-dark { background: var(--e-global-color-c8af26f); color: var(--e-global-color-316eccb); }
.ui-ground-dark .elementor-widget-heading .elementor-heading-title { color: var(--e-global-color-316eccb); }
.ui-ground-light { background: var(--e-global-color-42a1059); color: var(--e-global-color-c8af26f); }
.ui-ground-light .elementor-widget-heading .elementor-heading-title { color: var(--e-global-color-c8af26f); }

/* ---- theme flip: swap the two semantic slots under light mode ----
   Sources are stable globals the flip never touches, so nothing is
   circular. Every widget wired to c8af26f (98 uses / 16 posts) or
   316eccb (224 uses / 24 posts) flips for free -- no widget data
   changes, ever.
   Spec: specs/2026-08-11-palette-role-system-design.md  §5
   NOTE --e-global-color-primary is still set here ON PURPOSE: bare
   headings currently ride that fallback. It comes OUT in P4, once
   headings are wired explicitly to --t7-primary, so the System slots
   can carry the green tripwire (§6). Removing it before then would
   ship invisible headings in light mode. */
html.theme-light .elementor-kit-89 {
  --e-global-color-primary: var(--e-global-color-27abaeb); /* TEMPORARY -- removed in P4 */
}
html.theme-light a { color: var(--e-global-color-d3cd34a, #2651C0); }        /* Cobalt links on light */
html.theme-light a:hover { color: var(--e-global-color-bc74cd9, #F2452E); }  /* Ember hover both modes */

/* ============================================================
   t7 layout contexts — HOW A CARD KNOWS WHERE IT LIVES
   Default: a t7 card is CONTENT-SIZED. Containers opt in to more:
     .t7-cards--equal   (on the loop-grid WIDGET)  -> equal per-row heights
     (.t7-cards--masonry was the third option here - RETIRED 2026-09-03
      with its JS engine; see the retirement note further down this file)
     inside a swiper (structural, no class needed) -> slide owns the height;
                        fixed carousels add .t7-carousel-fixed + --t7-carousel-h
   Applied in Elementor > Advanced > CSS Classes on the loop-grid widget;
   each carries a breadcrumb in its Custom CSS block.
   ============================================================ */
/* default: content-sized — never stretch, no phantom min-heights */
.elementor-loop-container.elementor-grid { align-items: start; }
.elementor-loop-container .e-loop-item .t7-card { height: auto; min-height: 0; }
/* equal rows: opt back in to per-row stretch */
.t7-cards--equal .elementor-loop-container.elementor-grid { align-items: stretch; }
.t7-cards--equal .e-loop-item { display: flex; }
.t7-cards--equal .e-loop-item .t7-card { height: auto; min-height: 100%; width: 100%; }
/* carousel: the slide is the sizer; card fills it.
   A fixed-height flex-column card must NEVER wrap into a phantom second
   column (mobile bug 2026-08-07): nowrap + clip, the PHOTO gives ground
   first (crops, never pushes), and the blurb line-clamps last. */
.swiper-slide .t7-card { height: 100%; flex-wrap: nowrap !important; overflow: hidden; }
/* 620 = worst real column budget (photo floor + 2-line title + 4-line
   blurb + flowed plate) with margin; tune with --t7-carousel-h in one place */
.t7-carousel-fixed .swiper-slide { height: var(--t7-carousel-h, 620px); }
.t7-carousel-fixed .swiper-slide .e-loop-item { height: 100%; }

/* ============================================================
   QUOTE STRIP  —  --t7-carousel-h for a CAROUSEL OF QUOTES  (2026-09-03)
   Breadcrumb: elect "t7-chrome t7-carousel-fixed t7-quote-strip" on the
   loop-carousel WIDGET (playground 759 widget t7qrig5 today); the card
   itself is loop template 2069 "Testimonial Card" (t7-card t7-frame
   t7-quote), and each host carries a breadcrumb in its Custom CSS block.

   WHY THIS EXISTS. The 620px default above is the tmpl-104 CARD budget:
   a photo floor, a bounded title, a line-clamped blurb. A quote card has
   no photo to give ground with and no clamp — it is text all the way
   down. Its height is therefore set by the LONGEST QUOTE at the NARROWEST
   card, and that number moves OPPOSITE to the viewport: the narrower the
   card, the taller it must be. One fixed value cannot serve all three.

   MEASURED on 759 with the seven seeded quotes (slides 2 / 1 / 1,
   longest = Brian Park at 383 characters), height the tallest card needed:
       1280   card 539px  ->  553px
        768   card 663px  ->  378px
        375   card 285px  ->  723px
   The values below are those maxima with roughly 12% headroom.

   `.swiper-slide .t7-card` clips (overflow: hidden) and a quote is not
   clamped, so a value set too low SILENTLY EATS THE END OF SOMEBODY'S
   SENTENCE — no scrollbar, no ellipsis, no error. Re-measure here
   whenever a longer quote is tagged into one of these carousels.

   3-up was tried first and rejected: at 1280 it gives a 355px card that
   needs 875px of height. Width is the cheaper axis for a quote, which is
   why the widget runs 2-up and drops to 1-up at tablet.

   No colour lives here on purpose — the card's fill, frame and type all
   come from the existing t7-card / t7-quote token paths, which already
   flip with the mode. This block is a LENGTH DIAL and nothing else.
   REVISED 2026-09-03 (same day): Paul took these carousels to 1-UP at
   every breakpoint — "one up at a time" — and the desktop half of the
   note above no longer describes what ships. At 1-up the desktop card
   is 714px wide, not 539px, so the same quotes need LESS height, and
   620px left roughly 220px of dead card under the shortest ones.
   RE-MEASURED at 1280 with the five homepage quotes, card 714px:
       Brian Park (longest, 383 chars)  ->  402px
       shortest (Gabi)                  ->  259px
   Desktop is therefore 450px = the 402px maximum plus ~12% headroom.
   The 1024 and 767 values are UNCHANGED and still the agent's original
   measurements: those breakpoints were already 1-up before this change,
   so nothing about them moved. The clipping warning above still governs
   — re-measure before tagging a longer quote into one of these.
   Hosts today: homepage 90 widget b7e5c10, music 62 widget t7mus62,
   playground 759 widget t7qrig5.
   SUPERSEDED 2026-10-14: quote-strip slides are height:auto + stretch at
   every width (see the t7-quote block below); these values no longer
   size the slides.
   ============================================================ */
.t7-carousel-fixed.t7-quote-strip { --t7-carousel-h: 450px; }
@media (max-width: 1024px) {
  .t7-carousel-fixed.t7-quote-strip { --t7-carousel-h: 440px; }
}
@media (max-width: 767px) {
  .t7-carousel-fixed.t7-quote-strip { --t7-carousel-h: 800px; }
}
/* tmpl-104 photo box: the ONLY element that yields — crops via cover down
   to a floor; text is never sacrificed to it */
.swiper-slide .t7-card .elementor-element-b85dbac { flex-shrink: 999; min-height: 80px; overflow: hidden; }
.swiper-slide .t7-card .elementor-element-b85dbac img { width: 100%; height: 100%; object-fit: cover; }
/* tmpl-104 photo LINK: a block box, not the image widget's stock
   inline-block.                                          (2026-09-30)
   The link wraps the featured image and had no width of its own, so a
   lazy image that had not loaded yet reserved 0x0 and the title sat where
   the photo goes; when the image arrived it pushed the card text down
   ~319px (Showcase 2676 grid at 1440). As a block the link takes the
   column width and the image reserves its square (345x345 at 1440) from
   its width/height attributes before it loads. Loaded layout unchanged:
   measured identical on 2676 and on every tmpl-104 carousel (home 90,
   industries 62/63/204/205, service template 148) at 1440/768/390.
   The 1-column phone grid also needs the photo BOX rule below.
   Breadcrumb: tmpl 104 widget b85dbac custom CSS. */
.elementor-104 .elementor-element-b85dbac a { display: block; }
/* tmpl-104 photo BOX holds the square itself.             (2026-09-30)
   The block link alone did not reach the 1-column phone grid: the grid
   sized its rows as if the percentage-width image were not there, so
   the box collapsed to one line (~22px) until the image loaded and the
   text jumped ~320px. aspect-ratio + width:100% on the image widget make
   the box square from its own width at any phone size (344 at 390, 314
   at 360). Proportional on purpose: a px width breaks across phones.
   Loaded layout unchanged: measured identical on Showcase 2676 and every
   tmpl-104 host (home 90, industries 62/63/204/205, service template 148)
   at 1440/768/390/360. Assumes square card photos (every tmpl-104
   featured image is square today); a non-square one would be boxed
   square. Breadcrumb: tmpl 104 widget b85dbac custom CSS. */
.elementor-104 .elementor-element-b85dbac { aspect-ratio: 1 / 1; width: 100%; }
/* copy panel NEVER clips mid-line (shrink 0); the blurb self-bounds by clamp */
.swiper-slide .t7-card .t7-panel { flex-shrink: 0; min-height: 0; }
.swiper-slide .t7-card .t7-panel .elementor-widget-text-editor {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden;
}
/* in a fixed slide the plate leaves the corner-pin contract and joins the
   flow AFTER the paragraph — margin-top:auto is its gravity: it sinks to
   the card's bottom-right whatever the copy does (Paul, 2026-08-07) */
.swiper-slide .t7-card .elementor-widget-icon.t7-plate {
  position: static !important;
  order: 99;
  align-self: flex-end;
  width: auto !important;
  margin: var(--gap-width, 13px) var(--gap-width, 13px) var(--gap-width, 13px) 0 !important;
  margin-top: auto !important;
  flex-shrink: 0;
}
/* relative (NOT static): the icon must stay the containing block for its
   own ::before wipe — static let the wipe flood the whole card */
.swiper-slide .t7-card .elementor-widget-icon.t7-plate .elementor-icon {
  position: relative !important;
}
/* the corner-pin's reserved bottom zone isn't needed once the plate flows */
.swiper-slide .t7-card:has(.t7-plate) .t7-panel { padding-bottom: 0 !important; }
/* MASONRY RETIRED 2026-09-03. WPCode snippet 761 "t7: masonry engine (case
   grids)" was set to draft and dropped from the wpcode_snippets cache, and
   that snippet was the ONLY thing that ever stamped .t7-masonry-ready onto a
   loop container. Every masonry rule was gated behind that stamp, so all of
   them became unreachable the moment the engine went off - dead code that
   still reads as live. Swept here in the same pass as the carousel border
   hand-back (3.16.2). Verified independently before removal: zero LIVE posts
   or templates carry t7-cards--masonry or t7-masonry-ready; the only DB hits
   left are old revision rows, and page 209's case grid fbc1032 now wears
   t7-cards--equal. Fixed-height cards won. The rules were, verbatim:
     .t7-cards--masonry .elementor-loop-container.t7-masonry-ready {
       grid-auto-rows: 4px;        (ROW in snippet 761 had to match)
       row-gap: 0 !important;      (visual gap lived in the span math)
       align-items: start; }
     .t7-cards--masonry .t7-masonry-ready .e-loop-item { grid-row: span var(--t7-span, 40); }
     .t7-cards--masonry .e-loop-item .t7-card { height: auto; }
   Bringing masonry back means bringing the ENGINE back first - a class with
   nothing to stamp it does nothing at all. */

/* ============================================================
   forms - the field lines, and a correction.        (2026-08-31)
   ------------------------------------------------------------
   HONEST NOTE: the two typography rules below are DEAD on the real
   form. Elementor emits the same properties for widget 389d061 at
   (0,5,0) from the per-post sheet, which loads after this file, so
   these lose every contest. They are kept only as a fallback for a
   form widget with no globals bound. Do not "fix" a form by editing
   them - the live values are the widget's own __globals__ bindings
   (17 of them), which is where Paul's edits kept silently losing.
   See the CONTACT FORM note in the worklog.

   THE LINES (Paul, 2026-08-31): each field is an 8px bottom border.
   Idle colour is now BOUND IN ELEMENTOR to the Figure slot
   (field_border_color -> fgur7b2), per governance rule 1 - it is a
   basic, so it lives in the panel, and it flips correctly in light
   mode instead of being pinned white. Focus is the exception: the
   form widget exposes NO focus-state border control at all, so the
   active colour is the legitimate child-sheet case. Ember, full
   opacity, no alpha anywhere in the pair.
   ============================================================ */
.elementor-widget-form .elementor-field-group .elementor-field-textual:focus,
.elementor-widget-form .elementor-field-group .elementor-field-textual:focus-visible {
  /* !important is load-order, not escalation: Elementor's own idle
     border-color rule sits at (0,5,0) in a sheet that loads after this
     one, so no class-level selector here can outrank it. */
  border-color: var(--e-global-color-bc74cd9) !important;
  opacity: 1;
  outline: none;
}
/* the lines are opaque by contract - nothing may fade them */
.elementor-widget-form .elementor-field-group .elementor-field-textual { opacity: 1; }

.elementor-field-group .elementor-field-textual {
  font-family: var(--e-global-typography-b55f19d-font-family), serif;
  font-size: var(--e-global-typography-b55f19d-font-size);
  font-weight: var(--e-global-typography-b55f19d-font-weight, 200);
  line-height: var(--e-global-typography-b55f19d-line-height);
  letter-spacing: var(--e-global-typography-b55f19d-letter-spacing);
  color: var(--e-global-color-fgur7b2);
}
.elementor-field-group .elementor-field-textual::placeholder {
  color: var(--e-global-color-fgur7b2);
  opacity: .55;
}

/* ============================================================
   touch physics (Paul's phone QA 2026-08-07)
   ============================================================ */
/* vertical drags over a carousel scroll the PAGE; only horizontal
   drags move slides */
.t7-carousel-fixed .swiper,
.t7-carousel-fixed .swiper-wrapper,
.t7-carousel-fixed .swiper-slide { touch-action: pan-y; }
/* touch screens have no hover: the Ember wipes would slide in on tap and
   STICK (iOS keeps :hover). On hover-less devices the wipes stay parked. */
@media (hover: none) {
  .t7-wipe:hover::before { transform: scaleX(0); }
  /* ...but :active brings the Ember back WHILE PRESSED — feedback that
     retracts on release instead of sticking */
  .t7-wipe:active::before { transform: scaleX(1); }
  .elementor-widget-icon.t7-plate .elementor-icon:active svg,
  .elementor-widget-icon.t7-plate .elementor-icon:active svg * { fill: var(--t7-plate-glyph-flip) !important; color: var(--t7-plate-glyph-flip) !important; }
  .elementor-social-icon:active i, .elementor-social-icon:active svg, .elementor-social-icon:active svg * { color: var(--t7-btn-fg-flip) !important; fill: var(--t7-btn-fg-flip) !important; }
  .elementor-button:active { color: var(--t7-btn-fg-flip) !important; }
}

/* ---- DARK/LIGHT mode pill — PLAYGROUND 759 ONLY as of 2026-09-02.
   WPCode 815 no longer injects it site-wide; the site-wide control is
   now the header switch below (.t7-sw). 759 carries its own pill widget
   and its own flip script, so these rules stay until that page is
   converted.
   The GLASS/SOLID invert pill that used to share this rule retired
   2026-08-19 with its CSS — surface is a per-element election now. ---- */
#t7-mode-pill {
  position: fixed; bottom: 18px; right: 14px; z-index: 99999;
  background: var(--e-global-color-fgur7b2); color: var(--e-global-color-grnd7a1);
  border: 2px solid var(--e-global-color-grnd7a1); box-shadow: 4px 4px 0 color-mix(in srgb, var(--e-global-color-c8af26f) 92%, transparent);
  padding: 12px 18px; font-weight: 700; letter-spacing: .05em;
  cursor: pointer; user-select: none;
}

/* ---- DARK/LIGHT mode SWITCH (header) ----
   Markup = HTML widget a7d3f19 in Elementor Header #99, class
   .t7-mode-sw; flip + persistence JS = WPCode 815 (moved to
   site_wide_header 2026-09-02 so the saved theme lands BEFORE paint —
   the footer copy gave light-mode visitors a dark flash on every load).
   Replaces the floating #t7-mode-pill 2026-09-02 (Paul: "up in the
   header, not the floating footer button we have now"). The pill rules
   above STAY: playground 759 still carries its own pill widget.

   Reference: uiverse.io/vikramsinghnegi/shaggy-owl-31 (MIT) — a moon
   slides into a dashed sun. Square track per the frame language; the
   KNOB stays round, because a square crescent reads as a corner-shadow
   bug and square dashed rays mush at this size.

   REDESIGNED 2026-09-03 to Paul's drawing. It is no longer a knob sliding
   along a track: it is TWO FIXED CELLS, and only the active one is lit.
   Left cell = night (Ink, yellow semicircle moon), right cell = day (Cobalt,
   white sun). The inactive cell greys out to Ash on BOTH sides (Paul, 2026-09-03:
   Slate on the light half was too close to the Ink frame to read as a
   separate cell) — Ash behind the sun, Ash behind
   the moon — and the icon on the inactive side goes to opacity 0, so the
   control reads as "here are the two modes, this is the one you are in."

   The two cells carry LITERAL palette globals (Ink, Slate, Ash, Cobalt,
   Voltage, Marshmallow), not role slots, because night is always dark and
   day is always blue no matter which mode the page is in. The FRAME is the
   one thing that flips: it takes the Figure slot, so it is Eggshell in dark
   and Ink in light, which is what makes the whole control invert with the
   page. Voltage moon on Ink and Marshmallow sun on Cobalt both keep the
   accent law (Voltage on dark, Cobalt-family on light).
   ---- */
.t7-mode-sw { width: auto; flex: 0 0 auto; margin-left: 22px; }
/* the row is `space-between` with two children today; an auto margin
   eats the free space FIRST, so the nav keeps its right-edge position
   and the switch rides beside it instead of the three items spreading */
.elementor-location-header .elementor-widget-nav-menu { margin-left: auto; }

body.elementor-kit-89 button.t7-sw {
  position: relative; display: block; width: 60px; height: 34px;
  margin: 0; padding: 0;
  border: 5px solid var(--e-global-color-fgur7b2); border-radius: 0;
  background: linear-gradient(to right,
      var(--e-global-color-c8af26f) 0 50%,
      var(--e-global-color-3bc915a) 50% 100%);
  box-shadow: none;
  cursor: pointer; line-height: 0;
  -webkit-appearance: none; appearance: none;
  transition: background .25s ease-in, border-color .25s ease-in;
}
/* re-assert against the kit's own :hover swap; Ember = hover, per the accent law */
body.elementor-kit-89 button.t7-sw:hover,
body.elementor-kit-89 button.t7-sw:focus-visible {
  background: linear-gradient(to right,
      var(--e-global-color-c8af26f) 0 50%,
      var(--e-global-color-3bc915a) 50% 100%);
  box-shadow: none;
  border-color: var(--e-global-color-bc74cd9);
}
body.elementor-kit-89 button.t7-sw:focus-visible {
  outline: 2px solid var(--e-global-color-accntsl); outline-offset: 3px;
}
/* MOON — left cell. Semicircle: rounded on the left, flat edge facing the
   divide, so the two icons face each other across it. */
body.elementor-kit-89 button.t7-sw::before {
  content: ""; position: absolute; z-index: 1;
  left: 8.5px; top: 50%; width: 8px; height: 16px; margin-top: -8px;
  border-radius: 8px 0 0 8px / 8px 0 0 8px;
  background: var(--e-global-color-230debb);
  opacity: 1;
  transition: opacity .2s ease-in, background-color .2s ease-in;
}
/* SUN — right cell. Disc plus eight rays as spread-shrunk box-shadows on the
   one element, because a ring of eight real nodes needs markup we do not
   control from here (the button ships from HTML widget a7d3f19). */
.t7-sw .t7-sw__knob {
  position: absolute; z-index: 1; left: 33.5px; top: 50%;
  width: 8px; height: 8px; margin-top: -4px;
  border-radius: 50%;
  background: var(--e-global-color-42a1059);
  box-shadow:
      0    -7.5px 0 -2.5px var(--e-global-color-42a1059),
      0     7.5px 0 -2.5px var(--e-global-color-42a1059),
     -7.5px  0     0 -2.5px var(--e-global-color-42a1059),
      7.5px  0     0 -2.5px var(--e-global-color-42a1059),
     -5.3px -5.3px 0 -2.5px var(--e-global-color-42a1059),
      5.3px -5.3px 0 -2.5px var(--e-global-color-42a1059),
     -5.3px  5.3px 0 -2.5px var(--e-global-color-42a1059),
      5.3px  5.3px 0 -2.5px var(--e-global-color-42a1059);
  opacity: 0;
  transition: opacity .2s ease-in, background-color .2s ease-in;
}
/* checked = light mode. AFTER the hover arm on purpose (see header note):
   a hover in light mode keeps the lit sun and only takes the Ember frame. */
body.elementor-kit-89 button.t7-sw[aria-checked="true"],
body.elementor-kit-89 button.t7-sw[aria-checked="true"]:hover {
  background: linear-gradient(to right,
      var(--e-global-color-3bc915a) 0 50%,
      var(--e-global-color-d3cd34a) 50% 100%);
}
body.elementor-kit-89 button.t7-sw[aria-checked="true"]::before { opacity: 0; }
body.elementor-kit-89 button.t7-sw[aria-checked="true"] .t7-sw__knob { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  body.elementor-kit-89 button.t7-sw, .t7-sw .t7-sw__knob { transition: none; }
}
/* TABLET AND DOWN: Elementor swaps the nav for the hamburger at
   max-width 1024 (verified against post-99.css, not assumed), so the
   switch takes its LEFT from that breakpoint down — one tap, no drawer.
   Ordering is explicit because the auto margin above is doing the
   desktop placement. */
@media (max-width: 1024px) {
  .elementor-location-header .t7-mode-sw { order: 1; margin-left: auto; margin-right: 14px; }
  .elementor-location-header .elementor-widget-nav-menu { order: 2; margin-left: 0; }
  /* NB the 14px above is the whole gap because the nav widget's tablet
     width was returned to Inline/auto IN THE PANEL (6734fff had a
     drag-artifact 122.992px custom width, whose 48px of dead column
     right-aligned the burger and stranded this switch ~62px away).
     Fixed in _elementor_data on 2026-09-02, not overridden here — a CSS
     override of a panel value is invisible from the panel and gets
     hunted. Backup: uploads/_page99-data.before-modesw.json */
}
/* ============================================================
   literature-pass primitives (case-study chapters, 2026-08-08;
   REGISTERS split 2026-08-28 — Paul: "tab--caption vs
   tab--heading; in Elementor it's doing too much")
   .t7-tab           = chip base. Bare election = the HEADING
                       register (section-scale: "The Brief:").
   .t7-tab--caption  = modifier ON TOP of t7-tab: label-scale chip
                       a framed box wears at its corner — L1 type,
                       tighter padding, and the corner GEOMETRY, so
                       every box gets the same layout from the
                       election instead of hand-tuned absolutes.
                       Rules live after the type-recipe block below.
   .t7-pull = bare pullquote interlude on the page ground;
              color follows --t7-accent per mode
   COLOURS: FIGURE ground, GROUND text (Paul, 2026-09-03). The chip
   is a piece of the page inverted — Eggshell box with Ink caps in
   dark mode, Ink box with Eggshell caps in light — so it flips for
   free and never needs a mode-specific arm. It USED to ride the
   plate pair (--t7-plate-bg / --t7-plate-glyph), i.e. the accent.
   That is retired: ACCENT IS RESERVED FOR INTERACTABLES. A tab is a
   label, not something you can click, so it does not get to shout.
   Still DEFAULTS, NOT SEALS (unsealed 2026-08-28): the pair holds
   until an Elementor-authored colour beats it.
   Author with MODE-AWARE globals (Figure/Ground/accent slots),
   never literals, or the chip goes blind on the mode flip. The
   title arm is (0,3,0) ON PURPOSE: it out-ranks Elementor's
   unset-widget default (0,2,0) in BOTH surfaces (frontend and
   editor, where injected CSS wins order ties) and beats the
   .t7-solid heading arm by later-in-sheet tie-break, while still
   losing to a per-element authored colour (0,3,0 in per-post CSS,
   which loads after this sheet). Sweep 2026-08-28: the only
   authored tab colours site-wide are Dankley 80 pane 1
   (Figure bg / Ground title) — the pair this unseal is FOR.
   TYPE stays sealed per the component-word law (see the dissolved
   card-type-contract note below): you elect a tab BECAUSE you
   want its type recipe.
   First user: Eden 660. Breadcrumbs live in each element's
   Elementor custom-CSS block.
   ============================================================ */
.e-con.t7-tab { --width: fit-content; } /* chip, not bar: beat .e-con default */
.t7-tab {
  background: var(--e-global-color-fgur7b2);
  padding: 5px 10px; /* hugs the caps; hero bar only reads tight because 3 packed lines dwarf its 10px */
}
.t7-tab .elementor-widget-heading .elementor-heading-title { color: var(--e-global-color-grnd7a1); }
.t7-tab > .e-con-inner, .t7-card > .e-con-inner { padding: 0 !important; } /* boxed containers double-pad via e-con-inner default; declared padding = real padding */
.t7-tab > .elementor-widget-heading { width: auto; } /* fit-content chip: heading must size intrinsically */
.t7-pull .elementor-heading-title { color: var(--t7-accent) !important; }

/* case-study hero bar (tmpl 195 container 3f4749c): RETIRED 2026-08-31.
   The rule that stood here pinned the bar to Voltage in light mode with
   !important, per Paul's 2026-08-10 ruling ("on the light flip Paul wants
   it to JUMP"). Its premise has since moved: Elementor no longer authors
   that container Cobalt, Paul authors it Figure Muted, and the !important
   meant nothing he set in the panel could ever win. He hunted it twice.
   The bar now simply wears whatever the container is authored with, in
   both modes, which is what an unsealed element should do.
   If a mode-specific jump is ever wanted again, it belongs on a class the
   element elects — never an id-anchored !important, which is invisible
   from the panel and unbeatable from it. */

/* ---- card type contract — DISSOLVED 2026-08-20 (Paul) ----
   "The cards should not be dictating size or ANYTHING about the type.
   That's what Elementor is for." Correct — and the reason this rule
   existed is now gone. It was sealed with !important to be "immune to
   page-context leaks," and THE leak was kit 89's `text` slot carrying
   the H3 recipe. That is fixed at the kit now. The seal was scar tissue
   around a wound that has closed.

   THE DISTINCTION, so this never grows back:
     t7-tab / t7-quote are COMPONENT words — you elect them BECAUSE you
       want that exact type recipe. Type is the whole point. They keep it.
     t7-card is a BEHAVIOR word — it says "this reads as raised" and
       nothing else. Nobody electing it is asking for type.
   Card type now comes from the widget or the ramp, per CSS governance.
   Do not re-add any of it here. */
.t7-tab .elementor-heading-title {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif !important;
  font-size: var(--e-global-typography-b55f19d-font-size, clamp(1.0909rem, 0.9322rem + 0.5291vw, 1.3091rem)) !important;
  font-weight: 900 !important;
  line-height: 1em !important; /* caps only: no leading, bar hugs text */
  letter-spacing: -0.02em !important;
  text-transform: uppercase !important;
}

/* ---- CAPTION register (2026-08-28, Dankley 80 brand board) ----
   The chip a FRAMED BOX wears at its top-left corner ("Wordmark -
   Full color"). Elect "t7-tab t7-tab--caption" on the chip
   container INSIDE the frame; geometry lives here so every box
   gets the identical corner from the election — clear any
   hand-authored position/margins on the element or they compound
   with these offsets. Defaults ride the 13px frame bead; nudge
   with the dials, don't re-author position per element.
   Type = the L1 recipe at seal strength (component-word law);
   colours inherit the tab defaults above, so an authored
   mode-aware pair (e.g. Figure bg / Ground title) wins here too.
   Positioning context: .e-con is position:relative out of the box,
   so the frame container needs nothing extra. */
.t7-tab.t7-tab--caption {
  position: absolute;
  top: var(--t7-tab-dy, -5px);
  left: var(--t7-tab-dx, -7px);
  z-index: 2; /* above the boxed art */
  padding: 4px 8px;
}
.t7-tab.t7-tab--caption .elementor-heading-title {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif !important;
  font-size: var(--e-global-typography-5ddc419-font-size, 0.75rem) !important;
  font-weight: var(--e-global-typography-5ddc419-font-weight, 600) !important;
  line-height: 1em !important;
  letter-spacing: -0.02em !important;
  text-transform: uppercase !important;
}

/* COLOR SURVIVES — and it is NOT type, it is SURFACE. On a card, text
   colour must follow the card's own fill so t7-invert and scope flips
   carry it; a widget-authored mode colour goes blind the instant the
   card flips. This is the one text property the card legitimately owns.
   OPEN (Paul's ruling): if this goes to Elementor too, every invert
   card needs its text colour re-authored by hand, per card. */
.t7-card .elementor-widget-text-editor:not(.t7-display) {
  color: var(--t7-frost-text) !important; /* mode contrast: gray #7a7a7a leak killed 2026-08-08 */
}
/* t7-display (2026-08-10, unification): election class ON the text-editor
   WIDGET (Advanced > CSS Classes) that says "this is display copy — its
   authored/ramp typography wins, the card prose contract skips it."
   Needed once heroes and the homepage intro became fx-lift cards: their
   statements are widget-authored display type (globals-wired colors), not
   card body prose. Elected on: intro c05b07e (90), case hero tagline
   60632717 + lead 64edcf65 (tmpl 195).
   COLOR is NOT authored: on a card, text color follows the card surface
   (same rule the prose contract uses) so invert/scope flips carry it —
   widget-authored mode-slot colors go blind the moment the card leaves
   the page's mode. */
.t7-card .elementor-widget-text-editor.t7-display,
.t7-card .elementor-widget-text-editor.t7-display * {
  color: var(--t7-frost-text) !important;
}

/* CARD PROSE RHYTHM (Paul, 2026-09-10: "p tag carries some bottom margin that
   looks weird, kill it" and "margin below list doesn't exist").
   Theme defaults gave every <p> a .9rem bottom margin and lists none, so a card
   ending in a paragraph carried dead space, a card ending in a list did not, and a
   paragraph right after a list sat flush against it. Blocks now carry no outer
   margin; spacing lives only BETWEEN siblings, at the .9rem the theme already used. */
.t7-card .elementor-widget-text-editor :is(p, ul, ol) { margin-block: 0; }
.t7-card .elementor-widget-text-editor :is(p, ul, ol) + :is(p, ul, ol) { margin-block-start: .9rem; }

/* BLEED ON PHONES (.t7-bleed--mobile, 2026-09-10). Election class for an image
   slot that should run edge to edge on phones instead of sitting framed inside
   the band's padding (Paul: "picture feels small here, go full width on mobile as
   an optional"). calc(50% - 50vw) pulls the box out to the viewport edges from any
   centred containing block, whatever padding the ancestors carry. The frame border
   drops because a bleed has no edge to frame. !important because Elementor's
   per-post width rule loads after this file.
   Breadcrumb: Story Block 2243 Image and Text lists it as an option; elected on the
   two image slots of post 84 (their custom CSS points here). */
@media (max-width: 767px) {
  .e-con.t7-bleed--mobile {
    width: 100vw !important;
    max-width: 100vw !important;
    margin-inline: calc(50% - 50vw) !important;
    border-width: 0 !important;
  }
}

/* (the Music lead-in stopgap for card 12af3fa is gone as a special case:
   its selector is grouped into the DARK tone table at the end of this
   file — the authored-dark band now simply declares its tone. The clean
   ending is electing .t7-dark on that band in Elementor and deleting the
   grouped selector.) */

/* mobile centering: e-con width:100% ignores own margins -> card shoved
   13px past the right edge (33/7 gaps). auto = honest stretch. */
.e-con.t7-card { --width: auto; }

/* ---- t7-quote: canonical quote block (2026-08-08) ----
   Testimonial widgets inside a .t7-quote container. Works bare on the
   page ground OR inside an fx-lift card — colors ride the mode tokens.
   Demo lives on playground 759. */
.t7-quote .elementor-testimonial-content {
  font-family: var(--e-global-typography-c8f50e3-font-family, "Newsreader"), Georgia, serif !important;
  font-size: var(--e-global-typography-c8f50e3-font-size, clamp(1.5709rem, 1.3424rem + 0.7618vw, 1.8851rem)) !important;
  font-weight: var(--e-global-typography-c8f50e3-font-weight, 200) !important;
  font-style: italic;
  line-height: 1.15em !important;
  color: var(--t7-frost-text) !important;
}
.t7-quote .elementor-testimonial-name {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), sans-serif !important;
  font-size: var(--e-global-typography-b55f19d-font-size, 1.0909rem) !important;
  font-weight: 900 !important;
  line-height: 1em !important;
  letter-spacing: -0.02em !important;
  text-transform: uppercase !important;
  color: var(--t7-accent) !important;
}
.t7-quote .elementor-testimonial-job {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), sans-serif !important;
  font-size: var(--e-global-typography-2898ebd-font-size, 0.9091rem) !important;
  letter-spacing: 0.05em !important;
  text-transform: uppercase !important;
  color: var(--t7-frost-text) !important;
}
.t7-quote .elementor-testimonial-image img { filter: grayscale(1) contrast(1.05); }

/* t7-quote marks (2026-10-13, nmv-testimonial-unify): the open/close marks are
   two absolute Icon widgets (fas quote-left / quote-right) placed by hand per
   page, so each callout had them at different offsets, sitting on the text.
   One rule for all: the marks pin to the corners of the container that holds
   them, and that container pads the quote clear of them. Size = --t7-qmark. */
.t7-quote { --t7-qmark: 72px; }
@media (max-width: 767px) { .t7-quote { --t7-qmark: 36px; } }
.t7-quote .e-con:has(> .elementor-widget-icon.elementor-absolute) {
  position: relative;
  padding-inline: calc(var(--t7-qmark) + var(--gap-width, 13px)) !important;
  padding-block: calc(var(--t7-qmark) * 0.5) !important;
}
.t7-quote .elementor-widget-icon.elementor-absolute {
  width: var(--t7-qmark) !important;
  margin: 0 !important;
  padding: 0 !important;
  transform: none !important;
  pointer-events: none;
}
.t7-quote .elementor-widget-icon.elementor-absolute .elementor-icon {
  font-size: var(--t7-qmark) !important;
  line-height: 1;
}
.t7-quote .elementor-widget-icon.elementor-absolute .elementor-icon svg {
  width: var(--t7-qmark) !important;
  height: var(--t7-qmark) !important;
}
.t7-quote .elementor-widget-icon.elementor-absolute:has(.e-fas-quote-left) {
  top: 0 !important; left: 0 !important; right: auto !important; bottom: auto !important;
}
.t7-quote .elementor-widget-icon.elementor-absolute:has(.e-fas-quote-right) {
  top: auto !important; left: auto !important; right: 0 !important; bottom: 0 !important;
}
@media (max-width: 767px) {
  .t7-quote .e-con:has(> .elementor-widget-icon.elementor-absolute) {
    padding-inline: 0 !important;
    padding-block: calc(var(--t7-qmark) + var(--gap-width, 13px)) !important;
  }
}
/* the loop card (template 2069) IS the .t7-quote container and holds the
   marks itself: same rule, marks inset by the card's gap so they sit inside
   the box rather than on its edge. */
.e-con.t7-quote:has(> .elementor-widget-icon.elementor-absolute) {
  position: relative;
  padding-inline: calc(var(--t7-qmark) + 2 * var(--gap-width, 13px)) !important;
  padding-block: calc(var(--t7-qmark) * 0.5 + var(--gap-width, 13px)) !important;
}
.e-con.t7-quote > .elementor-widget-icon.elementor-absolute:has(.e-fas-quote-left) {
  top: var(--gap-width, 13px) !important; left: var(--gap-width, 13px) !important;
}
.e-con.t7-quote > .elementor-widget-icon.elementor-absolute:has(.e-fas-quote-right) {
  bottom: var(--gap-width, 13px) !important; right: var(--gap-width, 13px) !important;
}
@media (max-width: 767px) {
  .e-con.t7-quote:has(> .elementor-widget-icon.elementor-absolute) {
    padding-inline: var(--gap-width, 13px) !important;
    padding-block: calc(var(--t7-qmark) + 2 * var(--gap-width, 13px)) !important;
  }
}
/* the loop card inside a quote strip (Home 90, Music 62): the testimonial
   widget spans the card, and on phones the narrow card stacks the headshot
   over the name/role (side by side left the name a 13px column) and steps
   the quote down a size so it fits the fixed slide height. */
.t7-quote-strip .e-con.t7-quote > .elementor-widget-testimonial { width: 100%; align-self: stretch; }
@media (max-width: 767px) {
  .t7-quote-strip .t7-quote .elementor-testimonial-content { font-size: 1.2rem !important; }
  .t7-quote-strip .t7-quote .elementor-testimonial-meta-inner,
  .t7-quote-strip .t7-quote .elementor-testimonial-image,
  .t7-quote-strip .t7-quote .elementor-testimonial-details { display: block !important; text-align: center; padding: 0 !important; }
  .t7-quote-strip .t7-quote .elementor-testimonial-image img { display: block; margin-inline: auto; }
  .t7-quote-strip .t7-quote .elementor-testimonial-details { margin-top: 10px; }
  .t7-quote-strip .t7-quote .elementor-testimonial-name,
  .t7-quote-strip .t7-quote .elementor-testimonial-job { display: block; text-align: center !important; }
}
/* phones (2026-10-13): no side padding on the testimonial widget (its 50px
   left Home's quote a 114px column, and the longest quotes clipped at the
   old fixed 800px). */
@media (max-width: 767px) {
  .t7-quote-strip .e-con.t7-quote > .elementor-widget-testimonial { padding-inline: 0 !important; }
}
/* every width (2026-10-14, Paul): the slide height follows the TALLEST quote
   in the strip instead of --t7-carousel-h. Slides stretch to the tallest
   card, so cards stay equal and nothing clips (the fixed 450px cut Brian
   Park's quote on Home desktop; 440px clipped tablet). The dial values above
   no longer size quote-strip slides. The arrows centre on the slide row
   (the widget's height minus the 36px bullet row below it) instead of
   --t7-carousel-h / 2. */
.t7-carousel-fixed.t7-quote-strip .swiper-wrapper { align-items: stretch; }
.t7-carousel-fixed.t7-quote-strip .swiper-slide { height: auto; }
.t7-chrome.t7-chrome.t7-chrome.t7-carousel-fixed.t7-quote-strip.elementor-widget-loop-carousel .elementor-swiper-button {
  top: calc((100% - 36px) / 2);
  margin-top: calc(var(--t7-nav-size, 50px) / -2);
}

/* ============================================================
   t7-scope-invert  —  ALIAS (since v1.6.0)
   "Inside this container, pretend the site mode is the opposite."
   The 90-line token tables that lived here are gone: the class is
   grouped into the .t7-invert selector lists in the TONE TABLES at
   the end of this file, and every derived token re-resolves there.
   Nothing else to keep in sync. The case-study band nth-child
   selectors ride the same lists.
   ============================================================ */


/* ============================================================
   STAFF CARD PHOTO  (loop template 569, container ce5bdd7)
   Breadcrumb: tmpl 569 > photo container ce5bdd7 (custom_css points here)

   The box is a featured-image background (dynamic tag b0b1434) and every
   source headshot is a perfect 1:1 square. The authored box was NOT square
   — desktop 193x199 (0.97) and, worse, mobile 329x250 (1.32) from
   min_height_mobile:250px — so `cover` ate ~79px vertically on phones:
   40px off the top of the head, 40px off the chin.

   Box now follows the material: square at every breakpoint, zero crop.
   flex:0 0 auto is load-bearing — without it flex-shrink collapses the
   square to 145x199 (0.73) and crops the sides instead. Same lesson as
   t7-plate: pin canon geometry at class level, Elementor's compiled
   post CSS loads after this file and wins ties.

   Grayscale = the existing house recipe from .t7-quote headshots
   (grayscale(1) contrast(1.05)) — not a new treatment.

   Fit (cover / center) is pinned here too, duplicating the Elementor
   settings on purpose. The first render after an Elementor CSS flush
   (every editor save, `wp elementor flush-css`) emits the loop-569 inline
   CSS WITHOUT the background_size/background_position rule for this
   container — the headshot then renders at natural size from 0 0, i.e.
   a zoomed-in corner. Reload regenerates it correctly. Reproduced
   2026-09-23 on Elementor 3.35.8 / Pro 3.35.0. Behind a page cache the
   broken first render would be served to everyone, so don't rely on it.
   ============================================================ */
.elementor-569 .elementor-element-ce5bdd7 {
  filter: grayscale(1) contrast(1.05);
  background-size: cover;
  background-position: center center;
}
@media (min-width: 768px) {
  /* photo is a square column; the names panel takes the remainder.
     FIXED WIDTH (Paul via Gidget 1054, 2026-10-20): the photo box is EMPTY,
     so width:auto left its width to aspect-ratio x stretched height. WebKit
     does not reliably transfer a stretched height through aspect-ratio, so
     on Safari the column collapsed to 0 and the names slid left. 160px =
     81d3916 min-height 186 - 2 x 13 frame, i.e. what it rendered at anyway. */
  .elementor-569 .elementor-element-ce5bdd7 {
    width: var(--t7-staff-photo, 160px) !important;
    min-width: var(--t7-staff-photo, 160px);
    aspect-ratio: 1 / 1;
    align-self: stretch;
    flex: 0 0 var(--t7-staff-photo, 160px) !important;
  }
}
/* ---- PHONE ROW (<=767px) -- Paul, 2026-09-29, from his phone ----
   "picture on the left third, name and title stacked above the send icon,
   so you can get more people on one page." Replaces the old phone layout
   (card stacked, photo full-width square, ~490px per person).
   The card stays ONE ROW: square thumb on the left third, zoomed onto the
   face; name / title / mail plate stacked on the right, left-aligned.
   Tablet (768-1024) is untouched: it already runs the desktop row.

   Elementor half (tmpl 569, MOBILE values only -- set there, not here):
     93ad5a2 flex_wrap nowrap (Elementor wraps e-con rows on phones by default)
     81d3916 + 863ab0f min_height 0 (the 186px / 120px desktop floors)
     906e631 icon size 32  -> 44px box (32 + 2x6 padding) = --t7-staff-plate
   Breadcrumbs: ce5bdd7 + 906e631 custom_css point here.

   Dials:
     --t7-staff-thumb  photo width as a share of the card's inner width
     --t7-staff-zoom   background-size; sources are all 1:1, so >100% crops in
     --t7-staff-face   background-position; where the zoom lands (x y)
     --t7-staff-plate  icon box height, must match 906e631 size_mobile + padding

   The plate is absolute (a child of the card root, not of the names
   column), so the names column reserves a row for it under the title, and
   its left edge is computed from the thumb share. Geometry, from the root:
   frame border (gap) + thumb + names padding (gap).
   Specificity: Elementor's inline loop-569 CSS loads AFTER this file. Its
   fit rule is (0,4,0), its padding vars (0,3,0), its plate offset (0,4,1);
   each rule below sits one class above the one it replaces. No !important
   except where the desktop block above already uses it on the same box. */
@media (max-width: 767px) {
  .elementor-569 {
    --t7-staff-thumb: 0.3333;
    --t7-staff-zoom: 150%;
    --t7-staff-face: 50% 30%;
    --t7-staff-plate: 44px;
  }
  .elementor-569 .elementor-element-ce5bdd7 {
    min-height: 0 !important;
    aspect-ratio: 1 / 1;
    width: calc(100% * var(--t7-staff-thumb)) !important;
    flex: 0 0 auto !important;
    align-self: flex-start;
  }
  .elementor-569 .e-con > .elementor-element.elementor-element-ce5bdd7:not(.elementor-motion-effects-element-type-background) {
    background-size: var(--t7-staff-zoom);
    background-position: var(--t7-staff-face);
  }
  .elementor-569 .e-con > .elementor-element.elementor-element-3fe8e37 {
    --padding-bottom: calc(2 * var(--gap-width) + var(--t7-staff-plate));
  }
  body:not(.rtl) .elementor-569 .e-con > .elementor-element.elementor-element-906e631 {
    right: auto;
    left: calc(2 * var(--gap-width) + (100% - 2 * var(--gap-width)) * var(--t7-staff-thumb));
    bottom: calc(2 * var(--gap-width));
    line-height: 0;
  }
}

/* ============================================================
   FIRST-RENDER PINS  (loop templates with featured-image widgets)
   Breadcrumb: tmpl 123 > container c608b52 (service bucket photo)
               tmpl 2069 > testimonial widget t7q694e (headshot)

   Same Elementor bug as STAFF CARD PHOTO above: the first render after
   an Elementor CSS flush (every editor save) leaves out any rule that
   depends on the image being set, because the image comes from the
   featured-image dynamic tag and reads as empty on that pass. Reload
   fixes it, but a page cache would keep the broken first render.
   These lines duplicate the Elementor settings on purpose, so they
   hold no matter which render gets served. Keep them in sync if the
   settings change. Reproduced 2026-09-23 (Elementor 3.35.8 / Pro
   3.35.0): run `wp elementor flush-css`, then diff the inline
   <style id="loop-N"> on the 1st vs 2nd load.
   ============================================================ */
.elementor-123 .elementor-element-c608b52 {
  background-size: cover;
}
.elementor-2069 .elementor-element-t7q694e .elementor-testimonial-wrapper .elementor-testimonial-image img {
  width: 100px;
  height: 100px;
}

/* ============================================================
   BRANDED ICONOGRAPHY  ("redux b", 28 icons, Aug 2026)
   Breadcrumb: tmpl 149 > container .t7-icon-box (custom_css points here)

   The icons are KNOCKOUTS: a full-bleed square with the artwork
   subtracted. So the icon carries its own ground, and the visible
   "line colour" IS whatever sits behind the cut-outs. Two layers,
   two tokens, one rule:

     --t7-icon-tile  the solid field   (the masked svg)
     --t7-icon-box   what shows through the artwork (the container)

   COLOURS COME FROM THE KIT (changed 2026-08-12, Plan B). These used
   to be literals because c8af26f/316eccb flipped under theme-light,
   and a knockout icon that inverts is a broken icon. The flip now
   lives on the Ground/Figure slots instead, so Ink and Eggshell are
   honest in both modes and can be referenced directly. The icons
   still must NOT invert — that is now guaranteed by the globals
   themselves rather than by refusing to use them.

   The mask lives on the ICON WIDGET div, so it clips that widget's
   whole subtree — the box can only be painted by the wrapping
   container, never from inside. That is what .t7-icon-box is for.
   ============================================================ */
body,
html.theme-light body {
  --t7-icon-tile: var(--e-global-color-316eccb); /* Eggshell field */
  --t7-icon-box:  var(--e-global-color-c8af26f); /* Ink through the cut-outs */
}

.e-con.t7-icon-box {
  --width: fit-content;
  background-color: var(--t7-icon-box);
  align-self: flex-start;
  line-height: 0;
  /* the box NEVER yields: it's a flex child in the card's title row, and
     default shrink:1 squeezed it to 62x100 at ~1025vw while the 100px svg
     held size under the mask (Paul's "not staying square", 2026-08-12).
     The TITLE is the yielding element — it wraps; the tile is fixed. */
  flex: 0 0 auto;
}
.t7-icon-box > .e-con-inner { padding: 0 !important; }
.t7-icon-box .elementor-widget-icon,
.t7-icon-box .elementor-icon-wrapper,
.t7-icon-box .elementor-icon { display: block; line-height: 0; }
.t7-icon-box .elementor-icon > svg {
  fill:  var(--t7-icon-tile) !important;
  color: var(--t7-icon-tile) !important;
  display: block;
}

/* TILE GEOMETRY (2026-08-12) — the child theme owns the mask outright.
   Elementor's emitted size/position cannot be trusted for this widget:
   the per-item rules ship only mask-image, and WPCode snippet 550 (the
   first-item-mask fix) deletes the template-level rule that carried
   size/position — computed mask-size was literally `auto`, so each
   tile's height tracked its SVG's viewBox and the field never reached
   the box edges. cover + center = field bleeds flush on all four sides
   for any export; a slightly off-square viewBox gets a hairline CROP of
   the full-bleed field, never a stretch of the artwork. */
.t7-icon-box .elementor-widget-icon {
  -webkit-mask-size: cover !important;        mask-size: cover !important;
  -webkit-mask-position: center !important;   mask-position: center !important;
  -webkit-mask-repeat: no-repeat !important;  mask-repeat: no-repeat !important;
}

/* HOVER: RETIRED 2026-08-12 (Paul) — the offerings loop items link
   nowhere, so an interactive accent promised a click that doesn't exist.
   The Ember figure/ground flip rules that lived here are in the v1.5.1
   backup (_style.css.before-iconbox2-20260812.css) if the cards ever
   become links. */

/* TITLE COLUMN (2026-08-12) — with the tile rigid (flex:0 0 auto above),
   the title is the card's only yielding element. Elementor's own
   widget-loop-common.css sets `.e-loop-item * { word-break: break-word }`,
   which zeroes the title's min-content width — flex crushed the widget to
   ~97px and words broke mid-glyph ("THROUG/H"). So: the title takes the
   REST of the row (grow 1 / basis 0 needs !important — Elementor's
   emitted element rule outranks class level), and wraps at word
   boundaries; only a word wider than the whole column may split. */
/* …and the CARD fills its grid track. The template root carries
   width:auto, which made the frame hug its content — card widths varied
   with title length (426 vs 421 at desktop, collapsed at tablet).
   !important: the emitted element rule ties at (0,3,0) and loads later. */
.e-loop-item.elementor-149 > .e-con { --width: 100% !important; }

.elementor-149 .elementor-widget-theme-post-title,
.e-con.t7-icon-card > .elementor-widget-heading {
  width: auto !important;
  flex: 1 1 0 !important;
  min-width: 0;
}
.elementor-149 .elementor-widget-theme-post-title,
.elementor-149 .elementor-widget-theme-post-title *,
.e-con.t7-icon-card > .elementor-widget-heading,
.e-con.t7-icon-card > .elementor-widget-heading * {
  word-break: normal;
  overflow-wrap: break-word;
  hyphens: auto; /* a word wider than the column (PRODUCTION at tablet)
                    splits with a hyphen instead of a bare shear */
}

/* STATIC ICON CARDS (.t7-icon-card, 2026-09-11). The offering card built by
   hand instead of by loop 149: same knockout tile, same title column (the two
   selector lists above now name it). Elect on the card container. A static
   card's row wrapped on narrow screens and dropped the title under the tile
   (post 84 at 375: 235px cards, 120-152px tall instead of 98), so the row is
   pinned and the title is the only thing that yields.
   Breadcrumb: post 84 band 1, the five Challenge icon cards. */
.e-con.t7-icon-card { flex-wrap: nowrap !important; }

/* ============================================================
   THE COLUMN  —  essay layout for the long-form case studies (80, 83, 84).
   v1.2.0, 2026-08-10.

   Prose leaves the glass card and runs at a fixed measure; the chapter label
   moves out of the card and into the left gutter as a margin note. Cards are
   containers for a UNIT of information; a 3,000-word essay is a flow, and
   boxing a flow is what made these pages read like spec sheets.

   Apply .t7-column to the chapter's inner container. That container MUST be
   content_width:full — a boxed container wraps its children in .e-con-inner,
   which would sit between the grid and its items and break the two columns.

   Prose family/size/colour are deliberately NOT set here: bare text already
   inherits the Body (P3) + Eggshell page rules further up this file. The card
   type contract (.fx-lift .elementor-widget-text-editor) does not apply once
   the card is gone, which is the point — essay prose reads at Body, not B1.

   Pairs with .t7-pull for the pull-quote interlude. The pull-quote carries NO
   quote marks: quote marks belong to .t7-quote, where a real person is being
   quoted. A pulled line of our own copy is emphasis, not testimony.
   ============================================================ */
.e-con.t7-column {
  --width: 100%;
  /* the act runs full-width so mobile gets the whole viewport; the column
     carries its own boxed max-width and centring instead. A boxed act was
     squeezing this to a 250px inner at 390px wide. */
  max-width: calc(42rem + 2 * var(--gap-width, 13px)); /* measure + its own padding */
  margin-inline: auto;
  display: grid;
  /* Paul 2026-08-10: label sits INSIDE the panel, left aligned — not in a
     left gutter. Single measure column; the glass moved up off .t7-measure
     so one panel wraps the chapter label and its prose together. */
  grid-template-columns: minmax(0, 42rem);
  gap: var(--gap-width);
  /* glass (tint + blur) comes from the .t7-glass axis group, which
     lists .e-con.t7-column — the column is glass without card identity */
  padding: var(--gap-width, 13px);
  align-items: start;
  justify-content: start;
}

/* mobile needs no special case now: there is no gutter to collapse. */

/* THE COLUMN — type contract.
   Bare text on this site inherits the parent Hello body->"text" slot, which
   resolves to H3 scale in grey (--e-global-color-text). The card contract
   card is gone the bug is exposed, so The Column needs the same contract.
   Body = P3 global b55f19d (serif). Colour = Eggshell slot 316eccb, which
   flips to Ink under html.theme-light. !important for the same reason the
   card contract needs it: Elementor's post CSS loads after this file. */
.t7-column .elementor-widget-text-editor,
.t7-column .elementor-widget-text-editor p,
.t7-column .elementor-widget-text-editor li {
  font-family: var(--e-global-typography-b55f19d-font-family), serif !important;
  font-size: var(--e-global-typography-b55f19d-font-size) !important;
  font-weight: var(--e-global-typography-b55f19d-font-weight, 200) !important;
  line-height: var(--e-global-typography-b55f19d-line-height, 1.2em) !important;
  letter-spacing: var(--e-global-typography-b55f19d-letter-spacing) !important;
  color: var(--t7-frost-text) !important;
}
.t7-column .elementor-widget-text-editor ul { padding-left: 1.1em; }
.t7-column .elementor-widget-text-editor li { margin-bottom: .35em; }

/* THE COLUMN — glass substrate.
   Paul, 2026-08-10: "uncarding increases the need for the glass effect for
   text legibility." Correct — on the bare ground the particle field reads
   straight through the prose. So the measure keeps the glass SUBSTRATE
   (frost tint + 4px backdrop blur, the exact .fx-lift values) and drops the
   card IDENTITY (gap-width frame, gloss ::after, margin, hover lift).
   Glass is the legibility layer; the frame and the gloss are what make a
   card a card. The gutter chip stays bare on the ground beside it. */
.t7-column .t7-measure {
  /* glass now lives on .e-con.t7-column so the label is inside the panel */
  background: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  padding: 0;
}

/* ============================================================
   KNOCKOUT PLATE  (.t7-plate--knockout)
   Breadcrumb: tmpl 569 > icon widget 906e631 (custom_css points here)

   The email plate now uses a branded KNOCKOUT icon, so the token roles
   INVERT versus a line-art plate:

     line-art plate   container = accent field, svg = the glyph
     knockout plate   svg = accent FIELD, container shows through the marks

   Accent law unchanged and still doing its job — dark = Voltage field /
   ink marks, light = Cobalt field / eggshell marks. Line-art plates (the
   case-study star) are untouched; that is what the modifier is for.
   ============================================================ */
body { --t7-plate-knockout-src: url("/wp-content/uploads/2026/08/icon-email-marketing.svg"); }

.elementor-widget-icon.t7-plate--knockout .elementor-icon {
  background-color: var(--t7-plate-glyph) !important;  /* reads through the marks */
}
/* the .t7-card-scoped plate contract sets padding:14px !important at (0,4,0);
   the knockout is edge-to-edge, so this has to match that weight to win */
.t7-card .elementor-widget-icon.t7-plate--knockout .elementor-icon { padding: 0 !important; }
.elementor-widget-icon.t7-plate--knockout .elementor-icon,
.elementor-widget-icon.t7-plate--knockout .elementor-icon svg,
.elementor-widget-icon.t7-plate--knockout .elementor-icon svg * {
  fill:  var(--t7-plate-bg) !important;   /* the field carries the accent */
  color: var(--t7-plate-bg) !important;
}

/* The inherited ::before wipe sits BEHIND the svg. On a knockout that means
   it is only visible through the holes — it would sweep the MARKS, not the
   fill. Park it, and sweep the FIELD with an ::after clipped to the icon's
   own shape. mask-size:contain + centre matches the svg's own aspect fit
   (this icon's viewBox is 43.83x45.63 pending Paul's square re-export)
   so wipe and field stay registered.
   v1.2.4 RULE: NEVER animate a masked layer with transform — the mask
   scales with the layer (squashed ghost glyph mid-sweep, Paul 2026-08-10).
   The stencil stays pinned; the REVEAL moves: clip-path inset sweep. */
.elementor-widget-icon.t7-plate--knockout .elementor-icon::before { display: none !important; }
.elementor-widget-icon.t7-plate--knockout .elementor-icon::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: var(--e-global-color-bc74cd9, #F2452E);
  -webkit-mask-image: var(--t7-plate-knockout-src);
          mask-image: var(--t7-plate-knockout-src);
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  clip-path: inset(0 100% 0 0);
  transition: clip-path .45s cubic-bezier(.65,0,.35,1);
}
.elementor-widget-icon.t7-plate--knockout .elementor-icon:hover::after { clip-path: inset(0 0 0 0); }

/* a knockout has no line art to tone-flip, so park the inherited glyph flip —
   otherwise the field would fight the wipe mid-sweep */
.elementor-widget-icon.t7-plate--knockout .elementor-icon:hover svg,
.elementor-widget-icon.t7-plate--knockout .elementor-icon:hover svg * {
  fill: var(--t7-plate-bg) !important; color: var(--t7-plate-bg) !important;
}

@media (hover: none) {
  /* iOS sticky-hover: park the sweep, drive it from :active (same family as
     the line-art plate wipe, v1.1.1) */
  .elementor-widget-icon.t7-plate--knockout .elementor-icon:hover::after  { clip-path: inset(0 100% 0 0); }
  .elementor-widget-icon.t7-plate--knockout .elementor-icon:active::after { clip-path: inset(0 0 0 0); }
}

/* ============================================================
   CASE-STUDY BANDS — the page turn, declared ONCE for every case study.
   v1.2.1, 2026-08-10.

   Hangs off the post-content widget, so it catches every case study's
   chapters automatically — existing and future — with zero per-post data.
   Nothing is written into any post's Elementor JSON.

   RHYTHM is a 6-cycle in GROUPS OF TWO (Paul: "groups of 2-3 sections share
   a bg, then it shifts"), not a 1:1 alternation, which would read mechanical:
     acts 1-2  transparent  — the ampersand field stays visible
     acts 3-4  ink
     acts 5-6  paper
   Two adjacent acts sharing a tone merge into one taller band on purpose.

   BASES ARE LITERALS, never global slots. The theme flip swaps the Ink and
   Eggshell slots, which would hand light mode a dark paper band. Same gotcha
   the t7-film bases hit.

   ESCAPES: .t7-quote stages never take a band — a testimonial stage owns its
   own ground everywhere on the site. .t7-film opts out (Bourdain 82's hand-authored photo bands own
   their own tone) and .t7-noband is the per-page escape hatch.

   MODE FLIP: a band whose tone opposes the current site mode gets the
   scope-invert token table, folded into the .t7-scope-invert rules below
   rather than duplicated — dark mode flips the paper bands, light mode
   flips the ink bands. That resolves the open question from 2026-08-10
   about whether scope-invert should merge with the band treatment: it does.
   ============================================================ */
/* RETIRED 2026-08-29 (Paul: "let's can that", Dankley session): the
   positional band paint is gone -- every act now rides the page ground and
   the theme flip reaches the whole page. The two paired nth-child groups in
   the tone tables (--is-dark pins + derived token pins) left with it; the
   .t7-noband escape is moot but harmless where it remains in page data.
   The rhythm spec above stays as history; rules were, verbatim:
     6n+3, 6n+4  -> background-color: var(--e-global-color-c8af26f)  (Ink)
     6n+5, 6n    -> background-color: var(--e-global-color-316eccb)  (Eggshell) */

/* --- t7 offerings pyramid --------------------------------------------------
   Symmetrical row distribution for the Service-template offerings Loop Grid
   (template 148, widget 9a73cd5, loop item tmpl 149).

   The grid runs at 2x tracks and every card spans 2. On its own this renders
   PIXEL-IDENTICAL to the stock 3-col grid -- 2*((W-5g)/6)+g == (W-2g)/3, the gap
   cancels -- so if the snippet never runs, the layout is exactly today's.

   Row offsets are applied by WPCode snippet "t7: offerings pyramid", which must
   write inline styles with 'important' priority to beat the !important below.

   !important is REQUIRED here: post-148.css loads after child style.css and wins
   ties on grid-template-columns (verified 2026-08-11).

   Spec: 19th7th-Site/specs/2026-08-11-offerings-loop-symmetry-design.md
   --------------------------------------------------------------------------- */
@media (min-width: 1025px) {
  .t7-pyramid .elementor-loop-container { grid-template-columns: repeat(6, 1fr) !important; }
  .t7-pyramid .elementor-loop-container > .e-loop-item { grid-column: span 2 !important; }
}
@media (min-width: 768px) and (max-width: 1024px) {
  .t7-pyramid .elementor-loop-container { grid-template-columns: repeat(4, 1fr) !important; }
  .t7-pyramid .elementor-loop-container > .e-loop-item { grid-column: span 2 !important; }
}
/* MOBILE: one column (2026-08-12). The widget ran 2-col at phone widths —
   175px cards, and once the icon tile stopped yielding (flex:0 0 auto,
   "BRANDED ICONOGRAPHY") the title column collapsed to 10px. Same call
   as the homepage services loop: full-width cards on phones. */
@media (max-width: 767px) {
  .t7-pyramid .elementor-loop-container { grid-template-columns: 1fr !important; }
  .t7-pyramid .elementor-loop-container > .e-loop-item { grid-column: auto !important; }
}

/* ============================================================
   THE THEME ENGINE  (v1.6.0 — the whole thing, on one screen)

   SEMANTIC SLOTS (Plan B, 2026-08-12). Widgets reference these six,
   never the materials. TUNE THE MATERIALS (Ink, Slate, Smoke, Ash,
   Eggshell, Marshmallow, the accents). NEVER TUNE A SLOT.

     slot          dark        light         use
     Ground        Ink         Marshmallow   the surface you stand on
     Ground Muted  Slate       Eggshell*     secondary surface, one stop in
     Figure        Eggshell    Slate         primary marks: text, rules, frames
     Figure Muted  Ash         Smoke         captions, meta, de-emphasis
     Accent        Voltage     Cobalt        the accent law, as a slot
     On Accent     Ink         Marshmallow   the readable mark ON an accent field
     (* the light PAGE is Eggshell and the light CARD is Marshmallow —
        see the light table.)

   TONE: a subtree is dark or light; the two tables below are the ONLY
   place the mapping exists. Selector lists are the whole tone system:
     follows mode   = body rows
     .t7-dark/.t7-light = absolute declarations
     .t7-invert (the scope-invert / fx-invert / ui-ground-inverse
     aliases were purged 2026-08-20; the case-study band rhythm, and the
     Music lead-in 12af3fa stopgap) = relative rows
   Each table also picks the three ground-dependent extras: the accent
   of the OPPOSITE ground (bars/floods are Figure-material surfaces),
   the icon tint that reads on THIS ground, and the gloss recipe.

   Spec: specs/2026-08-11-palette-role-system-design.md
   ============================================================ */

/* ============================================================
   THE THEME ENGINE v2 (2026-08-19) — parent-relative tone.
   TONE = one inherited flag, --is-dark (1|0). Absolutes set it;
   inverts FLIP THE PARENT'S value via @container style() (every
   element is a style container for custom properties; the query
   reads the parent). FALLBACK rows (first) replicate the old
   mode-relative behavior for browsers without style queries
   (pre-Safari-18, pre-Firefox-151) — nothing gets worse anywhere.
   ORDER MATTERS: fallback rows, THEN the @container rules — same
   specificity, file order breaks the tie.
   SLOTS + TOKENS are ONE formula each, re-resolved on EVERY
   element: any container becomes a scope by wearing a class.
   Spec: specs/2026-08-19-surface-system-v2-design.md
   Plan: plans/2026-08-19-surface-system-v2.md
   ============================================================ */

/* ---- A. tone resolution ---- */
body.elementor-kit-89 { --is-dark: 1; }
html.theme-light body.elementor-kit-89 { --is-dark: 0; }
.t7-dark  { --is-dark: 1; }
.t7-light { --is-dark: 0; }

/* fallback: inverts read the SITE MODE (v1.6 behavior) */
.t7-invert { --is-dark: 0; }
:where(html.theme-light) .t7-invert,
:where(html.theme-light) .t7-invert { --is-dark: 1; }

/* real semantics: flip the nearest ancestor's tone */
@container style(--is-dark: 1) {
  .t7-invert { --is-dark: 0; }
}
@container style(--is-dark: 0) {
  .t7-invert { --is-dark: 1; }
}

/* case-study band tone pins retired 2026-08-29 with the band paint. */

/* Music lead-in stopgap (page 62) — replaces the old pinned-token
   block wholesale; delete by electing .t7-dark on the band */
html.theme-light .elementor-element-12af3fa { --is-dark: 1; }

/* ---- B. slots + derived tokens: ONE formula each, universal ----
   body row = (0,1,1), outranks Elementor's emitted kit values for
   the slot names; :where() rows re-resolve per element so tokens
   always follow the element's own inherited tone. */
body.elementor-kit-89, body.elementor-kit-89 :where(*) {
  /* the six slots (Plan B semantics preserved) */
  --e-global-color-grnd7a1: color-mix(in srgb, var(--e-global-color-c8af26f) calc(var(--is-dark) * 100%), var(--e-global-color-316eccb));
  --e-global-color-grndmut: color-mix(in srgb, var(--e-global-color-27abaeb) calc(var(--is-dark) * 100%), var(--e-global-color-42a1059));
  --e-global-color-fgur7b2: color-mix(in srgb, var(--e-global-color-316eccb) calc(var(--is-dark) * 100%), var(--e-global-color-27abaeb));
  --e-global-color-figrmut: color-mix(in srgb, var(--e-global-color-3bc915a) calc(var(--is-dark) * 100%), var(--e-global-color-07f7664));
  --e-global-color-accntsl: color-mix(in srgb, var(--e-global-color-230debb) calc(var(--is-dark) * 100%), var(--e-global-color-d3cd34a));
  --e-global-color-onaccnt: color-mix(in srgb, var(--e-global-color-c8af26f) calc(var(--is-dark) * 100%), var(--e-global-color-42a1059));
  /* the INVERTED accent - the accent of the OPPOSITE ground. Promoted to a
     real kit slot 2026-08-31 (Paul: "I think we need to have a proper
     inverted accent"), so it is now electable in Elementor like the other
     five instead of being a child-sheet token only two rules could reach.
     NB `onaccnt` is a different animal: it is the FOREGROUND that sits ON
     the accent (Ink / Marshmallow), not the accent for an inverted surface.
     --t7-accent-flip is kept as an alias so its existing consumers
     (--t7-cta-flood) need no migration. ONE formula. */
  --e-global-color-accntinv: color-mix(in srgb, var(--e-global-color-d3cd34a) calc(var(--is-dark) * 100%), var(--e-global-color-230debb));
  --t7-accent-flip:        var(--e-global-color-accntinv);
  /* roles (snippet 698 reads --t7-ground off body at apply time) */
  --t7-ground: var(--e-global-color-grnd7a1);
  --t7-primary: var(--e-global-color-fgur7b2);
  --t7-muted: var(--e-global-color-figrmut);
  /* surfaces + marks */
  --t7-surface: var(--e-global-color-grnd7a1);
  --t7-surface-text: var(--e-global-color-fgur7b2);
  /* NB: --t7-film-base-inv is deliberately NOT here — it is MODE-pinned
     (v1.6 semantics): a film--invert band opposes the SITE MODE, not its
     own (possibly scope-inverted) local tone. Declared below section C. */
  /* glass */
  --t7-frost-tint: color-mix(in srgb, var(--e-global-color-grndmut) 62%, transparent);
  --t7-frost-frame: var(--e-global-color-fgur7b2);
  --t7-frost-text: var(--e-global-color-fgur7b2);
  --t7-frost-shadow: 7px 8px 0 var(--e-global-color-grnd7a1), inset 0 -14px 26px color-mix(in srgb, var(--e-global-color-grnd7a1) 30%, transparent);
  /* accent law */
  --t7-accent: var(--e-global-color-accntsl);
  --t7-plate-bg: var(--e-global-color-accntsl);
  --t7-plate-glyph: var(--e-global-color-grnd7a1);
  --t7-plate-glyph-flip: var(--e-global-color-fgur7b2);
  --t7-btn-bg: var(--e-global-color-accntsl);
  --t7-btn-fg: var(--e-global-color-grnd7a1);
  --t7-btn-fg-flip: var(--e-global-color-fgur7b2);
  --t7-bar-bg: var(--t7-frost-frame); /* the bar IS the frame */
  --t7-bar-mark: var(--e-global-color-grnd7a1); /* Ground on a Figure field (Paul, 2026-09-01): tags read as type cut out of the frame, not coloured links */
  --t7-cta-flood: var(--t7-accent-flip);
  --t7-cta-flood-text: var(--e-global-color-fgur7b2);
  --t7-cta-flood-icon: var(--t7-cta-icon);
  /* lighting pair, resolved per tone scope */
  --t7-duo-shadow:    color-mix(in srgb, var(--t7-duo-dark-shadow) calc(var(--is-dark) * 100%), var(--t7-duo-light-shadow));
  --t7-duo-highlight: color-mix(in srgb, var(--t7-duo-dark-highlight) calc(var(--is-dark) * 100%), var(--t7-duo-light-highlight));
  /* drafted-pattern mapping (Paul, 2026-08-19): NO role flip — field =
     highlight, linework = shadow, both modes. DARK EXCEPTION (Paul,
     same day): Slate as field reads BROWN at this brightness, so the
     pattern (only — the global duo pair is untouched) runs literal
     black lines on Ink, the darkest material. Net law: the city's
     field IS the page ground in both modes, lines one step beyond. */
  --t7-pattern-field: color-mix(in srgb, var(--e-global-color-c8af26f) calc(var(--is-dark) * 100%), var(--t7-duo-highlight));
  /* light arm deepened (Paul: "the white is barely legible") — lines
     pulled toward Ash for the same ~35/channel bite the dark side has */
  --t7-pattern-line:  color-mix(in srgb, black calc(var(--is-dark) * 100%), color-mix(in srgb, var(--e-global-color-3bc915a) 65%, var(--e-global-color-316eccb)));
  /* CANONICAL TWO-TONE NAMES (2026-08-21, Paul). One vocabulary for
     every two-tone surface — patterns, and anything else that paints a
     mark on a field: --t7-bg IS the field, --t7-fg IS the mark.
     These are ALIASES, not a second system. The pattern pair above
     stays the source of truth, so nothing repaints and the tone axis
     (t7-dark / t7-light / t7-invert) keeps driving both for free. */
  --t7-bg: var(--t7-pattern-field);
  --t7-fg: var(--t7-pattern-line);
  /* THE VEIL (Paul, 2026-08-19): a 50% wash from the mode's highlight
     down into its shadow, laid over the city — evens the field, gives
     the ground depth. One formula; stops re-resolve per tone scope. */
  /* THE VEIL, final form (Paul 2026-08-19): a SOLID highlight-to-shadow
     ramp — no transparent window — overlaid at uniform 75% to mute the
     art beneath. Strength = the one number on both stops. */
  /* THE WASH (canon, 2026-08-19): the highlight-to-shadow ramp that
     sits on top of duotone. ONE formula, one strength dial; it
     re-resolves per tone scope like everything else here.
     COLOURS ARE ALWAYS KIT GLOBALS (via the duo pair) and STRENGTH IS
     ALWAYS A VAR — Paul's rule: he must know where to make the change.
     --t7-wash-strength is the one dial; --t7-city-veil is a var() alias,
     so retuning the wash retunes the shipped ground with it. At 75% the
     substituted formula is identical to the literal veil it replaced. */
  /* THE VEIL IS NO LONGER THE WASH (2026-08-21). They were one dial at
     75%, which is CORRECT for the ghost-emboss city — hard-edged vector
     art at ~35/channel survives a 75% scrim as Delta~9 and that muted
     read IS the approved look. It is WRONG for a photograph. A 75%
     scrim leaves any source 25% of its contrast; vector art has hard
     edges to hang on, continuous tone has nothing, so a duotoned photo
     collapsed to a near-flat cream. Confirmed on Bourdain 82 element
     1d68e5f: removing .t7-wash alone restored the full tonal range.
     Two contexts, two dials. Both still kit-global colours + a var
     strength, per the standing rule. */
  --t7-wash-strength: 30%;
  --t7-wash: linear-gradient(180deg,
    color-mix(in srgb, var(--t7-duo-highlight) var(--t7-wash-strength), transparent) 0%,
    color-mix(in srgb, var(--t7-duo-shadow) var(--t7-wash-strength), transparent) 100%);
  /* the shipped ground keeps the number it was tuned at */
  --t7-veil-strength: 75%;
  --t7-city-veil: linear-gradient(180deg,
    color-mix(in srgb, var(--t7-duo-highlight) var(--t7-veil-strength), transparent) 0%,
    color-mix(in srgb, var(--t7-duo-shadow) var(--t7-veil-strength), transparent) 100%);
}

/* ---- C. discrete tokens (gradients/filters can't color-mix):
   set at every tone-scope row, carried by inheritance between
   scopes. KEEP THIS LIST IN SYNC WITH SECTION A. ---- */
body.elementor-kit-89, .t7-dark,
html.theme-light .elementor-element-12af3fa {
  --t7-cta-icon: var(--t7-filter-eggshell);
  --t7-card-light: var(--t7-card-light-on-dark);
}
html.theme-light body.elementor-kit-89, .t7-light {
  --t7-cta-icon: brightness(0);
  --t7-card-light: var(--t7-card-light-on-light);
}
.t7-invert {
  --t7-cta-icon: brightness(0);
  --t7-card-light: var(--t7-card-light-on-light);
}
:where(html.theme-light) .t7-invert,
:where(html.theme-light) .t7-invert {
  --t7-cta-icon: var(--t7-filter-eggshell);
  --t7-card-light: var(--t7-card-light-on-dark);
}
@container style(--is-dark: 1) {
  .t7-invert {
    --t7-cta-icon: brightness(0);
    --t7-card-light: var(--t7-card-light-on-light);
  }
}
@container style(--is-dark: 0) {
  .t7-invert {
    --t7-cta-icon: var(--t7-filter-eggshell);
    --t7-card-light: var(--t7-card-light-on-dark);
  }
}
/* case-study band derived-token pins retired 2026-08-29 with the band paint. */


/* grain veil (drafted-city noise layer). NB: the legacy --t7-grain
   token's data-URI is UNENCODED (raw spaces/</>) and current Chrome
   rejects it — computes to none (found 2026-08-19; the legacy token
   may be dead everywhere it's consumed — audit separately). This rule
   carries its own properly-encoded copy of the same turbulence tile. */
.t7dg-grain { background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='140'%20height='140'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.85'%20numOctaves='2'/%3E%3C/filter%3E%3Crect%20width='140'%20height='140'%20filter='url(%23n)'/%3E%3C/svg%3E"); }

/* ---- LIGHTING MODEL (v2 spec §3, Paul's four vars 2026-08-19).
   THE RAMP ENDS ARE THE CONTRAST BUDGET — tune here, everything
   dimensional (duotone grounds, patterns, bevel, future glass
   recipe) follows. Kit-wired per zero-hex doctrine; the `black`
   keyword inside color-mix is an allowed literal. ---- */
body {
  /* Paul 2026-08-19 tune: "split the diff on both" — dark pair pulled
     closer (was Ink·55%·black: too contrasty), light pair pulled apart
     and off pure white (was Marshmallow/Eggshell: too pale). Both pairs
     now sit ~15/channel apart. */
  --t7-duo-dark-shadow: color-mix(in srgb, var(--e-global-color-c8af26f) 82%, black);
  --t7-duo-dark-highlight: var(--e-global-color-27abaeb);   /* Slate = charcoal */
  --t7-duo-light-shadow: color-mix(in srgb, var(--e-global-color-316eccb) 72%, var(--e-global-color-3bc915a));
  --t7-duo-light-highlight: var(--e-global-color-316eccb);  /* Eggshell = the light page itself */
}

/* ---- D. THE GLASS/SOLID PILL IS RETIRED (Paul, 2026-08-19) ----
   Four `html.theme-invert` blocks lived here: two page-level token
   overrides and two on-card accent overrides. They existed to serve a
   DEBUG TOGGLE that flipped every card between glass and solid at once.
   Surface is now an explicit per-element election (t7-solid / t7-duotone
   / t7-wash / t7-lift), so a global flip has nothing to say.
   Deleting them also retires this file's only ORDER-SENSITIVE section —
   those blocks had to sit at EOF to win cascade ties by file order.
   Nothing below depends on source order any more.
   NOTE `.t7-invert` (the tone class) is unrelated to the deleted
   `html.theme-invert` (the pill) and is very much alive.
   Also gone: WPCode 815's invert pill + 759's local copy. The DARK/LIGHT
   mode pill in 815 is untouched. ---- */


/* ============================================================
   INDUSTRY MAPPING — RETIRED 2026-08-26 (was Paul's 2026-08-21
   ruling, shipped in 3.3.0). The page->artwork defaults moved out
   of CSS and onto the five industry containers themselves as
   explicit `t7-pattern--<name>` labels, so the declaration is
   visible in the Elementor editor and any container can call any
   pattern. WPCode snippet 1208 still emits `t7-industry-<slug>`
   on <body>; nothing in this file reads it today, kept as a hook.
   ============================================================ */
/* ============================================================
   T7 CONCEPTS - one room, several ways to dress it.  (2026-09-14)
   ------------------------------------------------------------
   First user: Influencers Club 84, band 4 (column c2dab18).
   Pick a concept and every view plus the material row re-dress
   in place. The picker, the material chips AND the script live in
   ONE HTML widget inside the section, so cloning the section
   carries the behaviour with it (no WPCode snippet to forget).
   This rule owns only the stacking and the picker/chip faces.
   Frames, tabs, widths and the plan letterbox colour are
   Elementor-authored on the elements themselves.

   CONTRACT (elections, never ids):
     .t7-concepts               the column that holds it all
     .t7-concepts-view          each framed view container
     .t7-concepts-view--stage   the wide one: 16:9, image contained
     .t7-concept--<key>         each image widget, one per concept
     data-concept="<key>"       picker buttons + chip rows (HTML widget)
   The script toggles .is-on. It tours every 3.2s while on screen,
   the first click takes the wheel for good, and reduced motion gets
   no tour: the same law as the Dankley playground.

   COLOURS: marks ride the Ground slot and plates the Figure slot,
   because the first host sits on a Figure (turn) band. On a band
   wearing the page ground, swap the two dials on the column.
   Kit 89's Buttons global paints every bare <button> at (0,1,1) and
   (0,2,1); the picker arms below sit one class higher on purpose.
   ============================================================ */
.t7-concepts {
  --t7c-mark: var(--e-global-color-grnd7a1);
  --t7c-plate: var(--e-global-color-fgur7b2);
}

/* ---- views: images stacked, one on at a time ---- */
/* no overflow clip: the corner caption chip rides over the frame bead */
.e-con.t7-concepts-view { position: relative; aspect-ratio: 4 / 3; }
.e-con.t7-concepts-view--stage { aspect-ratio: 16 / 9; }
.t7-concepts-view > .elementor-widget-image {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; transition: opacity .6s ease;
}
.t7-concepts-view > .elementor-widget-image.is-on { opacity: 1; }
.t7-concepts-view > .elementor-widget-image > *,
.t7-concepts-view > .elementor-widget-image img { width: 100%; height: 100%; }
.t7-concepts-view > .elementor-widget-image img { object-fit: cover; max-width: none; border-radius: 0; }
/* the floor plans do not share one aspect: contain them so no wall is cropped */
.t7-concepts-view--stage > .elementor-widget-image img { object-fit: contain; }

/* ---- picker ---- */
.t7-concepts-picker { display: flex; flex-wrap: wrap; gap: var(--gap-width, 13px); margin: 0 0 var(--gap-width, 13px); }
.t7-concepts button.t7-concepts-pick,
.t7-concepts button.t7-concepts-pick:hover,
.t7-concepts button.t7-concepts-pick:focus {
  appearance: none; cursor: pointer; margin: 0;
  display: flex; align-items: baseline; gap: 10px;
  padding: 12px 18px; border: 2px solid var(--t7c-mark); border-radius: 0;
  background: transparent; color: var(--t7c-mark);
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif;
  font-size: 15px; font-weight: 700; line-height: 1.1; letter-spacing: .06em; text-transform: uppercase;
  transition: background-color .2s ease, color .2s ease;
}
.t7-concepts button.t7-concepts-pick:hover { background: color-mix(in srgb, var(--t7c-mark) 14%, transparent); }
.t7-concepts button.t7-concepts-pick[aria-pressed="true"],
.t7-concepts button.t7-concepts-pick[aria-pressed="true"]:hover,
.t7-concepts button.t7-concepts-pick[aria-pressed="true"]:focus { background: var(--t7c-mark); color: var(--t7c-plate); }
.t7-concepts-pick small { font-size: 11px; font-weight: 500; opacity: .7; letter-spacing: .1em; }

/* ---- material row: square chips, one row per concept, cross-fading ---- */
.t7-concepts-materials { position: relative; height: 44px; }
.t7-concepts-chips {
  position: absolute; inset: 0; display: flex; gap: 8px; align-items: center;
  opacity: 0; transition: opacity .6s ease; pointer-events: none;
}
.t7-concepts-chips.is-on { opacity: 1; }
.t7-concepts-chip {
  width: 44px; height: 44px; flex: 0 0 44px;
  background: var(--chip) center / cover no-repeat;
  box-shadow: inset 0 0 0 2px var(--t7c-mark);
}

@media (max-width: 767px) {
  .t7-concepts button.t7-concepts-pick,
  .t7-concepts button.t7-concepts-pick:hover,
  .t7-concepts button.t7-concepts-pick:focus { flex: 1 1 0; justify-content: center; padding: 10px 8px; font-size: 13px; }
  .t7-concepts-pick small { display: none; }
  .t7-concepts-materials { height: 32px; }
  .t7-concepts-chip { width: 32px; height: 32px; flex-basis: 32px; }
}
@media (prefers-reduced-motion: reduce) {
  .t7-concepts-view > .elementor-widget-image, .t7-concepts-chips { transition: none; }
}


/* ============================================================
   T7 SHOWCASE (2026-09-14) — template 1730 "Showcase Single".
   Container 5cbce0a elects `t7-showcase`. Its media widget (a7e1003)
   prints [t7_showcase_media] (WPCode 1729): a `.t7-showcase-media`
   wrapper carrying `--t7-ar` (width / height as a number, read from the
   provider's oEmbed html) and `t7-showcase-video--portrait|landscape`;
   1729 also puts `t7-showcase-portrait|landscape` on <body>.
   WHY: Elementor's `.elementor iframe { width: 100% }` stretches every
   embed while the height ATTRIBUTE stays fixed, so the box never matched
   the video — AT&T 78's portrait Vimeo (563x1000) rendered 909x1000 with
   black bars, and the landscape YouTube embeds rendered 909x450 (2:1,
   not 16:9). The ratio is derived, never typed: drop a link, page knows.
   ARRANGEMENT (Paul, 2026-10-05, replacing the 2026-09-14 text-left /
   video-right split): ONE layout for every showcase. Title, then the
   media in the same full-width 16:9 box a landscape video gets, then
   the summary. A portrait frame sits centred in that box at its own
   ratio, full box height, with the featured-image tile (a7e1003's
   background) showing either side. Under 768px a portrait frame stacks
   centred, capped at 78vh so a phone video stays phone-sized.
   `t7-showcase-portrait|landscape` on <body> is now just a hook.
   Dials: `--t7-showcase-max-h` (78vh) on any ancestor.
   ------------------------------------------------------------ */
.t7-showcase-media { display: block; }
.t7-showcase-media iframe,
.t7-showcase-media video {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;                          /* beats the height="…" attribute */
  margin: 0;
  aspect-ratio: var(--t7-ar, 1.7778);    /* 16:9 when the provider gave no size */
}
/* portrait, under 768px: stacked, never taller than the cap, never wider than the cell, centred */
.t7-showcase-media.t7-showcase-video--portrait iframe,
.t7-showcase-media.t7-showcase-video--portrait video {
  width: min(100%, calc(var(--t7-showcase-max-h, 78vh) * var(--t7-ar, 0.5625)));
  margin-inline: auto;
}
/* portrait, 768px up: the landscape box (full width, 16:9), frame centred in it */
@media (min-width: 768px) {
  /* behind the frame: the featured image once, covering the box (1730 authors a
     20% repeat tile, which reads as wallpaper when the sides show) */
  body.t7-showcase-portrait .e-con.t7-showcase > .e-con-inner > .elementor-widget-shortcode {
    background-size: cover;
    background-position: center center;
    background-repeat: no-repeat;
  }
  .t7-showcase-media.t7-showcase-video--portrait {
    display: flex;
    justify-content: center;
    aspect-ratio: 16 / 9;
    background: rgba(0, 0, 0, 0.3);      /* subtle darken so the frame reads */
  }
  .t7-showcase-media.t7-showcase-video--portrait iframe,
  .t7-showcase-media.t7-showcase-video--portrait video {
    width: auto;                         /* from aspect-ratio: height x --t7-ar */
    height: 100%;
    max-width: 100%;
    margin: 0;
  }
}


/* ============================================================
   T7 AUDIO - branded audio player skin (2026-09-14)
   Source of truth: ~/projects/19th/t7-audio-player/t7-audio.css (edit THERE, re-paste here).
   First user: Bourdain 82, track band. Markup + script live in the band's HTML widget.
   The play button also elects the existing .t7-wipe (Ember flood) above.
   ============================================================ */
/* =====================================================================
   t7-audio · branded audio player skin                        v0.2.0
   ---------------------------------------------------------------------
   Markup + boot script live in an Elementor HTML widget:
     <div class="t7-audio" data-source="soundcloud|audio" data-src="…">
   (contract: t7-audio-player/README.md). The script builds the skin.
   Tokens: Ground grnd7a1 · Figure fgur7b2 · Accent accntsl (mode-aware:
   Voltage dark / Cobalt light) · on-accent onaccnt · --gap-width.
   Site tokens reused: --t7-bar-bg / --t7-ground (THE BAR FACE) and the
   t7-wipe block (Ember flood + --t7-wipe-fg) for the play button hover.
   Zero-hex: the only literals are the kit fallbacks inside var().
   Square language: no radius, no circles, anywhere in this block.
   Kit trap: .elementor-kit-89 button paints at (0,1,1), :hover/:focus
   at (0,2,1). Button arms below sit at (0,2,1) rest and (0,3,1) states.
   Breadcrumb for the widget's Custom CSS box:
     /* styled in child style.css → "t7-audio" *\/
   ===================================================================== */

.t7-audio {
  --t7-audio-bg: var(--e-global-color-grnd7a1, #22211C);
  --t7-audio-fg: var(--e-global-color-fgur7b2, #F5F3E8);
  --t7-audio-hot: var(--e-global-color-accntsl, #FFDE00);
  --t7-audio-on-hot: var(--e-global-color-onaccnt, var(--t7-audio-bg));
  --t7-audio-plate: var(--t7-bar-bg, var(--t7-audio-fg));
  --t7-audio-glyph: var(--t7-ground, var(--t7-audio-bg));
  --t7-audio-gap: var(--gap-width, 13px);
  --t7-audio-mute: color-mix(in srgb, var(--t7-audio-fg) 45%, var(--t7-audio-bg));
  --t7-audio-soft: color-mix(in srgb, var(--t7-audio-fg) 72%, var(--t7-audio-bg));
  --t7-audio-font: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", Helvetica, Arial, sans-serif;
  --t7-audio-btn: 3.5rem;
  --t7-audio-bar-h: 2.75rem;

  container: t7-audio / inline-size;
  position: relative;
  display: block;
  box-sizing: border-box;
  margin: 0;
  color: var(--t7-audio-fg);
  font-family: var(--t7-audio-font);
  line-height: 1.2;
  -webkit-font-smoothing: antialiased;
}

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

/* ---- tabs: frame material (Figure plate, Ground text) --------------- */

.t7-audio .t7-audio-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--t7-audio-gap);
}

.t7-audio .t7-audio-tab,
.t7-audio a.t7-audio-tab,
.t7-audio a.t7-audio-tab:hover,
.t7-audio a.t7-audio-tab:focus {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  margin: 0;
  padding: calc(var(--t7-audio-gap) * 0.6) var(--t7-audio-gap) calc(var(--t7-audio-gap) * 0.35);
  border: 0;
  border-radius: 0;
  background: var(--t7-audio-fg);
  color: var(--t7-audio-bg);
  font-family: var(--t7-audio-font);
  font-size: 0.75rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}

.t7-audio a.t7-audio-source[hidden] {
  display: none;
}

.t7-audio a.t7-audio-source:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
}

.t7-audio a.t7-audio-source:focus-visible {
  outline: 3px solid var(--t7-audio-hot);
  outline-offset: 2px;
}

.t7-audio .t7-audio-arrow {
  width: 0.7em;
  height: 0.7em;
  fill: currentColor;
}

/* ---- frame: --gap-width border in Figure, Ground surface ------------ */

.t7-audio .t7-audio-frame {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "play meta meta"
    "bar  bar  time";
  align-items: center;
  column-gap: var(--t7-audio-gap);
  row-gap: calc(var(--t7-audio-gap) * 0.5);
  margin: 0;
  padding: var(--t7-audio-gap);
  border: var(--t7-audio-gap) solid var(--t7-audio-fg);
  border-radius: 0;
  background: var(--t7-audio-bg);
  color: var(--t7-audio-fg);
}

/* Wide container: one row. Queries the player's own width, not the
   viewport, so a half-column player wraps even on a desktop screen. */
@container t7-audio (min-width: 40rem) {
  .t7-audio .t7-audio-frame {
    grid-template-columns: auto minmax(8rem, 15rem) minmax(0, 1fr) auto;
    grid-template-areas: "play meta bar time";
    column-gap: calc(var(--t7-audio-gap) * 1.5);
  }
}

/* ---- play / pause: the footer social button, square -----------------
   Rest = THE BAR FACE (frame-material plate, ground glyph), the same pair
   the footer socials wear. Hover = the site's t7-wipe: the script puts
   class `t7-wipe` on this button, so the Ember flood (::before), its
   direction, timing, content fade and touch parking all come from that
   ONE block in this sheet. Nothing here copies the recipe; these rules
   only restate colours above the kit's bare-button paint. */

.t7-audio button.t7-audio-play,
.t7-audio button.t7-audio-play:hover,
.t7-audio button.t7-audio-play:focus,
.t7-audio button.t7-audio-play:active {
  grid-area: play;
  appearance: none;
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  width: var(--t7-audio-btn);
  height: var(--t7-audio-btn);
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--t7-audio-plate);
  color: var(--t7-audio-glyph);
  box-shadow: none;
  font: inherit;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  cursor: pointer;
  transition: background-color 0.15s ease;
}

/* flooded: content takes t7-wipe's foreground (its own :hover is only (0,2,0)) */
.t7-audio button.t7-audio-play:hover {
  color: var(--t7-wipe-fg, var(--t7-audio-fg));
}

/* playing: accent plate, on-accent glyph (Voltage/Ink dark, Cobalt/Marshmallow light) */
.t7-audio button.t7-audio-play[aria-pressed="true"],
.t7-audio button.t7-audio-play[aria-pressed="true"]:focus,
.t7-audio button.t7-audio-play[aria-pressed="true"]:active {
  background: var(--t7-audio-hot);
  color: var(--t7-audio-on-hot);
}

.t7-audio button.t7-audio-play[aria-pressed="true"]:hover {
  background: var(--t7-audio-hot);
  color: var(--t7-wipe-fg, var(--t7-audio-fg));
}

.t7-audio button.t7-audio-play:focus-visible {
  outline: 3px solid var(--t7-audio-hot);
  outline-offset: 3px;
}

.t7-audio button.t7-audio-play[aria-busy="true"] {
  cursor: progress;
  box-shadow: inset 0 0 0 3px var(--t7-audio-glyph);
}

.t7-audio button.t7-audio-play:disabled,
.t7-audio button.t7-audio-play:disabled:hover {
  background: var(--t7-audio-mute);
  color: var(--t7-audio-glyph);
  cursor: not-allowed;
}

.t7-audio button.t7-audio-play:disabled::before {
  transform: scaleX(0);
}

/* touch: t7-wipe parks the flood on hover-less devices and floods only
   while pressed; the glyph follows suit instead of sticking flipped
   on a parked (unflooded) plate. */
@media (hover: none) {
  .t7-audio button.t7-audio-play:hover { color: var(--t7-audio-glyph); }
  .t7-audio button.t7-audio-play[aria-pressed="true"]:hover { color: var(--t7-audio-on-hot); }
  .t7-audio button.t7-audio-play:active,
  .t7-audio button.t7-audio-play[aria-pressed="true"]:active { color: var(--t7-wipe-fg, var(--t7-audio-fg)); }
}

.t7-audio .t7-audio-icon {
  display: block;
  width: 38%;
  height: 38%;
  fill: currentColor;
}

.t7-audio .t7-audio-play[aria-pressed="true"] .t7-audio-icon-play,
.t7-audio .t7-audio-play[aria-pressed="false"] .t7-audio-icon-pause {
  display: none;
}

/* ---- title + artist ------------------------------------------------- */

.t7-audio .t7-audio-meta {
  grid-area: meta;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.t7-audio .t7-audio-title,
.t7-audio .t7-audio-artist {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.t7-audio .t7-audio-title {
  font-size: 1.0625rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.015em;
}

.t7-audio .t7-audio-artist {
  color: var(--t7-audio-soft);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.2;
}

.t7-audio .t7-audio-artist:empty {
  display: none;
}

.t7-audio[data-state="loading"] .t7-audio-title {
  color: var(--t7-audio-soft);
}

/* ---- seek bar: square track or bar waveform ------------------------- */

.t7-audio .t7-audio-bar {
  grid-area: bar;
  position: relative;
  height: var(--t7-audio-bar-h);
  min-width: 0;
  cursor: pointer;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.t7-audio .t7-audio-bar:focus {
  outline: none;
}

.t7-audio .t7-audio-bar:focus-visible {
  outline: 3px solid var(--t7-audio-hot);
  outline-offset: 3px;
}

.t7-audio .t7-audio-bar[aria-disabled="true"] {
  cursor: default;
}

.t7-audio .t7-audio-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 2px;
  pointer-events: none;
}

.t7-audio .t7-audio-bars > span {
  flex: 1 1 0;
  min-width: 1px;
  height: calc(var(--t7-audio-h, 0.06) * 100%);
  background: var(--t7-audio-mute);
}

.t7-audio .t7-audio-bars > span.t7-audio-flat {
  height: max(3px, calc(var(--t7-audio-gap) / 3));
}

.t7-audio .t7-audio-bars-played {
  clip-path: inset(0 calc(100% - var(--t7-audio-p, 0%)) 0 0);
}

.t7-audio .t7-audio-bars-played > span {
  background: var(--t7-audio-hot);
}

.t7-audio .t7-audio-thumb {
  position: absolute;
  top: 50%;
  /* travel stays inside the bar: centre runs gap/2 … 100% - gap/2 */
  left: calc(var(--t7-audio-gap) / 2 + (100% - var(--t7-audio-gap)) * var(--t7-audio-r, 0));
  width: var(--t7-audio-gap);
  height: var(--t7-audio-gap);
  background: var(--t7-audio-hot);
  box-shadow: 0 0 0 2px var(--t7-audio-bg);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.t7-audio .t7-audio-bar[aria-disabled="true"] .t7-audio-thumb {
  display: none;
}

/* ---- time ----------------------------------------------------------- */

.t7-audio .t7-audio-time {
  grid-area: time;
  font-size: 0.875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  white-space: nowrap;
}

.t7-audio .t7-audio-sep,
.t7-audio .t7-audio-total {
  color: var(--t7-audio-soft);
}

/* ---- engines + live region: present, never painted ------------------ */

/* A SoundCloud iframe must stay in the render tree (no display:none) or the
   widget may never send READY. 1px, transparent, inert. */
.t7-audio .t7-audio-engine {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 1px;
  height: 1px;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

.t7-audio .t7-audio-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- native fallback: playback blocked (iOS) → SoundCloud's own player
   takes the frame's place; tabs and frame stay. */

.t7-audio[data-state="native"] .t7-audio-frame {
  display: block;
  padding: 0;
}

.t7-audio[data-state="native"] .t7-audio-frame > :not(.t7-audio-native):not(.t7-audio-status) {
  display: none;
}

.t7-audio .t7-audio-native {
  display: block;
  width: 100%;
  height: 166px;
  margin: 0;
  border: 0;
}

/* ---- preferences ---------------------------------------------------- */

/* Only this block's own plate fade. The wipe's motion is t7-wipe's and
   matches the site's other buttons (see README: t7-wipe has no
   reduced-motion arm today). */
@media (prefers-reduced-motion: reduce) {
  .t7-audio button.t7-audio-play,
  .t7-audio button.t7-audio-play:hover,
  .t7-audio button.t7-audio-play:focus,
  .t7-audio button.t7-audio-play:active {
    transition: none;
  }
}

@media (forced-colors: active) {
  .t7-audio .t7-audio-frame { border-color: CanvasText; }
  .t7-audio .t7-audio-bars > span { background: GrayText; }
  .t7-audio .t7-audio-bars-played > span,
  .t7-audio .t7-audio-thumb { background: Highlight; }
  .t7-audio button.t7-audio-play { border: 2px solid ButtonText; }
}

/* ============================ /t7-audio ============================= */

/* ============================================================
   HOSPITALITY HIGHLIGHT CARDS  (2026-09-27, Hospitality page 204)
   Breadcrumb: page 204 loop-grid h7a1d02 (.t7-hl-grid)
               page 206 loop-grid cbhl7a1 (.t7-hl-grid), Consumer Brands,
                 query id consumer_brands_highlights (2026-09-30)
               > loop template 3005 "Hospitality Highlight Card"
               > h7a1a02 .t7-hl-card, h7a1a03 .t7-hl-media

   One card per work with ACF "Hospitality Highlight" ticked (query id
   hospitality_highlights, WPCode 3004). The image half is the featured
   image as a container background; it fades through a gradient into
   the solid Ground panel that holds the text. Editorial stagger: every
   EVEN loop item flips (image right, fade leftward). Phones stack
   image over text, fade downward, no flip.

   Halves (50/50, 100% on phones), heights (480 / 400 / image 280 on
   phones), padding and type are Elementor settings on template 3005.

   nth-OF-TYPE, not nth-child: Elementor prints per-item <style> tags
   as siblings of the items inside .elementor-loop-container, so
   nth-child counts them and the stagger lands on the wrong cards.
   Only the fade, the flip and the first-render bg pin live here.

   The flip overrides Elementor's own --flex-direction variable, so the
   Direction control in the editor still owns the ODD cards. The fade
   ends in the same kit global the panel wears (.t7-solid = Ground), so
   it follows the mode for free.

   background-size / -position repeat the template's settings on
   purpose: FIRST-RENDER PINS (above) - a featured-image dynamic
   background drops them on the first render after a CSS flush.
   ============================================================ */
.t7-hl-card .t7-hl-media {
  position: relative;
  background-size: cover;
  background-position: center center;
}
.t7-hl-card .t7-hl-media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(var(--t7-hl-fade, to right), transparent var(--t7-hl-fade-start, 45%), var(--e-global-color-grnd7a1) 100%);
}
@media (min-width: 768px) {
  .t7-hl-grid .e-loop-item:nth-of-type(even) .t7-hl-card { --flex-direction: row-reverse; }
  .t7-hl-grid .e-loop-item:nth-of-type(even) .t7-hl-media { --t7-hl-fade: to left; }
}
@media (max-width: 767px) {
  .t7-hl-card .t7-hl-media { --t7-hl-fade: to bottom; }
}
/* ======================= /HOSPITALITY HIGHLIGHT CARDS ======================= */

/* ============================================================
   IN-COPY LINKS  (2026-09-29, Paul: "when we have links in copy ...
   they look good" - first seen in the Hospitality highlight blurb,
   loop template 3005 widget h7a1b03, Peter McManus 77's blurb)
   Breadcrumb: no per-element election - applies to any text-editor
   or post-content widget. Links that sit INSIDE A PARAGRAPH of body
   copy wear the accent, a heavier weight, and the site's Ember hover.
   Without this they fell through to Hello Elementor's reset.css
   (a { color:#c36 }) = the default magenta, in dark mode.

   Scope is deliberately narrow: <p> descendants of text-editor /
   post-content widgets only. Footer/nav link lists are <ul><li>
   (and .t7-runin is excluded outright), buttons carry
   .elementor-button, and loop-card whole-links are never inside a
   <p>, so none of those match.

   Colours are kit globals, zero hex: accntsl (mode- AND tone-aware,
   Voltage on dark / Cobalt on light, re-resolved per element) at rest,
   Ember bc74cd9 on hover - the same hover the light-mode link law uses.
   Specificity (0,3,2) on purpose: beats that law's (0,1,2)/(0,2,2).
   ============================================================ */
:is(.elementor-widget-text-editor, .elementor-widget-theme-post-content):not(.t7-runin) p a:not(.elementor-button) {
  color: var(--e-global-color-accntsl);
  font-weight: 700;
  text-decoration: none;
  transition: color .2s ease;
}
:is(.elementor-widget-text-editor, .elementor-widget-theme-post-content):not(.t7-runin) p a:not(.elementor-button):is(:hover, :focus-visible) {
  color: var(--e-global-color-bc74cd9);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
}
/* ============================ /IN-COPY LINKS ============================ */

/* ============================================================
   VIDEO CARDS 16:9 (Paul, 2026-10-08)
   Video card thumbnails show at 16:9 in two places only:
   - /videos/ (page 3283) grid             (card nmv-videos-grid)
   - Music 62 videos carousel 2bd0d65, elected by .t7-video-carousel on
     the loop-carousel widget             (card nmv-videos-carousel)
   Overrides the 1:1 rules there: the image box ("tmpl-104 photo BOX"
   above) and the img (WPCode 3273, #t7-video-card).
   Work, the other carousels and card template 104 stay square.
   ============================================================ */
body.page-id-3283 .e-loop-item.type-video .elementor-element-b85dbac,
.t7-video-carousel .e-loop-item.type-video .elementor-element-b85dbac {
  aspect-ratio: 16 / 9;
}
body.page-id-3283 .e-loop-item.type-video .elementor-element-b85dbac img,
.t7-video-carousel .e-loop-item.type-video .elementor-element-b85dbac img {
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
/* ============================ /VIDEO CARDS 16:9 ============================ */

/* ============================================================
   HOME HERO INDUSTRY ICONS  (.t7-industry-icon)  2026-10-15
   Breadcrumb: page 90 > icon row 1576684 (widgets 88b1ce2 8f414a6
   4f390b0 8e46904 529ff79), each linked to its industry page.

   Hover = the Team email plate's hover (tmpl 569 > 906e631), in its
   KNOCKOUT form (see KNOCKOUT PLATE below): these icons are knockouts
   (svg = eggshell field, marks cut out), so the Ember sweep runs over
   the FIELD through a mask of the icon's own shape. Same colour, same
   .45s cubic-bezier(.65,0,.35,1), same left-to-right reveal, same
   hover:none / :active handling. Resting look is NOT changed (no
   t7-plate class: that would recolour the field to the plate tokens).
   Each widget hands its own svg to the mask via --t7-plate-knockout-src.
   ============================================================ */
.elementor-element-88b1ce2 { --t7-plate-knockout-src: url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/icon-music.svg"); }
.elementor-element-8f414a6 { --t7-plate-knockout-src: url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/icon-entertainment.svg"); }
.elementor-element-4f390b0 { --t7-plate-knockout-src: url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/icon-hospitality.svg"); }
.elementor-element-8e46904 { --t7-plate-knockout-src: url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/icon-sports.svg"); }
.elementor-element-529ff79 { --t7-plate-knockout-src: url("https://n19thand7thstg.wpenginepowered.com/wp-content/uploads/2026/08/icon-consumer-brands.svg"); }
.elementor-widget-icon.t7-industry-icon .elementor-icon { position: relative; overflow: hidden; }
.elementor-widget-icon.t7-industry-icon .elementor-icon svg { position: relative; z-index: 1; }
.elementor-widget-icon.t7-industry-icon .elementor-icon::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: var(--e-global-color-bc74cd9, #F2452E);
  -webkit-mask-image: var(--t7-plate-knockout-src);
          mask-image: var(--t7-plate-knockout-src);
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  clip-path: inset(0 100% 0 0);
  transition: clip-path .45s cubic-bezier(.65,0,.35,1);
}
.elementor-widget-icon.t7-industry-icon .elementor-icon:hover::after,
.elementor-widget-icon.t7-industry-icon .elementor-icon:focus-visible::after { clip-path: inset(0 0 0 0); }
@media (hover: none) {
  .elementor-widget-icon.t7-industry-icon .elementor-icon:hover::after  { clip-path: inset(0 100% 0 0); }
  .elementor-widget-icon.t7-industry-icon .elementor-icon:active::after { clip-path: inset(0 0 0 0); }
}

/* ===== COOKIE NOTICE (.t7-cookie): WPCode snippet "t7: cookie notice". Wording pending Will (2026-10-15). ===== */
.t7-cookie{position:fixed;left:var(--gap-width,20px);right:var(--gap-width,20px);bottom:var(--gap-width,20px);z-index:9990;max-width:760px;display:flex;gap:20px;align-items:flex-end;padding:20px;background:var(--e-global-color-grnd7a1);color:var(--e-global-color-fgur7b2);border:1px solid var(--e-global-color-figrmut);font-family:var(--e-global-typography-2898ebd-font-family),sans-serif;font-size:var(--e-global-typography-2898ebd-font-size);font-weight:var(--e-global-typography-2898ebd-font-weight);line-height:1.4}
.t7-cookie[hidden]{display:none}
.t7-cookie__text{margin:0}
.t7-cookie__text a{color:inherit;text-decoration:underline}
.t7-cookie__ok{flex:none;border:0;border-radius:0;padding:10px 20px;cursor:pointer;background:var(--e-global-color-accntsl);color:var(--e-global-color-onaccnt);font:inherit;font-weight:700}
.t7-cookie__ok:hover,.t7-cookie__ok:focus-visible{background:var(--e-global-color-fgur7b2);color:var(--e-global-color-grnd7a1)}
@media (max-width:600px){.t7-cookie{flex-direction:column;align-items:stretch}}

/* ===== FORM HTML FIELDS (.elementor-field-type-html), e.g. "Your Links" intro on Artist Questionnaire 3517 (2026-10-15) ===== */
.elementor-form .elementor-field-type-html{color:var(--e-global-color-fgur7b2);}
.elementor-form .elementor-field-type-html h3{margin:.75em 0 .25em;color:inherit;font-family:var(--e-global-typography-16936e3-font-family),sans-serif;font-size:var(--e-global-typography-16936e3-font-size);font-weight:var(--e-global-typography-16936e3-font-weight);line-height:var(--e-global-typography-16936e3-line-height);letter-spacing:var(--e-global-typography-16936e3-letter-spacing);}
.elementor-form .elementor-field-type-html p{margin:0 0 .5em;color:inherit;font-family:var(--e-global-typography-b55f19d-font-family),sans-serif;font-size:var(--e-global-typography-b55f19d-font-size);font-weight:var(--e-global-typography-b55f19d-font-weight);line-height:var(--e-global-typography-b55f19d-line-height);letter-spacing:var(--e-global-typography-b55f19d-letter-spacing);}
/* ===== FORM HELP TEXT (.t7-field-help, WPCode "t7: form field help text"; Gidget msg 534, 2026-10-17): a field's hint under its label, muted caption ===== */
.elementor-form .t7-field-help{flex-basis:100%;width:100%;margin:0 0 .4em;color:var(--e-global-color-figrmut);font-family:var(--e-global-typography-2898ebd-font-family),sans-serif;font-size:var(--e-global-typography-2898ebd-font-size);font-weight:var(--e-global-typography-2898ebd-font-weight);line-height:var(--e-global-typography-2898ebd-line-height);letter-spacing:var(--e-global-typography-2898ebd-letter-spacing);}

/* ===== HOME SERVICES STACK (.t7-home-svc): page 90 hero, loop grid of `services` posts, template "Home Service Box". Icon = [t7_svc_icon] (WPCode), sized/colored like the old Icon widget 9ed4221. 2026-10-15 ===== */
.t7-home-svc .t7-svc-icon .elementor-icon-wrapper{line-height:0;}
.t7-home-svc .t7-svc-icon .elementor-icon{display:inline-block;font-size:100px;line-height:1;color:var(--e-global-color-42a1059);}
.t7-home-svc .t7-svc-icon .elementor-icon svg{display:block;position:relative;width:1em;height:1em;fill:var(--e-global-color-42a1059);}

/* ===== TESTIMONIALS PAGE WALL (.t7-testi-wall, page 3523 /testimonials/, 2026-10-15) =====
   Loop grid of template 2069 with Elementor masonry, 2 cols desktop/tablet, 1 on phones.
   It also carries t7-quote-strip so the card matches the Home/Music carousels at every width,
   but NOT t7-carousel-fixed, so each card is as tall as its own quote.
   In a half-width column the testimonial widget's 50px side padding left a ~235px text
   column; drop it here (phones already do this in every strip). */
.t7-testi-wall .e-con.t7-quote > .elementor-widget-testimonial { padding-inline: 0 !important; }

/* ===== SITE MAP LINKS (.t7-sitemap, page 3516 /site-map/, WPCode [t7_sitemap] 3518; Paul 2026-10-16) =====
   Dark mode had no link colour of its own, so these fell back to the Hello theme's pink (#c36).
   Use the site accent pair instead: accntsl = Voltage on dark, Cobalt on light (the light value
   matches the global html.theme-light a rule). Hover = Ember in both modes, as that rule says. */
.t7-sitemap a { color: var(--e-global-color-accntsl); }
.t7-sitemap a:hover,
.t7-sitemap a:focus-visible { color: var(--e-global-color-bc74cd9, #F2452E); }

/* ===== CARD GLOSS UNDER THE HEADER (Paul 2026-10-16, batch #2) =====
   The gloss ::after is z-index 99, same as the fixed header, and .t7-card
   made no stacking context, so later-in-DOM cards painted their gloss over
   the header/nav while scrolling (/work/, /showcase/, /videos/, /about/).
   isolation traps the 99 inside the card: gloss stays on top of the card,
   the card stays under the header. */
.t7-card { isolation: isolate; }


/* ===== TESTIMONIALS WALL CARD TONE (.t7-testi-wall, page 3523; Paul 2026-10-16) =====
   Card template 2069 is .t7-invert so it pops off the Home/Music bands. On the wall Paul wants
   the cards to follow the site mode instead: dark cards in dark mode, light cards in light mode.
   Pin the card tone back to the page mode here (wall only; Home/Music keep the invert).
   Keep the discrete tokens in step with section C of the tone tables. */
.t7-testi-wall .e-con.t7-quote.t7-invert {
  --is-dark: 1;
  --t7-cta-icon: var(--t7-filter-eggshell);
  --t7-card-light: var(--t7-card-light-on-dark);
}
html.theme-light .t7-testi-wall .e-con.t7-quote.t7-invert {
  --is-dark: 0;
  --t7-cta-icon: brightness(0);
  --t7-card-light: var(--t7-card-light-on-light);
}

/* ===== TESTIMONIALS WALL MASONRY (.t7-testi-wall, WPCode "t7: testimonials wall masonry"; Paul 2026-10-16) =====
   Shortest-column masonry: the snippet measures each card and sets grid-row-end: span <height+gap>
   on 1px rows, then adds .t7-mason-on. Grid auto-placement puts each card in the shortest column.
   Without JS (or on one column) it is the plain grid. The loop grid's own Masonry setting is OFF. */
.t7-testi-wall .elementor-loop-container { align-items: start; } /* cards keep their own height, so the JS measures them true */
.t7-testi-wall .elementor-loop-container.t7-mason-on {
  grid-auto-rows: 1px;
  row-gap: 0 !important;
}

/* ===== CONSENT BANNER (Complianz, banner 1; Gidget msg 338, 2026-10-16) =====
   Replaces the old .t7-cookie notice (WPCode 3519, now draft). Same look: Ground panel, Figure text,
   1px Figure Muted rule, square corners, body type. Everything rides the kit tokens, so it flips with
   html.theme-light by itself. Accept = accent pair (Voltage on dark, Cobalt on light); Deny is the
   same size and weight in Figure, so refusing is as easy as accepting; Preferences is outlined.
   Complianz reads its colours from --cmplz_* (on :root in uploads/complianz/css); we set them on the
   banner itself, so the plugin's banner settings can stay at defaults. */
.cmplz-cookiebanner,
.cmplz-manage-consent,
.cmplz-blocked-content-notice {
  --cmplz_banner_background_color: var(--e-global-color-grnd7a1);
  --cmplz_banner_border_color: var(--e-global-color-figrmut);
  --cmplz_banner_border_width: 1px;
  --cmplz_banner_border_radius: 0;
  --cmplz_text_color: var(--e-global-color-fgur7b2);
  --cmplz_hyperlink_color: var(--e-global-color-fgur7b2);
  --cmplz_button_accept_background_color: var(--e-global-color-accntsl);
  --cmplz_button_accept_border_color: var(--e-global-color-accntsl);
  --cmplz_button_accept_text_color: var(--e-global-color-onaccnt);
  --cmplz_button_deny_background_color: var(--e-global-color-fgur7b2);
  --cmplz_button_deny_border_color: var(--e-global-color-fgur7b2);
  --cmplz_button_deny_text_color: var(--e-global-color-grnd7a1);
  --cmplz_button_settings_background_color: transparent;
  --cmplz_button_settings_border_color: var(--e-global-color-fgur7b2);
  --cmplz_button_settings_text_color: var(--e-global-color-fgur7b2);
  --cmplz_button_border_radius: 0;
  --cmplz_slider_active_color: var(--e-global-color-accntsl);
  --cmplz_slider_inactive_color: var(--e-global-color-figrmut);
  --cmplz_slider_bullet_color: var(--e-global-color-grnd7a1);
  --cmplz_category_header_always_active_color: var(--e-global-color-fgur7b2);
  --cmplz_text_font_size: 15px;
  --cmplz_link_font_size: 14px;
  --cmplz_title_font_size: 16px;
  --cmplz_button_font_size: 15px;
  --cmplz_category_body_font_size: 14px;
  --cmplz_category_header_title_font_size: 15px;
  --cmplz_category_header_active_font_size: 14px;
  font-family: var(--e-global-typography-2898ebd-font-family), sans-serif;
}
.cmplz-cookiebanner { box-shadow: none !important; }
.cmplz-cookiebanner .cmplz-title { font-weight: 700; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn,
.cmplz-manage-consent,
.cmplz-blocked-content-notice {
  font-family: var(--e-global-typography-2898ebd-font-family), sans-serif;
  font-weight: 700;
  letter-spacing: 0;
}
/* the global light-mode link law (html.theme-light a = Cobalt) would win on specificity; keep banner links in Figure */
.cmplz-cookiebanner .cmplz-links .cmplz-link,
html.theme-light .cmplz-cookiebanner .cmplz-links .cmplz-link { color: var(--e-global-color-fgur7b2); text-decoration: underline; }
.cmplz-cookiebanner .cmplz-close svg path { fill: var(--e-global-color-fgur7b2); }
/* hover/focus: same swap the old notice used (Figure fill, Ground text); Ember on links */
.cmplz-cookiebanner .cmplz-buttons .cmplz-btn:hover,
.cmplz-cookiebanner .cmplz-buttons .cmplz-btn:focus-visible { background-color: var(--e-global-color-fgur7b2); border-color: var(--e-global-color-fgur7b2); color: var(--e-global-color-grnd7a1); }
.cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:hover,
.cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:focus-visible { background-color: transparent; color: var(--e-global-color-fgur7b2); }
.cmplz-cookiebanner .cmplz-links .cmplz-link:hover,
.cmplz-cookiebanner .cmplz-links .cmplz-link:focus-visible { color: var(--e-global-color-bc74cd9, #F2452E); }
.cmplz-cookiebanner :focus-visible,
.cmplz-blocked-content-notice:focus-visible { outline: 2px solid var(--e-global-color-accntsl); outline-offset: 2px; }

/* blocked-video placeholder (YouTube until consent): the video's own still, dimmed, with one clear button */
.cmplz-blocked-content-container,
.cmplz-wp-video { background-color: var(--e-global-color-grnd7a1); }
.cmplz-blocked-content-container .cmplz-blocked-content-notice,
.cmplz-wp-video .cmplz-blocked-content-notice {
  max-width: min(420px, calc(100% - 40px));
  padding: 12px 20px;
  border: 0;
  border-radius: 0;
  background: var(--e-global-color-accntsl);
  color: var(--e-global-color-onaccnt);
  font-family: var(--e-global-typography-2898ebd-font-family), sans-serif; /* the kit button rule would hand it Courier */
  font-weight: 700;
  font-size: 15px;
  line-height: 1.35;
  text-align: center;
  text-transform: none;
  cursor: pointer;
}
.cmplz-blocked-content-container .cmplz-blocked-content-notice:hover,
.cmplz-blocked-content-container .cmplz-blocked-content-notice:focus-visible { background: var(--e-global-color-fgur7b2); color: var(--e-global-color-grnd7a1); }

/* ===== A11Y 10-16 (19th-builder, Gidget msg 378) =====
   Fixed header is 126px (desktop), 109px (tablet), 95px (mobile): keep keyboard focus and
   skip/anchor jumps clear of it, and clear of the Complianz "Manage consent" tab at the bottom. */
html { scroll-padding-top: 140px; scroll-padding-bottom: 64px; }
@media (max-width: 1024px) { html { scroll-padding-top: 122px; } }
@media (max-width: 767px) { html { scroll-padding-top: 108px; } }
/* Skip-link target from snippet "t7: a11y fixes" (empty, never visible) */
#t7-main { outline: none; }
/* Visually hidden text (hidden H1s on pages without a visible title) */
.t7-sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* Artist Questionnaire form intro: "Your Links" is now an h2 (outline fix), keep the h3 look */
.elementor-form .elementor-field-type-html h2.t7-h3{margin:.75em 0 .25em;color:inherit;font-family:var(--e-global-typography-16936e3-font-family),sans-serif;font-size:var(--e-global-typography-16936e3-font-size);font-weight:var(--e-global-typography-16936e3-font-weight);line-height:var(--e-global-typography-16936e3-line-height);letter-spacing:var(--e-global-typography-16936e3-letter-spacing);}

/* Case-study H1 (template 195): "Case Study:" kicker on its own line via CSS, was a <br> in the title's before-text */
.t7-cs-kicker{display:block;}

/* ============================================================
   MOTION 10-17 — background-video pause/play (a11y call A)
   The button is built by WPCode "t7: motion controls" and sits in
   the bottom-right corner of any container with an Elementor
   background video. It is the t7-audio play button (Bourdain 82,
   Hems) sized down: THE BAR FACE at rest (frame-material plate,
   ground glyph), Ember t7-wipe on hover, Accent plate while
   pressed (= paused), same 16px glyphs at 38%. Square, no radius.
   Kit trap: .elementor-kit-89 button paints at (0,1,1) and
   (0,2,1) on :hover/:focus, so these arms sit at (0,2,3)+.
   ============================================================ */
html body button.t7-motion-btn.t7-wipe,
html body button.t7-motion-btn.t7-wipe:hover,
html body button.t7-motion-btn.t7-wipe:focus,
html body button.t7-motion-btn.t7-wipe:active {
  position: absolute;
  right: var(--gap-width, 13px);
  bottom: var(--gap-width, 13px);
  left: auto;
  top: auto;
  z-index: 3;
  appearance: none;
  overflow: hidden;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--t7-bar-bg, var(--e-global-color-fgur7b2, #F5F3E8));
  color: var(--t7-ground, var(--e-global-color-grnd7a1, #22211C));
  box-shadow: none;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s ease;
}
html body button.t7-motion-btn.t7-wipe:hover { color: var(--t7-wipe-fg, var(--e-global-color-fgur7b2, #F5F3E8)); }
html body button.t7-motion-btn.t7-wipe[aria-pressed="true"],
html body button.t7-motion-btn.t7-wipe[aria-pressed="true"]:focus,
html body button.t7-motion-btn.t7-wipe[aria-pressed="true"]:active {
  background: var(--e-global-color-accntsl, #FFDE00);
  color: var(--e-global-color-onaccnt, var(--e-global-color-grnd7a1, #22211C));
}
html body button.t7-motion-btn.t7-wipe[aria-pressed="true"]:hover {
  background: var(--e-global-color-accntsl, #FFDE00);
  color: var(--t7-wipe-fg, var(--e-global-color-fgur7b2, #F5F3E8));
}
html body button.t7-motion-btn.t7-wipe:focus-visible {
  outline: 3px solid var(--e-global-color-accntsl, #FFDE00);
  outline-offset: 3px;
}
html body button.t7-motion-btn[hidden] { display: none; }
html body button.t7-motion-btn svg {
  display: block;
  width: 38%;
  height: 38%;
  fill: currentColor;
}
@media (hover: none) {
  html body button.t7-motion-btn.t7-wipe:hover { color: var(--t7-ground, var(--e-global-color-grnd7a1, #22211C)); }
  html body button.t7-motion-btn.t7-wipe[aria-pressed="true"]:hover { color: var(--e-global-color-onaccnt, var(--e-global-color-grnd7a1, #22211C)); }
  html body button.t7-motion-btn.t7-wipe:active { color: var(--t7-wipe-fg, var(--e-global-color-fgur7b2, #F5F3E8)); }
}
@media (max-width: 767px) {
  html body button.t7-motion-btn.t7-wipe,
  html body button.t7-motion-btn.t7-wipe:hover,
  html body button.t7-motion-btn.t7-wipe:focus,
  html body button.t7-motion-btn.t7-wipe:active { width: 2rem; height: 2rem; }
}

/* ============================================================
   HERE — OUTLINED inactive rail items.          (2026-10-17)
   ------------------------------------------------------------
   a11y call B (Paul): the dim reading (.22 opacity) failed 3:1 on
   both grounds. Inactive items are now OUTLINED in the full text
   colour: transparent fill, stroke in currentColor, so they pass on
   light and dark exactly like the text itself. The active item is
   unchanged (solid, no stroke). Same 260ms easing as the rail.
   Desktop only by nature: below 1025px the rail is hidden and the
   labels ride above their groups at full strength.
   The accent reading (t7-here--voltage) is left as it was.
   Fallback without text-stroke: solid text at full opacity — the
   reading position still shows in the card frames (snippet 1293).
   ============================================================ */
.t7-here:not(.t7-here--voltage) > * { opacity: 1; }
@supports (-webkit-text-stroke: 1px) {
  .t7-here:not(.t7-here--voltage) > * .elementor-heading-title {
    -webkit-text-fill-color: currentColor;
    -webkit-text-stroke: 0 currentColor;
    transition:
      -webkit-text-fill-color 260ms cubic-bezier(.4, 0, .2, 1),
      -webkit-text-stroke-width 260ms cubic-bezier(.4, 0, .2, 1);
  }
  .t7-here:not(.t7-here--voltage) > :not(.is-here) .elementor-heading-title {
    -webkit-text-fill-color: transparent;
    -webkit-text-stroke: var(--t7-here-stroke, 2px) currentColor;
  }
}
@media (prefers-reduced-motion: reduce) {
  .t7-here:not(.t7-here--voltage) > * .elementor-heading-title { transition-duration: 1ms; }
}
/* ===== FOOTER NAV ROWS 10-17 (19th-builder, Gidget msgs 430/434) =====
   Footer 100 widget a1f0071 now holds three bars (header menu minus
   Work / services / industries), one <ul> each. Consecutive bars sit
   at the bar's own wrap leading so the three read as one nav group,
   with the legal row keeping its larger container gap below. */
.elementor-element-a1f0071 ul + ul { margin-top: var(--t7-runin-leading, .42em); }
/* desktop: the column is flush right (legal row, socials, copyright),
   so the nav rows end on the same edge; phones keep the left start. */
@media (min-width: 768px) { .elementor-element-a1f0071 ul { justify-content: flex-end; } }


/* ===== CONSENT BANNER LAYOUT 10-17 (19th-builder, Gidget msgs 420/428/432) =====
   Layout + frame only; consent logic, categories and strings untouched (Complianz settings).
   - Frame: the card frame, var(--gap-width) solid --t7-frost-frame (same as .t7-frame).
     On phones the banner is a bottom bar, so it carries only the top edge (the .t7-rule idiom).
   - Close: 32x32 hit area (WCAG 2.5.8 needs 24), 16px glyph, name "Close dialog" from Complianz.
   - Phones (<=768, Complianz's own breakpoint): slim bar. Title shown (Complianz hides it <=425),
     smaller body type, Accept / Deny / Preferences on one row, Accept and Deny the same size.
   - html.t7-cmplz-early: set in <head> by WPCode "t7: consent banner early paint" when no
     banner-status=dismissed cookie exists. It paints the server-rendered banner as soon as it is
     parsed, instead of waiting ~0.5s+ for complianz.js; that wait made the banner blink out on
     every page change, in step with the preloader. Removed on cmplz_cookie_warning_loaded,
     from then on Complianz's own classes rule. */
/* Every arm starts at #cmplz-cookiebanner-container: banner-1-optin.css loads after this file
   and its own phone rules (title hidden <=425, stacked buttons) sit at the same class weight. */
#cmplz-cookiebanner-container .cmplz-cookiebanner {
  --cmplz_banner_border_width: var(--gap-width, 13px);
  --cmplz_banner_border_color: var(--t7-frost-frame);
}
html.t7-cmplz-early #cmplz-cookiebanner-container .cmplz-cookiebanner.cmplz-hidden:not(.cmplz-dismissed) { display: grid !important; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close {
  width: 32px; height: 32px; margin: -6px -6px -6px 0;
  display: flex; align-items: center; justify-content: center; line-height: 1; font-size: 16px;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close svg { width: 16px; height: 16px; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close:hover { font-size: 16px; line-height: 1; }
@media (max-width: 768px) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner {
    border-width: var(--gap-width, 13px) 0 0 0;
    padding: 10px 14px 12px;
    grid-gap: 8px; grid-row-gap: 8px;
    max-height: 85vh;
  }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header { grid-template-columns: 1fr auto; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-logo { display: none; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-title { display: block; grid-column-start: 1; justify-self: start; font-size: 15px; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close { grid-column-start: 2; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-divider { display: none; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-body { max-height: 45vh; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-message { font-size: 13px; line-height: 1.4; margin: 0; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons { flex-direction: row; gap: 8px; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn {
    flex: 1 1 0; min-width: 0; min-height: 44px; padding: 6px 4px;
    font-size: 13px; line-height: 16px; white-space: normal;
  }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-links { font-size: 13px; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-documents { margin: 0; }
}

/* ===== CONSENT BANNER COMPONENTS + FADE 10-17 (19th-builder, Gidget msg 446) =====
   Built from site parts; palette, Accept/Deny hierarchy and the D layout above are unchanged.
   - Fade: ~300ms opacity-in whenever Complianz (or the early paint) shows the banner; none
     under prefers-reduced-motion. With saved consent the banner never shows, so never fades
     (WPCode "t7: consent banner early paint" v3 skips the early paint on ANY cmplz consent cookie).
     If the early paint ran it already faded the banner in, and it leaves html.t7-cmplz-ep set, so the
     hand-off to Complianz .cmplz-show does not fade it a second time.
   - Secondary actions (View / Save preferences) + the preferences panel = the header nav
     dropdown: nav type (bold uppercase), Ground panel, 3px Figure frame = the "framed" pointer
     around the active nav item (post-99: .e--pointer-framed, 3px). An open category wears the frame.
   - Close = the carousel arrow tab (CHROME, .t7-chrome .elementor-swiper-button): a Ground
     square with an accent glyph, Ember on hover, hung with its centre on the frame edge.
   - Category switches = the header theme switch (button.t7-sw): 60x34, 5px Figure frame, two
     fixed cells and a mark that moves to the lit one. The mark is a square bar, not the
     switch half-moon: Paul 10-17, "we really use no circles". Off = left cell Ink, Ash mark;
     on = right cell lit in the accent pair (Ink + Voltage on dark, Cobalt + Marshmallow on light). */
@keyframes t7-cmplz-in { from { opacity: 0; } to { opacity: 1; } }
html:not(.t7-cmplz-ep) #cmplz-cookiebanner-container .cmplz-cookiebanner.cmplz-show,
html.t7-cmplz-early #cmplz-cookiebanner-container .cmplz-cookiebanner.cmplz-hidden:not(.cmplz-dismissed) {
  animation: t7-cmplz-in .3s ease-out both;
}
@media (prefers-reduced-motion: reduce) {
  html:not(.t7-cmplz-ep) #cmplz-cookiebanner-container .cmplz-cookiebanner.cmplz-show,
  html.t7-cmplz-early #cmplz-cookiebanner-container .cmplz-cookiebanner.cmplz-hidden:not(.cmplz-dismissed) { animation: none; }
}

/* nav type for the secondary buttons and the category rows; Accept / Deny keep their own type */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-view-preferences,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-save-preferences,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-category-title,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-always-active {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif;
  font-weight: 700; text-transform: uppercase; letter-spacing: -0.02em;
}
/* secondary = framed nav item: transparent, 3px Figure frame */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-view-preferences,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-save-preferences {
  border-width: 3px; border-color: var(--e-global-color-fgur7b2); background-color: transparent; color: var(--e-global-color-fgur7b2);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-view-preferences:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-view-preferences:focus-visible,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-save-preferences:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-save-preferences:focus-visible {
  background-color: var(--e-global-color-fgur7b2); color: var(--e-global-color-grnd7a1);
}

/* preferences panel = the dropdown: Ground rows, nav type, frame on the open one */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category {
  background-color: var(--e-global-color-grnd7a1);
  border: 3px solid transparent;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category:not(:last-child) { margin-bottom: 4px; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category:has(.cmplz-category-toggle[aria-expanded="true"]),
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category:has(.cmplz-category-header:hover) {
  border-color: var(--e-global-color-fgur7b2);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-header {
  min-height: 44px; padding: 4px 10px; box-sizing: border-box;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-header .cmplz-category-title {
  font-size: 14px; color: var(--e-global-color-fgur7b2);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-header .cmplz-always-active {
  font-size: 12px; color: var(--e-global-color-figrmut);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-toggle { width: 32px; height: 32px; color: var(--e-global-color-fgur7b2); }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-toggle svg { fill: currentColor; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-category-header { grid-template-columns: 1fr auto 32px; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-description { padding: 0 10px 10px; }

/* switches = the theme switch */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox { width: 60px; height: 34px; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox {
  top: 0; left: 0; width: 60px; height: 34px; margin: 0;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox .cmplz-label {
  display: block; width: 60px; height: 34px; padding: 0;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox .cmplz-label::before {
  top: 0; left: 0; width: 60px; height: 34px; padding: 0;
  border: 5px solid var(--e-global-color-fgur7b2); border-radius: 0;
  background: linear-gradient(to right, var(--e-global-color-c8af26f) 0 50%, var(--e-global-color-3bc915a) 50% 100%);
  transition: background .25s ease-in, border-color .25s ease-in;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox .cmplz-label::after {
  top: 9px; left: 13.5px; width: 8px; height: 16px;
  border-radius: 0;
  background: var(--e-global-color-3bc915a);
  transition: left .25s ease-in, background-color .25s ease-in;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:checked + .cmplz-label::before {
  padding: 0; background: linear-gradient(to right, var(--e-global-color-3bc915a) 0 50%, var(--e-global-color-c8af26f) 50% 100%);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:checked + .cmplz-label::after {
  left: 38.5px; background: var(--e-global-color-230debb);
}
html.theme-light #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:checked + .cmplz-label::before {
  background: linear-gradient(to right, var(--e-global-color-3bc915a) 0 50%, var(--e-global-color-d3cd34a) 50% 100%);
}
html.theme-light #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:checked + .cmplz-label::after {
  background: var(--e-global-color-42a1059);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:hover + .cmplz-label::before { border-color: var(--e-global-color-bc74cd9); }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:focus + .cmplz-label::before { box-shadow: none; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category .cmplz-banner-checkbox input.cmplz-consent-checkbox:focus-visible + .cmplz-label::before {
  outline: 2px solid var(--e-global-color-accntsl); outline-offset: 3px;
}

/* close = the carousel arrow tab, hung on the frame. The banner stops clipping so the tab can
   leave it (the body still scrolls); Complianz own short-screen layout (<=400px tall) keeps its clip. */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close {
  position: relative; color: var(--e-global-color-accntsl); background: var(--e-global-color-grnd7a1);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close svg { display: none; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close::before,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 20px; height: 2.5px; margin: -1.25px 0 0 -10px;
  background: currentColor; transform: rotate(45deg); transition: background-color .2s;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close::after { transform: rotate(-45deg); }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close:focus-visible { color: var(--e-global-color-bc74cd9); }
@media (min-height: 401px) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner { overflow: visible; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close {
    position: absolute; z-index: 2; margin: 0; width: 40px; height: 40px; right: 16px;
    top: calc(var(--gap-width, 13px) / -2 - 20px);
  }
}
@media (max-width: 768px) and (min-height: 401px) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close {
    width: 44px; height: 44px; right: 14px; top: calc(var(--gap-width, 13px) / -2 - 22px);
  }
}
/* ===== /CONSENT BANNER COMPONENTS + FADE 10-17 ===== */

/* ===== CONSENT BATCH 10-17 (19th-builder, Gidget msgs 478/480/482/484; Paul's refs 10-17) =====
   - Manage consent tab: was the kit button rule's Courier with a 1px rule. Now the banner's secondary
     button: square, nav type (bold uppercase, banner button size), 3px Figure frame on Ground,
     Figure fill on hover. Complianz's own slide-up (fixed, bottom -35px -> 0 on hover) is untouched;
     html body = to outrank Complianz's own #cmplz-manage-consent rules, which load after this file.
   - Panel title "Manage Consent": an h2 (WPCode "t7: consent banner early paint" v7 swaps the div),
     weight 900, page-h2 size, left, in line with the body text. Styled by class so the swap can't jump.
   - Functional: locked (v7 sets it checked + disabled); "Always active" sits under the word FUNCTIONAL.
   - Thin Figure Muted rules between the category rows.
   - Accept / Deny: nav caps like Save preferences, and the site's Ember wipe (t7-wipe) on hover.
   - Cookie Policy / Privacy Policy links under the buttons: bold, accent (Voltage; Cobalt on light).
   - Policy pages 3 + 3558: a list followed by more copy got no gap (Hello's reset zeroes ul/ol margins);
     lists now take the paragraph gap (.9rem). */

/* tab */
html body #cmplz-manage-consent .cmplz-manage-consent {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif;
  font-weight: 700; font-size: 15px; line-height: 1.2; letter-spacing: -0.02em; text-transform: uppercase;
  border: 3px solid var(--e-global-color-fgur7b2); border-radius: 0;
  background-color: var(--e-global-color-grnd7a1); color: var(--e-global-color-fgur7b2);
}
html body #cmplz-manage-consent .cmplz-manage-consent:hover,
html body #cmplz-manage-consent .cmplz-manage-consent:focus-visible {
  background-color: var(--e-global-color-fgur7b2); color: var(--e-global-color-grnd7a1);
}
html body #cmplz-manage-consent .cmplz-manage-consent:focus-visible { outline: 2px solid var(--e-global-color-accntsl); outline-offset: 2px; }

/* title */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-logo { display: none; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-title {
  grid-column: 1 / 3; justify-self: start; text-align: left; margin: 0; padding: 0;
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif;
  font-weight: 900; font-size: 35px; line-height: 1.05; letter-spacing: -0.02em; text-transform: none;
  color: var(--e-global-color-fgur7b2);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-close { grid-column: 3; }
@media (max-width: 768px) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header { grid-template-columns: 1fr auto auto; }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-header .cmplz-title { font-size: 22px; }
}

/* Functional: locked, label under the name. The always-active wrapper drops out of the box tree, so the
   switch is placed in the switch column and its loose "Always active" text auto-places into row 2, col 1. */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-always-active {
  display: contents;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-category-header {
  grid-template-rows: auto auto; row-gap: 0;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-category-title { grid-column: 1; grid-row: 1; align-self: end; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-banner-checkbox { grid-column: 2; grid-row: 1 / 3; align-self: center; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-category-toggle { grid-column: 3; grid-row: 1 / 3; align-self: center; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-banner-checkbox input.cmplz-consent-checkbox:disabled,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-banner-checkbox input.cmplz-consent-checkbox:disabled + .cmplz-label { cursor: not-allowed; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category.cmplz-functional .cmplz-banner-checkbox input.cmplz-consent-checkbox:disabled:hover + .cmplz-label::before { border-color: var(--e-global-color-fgur7b2); }

/* thin rules between the rows (in the 5px gap, clear of the 3px open/hover frame) */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category:not(:last-child) { margin-bottom: 7px; position: relative; }
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-categories .cmplz-category:not(:last-child)::after {
  content: ""; position: absolute; left: -3px; right: -3px; bottom: -8px; height: 1px;
  background: var(--e-global-color-figrmut); pointer-events: none;
}

/* Accept / Deny: nav caps + the Ember wipe. The button is its own stacking context, so the flood
   (z-index -1) paints over the button's face and under its text. */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny {
  font-family: var(--e-global-typography-5ddc419-font-family, "neue-haas-grotesk-display"), "Helvetica Neue", sans-serif;
  font-weight: 700; text-transform: uppercase; letter-spacing: -0.02em;
  position: relative; overflow: hidden; isolation: isolate;
  transition: color .25s ease .08s;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept::before,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--e-global-color-bc74cd9, #F2452E);
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s cubic-bezier(.65,0,.35,1);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:focus-visible {
  background-color: var(--cmplz_button_accept_background_color); border-color: var(--cmplz_button_accept_border_color); color: var(--t7-btn-fg-flip);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:focus-visible {
  background-color: var(--cmplz_button_deny_background_color); border-color: var(--cmplz_button_deny_border_color); color: var(--t7-btn-fg-flip);
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:hover::before,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:focus-visible::before,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:hover::before,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:focus-visible::before { transform: scaleX(1); }
/* touch: like t7-wipe, no sticky flood; it shows only while pressed */
@media (hover: none) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:hover::before,
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:hover::before { transform: scaleX(0); }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:hover { color: var(--cmplz_button_accept_text_color); }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:hover { color: var(--cmplz_button_deny_text_color); }
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept:active::before,
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny:active::before { transform: scaleX(1); }
}
@media (prefers-reduced-motion: reduce) {
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-accept::before,
  #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-buttons .cmplz-btn.cmplz-deny::before { transition: none; }
}

/* policy links: bold accent */
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-links .cmplz-link,
html.theme-light #cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-links .cmplz-link {
  color: var(--e-global-color-accntsl); font-weight: 700;
}
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-links .cmplz-link:hover,
#cmplz-cookiebanner-container .cmplz-cookiebanner .cmplz-links .cmplz-link:focus-visible { color: var(--e-global-color-bc74cd9, #F2452E); }

/* policy pages: list -> paragraph gap */
.elementor-3 .elementor-widget-text-editor :is(ul, ol):not(:last-child),
.elementor-3558 .elementor-widget-text-editor :is(ul, ol):not(:last-child) { margin-block-end: .9rem; }
/* policy pages: links inside lists = the IN-COPY LINKS style (that law only reaches p a). Gidget msg 526 */
:is(.elementor-3, .elementor-3558) .elementor-widget-text-editor li a:not(.elementor-button) { color: var(--e-global-color-accntsl); font-weight: 700; text-decoration: none; transition: color .2s ease; }
:is(.elementor-3, .elementor-3558) .elementor-widget-text-editor li a:not(.elementor-button):is(:hover, :focus-visible) { color: var(--e-global-color-bc74cd9); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .18em; }
/* ===== /CONSENT BATCH 10-17 ===== */

/* ============================================================
   HOME HERO INDUSTRY LABELS 10-17  (.t7-ind-label)
   Gidget msg 438 for Paul. Each industry icon (.t7-industry-icon, above)
   shows its name on hover / keyboard focus, just above the icon and
   left-aligned with it. The label markup comes from WPCode snippet
   "t7: Home hero industry hover labels" and reuses the services box
   label classes (tmpl 3521 > 9056689 .elementor-button), so its look
   is Elementor's; this block only places and toggles it. Absolute, so
   it never moves the layout. Touch (hover:none): no label on tap.
   ============================================================ */
.t7-industry-icon .elementor-icon-wrapper { position: relative; }
.t7-industry-icon .t7-ind-label {
  position: absolute; left: 0; bottom: calc(100% + 8px); z-index: 5;
  white-space: nowrap; pointer-events: none; visibility: hidden;
}
.t7-industry-icon .t7-ind-label .elementor-element { display: block; margin: 0; }
.t7-industry-icon .t7-ind-label .elementor-button { transition: none; }
.t7-industry-icon .elementor-icon:hover + .t7-ind-label,
.t7-industry-icon .elementor-icon:focus-visible + .t7-ind-label { visibility: visible; }
@media (hover: none) {
  .t7-industry-icon .elementor-icon:hover + .t7-ind-label { visibility: hidden; }
  .t7-industry-icon .elementor-icon:focus-visible + .t7-ind-label { visibility: visible; }
}

/* ============================================================
   HOME ON PHONES 10-17  (Gidget msg 524 for Paul)
   Services label (tmpl 3521 button): never breaks inside a word.
   Phones (<768): the industry icon row sits 24px lower so a popped-up
   label clears the fixed header; the last icon's label lines up with the
   icon's RIGHT edge so "Consumer Brands" stays on screen.
   (Hero 80svh, the services text column and the intro alignment are
   Elementor settings on page 90 / template 3521.)
   ============================================================ */
.t7-home-svc .elementor-button { word-break: normal; overflow-wrap: normal; hyphens: none; }
@media (max-width: 767px) {
  .elementor-90 .e-con:has(> .t7-industry-icon) { margin-top: 24px; }
  .t7-industry-icon:last-child .t7-ind-label { left: auto; right: 0; }
}

/* ===== DANKLEY BRICOLAGE SPECIMEN AS OUTLINES (post 80, Gidget msg 552, 2026-10-18) =====
   The Bricolage Grotesque "Aa" pairs and the demo paragraph on the two type cards are SVG
   outlines (fill = the text colour), so no web font is loaded. The paragraph is one svg per word
   with real spaces between, so it wraps like text; word-spacing brings Arial's space (0.278em)
   to Bricolage's (0.230em); vertical-align:top + margin-top puts each word where the text sat in its 1.55 line, whatever the fallback font. */
.dnk-aa-brico svg.t7-ol-aa{display:block;overflow:visible;fill:currentColor;}
.dnk-demo-w p.t7-ol-p{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;word-spacing:-0.048em;}
.t7-ol-p svg.t7-ol-w{display:inline-block;vertical-align:top;margin-top:0.17em;overflow:visible;fill:currentColor;}
.t7-ol-p svg.t7-ol-defs{position:absolute;width:0;height:0;overflow:hidden;}

/* ===== FORM MESSAGES (Elementor .elementor-message; Gidget msg 628 PROPOSAL, 2026-10-18) =====
   Form-level success/error = a square Figure-framed slate in body type, a solid bar inside the left edge says which:
   Accent = sent, Ember = failed. Field-level "This field is required." = one caption-type line under
   the field, Figure text after an Ember bar, field frame turns Ember. No icons, no green/red pills. */
html body .elementor-form .elementor-message:not(.elementor-help-inline){margin:20px calc(var(--gap-width,13px) / 2) 0;padding:14px 18px;border:3px solid var(--e-global-color-fgur7b2);border-radius:0;padding-left:32px;background:var(--e-global-color-grnd7a1);color:var(--e-global-color-fgur7b2);font-family:var(--e-global-typography-b55f19d-font-family),sans-serif;font-size:var(--e-global-typography-b55f19d-font-size);font-weight:var(--e-global-typography-b55f19d-font-weight);line-height:1.35;letter-spacing:var(--e-global-typography-b55f19d-letter-spacing);}
.elementor-form .elementor-message::before{content:none;display:none;}
html body .elementor-form .elementor-message-success:not(.elementor-help-inline){box-shadow:inset 11px 0 0 var(--e-global-color-accntsl);}
html body .elementor-form .elementor-message-danger:not(.elementor-help-inline){box-shadow:inset 11px 0 0 var(--e-global-color-bc74cd9);}
html body .elementor-form .elementor-field-group .elementor-message.elementor-help-inline{display:block;width:100%;margin:.45em 0 0;padding:0 0 0 .7em;border-left:4px solid var(--e-global-color-bc74cd9);color:var(--e-global-color-fgur7b2);font-family:var(--e-global-typography-2898ebd-font-family),sans-serif;font-size:var(--e-global-typography-2898ebd-font-size);font-weight:var(--e-global-typography-2898ebd-font-weight);line-height:1.3;margin-bottom:10px;}
.elementor-form .elementor-field-group.elementor-error :is(input,textarea,select).elementor-field{border-color:var(--e-global-color-bc74cd9) !important;}
/* text colour: each form widget emits its own message colours at (0,5,0) (Accent / Ember); the slate keeps them as the bar and sets the text to Figure */
body .elementor-widget-form .elementor-form .elementor-message.elementor-message-success:not(.elementor-help-inline),
body .elementor-widget-form .elementor-form .elementor-message.elementor-message-danger:not(.elementor-help-inline),
body .elementor-widget-form .elementor-form .elementor-field-group .elementor-message.elementor-help-inline{color:var(--e-global-color-fgur7b2);}

/* ===== SERVICES + INDUSTRIES LANDING ROWS: PATTERN TEXT HALF (Gidget msg 724/726/760, 2026-10-18) =====
   Loop template 3033 (/services/ 1217, /industries/ 1219): the text half no longer paints a solid
   Ground; it is clear, so the site ground pattern (#t7-drafted-ground, sheet 1525, light/dark lines
   follow the mode) shows through, same asset and density as the other clear sections. The photo
   fade is now a mask (photo -> transparent -> pattern) instead of the colour overlay ending in solid
   Ground. Direction still comes from --t7-hl-fade (flip on even rows, downward on phones).
   Hospitality highlight cards (3005, .t7-hl-card without .t7-ld-card) are unchanged. Remove to revert. */
.t7-ld-card.t7-solid { background-color: transparent !important; }  /* beats the template's own per-element fill */
.t7-ld-card .t7-hl-media::after { content: none; }
.t7-ld-card .t7-hl-media {
  -webkit-mask-image: linear-gradient(var(--t7-hl-fade, to right), #000 45%, transparent 100%);
          mask-image: linear-gradient(var(--t7-hl-fade, to right), #000 45%, transparent 100%);
}

/* ===== SERVICES + INDUSTRIES LANDING ROWS: ICON CHIP WITHOUT FRAME (Gidget msg 766/778, 2026-10-18) =====
   The landing-row chips (.t7-ld-card, loop template 3033) drop the 13px .t7-frame border; the Ink
   backfill and the icon colours stay. Industry-page hero chips (no .t7-ld-card) keep their frame.
   Remove to revert. */
.t7-ld-card .t7-icon-chip__box { border-width: 0; }

/* ===== ARTIST QUESTIONNAIRE: NO FOOTER CONTACT FORM (page 3517, Gidget msg 936, 2026-10-19) =====
   Footer 100 container 54d78dc ("Let's work together." + form 389d061) carries class t7-footer-form.
   One footer template; the form section is hidden only on this page. */
body.page-id-3517 .t7-footer-form { display: none !important; }

/* ============================================================
   STAFF CARD: NO EMAIL = NO MAIL PLATE  (Paul via Gidget 974, 2026-10-20)
   Breadcrumb: tmpl 569 > icon widget 906e631, link = [staff_mailto]
   (WPCode 571), which returns "#" when the staff post's ACF email is
   empty. Hide the plate on those cards and release the space reserved
   for it. Data-driven: fill in an email and the plate comes back.
   ============================================================ */
.elementor-569 .elementor-widget-icon.t7-plate:has(a[href="#"]) { display: none !important; }
.elementor-569 .t7-card:has(.t7-plate a[href="#"]) .t7-panel { padding-bottom: 0 !important; }
@media (max-width: 767px) {
  .elementor-569 .t7-card:has(.t7-plate a[href="#"]) .e-con > .elementor-element.elementor-element-3fe8e37 { --padding-bottom: var(--gap-width); }
}

/* reCAPTCHA v3 field (t7rc) in the 4 site forms (Gidget 976, 2026-10-20).
   The badge is position:fixed (bottom-left), so the field's own group is an
   empty row: collapse it so it adds no gap above the submit button. */
.elementor-form .elementor-field-group.elementor-field-type-recaptcha_v3:not(.recaptcha_v3-inline) { margin: 0 !important; padding: 0 !important; min-height: 0 !important; height: 0; }
