/* ==========================================================================
   Corvyn Plus Technologies — brand overrides

   Loaded after the Webflow stylesheets on every page. Everything that
   diverges from the scraped template belongs here, so the generated Webflow
   CSS stays untouched and the rebrand stays reviewable in one file.
   ========================================================================== */

:root {
  --corvyn--logo-width: 150px;
  --corvyn--logo-width-scrolled: 128px;
  --corvyn--logo-ratio: 5.274;  /* 2120 x 402, the trimmed artwork */
}

/* --------------------------------------------------------------------------
   BRAND PALETTE  —  Brand Guidelines v1.0, section 03

   Ratios below are WCAG 2.1 measured, not estimated.

   ENFORCEMENT — neither rule is catchable by a linter, because the failing
   combination is a valid colour in a valid property:

     1. --corvyn--silver MUST NOT carry text or a meaningful border on any
        light ground. It measures 1.78:1 on white and 1.67:1 on porcelain,
        under even the 3:1 non-text threshold. It is a fill, and on navy
        (9.12:1) it is a legitimate text colour. Nothing else.
     2. --corvyn--navy MUST NOT sit on --corvyn--onyx or vice versa. Both
        are dark; together they measure 1.13:1 and vanish.
   -------------------------------------------------------------------------- */

:root {
  /* from the guide */
  --corvyn--navy:      #0B0773;  /* primary. 16.25:1 on white */
  --corvyn--onyx:      #141414;  /* text & depth. 18.42:1 on white */
  --corvyn--ivory:     #FFFCEE;  /* background */
  --corvyn--porcelain: #F6F9F1;  /* background, cards */
  --corvyn--silver:    #C2C2C2;  /* FILLS ONLY on light. 9.12:1 on navy */
  --corvyn--white:     #FFFFFF;

  /* derived — the guide gives no token for these two roles, and the two
     nearest brand colours both fail the threshold the role requires. */
  --corvyn--muted:     #5E5E5E;  /* secondary text. 6.48:1 white / 6.09:1 porcelain */
  --corvyn--line:      #8C8C8C;  /* borders, focus rings, UI edges.
                                    3.36:1 white / 3.16:1 porcelain / 3.27:1 ivory.

                                    Was #929292, lifted straight from the logo
                                    artwork. That measured 3.11:1 on white and
                                    was signed off on that number alone, but it
                                    is 2.92:1 on PORCELAIN, under the 3:1 floor
                                    for a UI boundary. It was caught by the
                                    outlined button on the closing CTA, which
                                    sits on porcelain.

                                    A border token has to clear 3:1 on EVERY
                                    ground it can land on, not just the one it
                                    was measured against. #8C8C8C clears all
                                    three with margin. */
}

/* --------------------------------------------------------------------------
   MAPPING ONTO THE TEMPLATE

   Webflow runs two layers: colour primitives, and a --_theme--- indirection
   layer that .u-theme-dark flips from the light set to the dark set. Only the
   primitives are overridden here, so every section, card, border and surface
   follows automatically and the light/dark machinery keeps working untouched.

   Proportion target from guide 03.3 — 50% porcelain/white, 30% navy,
   12% silver, 8% onyx — is what drives white as the page ground, porcelain
   as the card surface, and navy as both the action colour and the dark
   section ground.
   -------------------------------------------------------------------------- */

:root {
  /* light */
  --background--bg-default-light:      var(--corvyn--white);
  --neutrals--text-light:              var(--corvyn--onyx);
  --neutrals--text-subtle-light:       var(--corvyn--muted);
  --border--border-light:              var(--corvyn--onyx);
  --border--border-subtle-light:       var(--corvyn--line);
  --border--border-subtle-light-solid: var(--corvyn--line);
  --surface--surface-light:            var(--corvyn--porcelain);
  --surface--surface-subtle-light:     var(--corvyn--ivory);
  --surface--surface-action-light:     var(--corvyn--navy);
  --surface--surface-action-hover:     var(--corvyn--onyx);

  /* dark — the anchor sections. Ground is navy, per guide 03.3.
     Surfaces on navy are white overlays rather than a second solid colour:
     the palette has no second dark tone that stays visible against navy
     (onyx measures 1.13:1 against it). */
  --background--bg-default-dark:  var(--corvyn--navy);
  --neutrals--text-dark:          var(--corvyn--white);
  --neutrals--text-subtle-dark:   var(--corvyn--silver);   /* 9.12:1 on navy */
  --border--border-dark:          var(--corvyn--white);
  --border--border-subtle-dark:   #ffffff2e;
  --surface--surface-dark:        #ffffff12;
  --surface--surface-subtle-dark: #ffffff24;
  --neutrals--text-overlay-subtle: #ffffffb3;
}

/* Selection. The template sets this in a <style> block inside <body>, so it
   needs the same specificity trick the scrolled logo does. */
html ::selection {
  background: #0b077326;
  color: currentColor;
}

html .u-theme-dark ::selection {
  background: #ffffff33;
  color: currentColor;
}

/* --------------------------------------------------------------------------
   KEYBOARD FOCUS

   The template ships no visible focus style — it relies on the UA default,
   which it then suppresses via .wf-force-outline-none. This restores one.
   -------------------------------------------------------------------------- */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--corvyn--navy);
  outline-offset: 2px;
  border-radius: 0;
}

.u-theme-dark :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
.c-section.footer :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline-color: var(--corvyn--white);
}

/* --------------------------------------------------------------------------
   HEADER LOGO LOCKUP

   The brand guide (02.4) forbids rebuilding the wordmark in another
   typeface, so the lockup is the supplied artwork, not text. Two copies
   ship: full-colour for light backgrounds, reversed white for navy
   sections. `.c-header.is-dark` picks the reversed one — nothing sets that
   class yet, since no dark section currently sits under the header.

   Guide 02.2 sets a 120px minimum width for the full lockup. Every width
   below clears it, including the 375px breakpoint. Do not go under 120px —
   use the icon mark alone instead, per the same section.

   The footer carries the reversed lockup directly (it sits on navy), so it
   needs no swap — it uses .c-logo-mark with the white file.
   -------------------------------------------------------------------------- */

.c-header .c-logo {
  width: auto;
  flex: none;
}

.c-header .c-logo-svg {
  width: auto;
  align-items: center;
}

.c-logo-mark {
  width: var(--corvyn--logo-width);
  height: auto;
  aspect-ratio: var(--corvyn--logo-ratio);
  flex: none;
  display: block;
  transition: width 0.3s var(--io5);
}

/* Reversed lockup sits in the DOM alongside the full-colour one and only
   one is ever shown. Swapping visibility rather than swapping `src` means
   both files are already decoded when a theme changes — no flash. */
.c-logo-mark.is-reversed {
  display: none;
}

.c-header.is-dark .c-logo-mark {
  display: none;
}

.c-header.is-dark .c-logo-mark.is-reversed {
  display: block;
}

/* Scrolled state.

   The template's `.c-header.scrolled .c-logo { width: 112px }` lives in a
   <style> block inside <body>, so at equal specificity it wins on source
   order over anything in this file. The leading `header` type selector is
   what tips specificity the other way. */
header.c-header.scrolled .c-logo {
  width: auto;
  transform: none;
}

header.c-header.scrolled .c-logo-mark {
  width: var(--corvyn--logo-width-scrolled);
}

/* --------------------------------------------------------------------------
   HEADER NAV

   The template's own `.c-btn-group.hdm` carried `margin-left: auto`, which is
   what pushed the right-hand side of the header out. That button block is not
   in the header (Contact is a plain nav link instead), so without this the
   nav collapses against the logo.

   Only applies at desktop widths: below 992px the nav is display:none and the
   mobile drawer takes over.

   NOTE: if a CTA button is ever added back to the header, this rule comes
   out. Keeping both pushes the nav and the button apart.
   -------------------------------------------------------------------------- */

@media screen and (min-width: 992px) {
  .c-header .c-header-nav {
    margin-left: auto;
  }
}

@media screen and (max-width: 375px) {
  :root {
    --corvyn--logo-width: 124px;          /* still above the 120px minimum */
    --corvyn--logo-width-scrolled: 124px;
  }
}

/* --------------------------------------------------------------------------
   INLINE-<style> OVERRIDES

   The template ships <style> blocks inside <body>. They beat the external
   stylesheets on source order at equal specificity, so anything hardcoded
   there ignores the tokens above entirely. Each rule here is prefixed with
   `.c-body` purely to win that fight — the extra class is load-bearing, not
   decoration. Do not "simplify" it away.
   -------------------------------------------------------------------------- */

/* Titan's brand was a #F90 orange radial glow over #111. Both were baked into
   an inline rule, which is why the footer stayed dark after the tokens moved
   across. The guide specifies no gradients on UI surfaces.

   The footer is NAVY, matching the hero, so the page opens and closes on the
   brand colour. This was onyx earlier in the build; onyx is still the type
   and depth colour, it just no longer grounds the footer.

   The generated rule already reads --background--bg-default-dark, which is
   navy, so this override is technically redundant. It is kept explicit so the
   footer ground is stated in one obvious place rather than inherited three
   files away, and so the ground and the token below cannot drift apart. */
.c-body .c-section.footer {
  background: var(--corvyn--navy);
  --background--bg-default-dark: var(--corvyn--navy);
}

/* Nav dropdown panel was rgba(17,17,17,.9). */
.c-body .c-header.scrolled .c-dd-group {
  background: #0b0773eb;
}

/* --------------------------------------------------------------------------
   TEAM SECTION — a light section wearing dark-theme tokens

   The section carries .u-theme-dark, but the Webflow variant below paints
   its .c-section-bg with --neutrals--text-dark (white). So it renders light
   while every --_theme--- token inside it resolves to the dark set.

   Titan got away with this: its dark subtle text was #8a8a87, which is
   3.0:1 on white and scraped past the large-text threshold. Silver is
   1.78:1 and does not. Re-point the theme tokens to the light set.

   Scoped to the variant deliberately, NOT to `.c-section.u-theme-dark.team`.
   Without the variant that same section paints #0a0a0a, and forcing light
   tokens onto it would put dark text on a near-black ground.
   -------------------------------------------------------------------------- */

.c-body .c-section.u-theme-dark.team.w-variant-95363372-b1ec-8bda-d715-f50080c90a8a {
  --_theme---text:           var(--neutrals--text-light);
  --_theme---text-subtle:    var(--neutrals--text-subtle-light);
  --_theme---border-subtle:  var(--border--border-subtle-light);
  --_theme---border:         var(--border--border-light);
  --_theme---surface-bg:     var(--surface--surface-light);
}

/* --------------------------------------------------------------------------
   FOOTER SIZING

   .c-footer-col ships min-height: 25.625em, sized for Titan's 70-link wall
   (13 product lines, legal, help centre). With justify-content: space-between
   that stretched our five links across ~364px of empty space.

   Dropping the min-height alone is not enough: space-between had been doing
   all the spacing work, so without it the rights block collides with the nav
   above it. The gap replaces what the min-height was providing.
   -------------------------------------------------------------------------- */

.c-body .c-section.footer .c-footer-col {
  min-height: 0;
  gap: 4em;
}

/* --------------------------------------------------------------------------
   TYPOGRAPHY  —  Brand Guidelines v1.0, section 04

   The guide names exactly two typefaces: TITAN for display (caps, tracked,
   headlines only) and Montserrat for body, UI, captions and data. The
   template shipped Geist + Geist Mono, both pulled from fonts.gstatic.com.

   Montserrat is self-hosted as a single variable woff2 (37 KB, latin subset)
   covering 300-700. That is one request instead of four, and no third-party
   connection — which matters on the mobile data this site gets read on.

   TITAN IS NOT INSTALLED. The brand PDF embeds only a 2.5 KB subset, which
   is a specimen, not a usable font. Until the licensed woff2 arrives,
   --corvyn--font-display falls back to Montserrat and .t-display-* carries
   the caps-and-tracking treatment the guide specifies for TITAN. When the
   real files land: drop them in fonts/, add the @font-face, and change the
   one token below. Nothing else needs to move.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 300 700;         /* variable axis */
  font-display: swap;
  src: url("../fonts/montserrat-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --corvyn--font-body: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --corvyn--font-display: var(--corvyn--font-body);   /* <- becomes "TITAN", ... */

  --_typography---fonts--primary: var(--corvyn--font-body);
  --_typography---fonts--mono:    var(--corvyn--font-body);
}

@font-face {
  font-family: "Montserrat";
  font-style: italic;
  font-weight: 300 700;         /* variable axis */
  font-display: swap;
  src: url("../fonts/montserrat-latin-italic-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Display treatment — deliberately NOT applied blanket to h1/h2.

   Guide 04.1 says TITAN is "always set in capitals with generous
   letter-spacing" and is "not intended for body copy, long paragraphs or
   small UI text". Read against 04.3, the H1 specimen is a single word
   (AUTOMATE) while the H2 specimen — "A dependable operations layer" — is
   Montserrat in sentence case.

   So caps is for SHORT display moments, not for sentence-length headlines.
   Forcing it on every h1/h2 makes a full-sentence headline shouty and hard
   to read, and contradicts the guide's own H2 example. The treatment is a
   class to apply on purpose, not a default. */

.u-display {
  font-family: var(--corvyn--font-display);
  text-transform: uppercase;
  letter-spacing: 0.06em;   /* "generous", per 04.1 */
  font-weight: 700;
}

/* NOTE — do not blanket-apply caps to .c-eyebrow-title or .t-micro-2.
   Despite the name, .c-eyebrow-title is a WRAPPER that contains the hero
   h1, and the template sets `h1..h6 { text-transform: inherit }`, so any
   text-transform on that wrapper cascades straight into the headline and
   uppercases the whole thing. Apply .u-display to the specific element
   that should carry the treatment instead. */

/* Montserrat's default line-height is tight for long-form. Guide 04.3 sets
   body at Regular; 1.6 is the readable floor for this face at these sizes. */
.t-base, .t-medium, .t-large, p {
  line-height: 1.6;
}

/* --------------------------------------------------------------------------
   SECTION 03 — WHAT WE BUILD  (.c-section.build)

   Keeps the template's 50/50 duplex layout: the four service lines stack in
   the left column, image in the right. Only the eyebrow spacing and the
   image treatment are ours.

   The image is a PLACEHOLDER (assets/placeholder-3x4.png) and is meant to be
   obvious. Replace it, do not ship it.

   Duotone: guide 06.1 asks for a navy duotone over real photography. It is
   applied here in CSS rather than baked into the file, so a swapped-in image
   picks up the treatment automatically and the source stays untouched.
   -------------------------------------------------------------------------- */

.c-section.build .c-duplex-title {
  display: flex;
  flex-flow: column;
  gap: 1em;
}

.c-section.build .c-img-contain.duplex-visual {
  background: var(--corvyn--navy);
}

/* --------------------------------------------------------------------------
   AUTOPLAY SLIDESHOW  (.c-slideshow)

   Used by the hero (4 slides, 16:9) and section 03 (5 slides, 3:4).

   Deliberately CSS only. Swiper is already in the bundle at 154 KB, but these
   slideshows take no input at all: no arrows, no dots, no swipe, no pause. A
   library that exists to handle interaction is the wrong tool when the
   requirement is that there is none.

   THE SLIDE COUNT IS IN THE MARKUP, as data-slides, and the CSS keys off it.
   That is not decoration. The crossfade maths depends on the count: each
   slide owns 100/n percent of the cycle, so a 5-slide keyframe run against 4
   images leaves a gap at the end of every loop with nothing on screen. Tying
   both to one attribute makes the two impossible to fall out of step.

   To add or remove a slide: change data-slides in the HTML, and add the
   matching keyframe block plus nth-child delays below if that count is new.
   -------------------------------------------------------------------------- */

.c-slideshow {
  --slide-hold: 5s;
  position: relative;
  overflow: hidden;
}

.c-slideshow .c-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* LONGHAND, not the `animation` shorthand. The shorthand resets
     animation-delay to 0, which would wipe the per-slide delays below and
     run every slide in lockstep so only the first one ever appears. */
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* delays are count-independent: slide k simply starts k holds in */
.c-slideshow .c-img:nth-child(1) { animation-delay: calc(0 * var(--slide-hold)); }
.c-slideshow .c-img:nth-child(2) { animation-delay: calc(1 * var(--slide-hold)); }
.c-slideshow .c-img:nth-child(3) { animation-delay: calc(2 * var(--slide-hold)); }
.c-slideshow .c-img:nth-child(4) { animation-delay: calc(3 * var(--slide-hold)); }
.c-slideshow .c-img:nth-child(5) { animation-delay: calc(4 * var(--slide-hold)); }
.c-slideshow .c-img:nth-child(6) { animation-delay: calc(5 * var(--slide-hold)); }

/* 4 slides: 25% each, 4% fade */
.c-slideshow[data-slides="4"] .c-img {
  animation-name: c-slideshow-fade-4;
  animation-duration: calc(4 * var(--slide-hold));
}
@keyframes c-slideshow-fade-4 {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  25%  { opacity: 1; }
  29%  { opacity: 0; }
  100% { opacity: 0; }
}

/* 5 slides: 20% each, 4% fade */
.c-slideshow[data-slides="5"] .c-img {
  animation-name: c-slideshow-fade-5;
  animation-duration: calc(5 * var(--slide-hold));
}
@keyframes c-slideshow-fade-5 {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  20%  { opacity: 1; }
  24%  { opacity: 0; }
  100% { opacity: 0; }
}

/* The box needs its own height because every slide is absolutely positioned. */
.c-section.build .c-slideshow { aspect-ratio: 3 / 4; }
.c-section.hm-hero .c-slideshow { aspect-ratio: 16 / 9; }

/* Navy duotone, guide 06.1, is BAKED INTO THE FILES, not applied here.

   Two CSS approaches were tried and both failed on these sets.
   `mix-blend-mode: color` keeps each photo's luminance and only swaps hue, so
   mid greys came out periwinkle. `screen` over navy is a real duotone, but
   the sources have very different tonal keys: the control room looked right
   while the refinery's bright sky lifted to flat lavender. No single blend
   mode can normalise photographs that differ that much.

   Baking it in processing allows per-image auto-levels first, so each set
   reads as one treatment. To retreat them, re-run the processing step against
   the originals in Downloads; the CSS needs no change.

   The navy background stays as the ground behind a slide that has not decoded
   yet, so the box is never a white hole. */

/* An animation that cannot be paused is exactly what this rule is for. */
@media (prefers-reduced-motion: reduce) {
  .c-slideshow .c-img {
    animation-name: none;
  }
  .c-slideshow .c-img:first-child {
    opacity: 1;
  }
}

/* .c-eyebrow already uppercases via the template; this only adds the tracking.
   Safe to target generally: .c-eyebrow never wraps a heading (unlike
   .c-eyebrow-title, which wraps the hero h1 - see the note further up). */
.c-eyebrow .t-micro-2 {
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* "View more" per service line. Uses the template's own .c-btn.link variant
   (transparent, no padding, arrow glyph) rather than a new component.

   The link shares a row with the service title (.c-duplex-item-head) rather
   than sitting under the paragraph, which keeps each item two lines shorter.
   Baselines are aligned, not centres: the title is .t-large and the link is
   small uppercase, so centring them leaves the link floating mid-cap-height.

   The four labels are identical, which is ambiguous for anyone navigating by
   link list, hence the aria-label naming the service on each one. */

/* .c-duplex-item-content caps at 27.375em, which is the right measure for the
   paragraph but would park the link mid-column, out of line with the rules
   above and below it. Release the cap on the container, re-apply it to the
   paragraph only, and the link lands flush with the item's right edge. */
.c-section.build .c-duplex-item-content {
  max-width: none;
}

.c-section.build .c-duplex-item-content > .t-base {
  max-width: 27.375em;
}

.c-section.build .c-duplex-item-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;          /* long title on a narrow screen drops the link
                               to its own line rather than crushing it */
  column-gap: 1.5em;
  row-gap: 0.5em;
  width: 100%;
}

/* The link must not be squeezed or wrapped when a title runs long. */
.c-section.build .c-duplex-item-head .c-btn.link {
  flex: none;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   SECTION 02 — THE PROBLEM  (.c-section.problem)

   Uses the template's .c-title-wrap component unmodified: heading, then three
   .c-title-item rows each with an icon and a line of copy, separated by
   hairlines. The triad is naturally three things, so nothing had to be
   restructured.

   Icons are Lucide, the same library the template's own content icons come
   from (its dollar-sign is Lucide's, transform and stroke attributes
   identical). See assets/icons/LICENCE.txt.

   Type-led otherwise, with no photography: section 03 immediately below runs
   a six-slide duotone slideshow, and two image-led sections in a row blunt
   each other. The quiet here is what gives that one its weight.
   -------------------------------------------------------------------------- */

/* The bold lead-in is what makes the list scannable; without it all three
   rows read as one grey block. */
.c-section.problem .c-title-item strong {
  font-weight: 600;
  color: var(--corvyn--onyx);
}

.c-section.problem .c-title-item p {
  color: var(--corvyn--muted);
}

/* --------------------------------------------------------------------------
   SECTION 01 — HERO  (.c-section.hm-hero.u-theme-dark)

   Navy ground, per guide 03.3 where navy is the dominant brand colour. The
   page reads navy -> light middle -> navy footer, so the brand colour brackets
   the page and the 30% navy allocation is spent at the two moments that
   carry it: the first screen and the last.

   The video modal that lived here is gone: it played a Titan video from
   Vimeo behind a poster on Titan's CDN. The data-video-modal-trigger
   handlers in corvyn-main.js are now dead code for the purge pass.
   -------------------------------------------------------------------------- */

/* The template only sets a background on .c-section.u-theme-dark inside a
   mobile media query. At desktop its dark sections are painted by a
   .c-section-bg child div instead, and the hero has no such child, so the
   section came out transparent: white text on the white page ground.
   Setting it directly here rather than adding a .c-section-bg, which would
   also opt the hero into the GSAP scaleX scrub in corvyn-main.js. */
.c-section.hm-hero.u-theme-dark {
  background-color: var(--corvyn--navy);
}

/* STATS BAR — HIDDEN, NOT DELETED.
   Markup is live with the agreed labels (Projects completed / Clients /
   Founding 2026). It stays hidden until there are real figures to put in it:
   a 2026 founding date shown beside project and client counts tells a
   director the company is brand new, in the first thing they see.
   To bring it back: delete this rule and replace the two PLACEHOLDER values
   in index.html. Nothing else needs to change. */
.c-section.hm-hero .c-hm-hero_bt {
  display: none;
}

/* --------------------------------------------------------------------------
   HEADER OVER A DARK HERO

   .over-dark is set in the markup on index.html only. projects.html also
   carries data-page="home", so a body-attribute selector would catch the
   wrong page.

   The swap keys off :not(.scrolled) rather than a JS class: at the top the
   header sits on the navy hero and needs the reversed lockup, and once
   corvyn-main.js adds .scrolled past 50px the header gains its light glass
   background and the full-colour lockup is correct again. No extra JS.
   -------------------------------------------------------------------------- */

.c-header.over-dark:not(.scrolled) .c-logo-mark {
  display: none;
}

.c-header.over-dark:not(.scrolled) .c-logo-mark.is-reversed {
  display: block;
}

/* Mobile menu bars are drawn with background-color, not stroke. */
.c-header.over-dark:not(.scrolled) .c-nav-bar {
  background-color: var(--corvyn--white);
}

/* Buttons on navy. .c-btn fills with --surface--surface-action-light, which
   is navy, so on a navy ground the fill vanished and the button read as
   bare text. This is the enforcement rule at the top of this file biting in
   practice: navy on navy is 1:1. Inverted here, white fill and navy label,
   which measures 16.25:1 either way round. */
.u-theme-dark .c-btn {
  background-color: var(--corvyn--white);
  color: var(--corvyn--navy);
}

.u-theme-dark .c-btn:hover {
  background-color: var(--corvyn--porcelain);
  color: var(--corvyn--navy);
}

/* The hero headline is set to break after "Layer". At the inherited display-1
   size that first line overflows the 50% column and wraps anyway, producing
   three ragged lines instead of two. Trimming the step so the intended break
   is the only break. */
.c-section.hm-hero {
  --_responsive---heading-1: 3.2em;
}

/* The hero support line is set in italic. That needs a REAL italic face:
   without the @font-face above the browser synthesises one by slanting the
   roman, which on a geometric sans like Montserrat looks visibly wrong.
   Costs one extra 39 KB request, and only on pages that use italic. */
.c-section.hm-hero .c-hm-hero-note p {
  font-style: italic;
}

/* --------------------------------------------------------------------------
   CONTACT PAGE

   The template ships no branded form: its only form is the blog filter, and
   .w-input is Webflow's raw default (grey 1px border, 38px tall, 14px text).
   So the fields are styled here from the brand tokens.

   The navy intro band is deliberately short rather than a full-viewport hero.
   A contact page's job is to get someone to the form, not to delay them.
   -------------------------------------------------------------------------- */

.c-section.contact-intro {
  background-color: var(--corvyn--navy);
  padding-top: 8em;
  padding-bottom: 6em;
}

.c-contact-intro {
  display: flex;
  flex-flow: column;
  gap: 1em;
  max-width: 34ch;
}

.c-section.contact {
  padding-top: 6em;
  padding-bottom: 8em;
}

.c-contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 5em;
  align-items: start;
}

/* FORM FIELDS */

.c-contact-form {
  display: flex;
  flex-flow: column;
  gap: 1.75em;
}

.c-field {
  display: flex;
  flex-flow: column;
  gap: 0.5em;
}

/* Real labels, not placeholder text. A placeholder disappears the moment
   someone types, so they lose the question while answering it, and screen
   readers do not reliably announce placeholders as field names. */
.c-field-label {
  font-size: 0.875em;
  font-weight: 600;
  color: var(--corvyn--onyx);
}

.c-field-optional {
  font-weight: 400;
  color: var(--corvyn--muted);
}

.c-field-input {
  width: 100%;
  padding: 0.85em 1em;
  font: inherit;
  font-size: 1em;
  color: var(--corvyn--onyx);
  background: var(--corvyn--white);
  border: 1px solid var(--corvyn--line);   /* 3.11:1, the compliant neutral */
  border-radius: 0;
  transition: border-color 0.2s;
}

.c-field-input:hover {
  border-color: var(--corvyn--onyx);
}

.c-field-input:focus {
  outline: 2px solid var(--corvyn--navy);
  outline-offset: 2px;
  border-color: var(--corvyn--navy);
}

.c-field-textarea {
  resize: vertical;
  min-height: 8em;
}

.c-contact-submit {
  align-self: flex-start;
  border: none;
  cursor: pointer;
  font: inherit;
}

.c-contact-commit {
  color: var(--corvyn--muted);
  margin: 0;
}

/* DIRECT CONTACT, for anyone who will not use a form */

.c-contact-direct {
  display: flex;
  flex-flow: column;
  gap: 2.5em;
  border-top: 1px solid var(--corvyn--line);
  padding-top: 2em;
}

.c-contact-block {
  display: flex;
  flex-flow: column;
  gap: 0.4em;
}

.c-contact-label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--corvyn--muted);
  margin: 0;
}

.c-contact-value {
  color: var(--corvyn--onyx);
  text-decoration: none;
  margin: 0;
}

a.c-contact-value:hover {
  color: var(--corvyn--navy);
  text-decoration: underline;
}

@media screen and (max-width: 991px) {
  .c-contact-grid {
    grid-template-columns: 1fr;
    gap: 3.5em;
  }
}

/* --------------------------------------------------------------------------
   SECTION 05 — WHAT IT IS WORTH  (.c-section.price)

   Re-axed from the template's us-versus-them pricing table to a before/after
   of the visitor's OWN operation, so nobody is disparaged. The four figures
   are a stated worked example, not a claim, and .c-price-basis below carries
   the assumptions they rest on. If the figures change, that line changes
   with them.

   The template hardcodes #f3efeb on .c-price, which is Titan's warm off-white
   rather than a token, so it survived the palette work untouched.

   :not(.is-alt) IS LOAD-BEARING. Only the FIRST column is a card. The second
   is .c-price.is-alt, which the template deliberately sets to transparent so
   its items sit straight on the page with no container. A bare `.c-price`
   selector here matches both columns at equal specificity, wins on source
   order, and paints a card onto the column that is supposed to have none.
   -------------------------------------------------------------------------- */

.c-section.price .c-price:not(.is-alt) {
  background-color: var(--corvyn--porcelain);
}

/* The figures are stated, not computed, so the basis has to be visible.
   Without it they are unsupported numbers about someone else's operation,
   which is the one thing on this page a reader could check and find hollow. */
.c-section.price .c-price-basis {
  margin: 1em 0 0;
  max-width: 56ch;
  color: var(--corvyn--muted);
}



/* --------------------------------------------------------------------------
   PART-TO-WHOLE BAR

   Shows the proportion the two columns state numerically: 3,744 of 6,240.

   One bar, not two. Value is hours x a fixed rate, so a second bar for money
   would plot the identical proportion twice and teach the reader nothing.

   Both figures are direct-labelled, so there is no hidden value a tooltip
   would reveal and none is added.

   The track is NOT silver. #C2C2C2 measures 1.78:1 on porcelain, under the
   3:1 floor for a graphic that carries meaning, which is the same rule that
   keeps silver off borders. It uses --corvyn--line instead.
   -------------------------------------------------------------------------- */

.c-bar {
  display: flex;
  flex-flow: column;
  gap: 0.75em;
  width: 100%;
  /* .c-price-row stacks flush, so the bar has to carry its own clearance or
     its labels sit on top of the first bullet below. */
  margin: 1.5em 0 2em;
}

.c-bar-track {
  height: 14px;
  background: var(--corvyn--white);
  border: 1px solid var(--corvyn--line);
  overflow: hidden;
}

/* Square at the baseline, rounded at the data end, so the bar reads as a
   quantity growing from the left rather than a floating pill. */
.c-bar-fill {
  height: 100%;
  background: var(--corvyn--navy);
  border-radius: 0 4px 4px 0;
}

.c-bar-labels {
  display: flex;
  justify-content: space-between;
  gap: 1em;
  flex-wrap: wrap;
}

.c-bar-label {
  font-size: 0.875em;
}

.c-bar-label.is-strong {
  font-weight: 600;
  color: var(--corvyn--onyx);
}

.c-bar-label.is-muted {
  color: var(--corvyn--muted);
}

/* .c-price-values-item lays the figure and its label out with space-between.
   Titan's labels were two short words ("Annual Fee"); ours run longer and
   collide with the number as they wrap. */
.c-section.price .c-price-values-item {
  gap: 1.5em;
}

.c-section.price .c-price-values-item > .t-base {
  text-align: right;
  max-width: 11em;
}

/* Icon alignment against the bullet text.

   Measured, not guessed. .t-base renders a 10px box with a 22.76px
   line-height, so matching the icon box to either the line-height or the
   element box puts it 6px and 3px out respectively. Constraining the icon to
   1em and centring the glyph inside it tracks the text at any wrap depth and
   any font size, and measures closest of the four options tried.

   Was scoped to .c-section.price, where every item was one line and the bug
   could not show. The services and work pages reuse this list with items that
   wrap to two lines, and there the icon floated to the vertical centre of the
   wrapped block instead of sitting on the first line. Unscoped now: a tick
   that does not track its first line is wrong everywhere, not just here. */
.c-price-item {
  align-items: flex-start;
}

.c-price-item .c-icon.price {
  flex: none;
  height: 1em;
  display: flex;
  align-items: center;
}

/* --------------------------------------------------------------------------
   SECTION 07 — TEAM  (.c-section.team)

   Structure untouched: the template's slider, cards, plus-button overlay and
   the three .c-team-info-group slots are exactly as they shipped. Only the
   text changed, plus the third slot's label (FOCUS -> EMAIL) and a link style
   for the address it now holds.

   The overlay sits on a photograph, so the address is underlined rather than
   relying on colour: over an unknown image there is no contrast guarantee.
   -------------------------------------------------------------------------- */

.c-team-email {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  overflow-wrap: break-word;  /* with the <wbr> after the @, this breaks at
                                 the domain boundary and only mid-word as a
                                 last resort */
}

.c-team-email:hover {
  text-decoration-thickness: 2px;
}

/* --------------------------------------------------------------------------
   SECTION 09 — CLOSING CTA  (.c-section.cta)

   One line, one primary action, one secondary. The Flatiron sketch that sat
   beside it was Titan's New York head office; nothing replaces it, because
   the restraint is the point at the end of a page that has already spent its
   imagery on the hero, section 03 and the workflow diagram.

   Porcelain rather than white, so the page signals it is closing, and rather
   than navy, which is now the footer colour directly below: the two would
   merge into one navy slab with no visible boundary between them. Porcelain
   keeps the footer edge legible and makes the closing navy deliberate.
   -------------------------------------------------------------------------- */

.c-section.cta {
  background-color: var(--corvyn--porcelain);
}

/* .c-about-cta-main carries a right-hand divider that separated the text from
   the image. With the image gone it is a rule against nothing. */
.c-section.cta .c-about-cta-main {
  border-right: none;
  padding-right: 0;
}

/* The card is a flex row built for two children; with one it should fill. */
.c-section.cta .c-about-cta-main {
  width: 100%;
}

/* .c-about-cta-main is a flex column with a 4em gap, so two bare buttons
   would stack. The group keeps them on one row and lets them wrap together
   on a narrow screen. */
.c-btn-group.cta {
  display: flex;
  flex-flow: row wrap;
  gap: 1em;
  align-items: center;
}

/* .c-split-card is a flex column with a 3.5em gap, which is right between the
   heading block and the body, but far too much between a section label and
   the heading it belongs to. Grouping them lets the card keep its big gap
   while the label sits close to its own heading. */
.c-section.split .c-split-head {
  display: flex;
  flex-flow: column;
  gap: 0.75em;
}

/* --------------------------------------------------------------------------
   CLIENT LOGO STRIP  (.c-section.logos)

   HIDDEN. Nothing about it renders until there are real client logos to put
   in it. A "trusted by" strip holding placeholders is the one thing on this
   page that would be a straightforward untruth, so the switch is off by
   default rather than relying on someone remembering to turn it off.

   TO ENABLE:
     1. replace assets/logo-placeholder.svg with the real marks, one <img>
        per client inside .c-logos-track
     2. keep the set DUPLICATED exactly twice, the loop depends on it
     3. delete the display:none rule immediately below
     4. remove aria-hidden="true" from the section in index.html

   NOTE: the class is `logos`, not `clients`. corvyn-main.js has a
   `.c-section.clients` handler that runs a GSAP Flip against
   `.c-video.clients`; with no such video on the page it would throw.
   -------------------------------------------------------------------------- */

.c-section.logos {
  display: none;      /* <- delete this line to switch the strip on */
}

/* Slim by design: this is a band of reassurance between two sections that do
   the arguing, not a section in its own right. */
.c-section.logos {
  padding-top: 2.5em;
  padding-bottom: 2.5em;
  border-top: 1px solid var(--corvyn--line);
  border-bottom: 1px solid var(--corvyn--line);
}

.c-logos {
  overflow: hidden;
  /* fade the strip out at both edges so logos enter and leave rather than
     being cut off mid-mark */
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.c-logos-track {
  display: flex;
  align-items: center;
  gap: 4.5em;
  width: max-content;
  animation: c-logos-scroll 40s linear infinite;
}

/* -50% lands exactly on the start of the duplicated set, so the loop has no
   visible seam and no jump. Change the duplication and this breaks. */
@keyframes c-logos-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.c-logos:hover .c-logos-track {
  animation-play-state: paused;
}

.c-logos-item img {
  display: block;
  height: 2.5em;
  width: auto;
  opacity: 0.75;
  filter: grayscale(1);   /* client marks sit as one set, not a colour clash */
}

/* A continuous marquee cannot be paused by a reader who needs it still. */
@media (prefers-reduced-motion: reduce) {
  .c-logos-track {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   SERVICES PAGE  (offerings.html)

   The page was Titan's offerings index: a hero over a hot-linked Greek
   temple, then 22 links to /offerings/strategy/* routes that do not exist.
   All of it is gone. What survives of the original file is the head, the
   header, the mobile nav and the footer.

   offerings.html now loads the INDEX Webflow stylesheet rather than its own.
   Its own sheet carried no base rules for .c-duplex-item, and only the mobile
   media-query fragments for .c-key-item and .c-price-item, so the home page's
   components could not have rendered on it. projects.html and contact.html
   were switched the same way when they were rebuilt; this makes offerings the
   third, and drops a 73 KB stylesheet from the page.

   THE HERO IS THE HOME PAGE'S HERO. It carries .c-section.hm-hero, so the
   navy ground, the 16:9 slideshow box, the trimmed display-1 step and the
   italic support line all come from the rules further up this file. Nothing
   is redefined here, which is the point: two heroes that are meant to look
   identical should not be described twice.

   Navy sequence, matching home and contact: navy hero and jump band, light
   middle, navy engagement section, navy footer. Guide 03.3.
   -------------------------------------------------------------------------- */

/* WHAT WE BUILD, IN THE HERO — this replaced the hero's CTA button, so it
   sits inside .c-hm-hero-main where the button was. One column, because the
   hero's left column is half the container and two columns of service names
   would wrap inside it.

   Each row is an anchor to its section further down. The numbers match the
   eyebrow on each card (01 / Digital transformation), so the list and the
   sections read as one sequence rather than a menu and some content. */
.c-srv-jump-list {
  display: grid;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--border--border-subtle-dark);
  width: 100%;
}

.c-srv-jump {
  display: flex;
  align-items: baseline;
  gap: 1.25em;
  padding: 1em 0.5em 1em 0;
  border-bottom: 1px solid var(--border--border-subtle-dark);
  color: var(--corvyn--white);
  text-decoration: none;
  transition: background-color 200ms var(--o3);
}

.c-srv-jump:hover {
  background-color: var(--surface--surface-dark);
}

.c-srv-jump-num {
  color: var(--corvyn--silver);
  flex: none;
}

/* DETAIL SECTIONS — .o-row.split is the home page's two-column layout, used
   here four times with the photograph alternating side. Source order carries
   it: visual first for 01 and 03, card first for 02 and 04. No CSS ordering,
   so the reading order a screen reader gets is the order on screen. */
.c-section.srv-detail.is-alt {
  background-color: var(--corvyn--porcelain);
}

/* THE ROW MUST SIZE FROM THE CARD, NOT THE IMAGE.

   .c-split-card-wrap ships aspect-ratio: 1/1, which is right on the home page
   where a square photograph sits beside a card holding two paragraphs. These
   cards hold a statement, three labelled blocks, a six-item list and a button:
   711px of content inside a 551px square, so the card's own border cut across
   "How it runs" and the button hung outside it entirely.

   Released here. The card sets the row height, and the photograph stretches to
   whatever that turns out to be. min-height covers the shortest card, since an
   image column has no content of its own to give it height.
   -------------------------------------------------------------------------- */
.c-section.srv-detail .c-split-card-wrap {
  aspect-ratio: auto;
}

.c-section.srv-detail .o-row.split {
  align-items: stretch;
}

.c-section.srv-detail .c-split-card {
  height: 100%;
}

.c-section.srv-detail .c-img-contain.split-visual {
  height: 100%;
  min-height: 28em;
}

/* Only two photographs clear the licensing and register tests, so each is
   used twice. Different framing on the second appearance keeps the pair from
   reading as a repeat: the same site, looked at differently. */
.c-img-contain.split-visual.is-crop-alt .c-img {
  object-position: 72% center;
}

/* .c-split-card ships a 1em gap between its head and body, which is right
   when the body is two paragraphs. Here the body is three labelled blocks
   spaced 2.5em apart, so the statement sat tighter to the first label than
   the labels are to each other. Matching the two restores the rhythm. */
.c-section.srv-detail .c-split-card {
  gap: 2.5em;
}

.c-srv-block {
  display: flex;
  flex-flow: column;
  gap: 0.75em;
}

/* The label is the scannable element: a director skims three labels down each
   card and stops at the one they need. The icon is a landmark for that skim,
   which is why it sits on the label rather than beside the prose. */
.c-srv-block-head {
  display: flex;
  align-items: center;
  gap: 0.6em;
}

.c-icon.srv {
  flex: none;
  display: flex;
  align-items: center;
}

.c-srv-label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--neutrals--text-subtle-light);
}

/* The icons align to the label, so the prose under them hangs at the same
   left edge as everything else in the card rather than indenting to clear
   them. Deliberate: an indent here would fight the card's own padding. */
.c-section.srv-detail .c-price-list {
  gap: 0.75em;
}

/* "Talk to us" closes each card, bottom right. The template's own .c-btn.link
   variant: transparent, no padding, arrow glyph, same as the four "View more"
   links on the home page. A second filled button per card would have put five
   primary actions on the page competing with the one at the end of it.

   The label is not "View work". These point at the contact page, and a link
   that promises work and opens a form is the kind of small dishonesty this
   site cannot afford. When projects.html holds real case studies, this label
   becomes "View work" and the href moves with it.

   margin-top:auto rather than a fixed offset, so when a card is shorter than
   the photograph beside it the link still lands on the card's bottom edge. */
.c-srv-cta {
  align-self: flex-end;
  margin-top: auto;
  padding-top: 0.5em;
}


/* --------------------------------------------------------------------------
   WORK PAGE  (projects.html)

   Was a two-line stub behind three entry points: the header nav, the footer
   and the home page's "View our work" button.

   Everything on it is drawn from the inspection system's own README and
   docs/README.md. No figure is rounded and none is invented, because this is
   the page a director checks. RSLC's own clients are deliberately absent:
   those are RSLC's relationships, not Corvyn's, and listing them under a
   Corvyn heading would claim something untrue.

   Reuses the services page's card anatomy (.c-split-card, .c-srv-block,
   .c-srv-label, .c-price-list) so the two pages read as one site. Only the
   two components this page needs and that one does not are defined here.
   -------------------------------------------------------------------------- */

.c-section.work-intro {
  background-color: var(--corvyn--navy);
  padding-top: 10em;
  padding-bottom: 6em;
}

.c-work-intro {
  display: flex;
  flex-flow: column;
  gap: 1em;
  max-width: 46ch;
}

/* The detail sections share the services page's layout and the same fix: the
   card sets the row height, not a square image column. */
.c-section.wrk-detail .c-split-card-wrap {
  aspect-ratio: auto;
}

.c-section.wrk-detail .o-row.split {
  align-items: stretch;
}

.c-section.wrk-detail .c-split-card {
  gap: 2.5em;
  height: 100%;
}

.c-section.wrk-detail.is-alt {
  background-color: var(--corvyn--porcelain);
}

.c-section.wrk-detail .c-price-list {
  gap: 0.75em;
}

/* A .c-srv-block's 0.75em gap suits a label above one paragraph. The incident
   on this page runs to two, and at that gap they read as one block of text
   with a line break in it rather than two beats. */
.c-srv-block > p + p {
  margin-top: 0.5em;
}

/* SEVERITY TABLE — the considered data detail that stands in for a product
   screenshot. The build guide is explicit that a mockup of something that
   does not exist is worse than one real detail, and this is the detail that
   decides every verdict the system reaches, so it is the right one to show.

   Not a <table>: three unequal columns that stack on a phone, with the mark
   itself as the anchor. role="table" carries the semantics without inheriting
   table layout behaviour. */
.c-wrk-table {
  display: flex;
  flex-flow: column;
  gap: 1.5em;
  padding: 3em;
  border: 1px solid var(--border--border-subtle-light);
  border-radius: 28px;   /* matches .c-split-card beside it */
  height: 100%;
}

.c-wrk-row {
  display: grid;
  grid-template-columns: 3.5em 1fr;
  grid-template-areas: "mark meaning" ".    effect";
  column-gap: 1.25em;
  row-gap: 0.35em;
  padding-top: 1.5em;
  border-top: 1px solid var(--border--border-subtle-light);
}

.c-wrk-mark {
  grid-area: mark;
  color: var(--corvyn--navy);
  line-height: 1;
}

.c-wrk-meaning { grid-area: meaning; }
.c-wrk-effect  { grid-area: effect; }

/* The note explains whose marks these are. It is not a caption on a chart, it
   is the sentence that stops a reader assuming we invented the severity
   model, so it sits in the box rather than under it. */
.c-wrk-note {
  margin-top: auto;
  padding-top: 1em;
}

/* PULL QUOTE — the line from the system's own README. It sits where a second
   screenshot would, and says more than a screenshot would have. */
.c-wrk-quote {
  display: flex;
  flex-flow: column;
  gap: 1.5em;
  padding: 3em;
  background-color: var(--corvyn--white);
  border: 1px solid var(--border--border-subtle-light);
  border-radius: 28px;

  /* Sized to its content and pinned to the top, not stretched to the card
     beside it. The RSLC card runs to 866px and this quote is four lines; a
     full-height box would be three-quarters empty. Sticky so it stays in view
     while the incident and the workbook findings are read past it, which is
     the whole reason it is on the page. */
  align-self: start;
  position: sticky;
  top: 7em;
  height: auto;
}

/* HOW THE ENGINE TRAVELS — .c-key-list on navy, same treatment the services
   page used before that section was dropped. */
.c-section.wrk-travel {
  background-color: var(--corvyn--navy);
}

.c-section.wrk-travel .c-key-num {
  color: var(--corvyn--silver);   /* 9.12:1 on navy; u-opacity-50 was under it */
}

.c-key-list.wrk-steps {
  max-width: none;
  width: 100%;
}

.c-key-list.wrk-steps .c-key-item {
  display: grid;
  grid-template-columns: 22em 1fr;
  gap: 2em;
  align-items: start;
}

/* THE LIMIT. This paragraph is the reason the section is credible rather than
   a sales claim, so it is given a rule and its own space instead of being
   folded in as a fourth row. The system's own docs say plainly that a
   different workflow is engine work and must not be sold as configuration;
   saying so here is cheaper than saying it in a recovery meeting. */
.c-wrk-limit {
  display: flex;
  flex-flow: column;
  gap: 0.75em;
  margin-top: 4em;
  padding-top: 2em;
  border-top: 1px solid var(--border--border-subtle-dark);
  max-width: 64ch;
}

.c-section.wrk-travel .c-srv-label {
  color: var(--corvyn--silver);
}

@media screen and (max-width: 991px) {
  .c-key-list.wrk-steps .c-key-item {
    grid-template-columns: 1fr;
    gap: 0.5em;
  }

  .c-wrk-table,
  .c-wrk-quote {
    padding: 2em;
  }
}

@media screen and (max-width: 767px) {
  .c-section.work-intro {
    padding-top: 8em;
    padding-bottom: 4em;
  }

  .c-work-intro {
    max-width: none;
  }

  .c-wrk-row {
    grid-template-columns: 1fr;
    grid-template-areas: "mark" "meaning" "effect";
  }
}

/* --------------------------------------------------------------------------
   ABOUT PAGE  (about.html)

   Unlike the services page, about.html KEEPS its own Webflow stylesheet.
   .c-ab-mission, .c-ab-card, .c-ab-split and .c-values-list exist only in
   corvyn.webflow.about...css and nowhere else on the site; switching it to
   the index sheet would delete the layout this page is built on. The team
   slider is the one component brought in from another page, and the about
   sheet already carries it.

   Everything Titan is gone: 18 images from their CDN, their executives in
   the team slider, and the eight strings that named the firm.
   -------------------------------------------------------------------------- */

.c-section.about-hero {
  background-color: var(--corvyn--navy);
}

/* The eyebrow sits above the h1 inside .c-about-hero-main, which is a flex
   column with a gap sized for heading-then-paragraph. Without this the label
   floats a full heading gap above the thing it labels. */
.c-section.about-hero .c-eyebrow {
  margin-bottom: -0.5em;
}

/* SECTION 4 — PRINCIPLES

   corvyn-main.js animates this section with GSAP ScrollTrigger, which is NOT
   in js/gsap.min.js (that file is GSAP core 3.15.0 only, no plugins). The
   handler still runs `gsap.set(".c-values-list", {yPercent: 50})` before it
   builds the timeline, and that line applies whether or not the plugin that
   was meant to animate it back exists.

   Result without this rule: the principles list loads pushed down by half
   its own height, overlapping whatever follows. The tween that would have
   undone it never runs on scroll, because there is no ScrollTrigger to fire.

   Overriding the transform here rather than editing the minified handler:
   the same handler also drives .c-values-item and .c-values-handle, and
   unpicking one line out of a minified bundle is the more fragile of the two
   fixes. If GSAP ScrollTrigger is ever added to the page, delete this block
   and the intended animation returns.

   The values section's own title button is gone from the markup: it pointed
   at href="#" for a philosophy page that does not exist. */
.c-section.values .c-values-list {
  transform: none !important;
}

.c-section.values .c-values-item {
  opacity: 1 !important;
  transform: none !important;
}

/* The title block lost its visual and its button, so it is heading alone.
   Without a floor it collapses against the list beside it. */
.c-section.values .c-values-title {
  min-height: 0;
}

/* --------------------------------------------------------------------------
   BLOG — HIDDEN  (blog.html)

   Build guide s6: the route stays built, the nav item stays hidden until
   three posts exist. One post looks abandoned, three looks deliberate.

   Eighteen links to blog.html across six pages are wrapped in HTML comments
   carrying one marker string, so restoring the nav is a find-and-replace on
   two strings rather than an edit in six files.

   The page body is commented out the same way. Unlinked was not enough on
   its own: the route still served the scraped Titan journal, 8 of their
   strings and 44 requests to their CDN, under a Corvyn header and footer. A
   page nobody links to is still a page anyone can reach. blog.html also
   carries noindex now, because unlinked is not the same as unindexed.

   What renders instead is the holding section below, which is honest about
   the state rather than pretending the route does not exist.
   -------------------------------------------------------------------------- */

.c-section.blog-hold {
  background-color: var(--corvyn--navy);
  padding-top: 12em;
  padding-bottom: 12em;   /* the page has one section; this carries the height */
  min-height: 60vh;

  /* The theme tokens are set here rather than left to .u-theme-dark. The blog
     stylesheet does define .c-section.u-theme-dark, but it does not flip the
     --_theme--- indirection layer the way the index and about sheets do, so
     the section came out navy with onyx text on it: 1.13:1, invisible.

     Declaring the eight explicitly makes this section render correctly under
     whichever page stylesheet happens to be loaded, which matters because it
     is the one section on the site that exists to be seen rarely and by
     accident. Values match the dark set at the top of this file. */
  --_theme---text:          var(--corvyn--white);
  --_theme---text-subtle:   var(--corvyn--silver);
  --_theme---background:    var(--corvyn--navy);
  --_theme---surface-bg:    #ffffff12;
  --_theme---border:        var(--corvyn--white);
  --_theme---border-subtle: #ffffff2e;
}

/* .c-work-intro is a flex column, so a button in it stretches to the measure
   and reads as a banner rather than an action. */
.c-section.blog-hold .c-btn {
  align-self: flex-start;
}

/* --------------------------------------------------------------------------
   CONTACT FORM — SUBMITTED STATE  (js/corvyn-form.js)

   The form used to POST normally, which sent the reader to Formspree's own
   thank-you page: not our typography, not our domain, and no way back except
   the browser's back button. A poor last impression on the one page whose
   job is to start a conversation.

   corvyn-form.js now submits in the background and swaps the form for the
   panel below. It is progressive enhancement: the markup keeps its real
   action and method, so with JavaScript off the original behaviour returns
   and nothing is lost.
   -------------------------------------------------------------------------- */

/* The panel replaces the form, so it inherits the space the form occupied
   rather than being sized here. Padding matches .c-contact-form's rhythm. */
.c-form-sent {
  display: flex;
  flex-flow: column;
  align-items: flex-start;
  gap: 1em;
  padding: 3em;
  border: 1px solid var(--border--border-subtle-light);
  border-radius: 28px;   /* matches the cards elsewhere on the site */
  background-color: var(--corvyn--porcelain);
}

/* It takes focus when it appears, so a screen reader lands on the
   confirmation instead of a button that no longer exists. The outline is
   suppressed because focus here is programmatic, not a keyboard journey;
   :focus-visible elsewhere in this file is untouched. */
.c-form-sent:focus {
  outline: none;
}

/* The tick is the only place on the site where navy fills a shape rather
   than drawing a line. Justified: it is the single confirmation the reader
   is looking for, and it appears once. */
.c-form-sent-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3em;
  height: 3em;
  border-radius: 50%;
  background-color: var(--corvyn--navy);
  color: var(--corvyn--white);
  flex: none;
}

/* Guide 08: 200-400ms, decisive easing, no overshoot. One fade and a short
   rise, once, on an element that has just replaced another. */
@keyframes c-form-sent-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.c-form-sent {
  animation: c-form-sent-in 320ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* ERROR STATE. Inline under the button, not a dialog: the reader has just
   pressed something and the answer belongs where they are looking. */
.c-form-status {
  margin-top: 0.5em;
}

.c-form-status.is-error {
  color: #9A2B1F;   /* 6.24:1 on white, 5.86:1 on porcelain. The palette has
                       no error colour; navy would read as success here and
                       silver fails contrast outright. */
}

/* The submit button while the request is in flight. Nothing spins: the
   label changes and the control stops accepting input, which is the honest
   amount of feedback for something that takes under a second. */
.c-contact-submit[aria-busy="true"] {
  opacity: 0.6;
  cursor: progress;
}

.c-contact-submit:disabled {
  pointer-events: none;
}

/* A confirmation that animates in cannot be read by someone who needs it
   still, and this one carries the only proof their message was sent. */
@media (prefers-reduced-motion: reduce) {
  .c-form-sent {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   PRIVACY PAGE  (privacy.html)

   A legal page a procurement officer will actually read, so it is set for
   reading rather than for burying: one column at a real measure, generous
   leading, headings that let someone find the one section they came for.

   Nothing on this page is a new colour or a new type size. It reuses the
   services page's label and tick-list components so the legal page looks
   like the rest of the site rather than like a document pasted into it.
   -------------------------------------------------------------------------- */

.c-legal {
  display: flex;
  flex-flow: column;
  gap: 3.5em;
  max-width: 68ch;   /* guide 09: 65-75 characters. Long-form needs a measure */
}

.c-legal-date {
  margin-bottom: -1.5em;   /* sits with the first heading, not as its own block */
}

.c-legal-block {
  display: flex;
  flex-flow: column;
  gap: 1em;
}

.c-legal-block h2 {
  margin: 0;
}

/* Paragraph spacing inside a block. The flex gap handles heading-to-body;
   this handles body-to-body, which wants to be tighter. */
.c-legal-block p + p {
  margin-top: -0.25em;
}

/* WHO WE ARE. Registered office, data contact and DPC status as three
   labelled facts rather than a paragraph, because these are the details
   someone scans for and copies out. */
.c-legal-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16em, 1fr));
  gap: 2em;
  padding: 2em;
  margin: 0.5em 0;
  border: 1px solid var(--border--border-subtle-light);
  border-radius: 28px;
  background-color: var(--corvyn--porcelain);
}

.c-legal-fact {
  display: flex;
  flex-flow: column;
  gap: 0.5em;
}

.c-legal-fact p {
  margin: 0;
}

/* THE TWO TABLES: what we collect, and who else sees it. Three unequal
   columns, so a grid rather than a <table>. Each row is a thing, its
   description, and where it goes. */
.c-legal-table {
  display: flex;
  flex-flow: column;
  border-top: 1px solid var(--border--border-subtle-light);
  margin: 0.5em 0;
}

.c-legal-row {
  display: grid;
  grid-template-columns: 14em 1fr 12em;
  gap: 1.5em;
  align-items: start;
  padding: 1.25em 0;
  border-bottom: 1px solid var(--border--border-subtle-light);
}

.c-legal .c-price-list {
  gap: 0.75em;
  margin: 0.5em 0;
}

/* Links in running legal text reuse the team email style: underlined rather
   than coloured, because a legal page should not depend on colour to say
   what is clickable. */
.c-legal a {
  color: inherit;
}

@media screen and (max-width: 767px) {
  .c-legal {
    gap: 2.5em;
  }

  .c-legal-row {
    grid-template-columns: 1fr;
    gap: 0.35em;
  }

  .c-legal-facts {
    padding: 1.5em;
    gap: 1.5em;
  }
}
