/**
 * Site layer — the comps, measured.
 *
 * This was the Site Editor's Additional CSS field. It has to live in the
 * theme: that field rejects any CSS containing < or >, so a child
 * combinator makes it unopenable, and 29 selectors here need one. Keeping
 * it in a file also means it is version-controlled and there is one place
 * to look for a rule — two homes for site CSS is how the same treatment
 * ended up written twice.
 *
 * Values are tokens. A literal here and a token in theme.json drift the
 * moment either is edited.
 */

/* Holler motion layer — CSS only, no JS, token-based.
   Targets real block markup so it survives editor round-trips. */

/* --- atmosphere: drifting washes behind dark bands --- */
.wp-block-group.alignfull.has-dark-bg-background-color{position:relative;overflow:hidden;isolation:isolate}
.wp-block-group.alignfull.has-dark-bg-background-color::before,
.wp-block-group.alignfull.has-dark-bg-background-color::after{content:"";position:absolute;border-radius:50%;pointer-events:none;z-index:-1;filter:blur(10px)}
.wp-block-group.alignfull.has-dark-bg-background-color::before{width:820px;height:820px;top:-360px;right:-260px;background:radial-gradient(circle,var(--wp--custom--tint--wash-accent),transparent 66%);animation:hd-drift-a 26s ease-in-out infinite}
.wp-block-group.alignfull.has-dark-bg-background-color::after{width:620px;height:620px;top:140px;left:-300px;background:radial-gradient(circle,var(--wp--custom--tint--wash-primary),transparent 66%);animation:hd-drift-b 32s ease-in-out infinite}
@keyframes hd-drift-a{0%,100%{transform:translate3d(0,0,0) scale(1);opacity:1}33%{transform:translate3d(-46px,38px,0) scale(1.08);opacity:.82}66%{transform:translate3d(28px,64px,0) scale(.95);opacity:.92}}
@keyframes hd-drift-b{0%,100%{transform:translate3d(0,0,0) scale(1);opacity:.9}40%{transform:translate3d(54px,-40px,0) scale(1.1);opacity:1}70%{transform:translate3d(20px,32px,0) scale(.96);opacity:.8}}

/* --- scroll reveal, no JS: scroll-driven animations where supported --- */
@supports (animation-timeline: view()){
  .wp-block-group.alignfull > .wp-block-heading,
  .wp-block-group.alignfull > .wp-block-columns,
  .wp-block-group.alignfull > .wp-block-buttons,
  .wp-block-group.alignfull > p{animation:hd-rise linear both;animation-timeline:view();animation-range:entry 5% cover 22%}
  @keyframes hd-rise{from{opacity:0;transform:translateY(26px)}to{opacity:1;transform:none}}
}

/* --- card lift --- */
.wp-block-columns .wp-block-column[style*="border-radius"]{transition:transform .45s var(--wp--custom--ease),box-shadow .45s}
.wp-block-columns .wp-block-column[style*="border-radius"]:hover{transform:translateY(-5px);box-shadow:0 26px 60px rgba(16,20,40,.16)}

/* --- link arrows nudge --- */
.wp-block-group a[href]:not(.wp-block-button__link):not(.wp-block-navigation-item__content){transition:color .2s}

/* --- buttons: lift on hover --- */
.wp-block-button__link{transition:transform .3s var(--wp--custom--ease),background-color .25s,box-shadow .3s}
.wp-block-button__link:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(16,20,40,.24)}

/* --- details: smoother disclosure --- */
.wp-block-details summary{transition:color .2s}
.wp-block-details[open] > *:not(summary){animation:hd-open .34s var(--wp--custom--ease) both}
@keyframes hd-open{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* --- section rhythm: let the closer breathe more than the rest --- */
.wp-block-group.alignfull.has-dark-bg-background-color:last-child{padding-top:var(--wp--custom--section--xxl);padding-bottom:var(--wp--custom--section--xxl)}

@media (prefers-reduced-motion: reduce){
  .wp-block-group.alignfull.has-dark-bg-background-color::before,
  .wp-block-group.alignfull.has-dark-bg-background-color::after,
  .wp-block-details[open] > *:not(summary){animation:none!important}
  .wp-block-group.alignfull > *{animation:none!important;opacity:1!important;transform:none!important}
  .wp-block-button__link:hover,.wp-block-column:hover{transform:none!important}
}

/* ===================================================================
   Hero + trust strip — structure. The motion layer above is
   atmosphere; keep both.
   =================================================================== */

/* Hero — eyebrow dot */

/* Hero — feature pills */
.hero-pills p {
  margin:0;
  border-radius:999px;
  text-transform:uppercase;
}
.hero-pills p::before { content:"\25CF"; color:var(--wp--preset--color--primary); }

/* Hero — portrait halo */
.hero-portrait .portrait-figure { position:relative; margin:0; }
.hero-portrait .portrait-figure::before {
  content:"";
  position:absolute;
  border-radius:50%;
  z-index:0;
}
.hero-portrait .portrait-figure img {
  position:relative;
  z-index:1;
  display:block;
  margin-inline:auto;
}

/* Hero — Google review card */
.review-card {
  position:relative;
  z-index:2;
  width:max-content;
  max-width:100%;
  background:var(--wp--preset--color--white);
  border:1px solid var(--wp--preset--color--hairline);
  box-shadow:var(--wp--preset--shadow--medium);
}
.review-card p { margin:0; }

/* Stats row — vertical dividers between figures */
.stat-row > .wp-block-column + .wp-block-column {
  border-left:1px solid var(--wp--preset--color--hairline);
  padding-left:var(--wp--preset--spacing--space-md);
}
@media (max-width:781px) {
  .stat-row > .wp-block-column + .wp-block-column { border-left:0; padding-left:0; }
}

/* Client trust strip */
.client-names p {
  margin:0;
  color:var(--wp--preset--color--neutral-mid);
  font-weight:600;
}

/* The motion layer's card-lift targets any radius'd column; keep the
   hero portrait column out of it so the halo doesn't float on hover. */
.hero-portrait:hover { transform:none; box-shadow:none; }

/* Rotating headline word — the swap is JS, the look is here.
   Without JS it stays a normal coloured word. */
.rotator {
  color: var(--wp--preset--color--primary);
  display: inline-block;
  text-align: left;
  transition: opacity .24s ease, transform .24s ease;
}
.rotator.is-swapping { opacity: 0; transform: translateY(-.18em); }
@media (prefers-reduced-motion: reduce) {
  .rotator { transition: none; }
  .rotator.is-swapping { opacity: 1; transform: none; }
}

/* ===================================================================
   Hero — measured against the comp (Holler Homepage v2).
   Five earlier passes stacked overrides on the same selectors; this
   block replaces all of them, so the hero has one place to edit.
   =================================================================== */

/* Atmosphere: cyan across the top-right, warm peach down the left,
   white through the middle. Full-bleed behind the whole band — the
   halo on its own read as a sticker on white. */
.hero-band.has-white-background-color {
  background-color: var(--wp--preset--color--white);
  background-image:
    radial-gradient(52% 58% at 97% 0%, rgba(43,209,232,.22), transparent 66%),
    radial-gradient(38% 46% at 6% 58%, rgba(232,66,43,.17), transparent 68%);
  background-repeat: no-repeat;
}

/* The comp runs the headline wide but holds the body copy to a much
   narrower measure; that contrast is what gives the block its shape. */
.hero-band .wp-block-column:first-child > p.has-text-large-font-size:last-of-type,
.hero-band .wp-block-column:first-child > p.has-text-large-font-size + p { max-width: 46ch; }

/* Hand-drawn stroke under the closing word. */
.stroke {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 12' preserveAspectRatio='none'%3E%3Cpath d='M3 8.4C46 3.2 148 2.4 197 5.6' stroke='%23E8422B' stroke-width='3.2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% .22em;
  padding-bottom: .1em;
}

/* Portrait sits inside the wash, so the halo stays light and the
   subject reads at the comp's scale instead of dominating. */
.hero-portrait .portrait-figure img { max-width: min(100%, 258px); }
.hero-portrait .portrait-figure::before {
  inset: -5% -8%;
  background:
    radial-gradient(62% 62% at 72% 16%, color-mix(in srgb, var(--wp--preset--color--accent) 26%, transparent), transparent 74%),
    radial-gradient(70% 70% at 26% 90%, color-mix(in srgb, var(--wp--preset--color--primary) 9%, transparent), transparent 74%),
    rgba(255,255,255,.42);
}

/* --- Feature pills ---------------------------------------------------
   Comp: Geist Mono 10px, .1em tracking, uppercase, mid-neutral sitting
   on the light-bg fill inside a hairline ring. Ours was body sans at
   13px on white, which read as running text rather than as chips. */
.hero-pills { row-gap: .6rem; }
.hero-pills p {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 10px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: .1em;
  padding: 8px 13px;
  background: var(--wp--preset--color--light-bg);
  border: 1px solid var(--wp--preset--color--hairline);
  color: var(--wp--preset--color--neutral-mid);
}
.hero-pills p::before { font-size: .5em; vertical-align: .35em; margin-right: .7em; }

/* --- Google review card ----------------------------------------------
   Comp left-aligns the three lines and steps them 12 / 19 / 9.5px.
   Centred and one size up, ours lost the badge quality. */
.review-card {
  margin: -3.5rem 0 0 2%;
  padding: 12px 15px;
  border-radius: 14px;
  text-align: left;
}
.review-card .stars {
  color: var(--wp--preset--color--review-star, var(--wp--preset--color--review-star));
  font-size: 12px;
  letter-spacing: .15em;
  line-height: 1.1;
}
.review-card p:nth-child(2) {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.25;
}
.review-card p:last-child {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 9.5px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--wp--preset--color--neutral-light);
  margin-top: 1px;
}

/* --- Founder line ----------------------------------------------------
   Centred under the portrait. The caption is mono 9.5px in the comp,
   not sans 11px, which is why it broke a word early. */
.hero-portrait > p { text-align: center; }
.hero-portrait > p:nth-last-child(2) {
  margin-top: 1.125rem;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--wp--preset--color--neutral-dark);
}
.hero-portrait > p:last-child {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .11em;
  line-height: 1.5;
  color: var(--wp--preset--color--neutral-light);
  margin: 4px auto 0;
  max-width: 268px;
}

/* --- Micro-type: beating WordPress's preset size classes ------------
   WP prints .has-<slug>-font-size with !important, so a preset size on
   the block always wins over design CSS. The comp's chip and badge type
   sits at 9.5-12px, below the bottom of the token scale (caption is
   14px), so these few bespoke elements are set here rather than being
   given a token that nothing else would use. Durable alternative: drop
   the fontSize attribute from these blocks in markup and let this
   block own them outright. */
.hero-pills p { font-size: 10px !important; }
.review-card .stars { font-size: 12px !important; }
.review-card p:nth-child(2) { font-size: 19px !important; }
.review-card p:last-child { font-size: 9.5px !important; }
.hero-portrait > p:nth-last-child(2) { font-size: 15.5px !important; }
.hero-portrait > p:last-child { font-size: 9.5px !important; }

/* Pill dot: the comp draws a 5px disc, not a bullet glyph. A ● sized
   in em renders well under its em box and sat too small and too tight. */
.hero-pills p { display: inline-flex; align-items: center; gap: 7px; }
.hero-pills p::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--wp--preset--color--primary);
  flex-shrink: 0;
  margin: 0;
}

/* ===================================================================
   Exhibit wireframes and the browser-framed screenshot.
   Both are inline SVG/HTML drawn in currentColor, so they take the
   band's text colour and need no second palette.
   =================================================================== */

.exhibit-art { margin: 1.1rem 0 1.25rem; color: var(--wp--preset--color--neutral-dark); }
.exhibit-art svg { display: block; width: 100%; height: auto; }

/* The flat line is the whole joke of Exhibit A, so it draws itself in
   once the card is on screen rather than arriving already flat. */
.exhibit-art .flat {
  stroke: var(--wp--preset--color--primary);
  stroke-width: 2.5;
  fill: none;
  stroke-linecap: round;
  stroke-dasharray: 320;
  stroke-dashoffset: 320;
  animation: hd-draw 1.5s cubic-bezier(.65,0,.35,1) both;
}
@supports (animation-timeline: view()) {
  .exhibit-art .flat { animation-timeline: view(); animation-range: entry 20% cover 40%; }
}
@keyframes hd-draw { to { stroke-dashoffset: 0; } }

/* Exhibit B's pages settle in rather than being drawn. Staggered so it
   reads as accumulation, which is what the copy is about. */
.exhibit-art .pg { opacity: 0; animation: hd-settle .5s ease both; }
@supports (animation-timeline: view()) {
  .exhibit-art .pg { animation-timeline: view(); animation-range: entry 18% cover 42%; }
}
.exhibit-art .pg:nth-child(2) { animation-delay: .04s }
.exhibit-art .pg:nth-child(3) { animation-delay: .08s }
.exhibit-art .pg:nth-child(4) { animation-delay: .12s }
.exhibit-art .pg:nth-child(5) { animation-delay: .16s }
.exhibit-art .pg:nth-child(6) { animation-delay: .2s }
.exhibit-art .pg:nth-child(7) { animation-delay: .24s }
.exhibit-art .pg:nth-child(8) { animation-delay: .28s }
.exhibit-art .pg:nth-child(9) { animation-delay: .32s }
.exhibit-art .pg:nth-child(10) { animation-delay: .36s }
@keyframes hd-settle { from { opacity: 0; transform: translateY(-7px) } to { opacity: 1; transform: none } }

@media (prefers-reduced-motion: reduce) {
  .exhibit-art .flat { animation: none; stroke-dashoffset: 0; }
  .exhibit-art .pg { animation: none; opacity: 1; }
}

/* Browser chrome around a real screenshot. The dots and the URL are
   decoration, so the bar is aria-hidden and the alt text carries the
   meaning. */
.browser-shot {
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--wp--preset--color--hairline);
  background: var(--wp--preset--color--white);
  box-shadow: var(--wp--preset--shadow--large);
}
.browser-shot .bar {
  height: 30px;
  background: var(--wp--preset--color--light-bg);
  border-bottom: 1px solid var(--wp--preset--color--hairline);
  display: flex;
  gap: 6px;
  align-items: center;
  padding: 0 13px;
}
.browser-shot .d { width: 8px; height: 8px; border-radius: 50%; background: var(--wp--preset--color--hairline); flex-shrink: 0; }
.browser-shot .u {
  margin-left: 10px;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 9.5px;
  letter-spacing: .08em;
  color: var(--wp--preset--color--neutral-light);
}
.browser-shot img { display: block; width: 100%; height: auto; }

/* The same chrome drawn around a core/image block, so a case-study
   screenshot stays a real image block — picker, srcset and alt text intact —
   instead of becoming raw HTML. The bar is the figure's own top padding and
   the three dots are one pseudo-element with two box-shadow copies, because
   a figure has no spare child to hang them on. */
.wp-block-image.browser-shot {
	position: relative;
	margin: 0;
	padding-top: 30px;
	border-radius: 14px;
	overflow: hidden;
	border: 1px solid var(--wp--preset--color--hairline);
	background: var(--wp--preset--color--light-bg);
	box-shadow: var(--wp--preset--shadow--large);
}

.wp-block-image.browser-shot::before {
	content: "";
	position: absolute;
	top: 11px;
	left: 13px;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--wp--preset--color--hairline);
	box-shadow: 14px 0 0 var(--wp--preset--color--hairline),
		28px 0 0 var(--wp--preset--color--hairline);
}

.wp-block-image.browser-shot img {
	display: block;
	width: 100%;
}

/* Most of these captures are the whole page — four or five thousand pixels
   of it — so the frame fixes the ratio and keeps the top, which is the part
   that reads as somebody's website rather than as a long grey strip. */
.wp-block-image.shot-crop img {
	aspect-ratio: 16 / 10;
	object-fit: cover;
	object-position: top center;
}

/* On a card the shot is the lid: square off the bottom so it meets the copy,
   and pull it out to the card's own edges. */
.pw-card .wp-block-image.browser-shot {
	margin: calc(var(--wp--custom--section--medium) * -1)
		calc(var(--wp--custom--section--medium) * -1)
		var(--wp--preset--spacing--space-md);
	border-radius: var(--wp--custom--radius--base) var(--wp--custom--radius--base) 0 0;
	border-width: 0 0 1px;
	box-shadow: none;
}

.pw-card .wp-block-image.shot-crop img {
	aspect-ratio: 16 / 9;
}

/* ===================================================================
   Dark services band: panels and the curve out of it.
   =================================================================== */

/* The comp gives each service its own translucent panel. Ours had the
   coloured top rule but no card behind it, so the three read as loose
   columns of text on navy. Inline styles still own the top border, so
   only the other three sides and the fill are set here. */
.is-style-section-dark .wp-block-columns > .wp-block-column[style*="border-radius"] {
  background: var(--wp--custom--on-dark--surface);
  border: 1px solid var(--wp--custom--on-dark--hairline);
  transition: background .35s, transform .45s var(--wp--custom--ease), box-shadow .45s;
}
.is-style-section-dark .wp-block-columns > .wp-block-column[style*="border-radius"]:hover {
  background: var(--wp--custom--on-dark--surface-hover);
  transform: translateY(-6px);
  box-shadow: 0 26px 60px rgba(0,0,0,.3);
}
@media (prefers-reduced-motion: reduce) {
  .is-style-section-dark .wp-block-columns > .wp-block-column[style*="border-radius"]:hover { transform: none; }
}

/* The curve is the transition out of the dark band: a light-bg strip
   with the navy shape sitting in it, so the two sections meet on an arc
   rather than a hard edge. */
.section-curve {
  height: 104px;
  margin-top: -1px;
  line-height: 0;
  background: var(--wp--preset--color--light-bg);
  position: relative;
  z-index: 2;
  padding-left: 0;
  padding-right: 0;
}
.section-curve svg { display: block; width: 100%; height: 100%; }
.section-curve path { fill: var(--wp--preset--color--dark-bg); }

/* Rotating word: it used to fade to nothing for a quarter second on
   every swap, so the headline read "A website that / as hard as your
   team does." to anyone who looked at the wrong moment — including a
   screenshot. It now dips rather than disappears. */
.rotator.is-swapping { opacity: .22; transform: translateY(-.1em); }
.rotator { transition: opacity .14s ease, transform .14s ease; }

/* ===================================================================
   Header lockup and the exhibit cards.
   =================================================================== */

/* Holler-Logo-2025.svg is a round icon, not a horizontal lockup, so a
   width alone let it grow tall and push the whole bar down. Cap the
   height and let the wordmark sit beside it. A real lockup asset would
   replace both of these. */
.site-header .brand-lockup { align-items: center; }
.site-header .wp-block-site-logo img {
  height: 38px;
  width: auto;
  max-width: none;
  display: block;
}
.site-header .wp-block-site-title {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1;
}
.site-header .wp-block-site-title a {
  color: var(--wp--preset--color--neutral-dark);
  text-decoration: none;
}

/* The comp closes the wordmark with a period in the accent colour that
   blinks like a terminal cursor -- on for most of the cycle, off for a
   beat. It is punctuation on a logotype, not content, so it is drawn
   rather than written into the site title. */
.brand-lockup .wp-block-site-title a::after {
  content: ".";
  color: var(--wp--preset--color--accent);
  animation: hd-blink 2.6s step-end infinite;
}
@keyframes hd-blink {
  0%, 62%, 100% { opacity: 1; }
  72%, 92% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .brand-lockup .wp-block-site-title a::after { animation: none; }
}

/* Exhibit cards: the comp gives them a hairline ring and a soft lift so
   they read as cards on a white band. Ours were white on white, so the
   card disappeared and the two exhibits ran together. */
.exhibit-art { color: var(--wp--preset--color--neutral-dark); }
.wp-block-column:has(> .exhibit-art) {
  border: 1px solid var(--wp--preset--color--hairline);
  box-shadow: var(--wp--preset--shadow--small);
}

/* The exhibit label is red in the comp — it is the thing that makes
   the pair read as evidence rather than as two more cards. */
.wp-block-column:has(> .exhibit-art) > p:first-child {
  color: var(--wp--preset--color--primary) !important;
}

/* The curve has to meet the next band with no seam. Sibling blocks in a
   constrained layout inherit the root blockGap as a top margin, which
   left a white strip under the arc. */
.section-curve { margin-bottom: 0; }
.section-curve + * { margin-top: 0 !important; }

/* ===================================================================
   Motion, rebuilt on IntersectionObserver.

   The reveal used animation-timeline: view() with a scrub range of
   "entry 5% cover 22%". Two problems. Anything already on screen at
   load never "enters", so it sits at the animation's start state —
   computed opacity 0, content invisible. And Safari and Firefox have no
   view timelines at all, so there the @supports guard meant no motion
   whatsoever. Scrubbing also tied opacity to scroll position, which is
   why full-page screenshots kept catching sections half-faded.

   Now: fire once on entry, stay put. The hd-motion class is added by
   JS, so with scripting off every element is simply visible.
   =================================================================== */

@supports (animation-timeline: view()) {
  .wp-block-group.alignfull > .wp-block-heading,
  .wp-block-group.alignfull > .wp-block-columns,
  .wp-block-group.alignfull > .wp-block-buttons,
  .wp-block-group.alignfull > p { animation: none; }
  .exhibit-art .flat { animation: none; }
  .exhibit-art .pg { animation: none; opacity: 1; }
}

.hd-motion .hd-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .62s var(--wp--custom--ease), transform .62s var(--wp--custom--ease);
  will-change: opacity, transform;
}
.hd-motion .hd-reveal.is-in { opacity: 1; transform: none; will-change: auto; }

/* Exhibit A's line draws itself; Exhibit B's pages accumulate. */
.hd-motion .exhibit-art .flat { stroke-dashoffset: 320; }
.hd-motion .exhibit-art.is-in .flat {
  transition: stroke-dashoffset 1.5s cubic-bezier(.65,0,.35,1) .15s;
  stroke-dashoffset: 0;
}
.hd-motion .exhibit-art .pg {
  opacity: 0;
  transform: translateY(-7px);
  transition: opacity .42s ease, transform .42s var(--wp--custom--ease);
}
.hd-motion .exhibit-art.is-in .pg { opacity: 1; transform: none; }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(1) { transition-delay: .05s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(2) { transition-delay: .1s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(3) { transition-delay: .15s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(4) { transition-delay: .2s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(5) { transition-delay: .25s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(6) { transition-delay: .3s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(7) { transition-delay: .35s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(8) { transition-delay: .4s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(9) { transition-delay: .45s }
.hd-motion .exhibit-art.is-in .pg:nth-of-type(10) { transition-delay: .5s }

@media (prefers-reduced-motion: reduce) {
  .hd-motion .hd-reveal,
  .hd-motion .exhibit-art .pg { opacity: 1 !important; transform: none !important; transition: none !important; }
  .hd-motion .exhibit-art .flat { stroke-dashoffset: 0 !important; transition: none !important; }
}

/* The Google review widget was replaced by the Trustindex plugin's
   [trustindex no-registration=google] shortcode, which renders all 26
   reviews live rather than five copied into the page by hand. Its
   markup and styles are archived in uploads/holler-blocks/archive/.
   Removing them also removed the five hard-coded avatar hexes that
   check-markup had been warning about. */

/* ===================================================================
   Process as rows, and the fit cards.
   =================================================================== */

/* The comp runs the three steps as rows with hairline rules between
   them, not as three columns. The markup stays a columns block — each
   "column" becomes a row, laid out on its own grid so the number,
   the heading pair and the body sit side by side. */
@media (min-width: 782px) {
  .process-rows { display: block !important; }
  .process-rows > .wp-block-column {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) minmax(0, 1.2fr);
    column-gap: var(--wp--preset--spacing--space-md);
    align-items: start;
    padding: 2rem 0;
    border-top: 1px solid var(--wp--preset--color--hairline);
  }
  .process-rows > .wp-block-column:first-child { border-top: 0; }
  /* number */
  .process-rows > .wp-block-column > p:nth-child(1) { grid-column: 1; grid-row: 1 / span 2; margin: 0; }
  /* heading */
  .process-rows > .wp-block-column > h3 { grid-column: 2; grid-row: 1; margin: 0 0 .35rem; }
  /* the red one-liner under the heading */
  .process-rows > .wp-block-column > p:nth-child(3) { grid-column: 2; grid-row: 2; margin: 0; }
  /* body copy */
  .process-rows > .wp-block-column > p:nth-child(4) { grid-column: 3; grid-row: 1 / span 2; margin: 0; }
}

/* The step number is a quiet mono label in the comp, not a display
   figure competing with the heading. */
.process-rows > .wp-block-column > p:nth-child(1) {
  font-family: var(--wp--preset--font-family--mono) !important;
  font-size: 11px !important;
  letter-spacing: .12em;
  color: var(--wp--preset--color--neutral-light) !important;
  padding-top: .35rem;
}
.process-rows > .wp-block-column > p:nth-child(3) { color: var(--wp--preset--color--primary); }

/* Org-chart callout: the dark card that closes the process section. */
.org-chart { gap: var(--wp--preset--spacing--space-md); }
.org-chart .wp-block-buttons { flex-shrink: 0; }

/* --- Fit cards -------------------------------------------------------
   The comp answers "probably" on light and "probably not" on navy, with
   a tick against one list and a cross against the other. Ours was two
   identical light cards with default bullets, which read as a
   comparison of equals rather than a filter. */
.fit-card .wp-block-list { list-style: none; padding-left: 0; margin-top: 1rem; }
.fit-card .wp-block-list li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: .75rem;
  line-height: 1.5;
}
.fit-card .wp-block-list li::before {
  position: absolute;
  left: 0;
  top: .05em;
  font-weight: 700;
  font-size: .95em;
}
.fit-card.is-yes .wp-block-list li::before { content: "\2713"; color: var(--wp--preset--color--primary); }
.fit-card.is-not .wp-block-list li::before { content: "\00D7"; color: var(--wp--preset--color--primary); font-size: 1.15em; top: -.06em; }
.fit-card.is-not .wp-block-list li { color: rgba(255,255,255,.82); }
.fit-card.is-not p { color: rgba(255,255,255,.55) !important; font-style: italic; }
.fit-card.is-not h3 { color: var(--wp--preset--color--white); }

/* Credential badges under the James section. */
.cred-badges p {
  margin: 0;
  flex: 0 1 210px;
  background: var(--wp--preset--color--white);
  border: 1px solid var(--wp--preset--color--hairline);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--wp--preset--color--neutral-dark);
}
.cred-badges p span {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wp--preset--color--neutral-light);
  display: inline-block;
  margin-top: 4px;
}

/* The handwritten asides are Caveat at a size that reads as a margin
   note, not as body copy. */
.hand-aside { line-height: 1.3; }

/* Full-bleed sections butt together. Every alignfull section inherited
   the root blockGap as a 24px top margin, so each pair of bands had a
   strip of page background between them — invisible between two white
   sections, a visible seam wherever the colours differ.

   This ships in the theme from 0.11.3; the copy here is so the fix is
   live before that deploy and can be dropped afterwards. */

/* The curve still overlaps its band by a pixel so no hairline shows
   where the navy meets the arc. */
.wp-block-post-content > .section-curve,
.entry-content > .section-curve { margin-block-start: -1px; }

/* Same seam one level up: header → main → footer each inherit the root
   blockGap. Shows most above the dark footer. Ships in the theme from
   0.11.4; this copy can be dropped after that deploy. */

/* probe */
.hd-probe::before { content: "\25CF"; }

/* ===================================================================
   Client marquee. The comp scrolls the trust strip continuously and
   fades it out at both ends; ours was a static flex row that simply
   ran out of names. The row is duplicated at runtime so a -50%
   translate loops seamlessly.
   =================================================================== */

.client-names {
  display: block !important;
  overflow: hidden;
  white-space: nowrap;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.client-names .mq-row { display: inline-block; animation: hd-marquee 34s linear infinite; }
.client-names .mq-half { display: inline-block; white-space: nowrap; }
.client-names:hover .mq-row { animation-play-state: paused; }
@keyframes hd-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.client-names p {
  display: inline-block;
  margin: 0;
  padding: 0 30px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -.025em;
  color: var(--wp--preset--color--neutral-light);
  transition: color .22s;
}
.client-names p:hover { color: var(--wp--preset--color--primary); }

@media (prefers-reduced-motion: reduce) {
  .client-names .mq-row { animation: none; }
  .client-names { -webkit-mask-image: none; mask-image: none; white-space: normal; }
}

/* Footer service links: a navigation list, not prose, so it carries no
   bullets. A modifier class rather than an inline override, so sibling
   and nth-child rules can still reach it. */
.footer-links {
  list-style: none;
  padding-left: 0;
  margin-left: 0;
}
.footer-links li { margin-bottom: 8px; }
.footer-links li:last-child { margin-bottom: 0; }
.footer-links a { text-decoration: none; }
.footer-links a:hover { text-decoration: underline; }

/* The footer's link colour is no longer set here. A dark section redefines
   what --hd-link means inside it, so links take the band's soft white
   without the footer needing a rule of its own — see Holler_Color_System.
   The site title stays an exception: pure white, not the muted tone. */
.site-footer .wp-block-site-title a,
.site-footer .wp-block-site-title a:hover {
  color: var(--wp--preset--color--white);
}

/* The land acknowledgement is a paragraph of prose, not a full-width band of
   text: the comp caps it so it wraps to three readable lines rather than
   running the whole 1312px.
 *
 * The cap works only because the paragraph sits in a flow-layout group. A
 * constrained group gives every direct child `margin-left/right: auto
 * !important`, so a narrower child centres itself; overriding those margins
 * does not fix it, it just moves the paragraph out to the footer's padding
 * edge, left of the content column everything else aligns to. Flow layout
 * writes no auto margins, so the cap simply shortens the line. */
.site-footer .land-ack {
  max-width: 900px;
}

/* The handwritten line sits in the band's soft white in the comp, not the
   accent. That value is an on-dark token rather than a palette colour, so
   it cannot be a textColor attribute and is set here instead -- which is
   why the block carries no colour of its own. */
.site-footer .footer-hand {
  color: var(--wp--custom--on-dark--secondary);
}

/* The two lines under the rule are set as mono labels in the comp -- small,
   uppercase, widely tracked, and dimmer than the body. */
.site-footer p.has-caption-font-size {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wp--custom--on-dark--faint);
}



/* ---------------------------------------------------------------------------
 * About — the portrait, framed.
 *
 * The comp draws James as a CSS background on an empty div so the mockup can
 * ship in one file. On the site it is a real image block instead: a photo of
 * a person is content, and a background div has no alt text, no srcset and
 * nothing for a reader to announce.
 *
 * So the frame moves here and the image brings itself. The gradient still
 * shows at the edges, which is what it was for -- the portrait is cut out,
 * and the wash is what it sits in.
 * ------------------------------------------------------------------------ */
.jphoto {
	max-width: 340px;
}

.jphoto img {
	display: block;
	width: 100%;
	height: auto;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--large);
	background: linear-gradient(160deg, var(--wp--custom--tint--wash-accent), var(--wp--custom--tint--wash-primary));
}

/* The rating line that closes the reviews section. It outlived the widget
   it came from: the Trustindex shortcode renders the reviews, and this
   sits under it as the one-line summary. A flex row rather than a
   paragraph so the stars, the score and the count stay on one baseline and
   wrap together on a phone.

   The figures are typed, not fetched -- see the paragraph in the page. */
.rating {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin-top: 24px; flex-wrap: wrap;
  font-size: 14.5px; color: var(--wp--preset--color--neutral-mid);
}
.rating .stars {
  color: var(--wp--preset--color--review-star);
  letter-spacing: 2px;
}

/* ---------------------------------------------------------------------------
 * UTM builder — mobile density
 *
 * The tool is the whole point of /utm-builder/: someone arrives from search
 * wanting to paste a URL and copy a tagged one back. On a phone the plugin's
 * six field groups run 100px each, which puts the Generate button below the
 * fold before the page has said anything.
 *
 * This tightens the rhythm without hiding any field — losing utm_term and
 * utm_content to make a number look better would be trading the tool's
 * function for its first impression.
 * ------------------------------------------------------------------------ */
@media (max-width: 781px) {
  .utm-builder-wrapper h2,
  #utm-campaign-url-builder { font-size: var(--wp--preset--font-size--heading-4); margin-bottom: 0.5rem; }
  .utm-form-group { margin-bottom: 0.75rem; }
  .utm-form-group label { margin-bottom: 2px; line-height: 1.3; }
  .utm-builder-form .utm-button { padding-top: 0.7rem; padding-bottom: 0.7rem; }
}



/* A designed graphic rather than a screenshot. `browser-shot` puts chrome
   around a capture and `shot-crop` keeps only its top, because the rest of a
   full-page capture is a long grey strip. Neither is right for a card built
   for one case study: it has no browser to imply and every part of it is
   load-bearing, so it gets the frame and nothing else. */
.wp-block-image.case-graphic {
	/* A figure's own margins go, but not the space above it: the hero's last
	   button would otherwise sit on the graphic's top edge. A browser shot
	   gets away with that because its chrome bar is pale; a dark graphic reads
	   as an overlap. */
	margin: var(--wp--preset--spacing--space-lg) 0 0;
	border-radius: 14px;
	overflow: hidden;
	box-shadow: var(--wp--preset--shadow--large);
}

.wp-block-image.case-graphic img {
	display: block;
	width: 100%;
	height: auto;
}

/* On a card the graphic is the lid, the same as a shot: out to the card's
   own edges, square where it meets the copy. */
.pw-card .wp-block-image.case-graphic {
	margin: calc(var(--wp--custom--section--medium) * -1)
		calc(var(--wp--custom--section--medium) * -1)
		var(--wp--preset--spacing--space-md);
	border-radius: var(--wp--custom--radius--base) var(--wp--custom--radius--base) 0 0;
}



/* The three proof cards on the home page take the same lid as a portfolio
   card — the shot out to the card's own edges, squared off where it meets the
   copy — but they are not .pw-card and their padding and radius are their own,
   so the bleed is written against those rather than reusing that rule. The
   radius is a pixel under the card's 26px because the card's 1px border sits
   outside the padding box the image is being pulled out to. */
.work-mini .wp-block-image.browser-shot {
	margin: calc(var(--wp--preset--spacing--space-md) * -1)
		calc(var(--wp--preset--spacing--space-md) * -1)
		var(--wp--preset--spacing--space-md);
	border-radius: 25px 25px 0 0;
	border-width: 0 0 1px;
	box-shadow: none;
}

.work-mini .wp-block-image.shot-crop img {
	aspect-ratio: 16 / 9;
}



/* The portrait halo is drawn 8% wider than the figure on each side, which is
   fine while the figure is a column of a wide row and the halo has room to
   bleed into. Once the columns stack, the figure is nearly the full width and
   that 8% lands outside the page: the home page scrolled sideways by up to
   28px on phones and small tablets, with nothing to show for it but a stray
   scrollbar. Clipping at the band keeps the bleed everywhere it used to have
   somewhere to go — at desktop the halo never reaches the band's edge — and
   takes it back only where it was leaving the document.

   `clip` rather than `hidden`: hidden would make the band a scroll container
   and break position: sticky for anything inside it. */
.hero-band {
	overflow-x: clip;
}
