/* AVS Partners — v3, "Controlled Force"
   Translated from the Claude Design prototype
   `AVS Variant 3 Controlled Force.dc.html`.

   The prototype keeps its styling inline, which is preserved here: it is the
   pixel spec. This sheet holds only what an inline attribute cannot express —
   the prototype's `style-hover` / `style-focus` attributes, the single 900px
   breakpoint its JS computed at runtime, and a few state selectors.

   Inline styles outrank class selectors, so every property a rule below needs
   to change on :hover or :focus was lifted OUT of the element's style
   attribute and lives here instead. Nothing uses !important.

   Palette  ivory #F1ECE3 · slate #2B4156 · walnut #4F2D08 · sand #C19D7B
            taupe #AE9E84 · sage #708671 · muted ivory #E6DCCB
   Break    900px — from the prototype's `matchMedia('(min-width: 900px)')`
*/

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
html, body { overflow-x: clip; }
body {
  margin: 0;
  background: #F1ECE3;
  color: #2B4156;
  font-family: "IBM Plex Sans", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: #2B4156; }
a:hover { color: #4F2D08; }
:focus-visible { outline: 2px solid #C19D7B; outline-offset: 3px; }

/* --------------------------------------------------------------- header */

.nav-link { color: #2B4156; }
.nav-link:hover { color: #4F2D08; }

/* Primary CTA, slate on ivory. */
.cta-slate {
  background: #2B4156;
  color: #F1ECE3;
  transition: background-color 200ms;
}
.cta-slate:hover { background: #4F2D08; color: #F1ECE3; }

/* Accent CTA, sand on slate. */
.cta-sand {
  background: #C19D7B;
  color: #2B4156;
  transition: background-color 200ms;
}
.cta-sand:hover { background: #AE9E84; color: #2B4156; }

/* Ivory link on a dark ground. */
.link-ivory { color: #F1ECE3; }
.link-ivory:hover { color: #C19D7B; }

.foot-link { color: #E6DCCB; }
.foot-link:hover { color: #F1ECE3; }

/* The prototype rendered the primary nav only above 900px, and offers no
   narrow-screen menu — the section anchors stay reachable by scrolling and
   the footer repeats every link. Both facts are the design's, not an omission. */
.nav-primary { display: none; }
/* 1180px, not the prototype's 900px: the nav gained a long
   "Revenue-Based Financing" item and would collide with the CTA below this. */
@media (min-width: 1180px) {
  .nav-primary { display: flex; }
}
.nav-link[aria-current="page"] { color: #4F2D08; box-shadow: inset 0 -2px 0 #C19D7B; }

/* Header logo. The full lockup and the CTA together overflow a phone, so below
   560px the wordmark drops away and the square mark carries the brand alone. */
.brand-full, .brand-mark { display: block; height: 36px; width: auto; }
.brand-full { display: none; }
@media (min-width: 560px) {
  .brand-full { display: block; }
  .brand-mark { display: none; }
}

/* ------------------------------------------------------------------ team */

.team-linkedin { color: #2B4156; }
.team-linkedin:hover { color: #4F2D08; }

/* CONTENT_NEEDED.md has no LinkedIn URLs yet, so the control keeps its place
   in the layout but is not a link. Cursor and screen-reader text say so. */
.team-linkedin[data-placeholder] { cursor: default; }
.team-linkedin[data-placeholder]:hover { color: #2B4156; }

.team-more { color: #4F2D08; }
.team-more:hover { color: #2B4156; }

/* Extended note: revealed on hover, on focus within the card, or by "More". */
.team-extra {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 420ms cubic-bezier(.2, .7, .2, 1),
              opacity 320ms cubic-bezier(.2, .7, .2, 1);
}
.team-card:hover .team-extra,
.team-card:focus-within .team-extra,
.team-card[data-open] .team-extra { max-height: 240px; opacity: 1; }

/* The prototype's grid pattern repeats every three members: columns 1-5, 5-9,
   9-13 of a 12-column grid, each with its own top offset. Below 900px every
   card is full width and the offsets collapse. */
.team-card { grid-column: 1 / -1; max-width: 440px; margin-top: 0; }
.team-card:nth-child(3n + 1) { justify-self: start; }
.team-card:nth-child(3n + 2) { justify-self: end; }
.team-card:nth-child(3n + 3) { justify-self: start; }

@media (min-width: 900px) {
  .team-card { justify-self: stretch; max-width: none; }
  .team-card:nth-child(3n + 1) { grid-column: 1 / 5;  margin-top: 0; }
  .team-card:nth-child(3n + 2) { grid-column: 5 / 9;  margin-top: clamp(40px, 8vw, 128px); }
  .team-card:nth-child(3n + 3) { grid-column: 9 / 13; margin-top: clamp(16px, 4vw, 64px); }
}

/* --------------------------------------------------------------- contact */

.field {
  border: 0;
  border-bottom: 1px solid #AE9E84;
  transition: border-color 200ms;
}
.field:focus { border-bottom-color: #C19D7B; outline: none; }

/* The select's own options render in the OS menu, which does not inherit the
   transparent field colouring. */
.field option { color: #2B4156; background: #F1ECE3; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

[hidden] { display: none !important; }

/* -------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: 0.01ms !important; }
}

/* ------------------------------------------------ revenue-based financing */

/* Native disclosure widget, so the answers stay in the HTML for crawlers and
   need no script. The marker is redrawn to match the page's hairline style. */
.faq summary { list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+";
  flex: none;
  font-family: "IBM Plex Sans", Helvetica, Arial, sans-serif;
  font-weight: 400;
  font-size: 26px;
  line-height: 1;
  color: #AE9E84;
  transition: transform 200ms;
}
.faq[open] summary::after { transform: rotate(45deg); }
.faq summary:hover { color: #4F2D08; }

/* The single-column label/body grid spans two tracks only when there are
   three; on a narrow screen the span would force an overflow. */
@media (max-width: 979px) {
  /* Was `[style*="grid-column:span 2"]`, which silently stopped matching:
     site.js writes el.style.opacity/transform/clipPath on reveal, and any
     write re-serialises the whole style attribute with normalised spacing
     ("grid-column: span 2"), so the substring no longer hit. A real class
     cannot be broken that way. */
  .span-2 { grid-column: auto !important; }
}

/* Generic underlined text link on ivory. */
.link-line { color: #2B4156; }
.link-line:hover { color: #4F2D08; }

/* Real team (two partners, long bios): wider cards, and the extended bio opens
   only on "More" or keyboard focus. Opening a long paragraph on hover made the
   page jump under the pointer. */
.team-pair .team-card:hover .team-extra { max-height: 0; opacity: 0; }
.team-pair .team-card:focus-within .team-extra,
.team-pair .team-card[data-open] .team-extra { max-height: 900px; opacity: 1; }
/* The firm is three people, so all three stand in one row. The cards are flush:
   equal columns, no staggered tops, so every portrait starts and ends on the
   same line and the three read as one set rather than a composition. */
@media (min-width: 1100px) {
  .team-pair .team-card:nth-child(3n + 1) { grid-column: 1 / 5;  margin-top: 0; }
  .team-pair .team-card:nth-child(3n + 2) { grid-column: 5 / 9;  margin-top: 0; }
  .team-pair .team-card:nth-child(3n + 3) { grid-column: 9 / 13; margin-top: 0; }
}

/* Between tablet and that, three columns would run the bios to a thread, so the
   cards fall back to two up and the third takes the left of a new row. */
@media (min-width: 900px) and (max-width: 1099px) {
  .team-pair .team-card:nth-child(3n + 1) { grid-column: 1 / 7;  margin-top: 0; }
  .team-pair .team-card:nth-child(3n + 2) { grid-column: 7 / 13; margin-top: 0; }
  .team-pair .team-card:nth-child(3n + 3) { grid-column: 1 / 7;  margin-top: 0; }
}

/* The bios are different lengths, so without this the cards end at three
   different heights. Stretching them to the tallest and pushing the LinkedIn /
   More row to the bottom lands every card on one baseline. */
.team-pair .team-card { height: 100%; }
.team-pair .team-card .team-actions { margin-top: auto; }

/* --------------------------------------------------------------------------
   Home hero: luminous topography

   Geometry is generated at build time (build.py -> hero_topography); this is
   only the motion and the palette. Ported from Motiq's LuminousTopography
   (MIT, motiq.dev) and recoloured: slate field, faint ivory contours, a sand
   light travelling across them.
   -------------------------------------------------------------------------- */

.lt { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.lt svg { display: block; width: 100%; height: 100%; }
.lt path { fill: none; vector-effect: non-scaling-stroke; }
.lt-bright { filter: drop-shadow(0 0 3px rgba(193, 157, 123, 0.6)); }

.lt-layer {
  transform-box: fill-box;
  animation: lt-drift var(--dur, 18s) ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes lt-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(var(--dx, 0px), var(--dy, 0px), 0); }
}

.lt-sweep {
  animation: lt-sweep 16s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes lt-sweep {
  from { transform: translateX(-330px); }
  to   { transform: translateX(330px); }
}

/* Parked while the hero is off-screen or the tab is in the background. */
.lt[data-paused="true"] .lt-layer,
.lt[data-paused="true"] .lt-sweep { animation-play-state: paused !important; }

/* On a phone the hero is portrait and the deepest layer only adds noise. */
@media (max-width: 640px) {
  .lt-layer[data-i="2"] { display: none; }
  .lt-bright { opacity: 0.82; }
}

@media (prefers-reduced-motion: reduce) {
  .lt-layer, .lt-sweep { animation: none !important; }
}

/* Forced colours drop gradients, masks and filters, which would erase the
   field entirely. Keep the plain contours so the hero is not a blank slab. */
@media (forced-colors: active) {
  .lt-bright, .lt-wash { display: none; }
  .lt svg { forced-color-adjust: none; }
  .lt-dim path { stroke: CanvasText !important; stroke-opacity: 0.5 !important; }
}

/* --------------------------------------------------------------------------
   Home hero, alternative field: floating paths

   Geometry is generated at build time (build.py -> hero_paths). framer-motion's
   pathLength / pathOffset are stroke-dasharray / stroke-dashoffset normalised
   by path length, and the SVG carries pathLength="1", so the dash values below
   are plain fractions of each path and no script is involved.
   -------------------------------------------------------------------------- */

.fp { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.fp svg { display: block; width: 100%; height: 100%; }

.fp path {
  fill: none;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  animation: fp-travel var(--dur, 24s) linear infinite;
  will-change: stroke-dashoffset, opacity;
}

@keyframes fp-travel {
  0%   { stroke-dasharray: 0.3 0.7; stroke-dashoffset:  0;   opacity: 0.35; }
  50%  { stroke-dasharray: 1 0.001; stroke-dashoffset: -1;   opacity: 0.68; }
  100% { stroke-dasharray: 0.3 0.7; stroke-dashoffset: -2;   opacity: 0.35; }
}

.fp[data-paused="true"] path { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .fp path { animation: none !important; stroke-dasharray: none; opacity: 0.4; }
}

@media (forced-colors: active) {
  .fp svg { forced-color-adjust: none; }
  .fp path { stroke: CanvasText !important; stroke-opacity: 0.4 !important; }
}

/* --------------------------------------------------------------------------
   Hero island

   The vendored component is written for Tailwind. Rather than pull Tailwind in
   for a brochure site, the handful of layout utilities it actually uses are
   declared here. Add to this list when a new component needs more; reach for
   the Tailwind CLI only if the list stops being short.
   -------------------------------------------------------------------------- */

.island-fill { position: absolute; inset: 0; }

.relative { position: relative; }
.absolute { position: absolute; }
.inset-0 { inset: 0; }
.isolate { isolation: isolate; }
.overflow-hidden { overflow: hidden; }
.pointer-events-none { pointer-events: none; }
.z-10 { z-index: 10; }

/* The vendored component glows its bright contours with a CSS drop-shadow.
   That filter establishes a rectangular filter region, and its edge was
   visible as a seam across the hero. On an ivory field a slate glow adds
   nothing anyway. Overridden here rather than by editing the component. */
#hero-island .mk-lt-bright { filter: none; }

/* --------------------------------------------------------------------------
   Hero mark — NOT IN USE as of 2026-10-06

   The lockup laid over the contour field was dropped from the hero: it fought
   the field rather than sitting in it. The rules are kept so putting it back is
   one line in build.py; nothing renders .hero-cipher today.

   The AVS monogram, filled with the hero's own background colour and laid over
   the contour field, so the lines break around it and the mark reads as a
   knock-out rather than as another drawn element. It sits between the field
   and the headline card in the DOM, which is the whole stacking order it
   needs — no z-index.
   -------------------------------------------------------------------------- */

.hero-cipher {
  position: absolute;
  /* Right of centre, clear of the headline card that sits bottom-left. */
  left: 68%;
  top: 44%;
  transform: translate(-50%, -50%);
  /* The lockup is wider than the monogram it replaced and carries a second
     line, so it is sized by width here: driving it by height pushed PARTNERS
     off the bottom of the hero and buried the headline. */
  width: 44%;
  max-width: 540px;
  height: auto;
  pointer-events: none;
  user-select: none;
}

/* On a phone the mark is taller than the screen if it keeps following height;
   width drives it there, and max-height stops it reaching the headline copy. */
@media (max-width: 767px) {
  .hero-cipher {
    left: 50%;
    top: 30%;
    width: 72%;
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   Page cipher

   The monogram laid into the empty right-hand side of a page's opening block,
   faint enough to read as a watermark rather than as content. Opt-in from
   build.py (page_hero(cipher=True)) — only About asks for it.
   -------------------------------------------------------------------------- */

.page-cipher {
  position: absolute;
  top: 50%;
  right: clamp(20px, 3vw, 64px);
  transform: translateY(-50%);
  /* The lockup is far wider than the monogram it replaced, so it is sized by
     width and kept to the right of the measure. Driven by height it ran under
     the lede and the second line of the H1. */
  width: 32%;
  max-width: 380px;
  height: auto;
  opacity: 0.1;
  pointer-events: none;
  user-select: none;
}

/* Narrower than this the heading and lede run the full measure and there is no
   clear right-hand side left, so the mark drops to the corner and fades. */
/* Narrower than this there is no clear right-hand side left, so the mark drops
   to the bottom corner and fades — still whole, never cropped by the section's
   overflow. */
@media (max-width: 1023px) {
  .page-cipher {
    top: auto;
    bottom: 4%;
    right: 4%;
    transform: none;
    width: 34%;
    max-width: 260px;
    opacity: 0.07;
  }
}

/* The monogram set solid beside a page heading, standing in for the firm's
   name. Sized in em against the row, which carries the heading's own clamp, so
   it tracks the type at every width. */
.hero-lead-mark {
  height: 0.78em;
  width: auto;
  flex: none;
  display: block;
}

@media (max-width: 599px) {
  .hero-lead-mark { height: 0.70em; }
}

/* The lead mark sits beside the heading only while both fit. Below this the
   heading is wider than what is left of the row, and as a flex item with the
   default min-width:auto it will not shrink — it just overflowed the screen
   and got clipped by the section. Stack them instead. */
.hero-lead-row {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 30px);
  /* The row carries the heading's own type scale so .hero-lead-mark can be
     sized in em against it. */
  font-size: clamp(44px, 6.5vw, 104px);
}

@media (max-width: 767px) {
  .hero-lead-row { display: block; }
  .hero-lead-row .hero-lead-mark { margin-bottom: 0.12em; }
}
