/*
 * Rules that exist only because WordPress is not the static build.
 *
 * main.css is shared verbatim with the static site and stays that way. Anything
 * here compensates for a difference Elementor introduces, and would be dead
 * weight in the static build.
 */

/*
 * Each section now lives in its own Elementor container, which is what makes the
 * page editable as a normal Elementor page. The cost is that the hero and the
 * first section are no longer siblings, so `.phero + section.sec` - which tunes
 * the seam between them - stops matching. The importer marks that first section
 * instead, and the rules are mirrored here against the marker.
 */
section.sec.afterhero {
	background: linear-gradient( 180deg, rgba( 255, 255, 255, .025 ), rgba( 255, 255, 255, 0 ) 380px );
}
section.sec.afterhero[style*="padding-top:92px"] { padding-top: 62px !important; }
section.sec.afterhero[style*="padding-top:76px"] { padding-top: 56px !important; }

@media ( max-width: 900px ) {
	section.sec.afterhero {
		background: linear-gradient( 180deg, rgba( 255, 255, 255, .025 ), rgba( 255, 255, 255, 0 ) 260px );
	}
	section.sec.afterhero[style*="padding-top:92px"] { padding-top: 52px !important; }
	section.sec.afterhero[style*="padding-top:76px"] { padding-top: 48px !important; }
}
@media ( max-width: 620px ) {
	section.sec.afterhero[style*="padding-top:92px"] { padding-top: 44px !important; }
	section.sec.afterhero[style*="padding-top:76px"] { padding-top: 42px !important; }
}

/* ==========================================================================
   Contact Form 7
   --------------------------------------------------------------------------
   CF7's template is the design's own markup, so the only thing to undo is the
   wrappers it adds around it: a <div class="wpcf7">, the <form>, and a <span>
   around every field. Left alone they become layout boxes inside a flex column
   and a two-column grid, and the form falls apart. display:contents removes them
   from layout while keeping them in the DOM, which is exactly what is wanted.
   ========================================================================== */
.fc > .wpcf7,
.fc > .wpcf7 > form,
.nlf > .wpcf7,
.nlf > .wpcf7 > form,
.fc .wpcf7-form-control-wrap,
.nlf .wpcf7-form-control-wrap {
	display: contents;
}

/* CF7's hidden meta fields and its screen-reader response list must not take a
   slot in the flex column. */
.fc .hidden-fields-container,
.nlf .hidden-fields-container,
.fc form > div[style*="display: none"],
.nlf form > div[style*="display: none"] { display: none !important; }
.fc > .wpcf7 > .screen-reader-response,
.nlf > .wpcf7 > .screen-reader-response {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect( 0 0 0 0 );
	clip-path: inset( 50% );
	white-space: nowrap;
}

/*
 * The submit control.
 *
 * The design's button is an <a> carrying a decorative gloss span, which cannot
 * wrap an <input>. The span keeps the button's skin and the input is stripped
 * back and stretched inside it, so the gloss and the gradient survive.
 */
.sysub { position: relative; padding: 0; overflow: hidden; display: inline-flex; }
.sysub input[type="submit"] {
	-webkit-appearance: none;
	appearance: none;
	background: none;
	border: 0;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	cursor: pointer;
	padding: 13px 26px;
	border-radius: inherit;
	position: relative;
	z-index: 2;
}
.nlf .sysub input[type="submit"] { padding: 12px 22px; }
.sysub input[type="submit"]:disabled { opacity: .6; cursor: progress; }

/* CF7's spinner sits inside the button and is only visible while a submission is
   in flight, but it reserves its 24px the whole time - which made the Subscribe
   button a third wider than the design's. Taken out of flow it costs nothing
   when idle and still appears in place when it matters. */
.fc .wpcf7-spinner,
.nlf .wpcf7-spinner {
	position: absolute;
	right: 10px;
	top: 50%;
	margin: -12px 0 0;
	z-index: 3;
}

/*
 * Validation and response messages.
 *
 * CF7 ships its own borders and colours for these; the design has a voice of its
 * own, so they are restated rather than inherited.
 */
.fc .wpcf7-response-output,
.nlf .wpcf7-response-output {
	width: 100%;
	margin: 14px 0 0;
	padding: 10px 14px;
	border: 1px solid var( --line );
	border-radius: 11px;
	font-size: 12.5px;
	color: var( --mu );
	background: rgba( 255, 255, 255, .03 );
}
.fc form.sent .wpcf7-response-output,
.nlf form.sent .wpcf7-response-output {
	border-color: rgba( 0, 245, 160, .45 );
	color: var( --em );
	background: rgba( 0, 245, 160, .05 );
}
.fc form.invalid .wpcf7-response-output,
.fc form.failed .wpcf7-response-output,
.nlf form.invalid .wpcf7-response-output,
.nlf form.failed .wpcf7-response-output {
	border-color: rgba( 255, 95, 168, .45 );
	color: #ff9ec9;
	background: rgba( 255, 95, 168, .05 );
}
.fc .wpcf7-not-valid-tip,
.nlf .wpcf7-not-valid-tip {
	font-size: 11.5px;
	color: #ff9ec9;
	margin-top: 6px;
	display: block;
}
.fc input.wpcf7-not-valid,
.fc textarea.wpcf7-not-valid,
.nlf input.wpcf7-not-valid { border-color: rgba( 255, 95, 168, .55 ); }

/* ==========================================================================
   Testimonial avatar — photo instead of an initial
   --------------------------------------------------------------------------
   The circle grows from 104px to 164px and holds a client photo. When there is
   no photo it holds an abstract disc drawn here, so a testimonial is never
   waiting on an asset to look finished.
   ========================================================================== */
.tside .tav.tavp {
	width: 164px;
	height: 164px;
	padding: 3px;
	box-shadow: 0 18px 44px rgba( 0, 0, 0, .5 );
}
.tav.tavp .tavr {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	overflow: hidden;
	position: relative;
	background: #0c0e11;
	display: flex;
	align-items: center;
	justify-content: center;
}
.tav.tavp .tavr img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* The glow behind the circle; colour comes from the card, set inline. */
.tside .tavh {
	position: absolute;
	z-index: 1;
	width: 230px;
	height: 230px;
	border-radius: 50%;
	filter: blur( 38px );
	opacity: .55;
	pointer-events: none;
}

/* The placeholder: brand-coloured mesh, faint grid, neutral figure. */
.tavph {
	position: absolute;
	inset: 0;
	border-radius: 50%;
	overflow: hidden;
	background: radial-gradient( 120% 120% at 22% 18%, #1a2f3a 0%, #14202c 42%, #0d1218 100% );
}
.tavph::before {
	content: '';
	position: absolute;
	inset: -20%;
	background:
		radial-gradient( closest-side at 28% 26%, rgba( 0, 245, 160, .55 ), transparent 70% ),
		radial-gradient( closest-side at 74% 34%, rgba( 139, 92, 246, .55 ), transparent 70% ),
		radial-gradient( closest-side at 52% 82%, rgba( 255, 95, 168, .42 ), transparent 70% );
	filter: blur( 14px );
}
.tavph::after {
	content: '';
	position: absolute;
	inset: 0;
	opacity: .16;
	background-image:
		linear-gradient( rgba( 255, 255, 255, .5 ) 1px, transparent 1px ),
		linear-gradient( 90deg, rgba( 255, 255, 255, .5 ) 1px, transparent 1px );
	background-size: 14px 14px;
}
.tavph svg {
	position: absolute;
	left: 50%;
	top: 52%;
	transform: translate( -50%, -50% );
	width: 78px;
	height: 78px;
	color: rgba( 255, 255, 255, .82 );
	filter: drop-shadow( 0 4px 14px rgba( 0, 0, 0, .45 ) );
}

@media ( max-width: 820px ) {
	.tside .tav.tavp { width: 124px; height: 124px; }
	.tside .tavh { width: 180px; height: 180px; }
	.tavph svg { width: 58px; height: 58px; }
}

/* ==========================================================================
   Client logos
   --------------------------------------------------------------------------
   The design drew every client as type because there were no logo files. Now
   there are, and a cell holds either. .has-logo only appears when an image is
   actually present, so a half-filled wall still reads as one deliberate row.
   ========================================================================== */
.cwi.has-logo,
.bsc.has-logo {
	display: flex;
	align-items: center;
	justify-content: center;
}
.cwi .clogo,
.bsc .clogo {
	max-width: 100%;
	max-height: 34px;
	width: auto;
	height: auto;
	object-fit: contain;
	/*
	 * The wall is monochrome by design; colour arrives on hover. Brightness is
	 * deliberately left alone - pushing it turned logos that ship on a white
	 * background into grey slabs.
	 */
	filter: grayscale( 1 );
	opacity: .78;
	transition: filter .4s var( --ease ), opacity .4s var( --ease );
}

/* Artwork drawn dark on white needs flipping to read on the dark panel. */
.cwi .clogo.is-invert,
.bsc .clogo.is-invert { filter: grayscale( 1 ) invert( 1 ); }
.cwi.has-logo:hover .clogo.is-invert,
.bsc.has-logo:hover .clogo.is-invert { filter: invert( 1 ); }
.cwi.has-logo:hover .clogo,
.bsc.has-logo:hover .clogo {
	filter: none;
	opacity: 1;
}
.bsc .clogo { max-height: 30px; }

@media ( max-width: 640px ) {
	.cwi .clogo,
	.bsc .clogo { max-height: 26px; }
}

/* Founder note: a real portrait replaces the monogram in the same plate. */
.fnportrait .fnav2p { padding: 0; overflow: hidden; }
.fnportrait .fnav2p img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: 50%;
	display: block;
}

/* Brand marquee: a client cell may carry a real mark instead of a name. */
.tblog .clogo {
	max-width: 100%;
	max-height: 34px;
	width: auto;
	height: auto;
	object-fit: contain;
	filter: grayscale(1) brightness(1.7);
	opacity: .62;
	transition: filter .4s var(--ease), opacity .4s var(--ease);
}
.tblog div:hover .clogo { filter: none; opacity: 1; }
.tblog .clogo.is-invert { filter: grayscale(1) invert(1) brightness(1.9); }
.tblog div:hover .clogo.is-invert { filter: invert(1); }

/* Careers roles: the build set this paragraph with an inline style. The widget
   gives it a class instead, so the rules live here rather than on every row. */
.role .rbody {
	font-size: 13px;
	color: var(--mu);
	font-weight: 300;
	margin-top: 9px;
	max-width: 640px;
	line-height: 1.65;
}

/* An ARIA button may hold only phrasing content, and these cards hold a heading
   and a paragraph — which is what made the accessibility tree ill-formed. The
   control moved inside the heading, so the button has to inherit the heading
   completely and leave no trace of itself. */
.exi h3 > .ext,
.ip h4 > .ipt {
	all: unset;
	display: block;
	width: 100%;
	cursor: pointer;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	text-align: inherit;
}
.exi h3 > .ext:focus-visible,
.ip h4 > .ipt:focus-visible {
	outline: 2px solid var(--em);
	outline-offset: 4px;
	border-radius: 4px;
}

/* The carousel arrows are divs the runtime wires up. They are reachable now, so
   they need a focus ring of their own. */
.ar:focus-visible,
.tdt:focus-visible,
.cx:focus-visible {
	outline: 2px solid var(--em);
	outline-offset: 3px;
}

/* The newsletter strip sizes its input by hand — min-width:230px, no flex-grow —
   and Contact Form 7's submit is an <input>, so it inherited that 230px and the
   Subscribe button came out three times its width. The design's submit was an
   <a>, which never matched that selector. */
.nlf input[type="submit"],
.nlf .wpcf7-submit {
	min-width: 0;
	width: auto;
}

/* ==========================================================================
   Thank-you page
   ==========================================================================
   Three pieces the rest of the site had no equivalent for: the hero's
   acknowledgement animation, the two-column layout the next-steps list needs
   to not look lost, and the closing band.
   -------------------------------------------------------------------------- */

/* --- The hero visual (.tyv) ---------------------------------------------
   Same idiom as the contact page's .ctx: one loop, var(--ease), no runtime.
   6s, because the four beats need room to read as a sequence rather than a
   flicker, and because the page is not asking anyone to watch it twice.     */

.tyv { position: relative; width: 300px; height: 320px;
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; }

.tystage { position: relative; width: 100%; height: 188px; display: grid; place-items: center; }

/* Two rings, half a second apart, so the acknowledgement reads as a pulse. */
.tyring { position: absolute; width: 128px; height: 128px; border-radius: 50%;
	border: 1px solid rgba( 0, 245, 160, .5 ); animation: tyring 6s var( --ease ) infinite; }
.tyring:nth-of-type( 2 ) { animation-delay: .45s; }
@keyframes tyring {
	0%, 30% { transform: scale( .5 ); opacity: 0; }
	46%     { opacity: .9; }
	100%    { transform: scale( 1.7 ); opacity: 0; }
}

/* The brief: three lines that fill, then hand over. */
.tycard { position: absolute; width: 214px; border-radius: 16px; border: 1px solid var( --line );
	background: #0b0e11; padding: 16px; box-shadow: 0 16px 40px rgba( 0, 0, 0, .5 );
	animation: tycard 6s var( --ease ) infinite; }
@keyframes tycard {
	0%        { transform: translateY( 14px ) scale( .96 ); opacity: 0; }
	8%, 24%   { transform: none; opacity: 1; }
	34%, 100% { transform: translateY( -10px ) scale( .9 ); opacity: 0; }
}
.tycard .cxh { display: flex; align-items: center; gap: 9px; margin-bottom: 13px; }
.tycard .cxh span { width: 28px; height: 28px; border-radius: 9px; background: rgba( 255, 255, 255, .05 );
	border: 1px solid var( --line ); display: flex; align-items: center; justify-content: center; color: var( --em ); }
.tycard .cxh span > svg { width: 14px; height: 14px; }
.tycard .cxh b { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var( --fa ); font-weight: 700; }
.tycard i { display: block; height: 7px; border-radius: 4px; background: rgba( 255, 255, 255, .09 );
	margin-bottom: 8px; transform-origin: left; animation: tyline 6s var( --ease ) infinite; }
.tycard i:nth-of-type( 1 ) { width: 100%; animation-delay: .1s; }
.tycard i:nth-of-type( 2 ) { width: 84%; animation-delay: .3s; }
.tycard i:nth-of-type( 3 ) { width: 56%; animation-delay: .5s; background: rgba( 0, 245, 160, .42 ); margin-bottom: 0; }
@keyframes tyline {
	0%, 4%    { transform: scaleX( 0 ); opacity: 0; }
	16%, 28%  { transform: scaleX( 1 ); opacity: 1; }
	34%, 100% { transform: scaleX( 1 ); opacity: 0; }
}

/* The acknowledgement. The tick draws rather than appearing. */
.tybadge { position: absolute; width: 106px; height: 106px; border-radius: 50%; background: var( --grad );
	display: flex; align-items: center; justify-content: center; color: var( --ink );
	box-shadow: 0 24px 64px -18px rgba( 0, 245, 160, .6 ); animation: typop 6s var( --ease ) infinite; }
@keyframes typop {
	0%, 30%   { transform: scale( .35 ); opacity: 0; }
	44%       { transform: scale( 1.07 ); opacity: 1; }
	54%, 100% { transform: scale( 1 ); opacity: 1; }
}
.tybadge svg { width: 48px; height: 48px; }
.tybadge path { fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
	stroke-dasharray: 34; stroke-dashoffset: 34; animation: tydraw 6s var( --ease ) infinite; }
@keyframes tydraw {
	0%, 42%   { stroke-dashoffset: 34; }
	60%, 100% { stroke-dashoffset: 0; }
}

.tyref { position: absolute; top: 4px; right: 6px; font-size: 10px; font-weight: 700; padding: 6px 12px;
	border-radius: 999px; background: rgba( 139, 92, 246, .16 ); border: 1px solid rgba( 139, 92, 246, .45 );
	color: #c4b5fd; animation: tyref 6s var( --ease ) infinite; }
@keyframes tyref {
	0%, 46%   { opacity: 0; transform: translateY( -6px ); }
	58%, 100% { opacity: 1; transform: none; }
}

.tyreply { border-radius: 16px; border: 1px solid rgba( 0, 245, 160, .4 ); background: rgba( 0, 245, 160, .08 );
	padding: 14px 16px; display: flex; align-items: center; gap: 12px; opacity: 0;
	animation: tyreply 6s var( --ease ) infinite; }
@keyframes tyreply {
	0%, 62%   { opacity: 0; transform: translateY( 12px ); }
	76%, 100% { opacity: 1; transform: none; }
}
.tyreply .cravt { width: 36px; height: 36px; border-radius: 50%; background: var( --grad );
	display: flex; align-items: center; justify-content: center; font-family: var( --fd ); font-size: 15px;
	color: var( --ink ); flex-shrink: 0; }
.tyreply b { font-size: 13px; display: block; }
.tyreply span { font-size: 11.5px; color: var( --mu ); }

/*
 * Motion here is decoration; the final frame is what carries the meaning.
 * So when motion is unwelcome the sequence does not slow down, it stops on
 * the frame that says the thing.
 */
@media ( prefers-reduced-motion: reduce ) {
	.tyring,
	.tycard { display: none; }
	.tybadge,
	.tyreply,
	.tyref,
	.tybadge path { animation: none; opacity: 1; transform: none; stroke-dashoffset: 0; }
}

@media ( max-width: 900px ) {
	.tyv { width: 100%; max-width: 300px; margin-inline: auto; }
}

/* --- Next steps, two columns --------------------------------------------
   .nxt carries max-width:430px from the CTA band it was designed for. On its
   own in a full-width section that leaves 700px of nothing beside it, so the
   panel fills the space with the questions the call will open with.          */

.tycols { display: grid; grid-template-columns: 430px 1fr; gap: 60px; align-items: start; }
@media ( max-width: 900px ) { .tycols { grid-template-columns: 1fr; gap: 32px; } }

.typanel { border: 1px solid var( --line ); border-radius: var( --rm ); background: var( --card ); padding: 28px 30px; }
.typanel h3 { font-family: var( --fd ); font-size: 21px; line-height: 1.25; margin-bottom: 7px; }
.typanel > p { color: var( --mu ); font-size: 13.5px; line-height: 1.6; margin-bottom: 6px; }
.tyq { display: flex; align-items: flex-start; gap: 12px; padding: 14px 0; border-top: 1px solid var( --line ); }
.tyq:first-of-type { margin-top: 12px; }
.tyq > span { flex-shrink: 0; width: 22px; height: 22px; border-radius: 7px; margin-top: 1px;
	background: rgba( 0, 245, 160, .12 ); border: 1px solid rgba( 0, 245, 160, .35 );
	display: flex; align-items: center; justify-content: center; color: var( --em ); }
.tyq > span svg { width: 12px; height: 12px; }
.tyq b { display: block; font-size: 13.5px; margin-bottom: 2px; }
.tyq p { color: var( --mu ); font-size: 12.5px; line-height: 1.55; }

/* --- Closing band -------------------------------------------------------- */

.tyend { border: 1px solid var( --line ); border-radius: var( --rl ); background: var( --lift );
	padding: 58px 40px; text-align: center; }
.tyend h2 { margin-bottom: 12px; }
.tyend p { color: var( --mu ); max-width: 560px; margin: 0 auto 26px; }
.tyend .hc { justify-content: center; }
@media ( max-width: 700px ) { .tyend { padding: 42px 22px; } }

/* --- Archive pagination -------------------------------------------------
   Only ever seen on a category or tag deep enough to have a second page, so
   it borrows the chip's shape rather than introducing another control style. */

.pag { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 38px; }
.pag .page-numbers { display: inline-flex; align-items: center; justify-content: center;
	min-width: 40px; height: 40px; padding: 0 14px; border-radius: 999px;
	border: 1px solid var( --line ); color: var( --mu ); font-size: 13px; font-weight: 600;
	transition: .3s var( --ease ); }
.pag .page-numbers:hover { border-color: var( --line2 ); color: var( --tx ); }
.pag .page-numbers.current { background: var( --grad ); border-color: transparent; color: var( --ink ); }
.pag .dots { border-color: transparent; }

/* --- Link card plates ---------------------------------------------------
   A flat gradient with a label on it reads as an image that did not load.
   The motif is a large ghosted glyph with three bars drifting behind it —
   enough to look considered, cheap enough to cost nothing: no requests, no
   layout, and it inherits the plate's own colour so every card stays on
   palette without a second decision.                                        */

.arc .arcv { position: relative; overflow: hidden; }

.arcart { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }

.arcart i { position: absolute; left: 14%; height: 8px; border-radius: 5px;
	background: rgba( 255, 255, 255, .16 ); animation: arcdrift 9s var( --ease ) infinite; }
.arcart i:nth-of-type( 1 ) { top: 30%; width: 46%; animation-delay: 0s; }
.arcart i:nth-of-type( 2 ) { top: 46%; width: 62%; animation-delay: -3s; }
.arcart i:nth-of-type( 3 ) { top: 62%; width: 34%; animation-delay: -6s; }
@keyframes arcdrift {
	0%, 100% { transform: translateX( 0 );    opacity: .5; }
	50%      { transform: translateX( 14px ); opacity: .95; }
}

.arcglyph { position: relative; display: flex; align-items: center; justify-content: center;
	width: 88px; height: 88px; border-radius: 26px;
	background: rgba( 0, 0, 0, .22 ); border: 1px solid rgba( 255, 255, 255, .16 );
	backdrop-filter: blur( 6px ); color: #fff;
	animation: arcfloat 7s var( --ease ) infinite; }
.arcglyph svg { width: 40px; height: 40px; opacity: .92; }
@keyframes arcfloat {
	0%, 100% { transform: translateY( 0 ); }
	50%      { transform: translateY( -7px ); }
}

/* The card is a link, so the motif answers the pointer rather than sitting inert. */
.arc:hover .arcglyph { transform: translateY( -7px ) scale( 1.05 ); animation-play-state: paused; }
.arc:hover .arcart i { opacity: 1; }

@media ( prefers-reduced-motion: reduce ) {
	.arcart i,
	.arcglyph { animation: none; }
}

@media ( max-width: 760px ) {
	.arcglyph { width: 70px; height: 70px; border-radius: 22px; }
	.arcglyph svg { width: 32px; height: 32px; }
}

/* The header is fixed, so an in-page jump has to stop short of it. */
.sy-anchor { scroll-margin-top: 104px; }

/* A glyph inside a button sits on the text baseline, not above it. */
.btn > .ic { display: inline-flex; align-items: center; width: 16px; height: 16px; margin-right: -2px; }
.btn > .ic svg { width: 100%; height: 100%; }

/* --- Article shell ------------------------------------------------------
   The two nudges the captured articles carried inline. They live here rather
   than in a per-element CSS field because a 20px offset that only exists in
   the database is a 20px offset nobody can find. */

.sy-pt20 { padding-top: 20px; }
.sy-mt20 { margin-top: 20px; }

/* The contents list, built from the article's own headings. */
.artoc { display: flex; flex-direction: column; gap: 2px; }
.artoc a { font-size: 12.5px; line-height: 1.5; color: var( --mu ); padding: 6px 0;
	border-top: 1px solid var( --line ); transition: color .3s var( --ease ); }
.artoc a:first-child { border-top: none; }
.artoc a:hover { color: var( --em ); }
.artoc a.sub { padding-left: 14px; font-size: 12px; color: var( --fa ); }

/* ==========================================================================
   Article sidebar spacing
   ==========================================================================
   The panels were 16px apart with 24px of padding inside them, so the gap
   between two cards read as tighter than the gap between a card's border and
   its own content — which is what makes a stack look crowded rather than
   spaced. And the contents links sat flush against the panel's left edge,
   touching the border they were meant to sit inside.
   -------------------------------------------------------------------------- */

.artside { gap: 22px; }

.artoc a { padding-left: 12px; }
.artoc a.sub { padding-left: 26px; }

/* The rule between entries starts where the text does, not at the border. */
.artoc a { border-top-color: var( --line ); }

@media ( max-width: 980px ) {
	.artside { gap: 18px; }
}

/* ==========================================================================
   The logo wall below desktop
   ==========================================================================
   Under 980px main.css turns the six-column grid into a horizontal scroller.
   Its item widths were 46% on tablet and 62% on phones, which shows two and a
   half logos on an iPad and one and a half on a phone — and a half a logo
   reads as a rendering fault, not as "there is more to the right".

   Whole logos only: three across on a tablet, two on a phone.
   -------------------------------------------------------------------------- */

@media ( max-width: 980px ) {
	.cwx { gap: 10px; }
	.cwx > * { flex: 0 0 31%; }
	.cwx .cwi { padding: 22px 12px; border-radius: var( --rs ); border: 1px solid var( --line ); }
}

@media ( max-width: 620px ) {
	.cwx > * { flex: 0 0 46%; }
	.cwx .cwi { padding: 20px 10px; min-height: 76px; }
}

/*
 * Scroll snapping and a continuous auto-scroll are the same mechanism pulling
 * in opposite directions: the snap drags the wall back to the nearest logo the
 * moment the script nudges it forward. The script turns snapping off on the
 * walls it drives, and leaves it on wherever it does not run - a phone with
 * reduced motion still gets a snapping swipe.
 */

/* ==========================================================================
   Featured image in the hero
   ==========================================================================
   .imph is a dashed upload placeholder. With a real image in it, the dashes,
   the grid and the corner marks are all describing something that is no longer
   missing — so the frame becomes a plain rounded window and gets out of the
   way.
   -------------------------------------------------------------------------- */

.imph.is-set { padding: 0; border: 1px solid var( --line ); background: var( --card ); overflow: hidden; }
.imph.is-set::before { content: none; }
.imph.is-set:hover { border-color: var( --line2 ); background: var( --card ); }
.imphimg { display: block; width: 100%; height: 100%; min-height: inherit; object-fit: cover; border-radius: inherit; }

/* A real image in a case-study figure gets the frame the placeholder had. */
.csfig .csfigimg { display: block; width: 100%; height: auto; border-radius: 16px;
	border: 1px solid var( --line ); background: var( --card ); }

/*
 * The sidebar panels are siblings inside the widget's own wrapper, not
 * children of the aside — so aside.artside's column gap applies to exactly one
 * element (the wrapper) and never reaches the panels, which is why they sat
 * flush against one another top to bottom. Space them directly instead, which
 * holds however deeply the builder wraps them.
 */
.artside .asbox + .asbox { margin-top: 22px; }

@media ( max-width: 980px ) {
	.artside .asbox + .asbox { margin-top: 18px; }
}

/* ==========================================================================
   The client band below the hero, on small screens
   ==========================================================================
   .bsgrid is the second logo surface — "Trusted by brands across 20+
   countries" on the homepage and the landing pages — and it had the same
   problem as the wall, worse: 58% cells on a tablet and 78% on a phone, which
   is one logo and a slice of the next. Same rule as the wall, so both logo
   surfaces on the site behave identically.
   -------------------------------------------------------------------------- */

@media ( max-width: 980px ) {
	.bsgrid { gap: 10px; padding: 10px 0; border-top: 1px solid var( --line ); }
	.bsgrid > * { flex: 0 0 31%; }
	.bsgrid .bsc { border-right: none; border: 1px solid var( --line ); border-radius: var( --rs );
		min-height: 92px; background: var( --bg ); }
}

@media ( max-width: 620px ) {
	.bsgrid > * { flex: 0 0 46%; }
	.bsgrid .bsc { min-height: 84px; }
}


/* ==========================================================================
   Logo marquee (below 980px)
   ==========================================================================
   Both logo surfaces become horizontal scrollers on small screens. Nudging
   scrollLeft a fraction of a pixel per frame does not work: browsers round the
   value back to whole pixels, so the position never accumulates and the row
   twitches instead of travelling — and scroll snapping drags it back besides.

   So it does not scroll. main.js moves the cells into a track and duplicates
   them once; this translates the track by exactly one set, which loops with no
   seam, runs on the compositor rather than the main thread, and stops on hover
   or when the visitor has asked for less motion.
   -------------------------------------------------------------------------- */

@media ( max-width: 980px ) {

	.cwx.is-marquee,
	.bsgrid.is-marquee { display: flex !important; overflow: hidden; gap: 0; scroll-snap-type: none; }

	.is-marquee .sytrack {
		display: flex;
		gap: 10px;
		width: max-content;
		/* One cell per three seconds reads as unhurried at any list length. */
		animation: sy-marquee calc( var( --sy-n, 12 ) * 3s ) linear infinite;
	}

	/* The width itself is written onto each cell by main.js, together with the
	   travel distance derived from it, so the two can never disagree. */
	.is-marquee .sytrack > * { flex: 0 0 auto; }

	/*
	 * Exactly one set, measured in JavaScript rather than derived from a cell
	 * width — the cells do not always end up the width the variable asks for,
	 * and a thirteen-pixel error times twelve is a jump every time the loop
	 * comes round.
	 */
	@keyframes sy-marquee {
		to { transform: translateX( calc( -1 * var( --sy-shift, 100% ) ) ); }
	}

	.is-marquee:hover .sytrack { animation-play-state: paused; }
}

@media ( prefers-reduced-motion: reduce ) {
	.is-marquee .sytrack { animation: none; }
	.cwx.is-marquee,
	.bsgrid.is-marquee { overflow-x: auto; }
}

/* ==========================================================================
   FINAL AUDIT — round 1
   ========================================================================== */

/* ---- the industries accordion stops collapsing on phones and tablets ------
   Below 900px the accordion stacks, and main.css gives the panels explicit
   heights for that: 104px closed, 260px open. A later unmediated rule -
   `.ip.on{flex:3.6}` from the round-18 slider work - reintroduced flex-basis:0
   on the open panel. In a column flex container flex-basis IS the height, so
   it beat the 260px and the open panel rendered as a two-pixel sliver with its
   content clipped away entirely.

   The visible result: "Industries we serve" opened on 01 Retail & eCommerce
   and showed 02, 03 and 04 with nothing above them. The first sector was
   simply absent from the page, on every screen under 900px.

   flex:0 0 auto returns the height to being the height. Scoped through .iacc
   so it outranks the bare .ip.on rule without having to be moved above it. */
@media ( max-width: 900px ) {
	.iacc .ip { flex: 0 0 auto; height: 104px; }
	.iacc .ip.on { flex: 0 0 auto; height: 260px; }
}

/* ---- "Currently serving" stops fighting for width on a phone -------------
   .gwcov is a flex row: an uppercase label, a bag of country chips, and a
   button. At 760px it already drops the button to full width, but the label
   keeps white-space:nowrap and its own column, which on a 390px screen leaves
   the chips about 190px to work in - so eleven countries stack nearly one per
   line beside a mostly empty left column.

   Below 560px the three parts become three rows. The label reads as a heading,
   which is what it is. */
@media ( max-width: 560px ) {
	.gwcov { flex-direction: column; align-items: stretch; gap: 12px; }
	.gwcovl { white-space: normal; }
	.gwcov .gwcc { width: 100%; }
}

/* ---- the footer drops to one column on a phone ---------------------------
   Two columns at 390px gives each about 168px, and the address does not fit:
   "Om Tower, 7th / Floor", "32 Chowringhee / Road", "Mon-Fri . 10:30- / 22:30
   IST". Every line breaks in the wrong place. One column is longer and reads
   correctly, which is the right trade on a screen you scroll anyway. */
@media ( max-width: 560px ) {
	.fg { grid-template-columns: 1fr; gap: 30px; }
}

/* ---- the "In a hurry?" pill stops pushing the page sideways ---------------
   .avail is an inline-flex pill with flex-wrap:nowrap holding a whole
   sentence: "In a hurry? - call +91 70034 16634 or email hello@syscodes.in and
   reference your enquiry." It cannot fit on a 390px screen, and because it
   refuses to wrap it measured 392px inside a 350px column - which made the
   thank-you page 412px wide and gave the whole document a horizontal scroll.

   A visitor arriving on that page has just submitted a form; a page that
   slides under the thumb is a poor first impression of the follow-through.
   Below 640px the pill wraps like the paragraph it actually is. */
@media ( max-width: 640px ) {
	.avail {
		display: flex;
		flex-wrap: wrap;
		max-width: 100%;
		border-radius: 18px;
		row-gap: 2px;
	}
}

/* ---- the contact map plate gets enough room to show the business ---------
   The embed is now a place embed rather than an address search, so it carries
   our own marker and the name chip beside it. Google centres both in the
   frame, and at 300px tall in a 513px-wide plate the chip was landing under
   the "Open in Google Maps" pill and the marker sat almost on the bottom edge.

   380px is the height at which the marker, its label and enough surrounding
   street to recognise the area are all in view at once. The phone breakpoint
   keeps a shorter plate, because there the map is a reference rather than
   something anyone navigates by. */
.cmap { min-height: 380px; }

@media ( max-width: 960px ) {
	.cmap { min-height: 300px; }
}

/* ---- the header's right-hand pair, and the nav beside it -----------------
   The WhatsApp pill and "Start a project" sit next to each other, and they
   were not the same height: 39px against 47px. Their centres lined up, which
   is why nothing looked broken, but two pills of different heights on one row
   read as misaligned rather than as a hierarchy - the eye compares the top and
   bottom edges before it finds the centre.

   Matching the height is the whole fix. 12px of vertical padding against the
   button's 13px, because the pill carries a 1px border the button does not,
   and 12 + 1 is 13.

   The glyph goes to 17px as well. A filled mark reads smaller than a stroked
   one at the same box size - it has no interior white space to lend it
   presence - so the 14px WhatsApp icon looked undersized beside 14px outline
   icons elsewhere in the chrome.

   The label stays muted at weight 500. It is the secondary action on the row
   and matching the button's weight would have the two competing. */
.nav-r .ph {
	font-size: 13.5px;
	font-weight: 500;
	gap: 9px;
	padding: 12px 18px;
}

.nav-r .ph .ic {
	width: 17px;
	height: 17px;
}

/* 13.5px was a size chosen for a nav that also had to survive a 1000px laptop.
   It is the primary navigation of the site and it is read at a glance, so it
   takes the extra pixel. The chevron follows it up so the pairing keeps its
   proportion. */
.nl a.top {
	font-size: 14.5px;
	padding: 10px 17px;
}

.nl .navcv {
	width: 14px;
	height: 14px;
}

/* The pill never wraps, and on a small laptop it drops its label.

   Growing it to match the button cost about twenty pixels, and at 1024px -
   where the full desktop nav is still on screen, because .nl only hides below
   1000 - that was twenty pixels the row did not have. The number broke onto a
   second line and took the whole header to 68px tall.

   So: it does not wrap, and between the nav's own breakpoint and 1140px it
   becomes the glyph alone in a circle. The number is a nice thing to show and
   the action is the necessary one; when only one of them fits, it is the
   action. aria-label carries "Chat on WhatsApp" either way, so nothing is lost
   to anyone reading with a screen reader. */
.nav-r .ph {
	white-space: nowrap;
	flex-shrink: 0;
}

@media ( max-width: 1140px ) {
	.nav-r .ph {
		/* font-size:0 rather than a selector, because the number is a bare text
		   node beside the icon span - there is nothing to hide it by. The SVG
		   is sized in pixels, so zero font-size does not touch it. */
		font-size: 0;
		gap: 0;
		width: 47px;
		/* Height stated, not inferred. With the label gone the line box is the
		   17px glyph, so padding alone gave a 43px circle beside a 47px button
		   - the very mismatch this set out to remove. */
		height: 47px;
		padding: 0;
		justify-content: center;
	}
}

/* And the burger joins them at 47.

   It was 44px, which is the accessible minimum and a perfectly good number on
   its own. Sitting third in a row of three it was 3px short at the top and 3px
   short at the bottom, and once the other two matched each other that gap was
   the only thing left out of line. Still comfortably above the minimum. */
.nav-r .burger {
	width: 47px;
	height: 47px;
}

/* ==========================================================================
   THE HERO DASHBOARD
   ========================================================================== */

/* The panel was drawn as an illustration of a dashboard rather than as one.

   Six wide bars in saturated green, a bright line tracing the same numbers the
   bars already drew, four hairlines nobody reads as a grid, no axis, no
   baseline, no legend, and a glow on the tallest bar that bled into its
   neighbour. Every element was turned up, and none of them said anything the
   others did not.

   What a real instrument does instead: one accent, used once. Quantities you
   can read off an axis. Two series that mean two different things. Restraint
   everywhere else. That is the whole change.

   The one honest addition is the second series: the line now carries the
   conversion rate rather than repeating revenue, so the picture makes the
   argument the copy makes - conversion moved first, revenue followed. */

/* ---- the plot and its value axis ---------------------------------------- */
.dchart {
	display: flex;
	gap: 10px;
	margin-bottom: 0;
}

.dyax {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	/* Fixed, so the plot starts at the same x whatever the numbers say, and
	   the month labels below can be offset by a number rather than a guess. */
	width: 34px;
	flex-shrink: 0;
	padding-bottom: 1px;
}

.dyax i {
	font-style: normal;
	font-size: 8.5px;
	font-weight: 600;
	line-height: 1;
	text-align: right;
	color: var( --fa );
	opacity: .6;
	font-variant-numeric: tabular-nums;
	/* Half a line up, so each label reads as sitting ON its gridline rather
	   than in the band beneath it. */
	transform: translateY( -3px );
}

.dplot {
	position: relative;
	flex: 1;
	min-width: 0;
	height: 126px;
}

/* ---- the column tracks --------------------------------------------------
   A faint column behind every bar, so a short bar reads as a low value rather
   than as a small object floating above nothing.

   Drawn as a repeating gradient rather than a wrapper per bar, because the bar
   is the element the runtime scales to reveal it - a track inside it would
   grow with it. The 2% gap is what makes this exact: six 15% columns and five
   2% gaps land the pattern flush on the right edge at any width, and put every
   column centre on a round fraction the trend line can be plotted against. A
   pixel gap could do neither. */
.dplot::before {
	content: '';
	position: absolute;
	inset: 0;
	background: repeating-linear-gradient( 90deg,
		rgba( 255, 255, 255, .022 ) 0 15%,
		transparent 15% 17% );
}

/* ---- the grid gets a floor ---------------------------------------------- */
.dplot .dgrid span { height: 0; background: none; border-top: 1px dashed rgba( 255, 255, 255, .05 ); }
.dplot .dgrid span:last-child { border-top: 1px solid rgba( 255, 255, 255, .13 ); }

/* ---- the bars -----------------------------------------------------------
   Quiet. All of them but one.

   Highlighting the current period and leaving the rest neutral is the oldest
   idiom in analytics, and it is the reason the sixth bar looked wrong before:
   it was not the accent in a calm series, it was the loudest of six loud
   things, wearing a glow that spilled onto M5.

   The cap across the top few per cent is what stops a bar reading as a slab -
   it terminates in a value marker rather than just stopping. */
.dbars { gap: 2%; }

.dbars i {
	border-radius: 5px 5px 2px 2px;
	background: linear-gradient( 180deg,
		rgba( 210, 230, 240, .17 ) 0,
		rgba( 210, 230, 240, .09 ) 24%,
		rgba( 210, 230, 240, .028 ) 100% );
	box-shadow: inset 0 1px 0 rgba( 255, 255, 255, .16 );
}

.dbars i:last-child {
	background: linear-gradient( 180deg,
		rgba( 0, 245, 160, .72 ) 0,
		rgba( 0, 245, 160, .3 ) 26%,
		rgba( 0, 245, 160, .05 ) 100% );
	box-shadow: inset 0 1px 0 rgba( 255, 255, 255, .45 );
}

/* ---- the two series, named ----------------------------------------------
   In the header row, not on one of its own. The panel lives in a fixed stage
   and a legend that costs 24px pushes the title up under the chip that hangs
   off the top-left corner - which is exactly what the first attempt did. */
.dhd { align-items: center; }

.dleg {
	display: inline-flex;
	align-items: center;
	gap: 12px;
	margin-left: auto;
	margin-right: 12px;
	font-size: 8.5px;
	font-weight: 600;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: var( --fa );
	white-space: nowrap;
}

.dleg > span { display: inline-flex; align-items: center; gap: 5px; }
.dleg i { width: 7px; height: 7px; border-radius: 2px; flex-shrink: 0; }
.dleg .lgb { background: rgba( 0, 245, 160, .55 ); }
.dleg .lgl { border-radius: 999px; background: linear-gradient( 90deg, #00f5a0, #8b5cf6 ); }

/* ---- the month axis -----------------------------------------------------
   Offset by the value axis and its gap, so a label sits under its own column. */
.dxax {
	display: flex;
	gap: 2%;
	margin: 7px 0 11px;
}

.dxax[ data-gutter ] { padding-left: 44px; }

.dxax i {
	flex: 1;
	min-width: 0;
	text-align: center;
	font-style: normal;
	font-size: 8.5px;
	font-weight: 600;
	letter-spacing: .1em;
	color: var( --fa );
	opacity: .65;
	font-variant-numeric: tabular-nums;
}

.dxax i:last-child { color: var( --em ); opacity: 1; }

/* ---- the metric cards ---------------------------------------------------
   Label, then figure and delta on one line, then the shape of the six months.
   The card was a lot of empty space beside a number; the sparkline is the
   thing a reader actually wants next to "5% from 2%", and it is six divs. */
.dfoot .dk { gap: 7px; }
.dkr { display: flex; align-items: baseline; gap: 8px; }

/* The sparkline is a line, not six bars.

   Six wide blocks under a large number read as a segmented progress bar - a
   different claim entirely, and one the panel is not making. The mark had to
   change, not its proportions. */
.dsp {
	display: block;
	height: 26px;
	margin-top: 6px;
}

.dsp svg { display: block; width: 100%; height: 100%; overflow: visible; }
.dspf { fill: rgba( 0, 245, 160, .12 ); }

.dspl {
	fill: none;
	stroke: rgba( 0, 245, 160, .8 );
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
	/* The viewBox is stretched to the card's width; without this the stroke
	   stretches with it and the line thins out horizontally. */
	vector-effect: non-scaling-stroke;
}

/* The trend line, thinner and without the halo it used to need to be seen
   over six bright bars. */
.dpath { stroke-width: 1.9; }
.ddot.go { r: 3.4; }

/* The revenue chip clears the panel title.

   It hangs off the top-left corner at top:9%, which was clear of "STORE
   REVENUE" while the panel was 430px tall. The panel is taller now - it
   carries a value axis and a month axis it did not have - and a taller panel
   in a fixed stage starts higher, which walked the title up under the chip.

   Nudged up rather than out: further left would push it past the hero column
   on a narrow desktop. Desktop only, because below 1000px the chips are
   repositioned wholesale and this would fight that. */
@media ( min-width: 1001px ) {
	.stage .f1 { top: 5.5%; }
}

/* ---- the panel and its chips on small screens ---------------------------
   Below 1000px the stage is pinned to 410px, a number chosen when the panel
   was 410px tall. The panel carries a value axis, a month axis and two
   sparklines now and stands at 472, so it overflowed its own stage by 62px -
   and because the floating chips are positioned against the stage rather than
   against the panel, the bottom chip stopped sitting under the metric cards
   and started sitting on top of the conversion figure.

   Letting the stage size to its contents fixes both at once. Nothing is lost:
   the fixed height existed to hold a composition that only exists on desktop,
   where the hero is two columns and the panel is tilted in perspective. */
@media ( max-width: 1000px ) {
	.stage { height: auto; }
}

/* The legend is desktop-only.

   Three things share the panel header - title, legend, range toggle - and at
   350px there is room for two. The legend was the one that lost: the metric
   cards below name both numbers anyway, so on a phone it repeats what is
   already there while pushing "STORE REVENUE" out of the row. */
@media ( max-width: 1000px ) {
	.dleg { display: none; }
}

/* ==========================================================================
   THE CHAT BOX
   ========================================================================== */

.sychat { position: fixed; right: 24px; bottom: 24px; z-index: 900; font-family: var( --fb ); }

@media ( max-width: 520px ) {
	.sychat { right: 14px; bottom: 14px; left: 14px; }
}

/* ---- launcher ---- */
.sylaunch {
	position: absolute; right: 0; bottom: 0;
	width: 58px; height: 58px; border-radius: 50%; border: 0; cursor: pointer;
	background: var( --grad ); color: var( --ink );
	display: grid; place-items: center;
	box-shadow: 0 16px 40px -12px rgba( 0, 245, 160, .55 ), inset 0 0 0 1px rgba( 255, 255, 255, .09 );
	transition: transform .4s var( --ease ), box-shadow .4s var( --ease );
}

.sylaunch:hover { transform: translateY( -3px ) scale( 1.04 ); }
.sylaunch svg { width: 24px; height: 24px; display: block; }
.sylaunch .ico-chat { display: grid; place-items: center; }
.sylaunch .ico-close { display: none; }
.sychat.is-open .sylaunch { background: var( --card2 ); color: var( --tx ); box-shadow: 0 10px 30px -14px #000, inset 0 0 0 1px var( --line2 ); }
.sychat.is-open .sylaunch .ico-chat { display: none; }
.sychat.is-open .sylaunch .ico-close { display: block; }

/* the unread dot: the launcher has to read as "there is something here" */
.sylaunch::after {
	content: ''; position: absolute; top: 3px; right: 3px;
	width: 11px; height: 11px; border-radius: 50%;
	background: var( --pk ); border: 2px solid var( --bg );
	opacity: 0; transform: scale( .4 ); transition: .35s var( --ease );
}

.sychat.has-ping .sylaunch::after { opacity: 1; transform: scale( 1 ); }
.sychat.is-open .sylaunch::after { opacity: 0; }

/* ---- the peek ---- */
.sypeek {
	position: absolute; right: 74px; bottom: 8px;
	width: max-content; max-width: 250px;
	background: var( --card2 ); border: 1px solid var( --line2 );
	border-radius: 16px 16px 4px 16px; padding: 13px 16px;
	font-size: 13px; line-height: 1.5; color: var( --tx );
	box-shadow: 0 20px 50px -20px rgba( 0, 0, 0, .9 );
	opacity: 0; transform: translateY( 8px ) scale( .96 ); transform-origin: 100% 100%;
	pointer-events: none; transition: .5s var( --ease );
}

.sychat.has-peek .sypeek { opacity: 1; transform: none; pointer-events: auto; }
.sypeek b { display: block; font-weight: 600; margin-bottom: 2px; }
.sypeek span { color: var( --mu ); font-size: 12px; }

/* 26, not 22. The audit counts anything under 24px as a tap target nobody can
   reliably hit, and it was right to: this is the one control on the widget a
   visitor presses when they want it to go away, and missing it opens the chat
   instead. */
.sypeekx {
	position: absolute; top: -10px; right: -10px; width: 26px; height: 26px;
	border-radius: 50%; background: var( --panel ); border: 1px solid var( --line2 );
	color: var( --mu ); cursor: pointer; display: grid; place-items: center;
	font-size: 13px; line-height: 1; padding: 0;
}

.sypeekx:hover { color: var( --tx ); }

@media ( max-width: 520px ) {
	.sypeek { right: 74px; left: 0; max-width: none; }
}

/* ---- panel ---- */
.sypanel {
	position: absolute; right: 0; bottom: 74px;
	width: 378px; height: min( 560px, calc( 100vh - 130px ) );
	display: flex; flex-direction: column; overflow: hidden;
	background: linear-gradient( 180deg, rgba( 16, 20, 24, .96 ), rgba( 8, 10, 13, .98 ) );
	backdrop-filter: blur( 26px ) saturate( 1.4 );
	border: 1px solid var( --line2 ); border-radius: var( --rl );
	box-shadow: 0 40px 90px -30px rgba( 0, 0, 0, .95 ), inset 0 0 0 1px rgba( 255, 255, 255, .03 );
	opacity: 0; transform: translateY( 14px ) scale( .97 ); transform-origin: 100% 100%;
	pointer-events: none;
	transition: opacity .38s var( --ease ), transform .38s var( --ease );
}

.sychat.is-open .sypanel { opacity: 1; transform: none; pointer-events: auto; }
.sypanel::before { content: ''; position: absolute; inset: 0 0 auto; height: 2px; background: var( --grad ); opacity: .85; }

@media ( max-width: 520px ) {
	.sypanel { width: auto; left: 0; right: 0; height: min( 74vh, 560px ); }
}

/* ---- head ---- */
.syhead { display: flex; align-items: center; gap: 12px; padding: 18px 18px 14px; border-bottom: 1px solid var( --line ); flex-shrink: 0; }

.syav {
	width: 38px; height: 38px; border-radius: 12px; flex-shrink: 0;
	display: grid; place-items: center;
	background: linear-gradient( 150deg, rgba( 0, 245, 160, .2 ), rgba( 139, 92, 246, .22 ) );
	border: 1px solid rgba( 255, 255, 255, .1 ); color: var( --em );
}

.syav svg { width: 19px; height: 19px; }
.syhead h4 { font-size: 14px; font-weight: 600; letter-spacing: -.01em; margin: 0; }
.syhead .syon { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var( --mu ); margin-top: 3px; }
.syhead .syon i { width: 6px; height: 6px; border-radius: 50%; background: var( --em ); box-shadow: 0 0 0 3px rgba( 0, 245, 160, .16 ); }

/* A close inside the panel as well as the launcher: on a phone the launcher is
   behind the sheet, and reaching past a dialog to dismiss it is not a gesture. */
.syclose {
	margin-left: auto; width: 30px; height: 30px; flex-shrink: 0;
	background: none; border: 1px solid var( --line ); border-radius: 9px;
	color: var( --mu ); cursor: pointer; display: grid; place-items: center; transition: .25s;
}

.syclose:hover { color: var( --tx ); border-color: var( --line2 ); }
.syclose svg { width: 14px; height: 14px; }

/* ---- progress ---- */
.syprog { height: 2px; background: rgba( 255, 255, 255, .05 ); flex-shrink: 0; }
.syprog i { display: block; height: 100%; width: 0; background: var( --grad ); transition: width .55s var( --ease ); }

/* ---- log ---- */
.sylog { flex: 1; overflow-y: auto; padding: 20px 18px 8px; display: flex; flex-direction: column; gap: 12px; scrollbar-width: thin; }
.sylog::-webkit-scrollbar { width: 5px; }
.sylog::-webkit-scrollbar-thumb { background: rgba( 255, 255, 255, .11 ); border-radius: 9px; }

.symsg { max-width: 84%; font-size: 13.5px; line-height: 1.6; padding: 11px 14px; border-radius: 16px; animation: syin .42s var( --ease ) both; }

@keyframes syin { from { opacity: 0; transform: translateY( 9px ); } to { opacity: 1; transform: none; } }

.symsg.bot { align-self: flex-start; background: var( --card2 ); border: 1px solid var( --line ); border-bottom-left-radius: 5px; color: var( --tx ); }
.symsg.bot a { color: var( --em ); }
.symsg.me { align-self: flex-end; background: linear-gradient( 140deg, rgba( 0, 245, 160, .17 ), rgba( 139, 92, 246, .17 ) ); border: 1px solid rgba( 0, 245, 160, .24 ); border-bottom-right-radius: 5px; color: var( --tx ); font-weight: 500; }
.symsg small { display: block; font-size: 11.5px; color: var( --mu ); margin-top: 4px; line-height: 1.5; }

.sytyping { align-self: flex-start; display: flex; gap: 4px; padding: 13px 15px; background: var( --card2 ); border: 1px solid var( --line ); border-radius: 16px; border-bottom-left-radius: 5px; }
.sytyping i { width: 5px; height: 5px; border-radius: 50%; background: var( --mu ); animation: sydot 1.3s infinite; }
.sytyping i:nth-child( 2 ) { animation-delay: .18s; }
.sytyping i:nth-child( 3 ) { animation-delay: .36s; }

@keyframes sydot { 0%, 60%, 100% { opacity: .25; transform: translateY( 0 ); } 30% { opacity: 1; transform: translateY( -3px ); } }

/* ---- composer ---- */
.syfoot { border-top: 1px solid var( --line ); padding: 12px 14px 14px; flex-shrink: 0; background: rgba( 0, 0, 0, .22 ); }

.sychips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 10px; }
.sychips:empty { display: none; }

.sychips button {
	font: inherit; font-size: 12px; font-weight: 500; color: var( --tx ); cursor: pointer;
	background: rgba( 255, 255, 255, .04 ); border: 1px solid var( --line2 );
	border-radius: 999px; padding: 8px 13px; transition: .25s var( --ease );
}

.sychips button:hover { border-color: rgba( 0, 245, 160, .45 ); background: rgba( 0, 245, 160, .1 ); color: var( --em ); transform: translateY( -1px ); }
.sychips button.ghost { color: var( --fa ); border-style: dashed; }
.sychips button.ghost:hover { color: var( --mu ); background: rgba( 255, 255, 255, .03 ); border-color: var( --line2 ); }

.syrow { display: flex; gap: 8px; align-items: flex-end; }
.syrow.is-off { opacity: .35; pointer-events: none; }

.syinput { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; background: rgba( 255, 255, 255, .04 ); border: 1px solid var( --line2 ); border-radius: 14px; padding: 0 4px 0 13px; transition: .25s; }
.syinput:focus-within { border-color: rgba( 0, 245, 160, .5 ); background: rgba( 0, 245, 160, .05 ); }
.syinput input { flex: 1; min-width: 0; background: none; border: 0; outline: none; color: var( --tx ); font: inherit; font-size: 13.5px; padding: 12px 0; }
.syinput input::placeholder { color: var( --fa ); }

/* The label is there for a screen reader; the placeholder is there for eyes. */
.syvis { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect( 0 0 0 0 ); clip-path: inset( 50% ); white-space: nowrap; }

.sydial { background: none; border: 0; border-right: 1px solid var( --line ); color: var( --mu ); font: inherit; font-size: 13px; padding: 11px 9px 11px 0; margin-right: 2px; outline: none; cursor: pointer; }
.sydial option { background: var( --panel ); }

.sysend { width: 38px; height: 38px; flex-shrink: 0; border: 0; border-radius: 12px; cursor: pointer; display: grid; place-items: center; background: var( --grad ); color: var( --ink ); transition: .3s var( --ease ); }
.sysend:hover { transform: translateY( -2px ); }
.sysend svg { width: 17px; height: 17px; }

.syfine { margin-top: 9px; font-size: 10.5px; color: var( --fa ); display: flex; align-items: center; gap: 6px; line-height: 1.5; }
.syfine svg { width: 11px; height: 11px; flex-shrink: 0; color: var( --em ); }

/* ---- the close ---- */
.sydone { text-align: center; padding: 8px 4px 4px; animation: syin .5s var( --ease ) both; }
.sydone .tick { width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%; display: grid; place-items: center; background: rgba( 0, 245, 160, .12 ); border: 1px solid rgba( 0, 245, 160, .4 ); color: var( --em ); }
.sydone .tick svg { width: 24px; height: 24px; }
.sydone h5 { font-family: var( --fd ); font-size: 20px; font-weight: 400; margin: 0 0 7px; }
.sydone p { font-size: 13px; color: var( --mu ); line-height: 1.6; max-width: 31ch; margin: 0 auto; }

.syref { display: inline-block; margin-top: 13px; font-size: 11.5px; letter-spacing: .06em; color: var( --em ); background: rgba( 0, 245, 160, .09 ); border: 1px solid rgba( 0, 245, 160, .28 ); border-radius: 999px; padding: 6px 13px; font-variant-numeric: tabular-nums; }

.sylinks { display: flex; gap: 8px; justify-content: center; margin-top: 16px; flex-wrap: wrap; }
.sylinks a { font-size: 12px; font-weight: 600; color: var( --tx ); text-decoration: none; border: 1px solid var( --line2 ); border-radius: 999px; padding: 9px 15px; transition: .25s; }
.sylinks a:hover { border-color: rgba( 0, 245, 160, .45 ); color: var( --em ); }

@media ( prefers-reduced-motion: reduce ) {
	.sychat *,
	.sychat *::before,
	.sychat *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* The conversation sits on the composer until it has enough to fill the panel.

   A 560px panel holding one greeting looked like a loading state - a bubble
   marooned at the top with 300px of nothing under it. Anchoring to the bottom
   is how every chat behaves and it makes the first message feel like it just
   arrived rather than like it was always there.

   A ::before with margin-top:auto rather than justify-content:flex-end, which
   makes content that overflows the top unreachable in a scroll container. */
.sylog::before { content: ''; margin-top: auto; }

/* Once the enquiry is in, the composer has nothing left to do. */
.sychat.is-done .syfoot { display: none; }

/* The chat steps aside for the mobile menu.
   The drawer covers the page at z-index 300, the chat sits at 900, so the
   launcher floated over an open menu. chat.js watches #mdrawer and sets this. */
.sychat.is-away { opacity: 0; visibility: hidden; pointer-events: none; }

/* Turnstile.
   The wrapper must take no space when the integration is off and none while an
   interaction-only widget is sitting quietly - but it must NOT be display:none
   at page load. Cloudflare's implicit renderer walks the document looking for
   .cf-turnstile the moment its script arrives, and a hidden container is a
   container it refuses to render into: "Could not find widget for provided
   container", no token, every form refused. An empty div is already zero
   pixels tall; it only needed its margins withheld until it has content. */
.sycap { width: 100%; margin: 0; }
.wpcf7-form .sycap:not( :empty ) { margin: 4px 0 0; }
/* The chat's widget is in execute mode, which reserves a 70px placeholder from
   the moment it renders - a gap in the middle of a chat window, for a challenge
   that will not run until the visitor has finished answering. Collapse it, and
   let chat.js open it for the few seconds the challenge is actually live: it
   must be visible then, or Cloudflare will not run in it. */
.sychat .sycap { max-height: 0; overflow: hidden; margin: 0; }
.sychat .sycap.is-live { max-height: 140px; overflow: visible; margin: 0 0 10px; }
