/* PROF public landing page (PRF-1184).
   All rules scoped under .prof-landing so nothing leaks to other pages:
   same discipline as prof-login.css / prof-legal.css.

   ==========================================================================
   HOW THIS FILE RELATES TO THE FIGMA DESIGN
   https://www.figma.com/design/vH5jodziAcRtBUG4VIJvHE/PROF-10-Platforme

   The design is a fixed 1728px-wide composition, and every number in this
   file is read out of it. But a Figma coordinate is NOT automatically a CSS
   coordinate: each measurement is classified before it is written down.

     FIXED          Border widths, border radii minimums, icon aspect ratios,
                    the logo's proportions. Written as plain px or ratios.

     CONTENT-DRIVEN Button widths, nav item widths, every text box's height,
                    card heights. Written as padding + line-height so the box
                    grows with its content. NEVER as width/height. This is
                    what keeps French from clipping.

     RESPONSIVE     Container width, section padding, type size, grid tracks,
                    the hero composition. Written as clamp()/%/fr so the
                    layout is the design's proportions at 1728px and reflows
                    below it.

   The design's 1528px content measure inside a 1728px canvas is reproduced
   by --pl-content + --pl-gutter: at a 1728px viewport .prof-landing__shell
   is exactly 1728 wide with exactly 1528 of content, and below that it is
   fluid at the same proportions. The page itself is never 1728px wide.

   Type sizes are clamp(floor, Xvw, FIGMA) where X = FIGMA / 1728 * 100, so
   every heading and label hits its exact Figma pixel size at 1728px and
   scales down to a readable floor instead of to nothing. Line heights are
   unitless ratios (Figma leading / Figma size) so they follow.

   var(--u) survives in a few places as "one design pixel at this section's
   width". It is used ONLY for decorative artwork geometry: the hero's curve
   mask, the features band's gradient matrix, two drop shadows. All cases
   where the number is part of a vector construction rather than of layout.
   No text, box or spacing value uses it.

   BREAKPOINTS (the ones already in use elsewhere in this project)
     1200px  small desktop, composition still side-by-side
      900px  tablet       , hero, cards and intro go single column,
                              nav links drop to their own centred row
      600px  phone        , feature grid halves, footer left-aligns

   Palette: this page uses its own navy (#004B99) rather than
   prof-theme.css's --prof-primary (#003DA5): the landing design is a
   distinct brand surface.
   ========================================================================== */

/* This page is standalone (it does not extend store-base.html), so it never
   picks up a CSS reset. Without this the browser's default body margin leaves
   an 8px white gutter down both sides of every full-bleed band. Scoped to the
   landing page's own body class so it cannot affect any other page. */
body.prof-landing-body {
  margin: 0;
  padding: 0;
  /* Scroll anchoring OFF, and this one is load-bearing.

     The bar is in flow and changes height when it docks (140px -> 85 at 1728).
     Anchoring exists to keep the reader's place when content above them
     resizes, so it "corrects" for that by moving the scroll offset down by the
     same ~55px — which drops it back under the threshold the script docks at,
     which undocks the bar, which grows it, which anchoring corrects again. It
     oscillated non-stop: measured at 68 state changes in 90 frames at 1728px
     with nothing touching the page, at every width from 900 up. (375px was
     immune only because its docked and undocked bars are the same height.)

     Chrome and Firefox honour this; Safari implements no scroll anchoring at
     all, so it never had the bug. The page loses nothing: anchoring's real job
     is holding your place when late-loading media reflows the page above you,
     and every image here is sized by CSS before it loads. */
  overflow-anchor: none;
}

.prof-landing {
  --pl-navy: #004B99;          /* headings, nav, footer, icon line art  */
  --pl-navy-deep: #003C7A;     /* button hover                          */
  --pl-slate: #2E5781;         /* body copy (Figma 275:1061)            */
  --pl-cyan: #00B3E4;          /* accent: arrows, Log In pill           */
  --pl-cyan-hover: #009BC7;
  --pl-pale: #E9FAFF;          /* pale card + badge fill                */
  --pl-pale-tint: #F5FBFF;     /* open FAQ row                          */
  /* Hero band: a 3-stop linear gradient down the frame.

     These are the RENDERED colours, sampled straight off frame 275:1061 at
     design y=0 / 413 / 1117, not the stop colours Figma's inspector reports.
     Do not "restore" the inspector values here. That layer is white ->
     #7CD8F1 -> #00B3E4 at 18% opacity, but it composites over a pale cyan
     base (~#E8FAFF), not over white. Reading the 18% as an alpha over white
     is the obvious mistake and it washes the whole band out to near-white;
     it has been tried twice. */
  --pl-hero-top: #EDFBFF;
  --pl-hero-mid: #D3F3FC;
  --pl-hero-end: #B8EBF9;
  /* Figma "Rectangle 5": the water band is the same navy as the headings. */
  --pl-water: #004B99;
  --pl-border: #CFE6F5;
  /* The frame carries no opaque fill of its own; its 18% gradient composites
     over the document's own #E9FAFF page rect. */
  --pl-page: #E9FAFF;

  /* ------------------------------------------------------------- layout */
  /* Measured off the design: every left-aligned element starts at x=100 and
     the Log In button ends at x=1628, so the content measure is 1528 inside
     a 1728 canvas. The gutter reaches exactly 100px at a 1728px viewport and
     shrinks with the window below that, so the whole composition stays on
     the design's proportions without the page ever being a fixed width. */
  --pl-content: 1528px;   /* the design's content measure, for reference */
  --pl-gutter: max(1.25rem, 5.787vw);

  /* What the sticky bar covers, for the anchor targets to clear. This is the
     DOCKED height (the state an anchor jump always lands in, because any
     scroll docks the bar), measured off the rendered bar and rounded up:
     85px at 1728, 71 at 1440, 60 at 1200, 50 at 900. The vw term is what
     carries it past 1728, where the bar keeps growing with the page; the rem
     floor covers everything below ~1570. Redeclared at the two breakpoints
     below, where the bar takes a second tier.

     A constant can only be a per-breakpoint guess, because the bar's real
     height depends on the translation (the phone bar wraps to 131px in French
     where English needs 97), so prof-landing.js overwrites this with the
     measured height of the docked bar as soon as the page is scrolled. These
     values are what stands before the first scroll and for a visitor without
     JavaScript, and they are rounded UP on purpose: too large only opens a
     little more air above the heading, too small hides it under the bar. */
  --pl-nav-h: max(5.5rem, 5.6vw);

  /* ------------------------------------------------------ type scale
     clamp(floor, FIGMA/1728*100 vw, FIGMA). Exact at 1728px, readable at
     375px. Line heights live on the elements as unitless Figma ratios. */
  --pl-fs-61: max(2rem, 3.5301vw);       /* hero + intro H1  */
  --pl-fs-49: max(1.625rem, 2.8356vw);   /* section headings */
  --pl-fs-39: max(1.5rem, 2.2569vw);     /* card titles      */
  --pl-fs-34: max(1.25rem, 1.9676vw);     /* hero tagline     */
  --pl-fs-31: max(1.25rem, 1.794vw);     /* intro copy       */
  --pl-fs-25: max(1.0625rem, 1.4468vw);  /* hero copy, FAQ q */
  --pl-fs-24: max(1.0625rem, 1.3889vw);     /* card copy        */
  --pl-fs-20: max(0.9375rem, 1.1574vw);    /* buttons, nav      */
  --pl-fs-17: max(0.9375rem, 0.9838vw);  /* FAQ answers      */
  --pl-fs-13: max(0.75rem, 0.7523vw);    /* player timecode  */

  /* The ® is drawn by Museo Sans Rounded small and raised to cap height;
     Nunito draws it full size on the baseline. These two ratios (measured off
     frame 275:1135: 25.155/39 and 13/25.155) put it back. em-based, so they
     follow the type scale instead of needing a value per size. */
  --pl-reg-size: 0.645em;
  --pl-reg-rise: 0.517em;

  font-family: 'Karla', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--pl-slate);
  line-height: 1.6;
  /* Not white: the sticky bar below 1200px is translucent, and at scroll 0 it
     composites against this. Matching --pl-hero-top is what keeps the bar and
     the hero band seamless. Every section paints its own background over this,
     so it is only ever visible behind the bar. */
  background-color: var(--pl-hero-top);
  /* Long unbroken strings (URLs, compound French words) break rather than
     push a section wider than the viewport. This is a content guard, not an
     overflow band-aid; there is deliberately no overflow-x: hidden here. */
  overflow-wrap: break-word;
}

.prof-landing *,
.prof-landing *::before,
.prof-landing *::after {
  box-sizing: border-box;
}

/* ------------------------------------------------------- in-page scrolling */

/* The bar is pinned at every width now, so an anchor jump that lands a heading
   at y=0 lands it UNDER the bar. Every one of this page's anchor targets
   (#top, #discover, #features, #faq) is a direct child of .prof-landing, so
   the child combinator addresses exactly them and nothing else. */
.prof-landing > [id] {
  scroll-margin-top: calc(var(--pl-nav-h) + 0.75rem);
}

/* Smooth scrolling belongs to the scrolling element, which is the document,
   not anything inside .prof-landing. :has() keeps the rule off every other
   page in the app: this stylesheet is also loaded by the demo, terms and
   privacy pages, and a browser without :has() simply jumps as it used to.
   no-preference rather than an unconditional rule, because a scroll animation
   is exactly what prefers-reduced-motion is about. */
@media (prefers-reduced-motion: no-preference) {
  :root:has(body.prof-landing-body) {
    scroll-behavior: smooth;
  }
}

/* The one container every section aligns to. Full-bleed backgrounds stay on
   the section; this only constrains content. */
/* The one container every section aligns to. Full-bleed backgrounds stay on
   the section; this only constrains content.

   There is deliberately NO max-width. The design's relationship is a 1528px
   content measure inside a 1728px canvas (a 100px gutter, or 5.787% either
   side), and that RATIO is what the container reproduces, at every width.
   Capping it at 1728 was tried and rejected: on a 3042px screen it left the
   whole composition as a narrow island with ~750px of dead space either side
   while the wave and background still ran full-bleed, which reads nothing
   like the design. Above 1728 the type scale, spacing and hero artwork all
   keep scaling on the same ratio (see --u and the type scale), so a large
   screen shows the Figma composition enlarged rather than stranded. */
.prof-landing__shell {
  width: 100%;
  margin-inline: auto;
  padding-inline: var(--pl-gutter);
}

/* ---------------------------------------------------------------- buttons */

/* Figma's pills are 175 / 162 / 94 x 54 around a 20/24 Semibold label. Those
   widths were measured around the English strings, so they are expressed as
   the padding that produces them (20px either side of a ~135px label) rather
   than as a width. The pill then grows with its own text and "Se connecter"
   fits where a 94px box clipped it. 15px of vertical padding either side of a
   24px line box is the design's 54px height, content-driven. */
.prof-landing__btn,
.prof-hero-fig__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: max(0.5rem, 0.868vw) max(0.875rem, 1.1574vw);
  border-radius: max(6px, 0.5787vw);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: var(--pl-fs-20);
  /* Without this the page's 1.6 body leading makes the pill 64px tall. */
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  /* No border: 15 + 24 + 15 is the design's 54px pill, and a 1px transparent
     border under border-box made every pill on the page 56. */
  border: 0;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.prof-landing__btn:hover,
.prof-landing__btn:focus,
.prof-hero-fig__btn:hover,
.prof-hero-fig__btn:focus {
  text-decoration: none;
}

.prof-landing__btn--navy {
  background-color: var(--pl-navy);
  color: #ffffff;
}

.prof-landing__btn--navy:hover,
.prof-landing__btn--navy:focus {
  background-color: var(--pl-navy-deep);
  color: #ffffff;
}

.prof-landing__btn--white {
  background-color: #ffffff;
  color: var(--pl-navy);
}

.prof-landing__btn--white:hover,
.prof-landing__btn--white:focus {
  background-color: var(--pl-pale);
  color: var(--pl-navy);
}

/* --------------------------------------------- shared: registered mark */

/* Page-wide fallback for the ® outside the Figma-measured frames. */
.prof-landing__mark {
  font-size: 0.6em;
  vertical-align: 0.5em;
  line-height: 0;
}

/* Inside the measured frames the mark uses the design's own ratio. The
   line-height: 0 is load-bearing rather than cosmetic: a smaller inline box
   sharing the line's leading gets positive half-leading, which grows the line
   box and drags the whole card taller. Zeroing it lets the main run set the
   line on its own. */
.prof-intro-fig .prof-landing__mark,
.prof-disc-fig__title .prof-landing__mark {
  font-size: var(--pl-reg-size);
  vertical-align: var(--pl-reg-rise);
  line-height: 0;
}

/* ------------------------------------------------------ language switcher */

/* A globe pill that opens a two-item menu, in place of the pair of flag
   buttons the bar used to carry.

   Why the flags went: a flag is a country, not a language, and the two the bar
   flew (GB and FR) name neither of the countries this product ships in. They
   were also 22x12 of painted box each, 8px apart, which is a lot of precision
   to ask of a thumb for the one control that changes the whole site. The pill
   is a single target that states the current language as text.

   Still a <form> posting to Django's set_language, which sets the cookie and
   redirects back to `next`, so the switch itself needs no JavaScript. The menu
   is a <details>: it opens, closes, and takes the keyboard on its own, and it
   is the element the FAQ on this page already uses, so the page has one
   disclosure pattern rather than two. prof-landing.js adds only the two things
   a bare <details> does not do -- close on an outside click, close on Escape.
   With the script blocked the switcher is still completely usable.

   Deliberately NOT a filled pill, and nothing to do with the orange in the
   mock. The bar already carries two filled pills with a rank between them
   (navy Request a Demo primary, cyan Log In secondary, see the CTA block), and
   a third fill flattens that to three shouting controls. Outlined, it reads as
   what it is: a utility beside two calls to action. The round end caps are the
   mock's and are what separate it from the two rounded-rectangle CTAs at a
   glance. */
.prof-landing__lang {
  display: flex;
  align-items: center;
  margin: 0;
}

.prof-lang {
  position: relative;
}

/* The <summary> IS the button: display:flex both lays the globe out beside the
   code and drops the disclosure triangle, with the two marker rules below for
   the engines that draw it anyway.

   min-height rather than more padding, so the target clears WCAG 2.2 SC 2.5.8
   (24x24) on a phone, where --pl-fs-20 bottoms out at 15px, without inflating
   the control on a desktop where the type has already grown past it. */
.prof-lang__btn {
  display: flex;
  align-items: center;
  gap: max(0.3rem, 0.35vw);
  min-height: 38px;
  padding: max(0.35rem, 0.55vw) max(0.65rem, 0.8vw);
  border: 1px solid var(--pl-border);
  border-radius: 999px;
  background-color: #ffffff;
  color: var(--pl-navy);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: var(--pl-fs-20);
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  list-style: none;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.prof-lang__btn::-webkit-details-marker {
  display: none;
}

.prof-lang__btn::marker {
  content: "";
}

.prof-lang__btn:hover,
.prof-lang__btn:focus-visible,
.prof-lang[open] > .prof-lang__btn {
  background-color: var(--pl-pale);
  border-color: var(--pl-navy);
}

.prof-lang__btn:focus-visible {
  outline: 2px solid var(--pl-navy);
  outline-offset: 2px;
}

/* em-sized, so the globe tracks the code beside it at every width instead of
   needing a value per breakpoint. Stroked in currentColor, which is what makes
   the hover and open states carry it along for free. */
.prof-lang__globe {
  display: block;
  flex: none;
  width: 1.15em;
  height: 1.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* "FR" on its own is a fine label to look at and a poor one to hear, so the
   word itself is in the accessible name and off the screen. */
.prof-lang__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Right-anchored, always. The switcher sits hard against the bar's right edge
   at every width (the logo's margin-right: auto takes all the slack), so a
   left-anchored or centred panel is the one that can reach past the viewport;
   from the right it opens inwards into the page by construction.

   min-width takes the wider of a readable menu and the button itself, so the
   panel is never narrower than the control it belongs to. */
.prof-lang__menu {
  position: absolute;
  top: calc(100% + 0.45rem);
  right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: max(9.5rem, 100%);
  padding: 0.3rem;
  border-radius: max(10px, 0.6vw);
  background-color: #ffffff;
  box-shadow: 0 0 0 1px rgba(0, 75, 153, 0.08),
              0 18px 38px -16px rgba(0, 60, 120, 0.45);
}

/* The options are the form's submit buttons, so choosing one posts the
   language and the page comes back translated. No JavaScript in that path. */
.prof-lang__opt {
  display: block;
  width: 100%;
  padding: max(0.45rem, 0.5vw) max(0.7rem, 0.8vw);
  border: 0;
  border-radius: max(7px, 0.45vw);
  background: none;
  color: var(--pl-slate);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 400;
  font-size: var(--pl-fs-20);
  line-height: 1.3;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.prof-lang__opt:hover,
.prof-lang__opt:focus-visible {
  background-color: var(--pl-pale-tint);
  color: var(--pl-navy);
}

/* The current language, marked three ways: fill, colour and weight. 600
   against the 400 of the other row is a real face -- both are fetched -- not a
   synthesised bold, and 700 here would be the latter. */
.prof-lang__opt.is-active {
  background-color: var(--pl-pale);
  color: var(--pl-navy);
  font-weight: 600;
}

/* A <details> cannot transition open, so the panel is animated on arrival
   instead. Purely decorative: with the animation suppressed the panel is
   simply there. */
@media (prefers-reduced-motion: no-preference) {
  .prof-lang[open] > .prof-lang__menu {
    animation: prof-lang-in 0.14s ease-out;
  }
}

@keyframes prof-lang-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* ==========================================================================
   HERO: Figma frame 275:1061 "HERO SECTION" (1728x1117)

   Was one absolutely-positioned 1728x1117 canvas. Now flow blocks on the
   shared container, with only the wave left as decorative artwork. The nav
   bar is no longer part of this section (it is a page-level sibling, so it
   can be sticky), which is why the section measures 977 rather than 1117.

   The design's vertical rhythm is reproduced as flow spacing and it adds
   back up. Reading down from the top of the page:

     nav      61 padding + 78.894 logo            = 139.894
     main     123.65 padding, then the card row     263.544  (card's own y)
     copy     +12.96 on top of that                 276.5    (headline y)
     row      572.8 tall (the card sets it)         836.344
     water    pulled up 351.3, 475 of clear water
              above the tagline, 116 below it       1117

   So nav + hero is the frame's 1117 exactly, and the hero on its own is
   1117 - 139.894 = 977.
   ========================================================================== */

.prof-hero-fig {
  container-type: inline-size;
  position: relative;
  /* Figma: the frame has no opaque fill, only an 18% gradient over the
     document's #E9FAFF page rect. Stop positions are the frame's own
     (923.5/1117 span, middle stop at 344.5/1117). All three stops carry the
     same alpha, so these rgba() values are exact rather than approximate.
     Held as percentages so the ramp follows the section however tall the
     content makes it. */
  background-color: var(--pl-page);
  background-image: linear-gradient(180deg,
      rgba(255, 255, 255, 0.18) 0%,
      rgba(124, 216, 241, 0.18) 30.843%,
      rgba(0, 179, 228, 0.18) 82.677%);
  /* Clips the wave, exactly as the Figma frame clips it. This is intentional
     artwork clipping on one section, not a page-level overflow band-aid. */
  overflow: hidden;
}

/* One design pixel at this section's width. Decorative geometry only, and
   deliberately uncapped: the whole composition scales past 1728 on the
   design's own ratio, so the wave and the tagline's well have to scale with
   it or the hero comes apart on a large screen. */
.prof-hero-fig__water,
.prof-hero-fig__card {
  --u: calc(100cqw / 1728);
}

/* ------------------------------------------------------------------- nav */

/* Figma "NAV" (275:1077): 100px/61px padding, logo left, menu right.

   A page-level bar, not part of the hero. Its background is the colour the
   hero's gradient composites to at 0%, so the two meet with no seam, and it
   is what makes the bar opaque once it is pinned. z-index keeps it over every
   later section (the features and trusted bands are positioned).

   Sticky at EVERY width. It used to pin only below 1200px, on the reasoning
   that a desktop window shows enough of the page not to need it; the bar is
   now the page's wayfinder (it marks the section being read) and carries the
   demo CTA, so it has to be there the whole way down. */
.prof-hero-fig__nav {
  position: sticky;
  top: 0;
  z-index: 40;
  background-color: var(--pl-hero-top);
  padding-top: max(1.25rem, 3.5301vw);
  padding-bottom: 0;
  transition: padding 0.25s ease, background-color 0.25s ease, box-shadow 0.25s ease;
}

/* ------------------------------------------------------------ docked bar

   At rest the bar is the design's: a 61px lead-in over a 240px logo, 140px of
   it at 1728. That is the right opening for the page and much too much to
   surrender permanently, so the bar compacts to 85px (71 at 1440, 60 at 1200)
   as soon as the page moves. prof-landing.js adds this class past 8px of
   scroll; without the script the bar just stays at its full height, which is a
   worse header but not a broken one.

   The padding change is animated, so the collapse takes the content below it
   with it smoothly instead of snapping 55px up in one frame. */
.prof-hero-fig__nav.is-stuck {
  padding-top: max(0.45rem, 0.9vw);
  padding-bottom: max(0.45rem, 0.9vw);
  /* Frosted rather than flat: content reads through the bar as it scrolls
     under, which is what stops a pinned bar feeling like a lid. The alpha is
     high enough that navy-on-pale stays legible over the dark water band. */
  background-color: rgba(237, 251, 255, 0.88);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  /* A hairline to sit the bar on, plus a soft lift that only shows once
     something is behind it. */
  box-shadow: 0 1px 0 rgba(0, 75, 153, 0.08),
              0 10px 30px -18px rgba(0, 60, 120, 0.45);
}

/* Figma's drop shadow silhouettes the logo and the pill rather than a band
   (the NAV frame has no fill). Once the docked bar carries a background and an
   edge shadow of its own, that reads as grime on the logo. */
.prof-hero-fig__nav.is-stuck .prof-hero-fig__nav-inner {
  filter: none;
}

/* The logo is what actually sets the bar's height, so docking has to shrink
   it: the padding alone would still leave a 79px lockup. Capped at 150 so it
   does not grow back past the point on a 2560px screen, floored at the phone
   size below. */
.prof-hero-fig__nav.is-stuck .prof-hero-fig__logo {
  width: clamp(104px, 7.5vw, 150px);
}

/* The bar still docks under reduced motion, it just does it in one step: the
   smoothing is a nicety, the compact bar is the layout. */
@media (prefers-reduced-motion: reduce) {
  .prof-hero-fig__nav,
  .prof-hero-fig__logo {
    transition: none;
  }
}

/* prof-landing.js hangs this on the bar for one frame when an in-page link is
   clicked from an undocked bar: the bar has to be at its docked height BEFORE
   the browser works out where that link lands, or the collapse moves the
   target out from under it while the scroll is in flight. Animating into place
   is exactly what must not happen there. */
.prof-hero-fig__nav.is-instant,
.prof-hero-fig__nav.is-instant .prof-hero-fig__logo {
  transition: none;
}

/* Figma puts a drop shadow on the NAV frame, which has no fill, so what it
   actually shadows is the logo/label/pill silhouettes rather than a band.
   The filter therefore belongs on this transparent inner wrapper, NOT on the
   bar: the bar now carries a background, and a drop-shadow on that silhouettes
   the whole rectangle and draws a seam across the page under it.
   In vw rather than --u, which only resolves inside the hero's container. */
.prof-hero-fig__nav-inner {
  filter: drop-shadow(0 1.2153vw 1.088vw rgba(0, 0, 0, 0.08));
}

/* Figma "Menu droite" gap is 37px. flex-wrap is what makes this survive a
   long translation: items drop to the next row rather than overlapping. */
.prof-hero-fig__nav-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: max(0.75rem, 2.1412vw);
}

/* The menu toggle does not exist above the phone/tablet breakpoint. Both
   halves are display:none rather than hidden some other way, so the checkbox
   is out of the tab order too: there is nothing to reach on a desktop bar,
   where every link and pill is already on screen. The rules that bring it
   back, and everything the drawer does, live in the < 900px block. */
.prof-nav-toggle,
.prof-nav-toggle__btn {
  display: none;
}

/* The drawer's card needs the links and the pills to be ONE box, so they are
   wrapped. display: contents unwraps them again here: the element boxes
   nothing, and its children are laid out as direct flex children of the bar,
   exactly as they were before the wrapper existed. The same trick the CTA pair
   already used one level down.

   Wrapping them made them adjacent in the source, which pushed the language
   switcher to the end of it -- the card has to be one contiguous run. `order`
   is what puts the desktop row back the way the design draws it, logo / links
   / language / pills, without the source having to match. */
.prof-nav-panel {
  display: contents;
}

/* `order` has to name the elements that are actually flex ITEMS, and with two
   display: contents wrappers in the way those are not the elements the markup
   nests them in. .prof-nav-panel boxes nothing and .prof-hero-fig__nav-cta
   boxes nothing, so the bar's real children are the logo, the links, the two
   pills and the language form. Ordering the CTA wrapper did nothing at all --
   the pills kept the default 0 and sorted to the front of the row, landing
   both of them left of the menu links. They have to be ordered themselves.

   The nav-cta line below is not redundant: on a phone that wrapper IS a box
   (the card makes it a flex column), and there it is what carries the pills
   under the links. */
.prof-hero-fig__logo-link { order: 0; }
.prof-hero-fig__links { order: 1; }
.prof-hero-fig__lang { order: 2; }
.prof-hero-fig__nav-cta { order: 3; }
.prof-hero-fig__nav-cta > .prof-hero-fig__btn,
.prof-nav-panel > .prof-hero-fig__btn { order: 3; }

/* margin-right: auto reproduces the design's space-between without pinning
   anything to a coordinate: logo left, everything else grouped on the right
   at the design's 37px gap. */
.prof-hero-fig__logo-link {
  flex: none;
  margin-right: auto;
  display: block;
  line-height: 0;
}

/* Figma "Layer_1" (275:1078): 240 x 78.894. Height is auto so the logo can
   never be distorted; the SVG carries the ratio. The transition is the docking
   above; it is on the logo rather than on the bar because width is what
   changes here. */
.prof-hero-fig__logo {
  display: block;
  width: max(120px, 13.889vw);
  height: auto;
  transition: width 0.25s ease;
}

/* ---------------------------------------------- nav: one markup, two modes */

/* Not in the Figma frame, but it is the only control for a feature this page
   ships (the copy is translated), so it is kept. It sits between the FAQ link
   and the Log In pill, on the row's own 37px gap. No auto margin here: the
   logo's margin-right: auto already takes all the free space, so the links,
   the switcher and the pill stay grouped hard against the right edge and the
   pill still closes on the design's x=1628.

   The form holds one child now (it held two flag buttons), so it states no gap
   of its own; the row's gap is what spaces it from its neighbours. */
.prof-hero-fig__lang {
  flex: none;
}

/* Figma 275:1118 is ONE text node reading
   "Discover the Platform     ·     Features     ·     FAQ", five spaces
   either side of each separator, which measure ~25px at 20px. Reproduced as
   a real gap on a real flex row rather than as literal spaces under
   white-space: pre, so the links can wrap when there is no room for them. */
.prof-hero-fig__links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: max(0.4rem, 1.4468vw);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: var(--pl-fs-20);
  line-height: 1.2;
  color: var(--pl-navy);
}

.prof-hero-fig__links a {
  position: relative;
  color: inherit;
  text-decoration: none;
  transition: color 0.15s ease;
}

.prof-hero-fig__links a:hover,
.prof-hero-fig__links a:focus {
  color: var(--pl-cyan);
  text-decoration: none;
}

/* ------------------------------------------------- the section being read

   prof-landing.js puts .is-active (and aria-current) on the link whose section
   is under the bar. Colour ALONE would not do: it is the same cyan as hover,
   so a reader with the pointer anywhere on the bar could not tell the two
   apart, and colour on its own is not a distinction everyone can see (WCAG 2.1
   SC 1.4.1). The rule under the label is the actual marker; the colour is what
   makes it read as one piece.

   Drawn as a pseudo-element rather than as an underline or a border so it can
   sit clear of the descenders in "Discover the Platform" without changing the
   link's box, which would move the whole row when the highlight lands. */
.prof-hero-fig__links a.is-active {
  color: var(--pl-cyan);
}

.prof-hero-fig__links a.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.3em;
  height: 2px;
  border-radius: 2px;
  background-color: var(--pl-cyan);
}

.prof-hero-fig__sep {
  opacity: 0.85;
}

/* Figma 275:1119: fill Bleu Ciel. No width, see the button block above. */
.prof-hero-fig__btn--login {
  flex: none;
  background-color: var(--pl-cyan);
  color: #ffffff;
}

.prof-hero-fig__btn--login:hover,
.prof-hero-fig__btn--login:focus {
  background-color: var(--pl-cyan-hover);
  color: #ffffff;
}

/* The two pills as one block.

   display: contents means this element boxes nothing: the pills inside it are
   laid out as direct flex children of the bar, so the row above is exactly
   what it was before the wrapper existed. The phone breakpoint turns it into a
   real flex row, and the pair moves to its own tier together.

   Both pills therefore keep their fill at every width. Log In was demoted to a
   text link on phones once, to buy tier-one room for the demo pill, and that
   is not a trade this bar should make: a button that stops looking like a
   button reads as a bug, however deliberate the CSS was. */
.prof-hero-fig__nav-cta {
  display: contents;
}

/* The demo CTA. Not in the Figma frame; it is the one action this page exists
   to produce, and it now rides the bar so it is on screen the whole way down
   rather than only in the hero and the footer.

   Navy, against the Log In pill's cyan: two filled pills of equal weight would
   read as one control split in half, and navy is both the page's dominant
   colour and the higher-contrast of the two, so the ranking is visible before
   either label is. No width here either, so "Demander une démo" sets its own
   pill in French. */
.prof-hero-fig__btn--demo {
  flex: none;
  background-color: var(--pl-navy);
  color: #ffffff;
}

.prof-hero-fig__btn--demo:hover,
.prof-hero-fig__btn--demo:focus {
  background-color: var(--pl-navy-deep);
  color: #ffffff;
}

/* --------------------------------------------------------- hero content */

/* Figma's "Textes intro" box runs y=118..835 and centres its 400px of content
   in it, which puts the headline at y=276.5, NOT at the 158.5 the frame
   reports, which is the offset INSIDE that box. The card box starts at
   263.54, so the row's own top is the card's and the copy is nudged the
   remaining 12.96 down. The logo ends at 139.894, so 123.65 of flow spacing
   opens the row exactly where the design does.

   The two tracks are the design's own measures: the copy runs x=100..854 and
   the card box x=854..1614.76 inside the 1528 content. Declaring them as fr
   keeps that ratio at every width instead of freezing the split at 1728. */
.prof-hero-fig__main {
  position: relative;
  z-index: 1;
  padding-top: max(1.5rem, 7.1557vw);
  display: grid;
  grid-template-columns: minmax(0, 754.19fr) minmax(0, 773.81fr);
  align-items: start;
}

/* Figma "Textes intro" (275:1069): auto-layout column, 40px gap. */
.prof-hero-fig__copy {
  padding-top: max(0rem, 0.75vw);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: max(1rem, 2.3148vw);
}

/* Figma 275:1070: 61px over 73px leading. No width and no height, the
   headline wraps to whatever the column gives it and the box follows. */
.prof-hero-fig__title {
  margin: 0;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-61);
  line-height: 1.19672;
  color: var(--pl-navy);
}

/* Figma 275:1071: 25/30, set on a 680 measure inside the 754 column. Kept as
   a proportion of the column so the design's four-line wrap survives at
   desktop while the paragraph is free to run longer in French. */
.prof-hero-fig__text {
  margin: 0;
  max-width: 90.16%;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-25);
  line-height: 1.2;
  color: var(--pl-slate);
}

/* Figma "Boutons Hero" (275:1072): 20px gap. Wraps so two long French labels
   cannot push the row past the column. */
.prof-hero-fig__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: max(0.625rem, 1.1574vw);
}

/* Figma 275:1073: fill Bleu. */
.prof-hero-fig__btn--primary {
  background-color: var(--pl-navy);
  color: #ffffff;
}

.prof-hero-fig__btn--primary:hover,
.prof-hero-fig__btn--primary:focus {
  background-color: var(--pl-navy-deep);
  color: #ffffff;
}

/* Figma 275:1075: no fill, dotted underline, Paragraphe. Keeps the same
   vertical padding as the filled pill so both sit on one 54px baseline. */
.prof-hero-fig__btn--text {
  color: var(--pl-slate);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: from-font;
}

.prof-hero-fig__btn--text:hover,
.prof-hero-fig__btn--text:focus {
  color: var(--pl-navy);
  text-decoration: underline;
  text-decoration-style: dotted;
}

/* Figma "Image - Rapport Eau" (275:1558): 760.569x572.802 at x=854.19,
   y=263.54. Width is a proportion of its own track; the height now follows
   the card's own content, which is built to the same 1.3277 ratio, so it
   still cannot be stretched. The drop-shadow filter follows the rendered
   shape, so it picks up the border-radius rather than drawing square corners
   the way a box-shadow would.

   It is a query container so the card's interior can be sized in cqw and
   scale as one piece with its column, exactly as the flattened image did. */
.prof-hero-fig__card {
  width: 98.29%;
  container-type: inline-size;
  filter: drop-shadow(0 0 calc(53.018 * var(--u)) rgba(0, 0, 0, 0.05));
}

/* ------------------------------------------ water analysis card interior */

/* The design ships this card as ONE flattened PNG with no sub-layers, so its
   nine strings ("pH", "Free chlorine", "In range", ...) sat outside gettext
   and the card could not be translated at all. It is rebuilt here as markup.

   Every number below is a design pixel on the 760.569-wide card. They were
   measured off a 4x export of the node ("Image - Rapport Eau", 3467x2716 with
   the drop shadow, card plate 3042.09x2290.83 => 760.523 x 572.706 design),
   cross-checked against the lossless PNG embedded in the HERO SECTION SVG.
   The two sources agree everywhere to within 0.76 design px.

   Type is Inter with TABULAR figures. That is not a guess: the export's "1"
   carries a foot serif, which is exactly what Inter's tnum "1" has and its
   proportional "1" does not. Hence font-feature-settings below; drop it and
   every number in the card changes shape and width.

   The export is hand-composed, not laid out on a grid: its rows are
   100.66 / 89.74 / 98.40 tall, its pills sit 4.76 / 8.51 / 0 back from the
   content edge, and its value-to-pill gaps are 23.4 / 22.5 / 14.9. All of
   that is reproduced per row rather than averaged, which is what holds the
   rebuild to a mean 0.50 design px against the export.

   None of it is absolute positioning: pills are pinned to the right edge and
   grow leftward on their own padding, so a longer translated word still
   fits. Verified in EN and FR down to 375px. */

.prof-wa {
  /* 1 unit == 1 design px on the 760.569 card. Declared on the descendant:
     cqw resolves against the nearest ANCESTOR container. */
  --c: calc(100cqw / 760.569);

  --wa-ink: #00071b;          /* values, units, action text */
  --wa-muted: #566784;        /* labels, both section headings */
  --wa-rule: #e4e8ee;
  --wa-panel: #f3f5f7;
  --wa-badge: #1f3e84;
  --wa-warn-bg: #f0e6db;
  --wa-warn-ink: #8f642b;
  --wa-ok-bg: #dbefe5;
  --wa-ok-ink: #007f4a;

  box-sizing: border-box;
  background: #fff;
  border-radius: calc(29.5 * var(--c));   /* four corner fits: 30.8 30.3 28.8 28.2 */
  /* Top padding is set from the title's INK, not its line box: the cap top
     sits 44.81 from the card top and Inter puts 5.94 of line box above the
     cap at this size. Bottom padding is the design's own 41.73. */
  padding: calc(39.51 * var(--c)) calc(41.78 * var(--c)) calc(42.58 * var(--c)) calc(42.99 * var(--c));
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-feature-settings: 'tnum' 1;   /* the export's figures are tabular */
}

.prof-wa__title {
  margin: 0;
  padding-left: calc(0.95 * var(--c));
  font-size: max(11px, calc(25.1 * var(--c)));
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: calc(0.24 * var(--c));
  text-transform: uppercase;
  color: var(--wa-muted);
}

/* The rule is each row's own top border, so they land at 98.50 / 199.16 /
   288.89 and the panel opens at 387.29, all as measured. */
.prof-wa__row {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  border-top: 1px solid var(--wa-rule);
}

/* Each row carries its own three measurements from the export, because the
   export is hand-placed and they genuinely differ per row:
     --pill-inset  pill right edge, back from the 719.32 content edge
     --pill-gap    unit/value ink to pill box
     --unit-gap    value ink to unit ink                                     */
.prof-wa__row:nth-of-type(1) {
  height: calc(100.66 * var(--c));
  padding-top: calc(9.3 * var(--c));
  --pill-inset: 4.76;
  --pill-gap: 23.4;
  --unit-gap: 15.5;
  --pill-pad: 19.3;
  --readout-shift: 2.84;
  --pill-shift: 1.04;
}
.prof-wa__row:nth-of-type(2) {
  height: calc(89.74 * var(--c));
  padding-top: calc(3 * var(--c));
  --pill-inset: 8.51;
  --pill-gap: 22.5;
  --unit-gap: 16.4;
  --pill-pad: 18.5;
}
.prof-wa__row:nth-of-type(3) {
  height: calc(98.40 * var(--c));
  padding-bottom: calc(6 * var(--c));
  --pill-inset: 0;
  --pill-gap: 14.9;
  --unit-gap: 14.9;
  --pill-pad: 15.45;
  --lbl-track: 0.319;
  --pill-shift: 1.5;
}

.prof-wa__row:first-of-type {
  margin-top: calc(28.29 * var(--c));
}

.prof-wa__label {
  flex: 1 1 auto;
  padding-left: calc(1.05 * var(--c));
  font-size: max(11px, calc(25.5 * var(--c)));
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: calc(var(--lbl-track, 0.15) * var(--c));
  color: var(--wa-muted);
}

.prof-wa__readout {
  position: relative;
  top: calc(var(--readout-shift, 0) * var(--c));
  display: flex;
  align-items: baseline;
  gap: calc(var(--unit-gap) * var(--c));
  flex-shrink: 0;
}

.prof-wa__value {
  font-size: max(13px, calc(31.62 * var(--c)));
  font-weight: 700;
  line-height: 1.2;
  color: var(--wa-ink);
}

.prof-wa__unit {
  font-size: max(12px, calc(27.5 * var(--c)));
  font-weight: 700;
  line-height: 1.2;
  color: var(--wa-ink);
}

/* Stadium pills, padding-driven so a translated word grows the pill rather
   than overflowing it. --pill-inset pins the right edge, so growth is
   leftward and the pill never crosses the card's content edge. */
.prof-wa__pill {
  position: relative;
  top: calc(var(--pill-shift, 0.6) * var(--c));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  min-height: calc(39.4 * var(--c));
  padding: 0 calc(var(--pill-pad) * var(--c));
  margin-left: calc(var(--pill-gap) * var(--c));
  margin-right: calc(var(--pill-inset) * var(--c));
  border-radius: 999px;
  font-size: max(10px, calc(19.9 * var(--c)));
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}

.prof-wa__pill--warn { background: var(--wa-warn-bg); color: var(--wa-warn-ink); }
.prof-wa__pill--ok   { background: var(--wa-ok-bg);   color: var(--wa-ok-ink); }

/* Recommended-action panel: 676.57 x 144.26 at y 387.29, on the same content
   edge as the rules. */
.prof-wa__action {
  box-sizing: border-box;
  height: calc(144.12 * var(--c));
  padding: calc(26.2 * var(--c)) calc(25 * var(--c));
  border-radius: calc(28 * var(--c));
  background: var(--wa-panel);
  display: flex;
  flex-direction: column;
}

.prof-wa__action-label {
  margin: 0;
  font-size: max(10px, calc(18.65 * var(--c)));
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: calc(1.93 * var(--c));
  padding-left: calc(2.28 * var(--c));   /* the export insets this label 2.28 past the badge */
  text-transform: uppercase;
  color: var(--wa-muted);
}

.prof-wa__action-body {
  display: flex;
  align-items: center;
  gap: calc(24.3 * var(--c));
  margin-top: calc(17.9 * var(--c));
}

.prof-wa__badge {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(67.75 * var(--c));
  height: calc(64.5 * var(--c));
  border-radius: calc(15 * var(--c));
  background: var(--wa-badge);
  color: #fff;
  font-size: max(11px, calc(26 * var(--c)));
  font-weight: 700;
  line-height: 1;
}

.prof-wa__action-text {
  margin: 0;
  font-size: max(12px, calc(26.8 * var(--c)));
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: calc(-0.352 * var(--c));
  color: var(--wa-ink);
}

/* ------------------------------------------------- water band + tagline */

/* Figma "Mask group" (275:1063): a navy rect with the water photo multiplied
   over it, clipped by the frame's own "Courbe bleue" vector.

   The design draws that as a fixed 1742x632 rect inside a 2941x1444 mask
   box. Here the band is a flow element that grows with its content and the
   crest has been re-cut (hero-curve-crest.svg) to span exactly the frame's
   width, so the same curve reads correctly whatever height the band ends up.
   Two mask layers: the crest at the top, and a solid layer covering
   everything below it. Their union is the band.

   The negative margin is the design's own overlap, it is what lifts the
   crest behind the analysis card, expressed in design pixels so it stays
   proportional instead of drifting as the window widens. */
.prof-hero-fig__water {
  position: relative;
  z-index: 0;
  margin-top: calc(-351.3 * var(--u));
  /* 960 - 485: clear water between the crest box and the tagline. */
  padding-top: calc(475 * var(--u));
  /* 1117 - 1001: the frame's own bottom margin, so the section needs none. */
  padding-bottom: calc(116 * var(--u));
  background-color: var(--pl-water);
  -webkit-mask-image: url("../img/prof/landing/hero-curve-crest.svg"), linear-gradient(#000, #000);
          mask-image: url("../img/prof/landing/hero-curve-crest.svg"), linear-gradient(#000, #000);
  /* The crest spans the band's full width but its DEPTH is a design pixel
     value, so past 1728 the wave flattens and widens instead of growing
     taller than the tagline's well beneath it. (The SVG carries
     preserveAspectRatio="none" for exactly this.) At or below 1728,
     400 * var(--u) is 23.15% of the width, identical to `auto`.

     The +2px on the lower layer is load-bearing, not slop. The two layers used
     to ABUT exactly at 400 * var(--u), and 400 * var(--u) is almost never a
     whole device pixel: wherever it landed on a fraction, neither layer fully
     covered that row and the hero gradient showed through the navy as a
     bright hairline across the whole band. It reproduced at 1200/1280/1600/
     1920 with a row delta of up to 158/255, and looked clean at 1366/1440/
     1728, which is exactly the "only sometimes" signature. Overlapping the
     layers removes the boundary; mask layers composite with `add`, so the
     overlap is invisible. */
  -webkit-mask-size: 100% calc(400 * var(--u)), 100% calc(100% - 400 * var(--u) + 2px);
          mask-size: 100% calc(400 * var(--u)), 100% calc(100% - 400 * var(--u) + 2px);
  -webkit-mask-position: top left, bottom left;
          mask-position: top left, bottom left;
  -webkit-mask-repeat: no-repeat, no-repeat;
          mask-repeat: no-repeat, no-repeat;
}

/* The photo multiplies over the navy fill, both inside the mask, same
   stacking as Figma. cover can never leave a gap at any band height, and the
   22.5% vertical position is the design's own crop: it puts the photo's top
   127 design pixels above the band, which is what lands the waterline. */
.prof-hero-fig__water::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../img/prof/landing/hero-water-photo.webp");
  background-size: cover;
  background-position: center 22.5%;
  background-repeat: no-repeat;
  mix-blend-mode: multiply;
}

/* Figma 275:1067: 34/41, at the same x=100 grid line as the logo, headline,
   paragraph and buttons. The design's node is a nowrap 618px box; here it
   wraps, because a French tagline that cannot wrap is a horizontal scrollbar. */
.prof-hero-fig__slogan {
  /* The photo layer above is an absolutely positioned ::before, so the
     tagline needs to be positioned too or it paints underneath it. */
  position: relative;
  z-index: 1;
  margin: 0;
  color: #ffffff;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-34);
  line-height: 1.20588;
}

/* Figma runs "safe" and "beautiful" at weight 900 against the 700 around them. */
.prof-hero-fig__slogan strong {
  font-weight: 900;
}

/* Figma sets the space before the mark at 31px against the line's 34. */
.prof-hero-fig__slogan-gap {
  font-size: 0.912em;
}

/* Figma sets the mark at 19.995 with its baseline 11px above the line's.
   Both as em of their own run, so they follow the fluid type. */
.prof-hero-fig__slogan sup {
  font-size: 0.588em;
  line-height: 0;
  vertical-align: 0.55em;
}

/* The ® is a plain glyph inside the hero's own runs in Figma, the face draws
   it small and raised on its own, so the page-wide shrink is neutralised
   here. */
.prof-hero-fig .prof-landing__mark {
  font-size: inherit;
  vertical-align: baseline;
  line-height: inherit;
}

/* French sets the mark as the letters MD, which the face has no small-and-
   raised treatment for: left at inherit they render full size on the baseline
   and read as a typo rather than a mark. Restore the page-wide shrink for
   them. Both classes are named in the selector so this outweighs the rule
   above outright, rather than depending on source order the way an
   equal-specificity override would. */
.prof-hero-fig .prof-landing__mark.prof-landing__mark--abbr {
  font-size: 0.6em;
  vertical-align: 0.5em;
  line-height: 0;
}

/* ==========================================================================
   INTRO: Figma frame 275:1121 "More Than Water Testing" (1728x436)

   Was a 436px-tall canvas with both children placed absolutely. Now a
   two-column grid on the shared container. The tracks are the frame's own
   measures (839 copy + 123 gap + 566 points = 1528) and the 42.5px offset
   Figma gives the bullet column is exactly what centring the shorter column
   against the taller one produces, so align-items does it for free.
   ========================================================================== */

.prof-intro-fig {
  background-color: #ffffff;
  padding-block: max(2.5rem, 5.787vw);
}

.prof-intro-fig__inner {
  display: grid;
  grid-template-columns: minmax(0, 839fr) minmax(0, 566fr);
  column-gap: max(1.5rem, 7.1181vw);
  align-items: center;
}

/* Heading 73 tall, paragraph starts at y=88, a 15px column gap. */
.prof-intro-fig__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: max(0.4rem, 0.868vw);
}

/* Figma 275:1123: Museo Sans Rounded 900 at 61/73, the same H1 style as the
   hero headline. Fill is Bleu. */
.prof-intro-fig__title {
  margin: 0;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-61);
  line-height: 1.19672;
  color: var(--pl-navy);
}

/* Figma 275:1124: the H4 style, 31/37, fill Paragraphe. */
.prof-intro-fig__text {
  margin: 0;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-31);
  line-height: 1.19355;
  color: var(--pl-slate);
}

/* Figma "3 points" (275:1125): three 37px rows, 20px apart. */
.prof-intro-fig__points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: max(0.6rem, 1.1574vw);
}

/* The design's rows are one line each and the old build forced that with
   white-space: nowrap, which is why a longer French label ran into the row
   below it. A flex row with a real gap lets the label wrap under itself
   instead, and the row grows. */
.prof-intro-fig__points li {
  display: flex;
  align-items: flex-start;
  gap: max(0.4rem, 0.5885vw);
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-31);
  line-height: 1.19355;
  color: var(--pl-cyan);
}

/* The frame's own "Vector 9", not a glyph or a border. Figma reports the
   node as 32 x 21.333, but the exported path carries a 4px stroke, so its
   visual box is 36.83 x 24.162, which is the SVG's viewBox. Height is auto
   so the arrow keeps that ratio; the top margin centres it on the first line
   of the label rather than on the whole wrapped block. */
.prof-intro-fig__points img {
  flex: none;
  display: block;
  width: max(22px, 2.1314vw);
  height: auto;
  margin-top: 0.21em;
}

/* ==========================================================================
   DISCOVER CARDS: Figma frame 275:1135 (1728x2058)

   Already a flow column; what was static was every dimension inside it. The
   cards now take their height from their copy (which is what makes them safe
   for French), the circle is a percentage of the card and each icon a
   percentage of the circle, and below 900px the row becomes a column.
   ========================================================================== */

.prof-disc-fig {
  background-color: #ffffff;
  /* The frame's own bottom padding is the gap to the next band. */
  padding-bottom: max(2.5rem, 5.787vw);
}

/* Figma "Frame 15" (275:1136): column, 30px gap. */
.prof-disc-fig__list {
  display: flex;
  flex-direction: column;
  gap: max(1rem, 1.7361vw);
}

/* Every card: radius 40, row, 50px gap, centred, padding 70/90/70/60. The
   asymmetric horizontal padding is the design's and is what makes the text
   column measure 1022 (1528 - 60 - 306 - 50 - 90). */
.prof-disc-fig__card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: max(1.25rem, 2.8935vw);
  border-radius: max(18px, 2.3148vw);
  padding: max(1.5rem, 4.0509vw) max(1.25rem, 5.2083vw)
           max(1.5rem, 4.0509vw) max(1.25rem, 3.4722vw);
}

/* Figma 275:2065. The angle, both stop colours and both stop positions are
   the frame's own, note the second stop sits at 150.39%, past the box, so
   the visible range is only the first two thirds of the ramp. */
.prof-disc-fig__card--gradient {
  background-image: linear-gradient(109.8640045102175deg,
      var(--pl-navy) 0.33153%,
      var(--pl-cyan) 150.39%);
}

.prof-disc-fig__card--pale {
  background-color: var(--pl-pale);
}

/* Figma "Frame 18": a 306px circle carrying no fill of its own, the card
   colour shows through. Sized against the viewport so it tracks the card's
   own padding, and aspect-locked so it is always round. */
.prof-disc-fig__icon {
  flex: none;
  width: max(96px, 17.7083vw);
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Each artwork has its own proportions and Figma centres each on the circle
   at its own size, so these are per-icon percentages of the 306 circle. The
   height follows each SVG's viewBox, so none of them can be distorted. */
.prof-disc-fig__icon img {
  display: block;
  height: auto;
}

.prof-disc-fig__icon--what img    { width: 61.76%; }  /* 189/306 */
.prof-disc-fig__icon--how img     { width: 62.09%; }  /* 190/306 */
.prof-disc-fig__icon--matters img { width: 67.32%; }  /* 206/306 */
.prof-disc-fig__icon--choose img  { width: 70.26%; }  /* 215/306 */

/* Figma "Frame 14"/"Frame 17": fills the remaining measure, column, 25px gap.
   min-width: 0 is what lets a flex child actually shrink to it. */
.prof-disc-fig__body {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: max(0.75rem, 1.4468vw);
}

/* Figma H3: Museo Sans Rounded 900 at 39/46. */
.prof-disc-fig__title {
  width: 100%;
  margin: 0;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-39);
  line-height: 1.17949;
}

/* Figma's text column holds exactly two children, the heading and ONE text
   node carrying every paragraph, so the column's 25px gap falls once, below
   the heading. Without this wrapper each <p> becomes a flex child and the gap
   repeats between them. */
.prof-disc-fig__copy {
  width: 100%;
}

/* Figma "Accroche": Myriad Pro Regular at 24/29. Figma separates paragraphs
   with a real blank line, a zero-width-space paragraph, not a margin. One
   blank line is exactly one line box, so the margin is 1 line height: as an
   em it stays one line at every type size. */
.prof-disc-fig__body p {
  margin: 0 0 1.20833em 0;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 400;
  font-size: var(--pl-fs-24);
  line-height: 1.20833;
}

.prof-disc-fig__body p:last-child {
  margin-bottom: 0;
}

/* Figma has no blank line between these two in "How PROF® Works", they run
   straight on. Every other pair in the section is separated. */
.prof-disc-fig__body p.is-tight {
  margin-bottom: 0;
}

.prof-disc-fig__card--gradient .prof-disc-fig__title  { color: #ffffff; }
.prof-disc-fig__card--gradient .prof-disc-fig__body p { color: var(--pl-pale); }
.prof-disc-fig__card--pale .prof-disc-fig__title      { color: var(--pl-navy); }
.prof-disc-fig__card--pale .prof-disc-fig__body p     { color: var(--pl-slate); }

/* ==========================================================================
   FEATURES: Figma frame 275:1236
   "Everything You Need in One Platform" (1728x1237)

   Was nine absolutely-placed items on a fixed-height canvas. The design
   staggers the middle row, x=214/614/1314 against 364/764/1164 above and
   below it, but that ±150px offset was dropped by request: all nine items
   sit on one regular three-column grid, so every circle lines up with the
   ones above and below it at any width.
   ========================================================================== */

.prof-feat-fig {
  container-type: inline-size;
}

.prof-feat-fig__canvas {
  --u: calc(100cqw / 1728);

  position: relative;
  /* The transformed gradient below is centred on the band and much larger
     than it, so this only backstops the corners while it paints. */
  background-color: #004B99;
  /* The frame's own padding is asymmetric: 100 above the heading, 76 below
     the last row of circles (1237 - 864 - 297). */
  padding-top: max(2.5rem, 5.787vw);
  padding-bottom: max(2rem, 4.3981vw);
  overflow: hidden;
}

/* The band is a Figma ANGULAR (conic) fill, not the linear one it reads as:
   #004B99 -> #00B3E4 across a single 360deg turn, which is what puts the hard
   seam across the lower left. Its matrix is reproduced rather than baked into
   an image, so the band stays vector at any size.

   The export wraps the gradient in a 1833.58px square centred on the frame
   and maps it with matrix(-0.9465 0.672262 -0.939101 -1.24066 864 618.052).
   864/618 is the frame's own centre, so anchoring at 50%/50% reproduces the
   translate without depending on the band still being 1237 tall. After the
   matrix the parallelogram measures ~2129 x 2853 design px about that centre,
   which covers the band with room to spare at any desktop height. */
.prof-feat-fig__canvas::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: 50%;
  top: 50%;
  width: calc(1833.58 * var(--u));
  height: calc(1833.58 * var(--u));
  background: conic-gradient(from 90deg, #004B99 0deg, #00B3E4 360deg);
  transform-origin: 0 0;
  transform: matrix(-0.9465, 0.672262, -0.939101, -1.24066, 0, 0)
             translate(calc(-916.792 * var(--u)), calc(-916.792 * var(--u)));
}

/* Figma's four white swooshes at 7%, the frame clips them, which is why the
   asset carries the full 1728x1237 box rather than a tight one. cover instead
   of 100% 100% so they are never stretched when the band grows; at the band's
   own proportions that is the design's framing exactly. */
.prof-feat-fig__canvas::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: url("../img/prof/landing/features-swooshes.svg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Figma: 49/58.408 white, centred, text box at y=100 with the first row of
   circles at y=218. The 2px top margin is a font-substitution correction, not
   a design value: Nunito's ascent runs taller than Museo Sans Rounded's, so
   at y=100 the glyphs land 2px high of the export. The same +2 is applied to
   the trusted and FAQ headings. */
.prof-feat-fig__title {
  position: relative;
  z-index: 1;
  margin: max(0px, 0.1157vw) 0 max(1.75rem, 3.3329vw) 0;
  padding-inline: var(--pl-gutter);
  text-align: center;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-49);
  line-height: 1.192;
  color: #ffffff;
}

/* Figma: three 200px columns spanning x=364..1364, a 1000px measure with
   200px gutters, centred. A percentage column gap holds that 200:200 ratio
   at every width, so the grid is the design's proportions rather than its
   coordinates. */
.prof-feat-fig__grid {
  position: relative;
  z-index: 1;
  /* 1000 of 1728 is 57.87%. max() holds the design's 1000px measure up to the
     design width and then lets it grow with the page, so the circles keep
     their share of a larger screen instead of freezing; min() hands the
     gutter back on anything narrower than the measure. */
  width: min(calc(100% - 2 * var(--pl-gutter)), max(1000px, 57.87vw));
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 20%;
  /* Figma stacks the rows on a fixed 323px pitch. A row here is 273 tall
     (200 circle + 25 + a two-line label), so 50 of gap reproduces that pitch
     exactly, and a row whose label runs to three lines in French simply
     grows instead of colliding with the one below it. */
  row-gap: max(1.5rem, 2.8935vw);
  align-items: start;
}

.prof-feat-fig__item {
  text-align: center;
}

/* Figma: 200x200 circle, Bleu Très Claire. Each icon SVG carries a viewBox
   re-origined onto its own circle, so filling the badge places the artwork
   exactly where Figma drew it, no centring or per-icon sizing involved. */
.prof-feat-fig__badge {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background-color: var(--pl-pale);
  overflow: hidden;
}

.prof-feat-fig__badge img {
  display: block;
  width: 100%;
  height: 100%;
}

/* Figma: Myriad Pro Semibold 20/24 in Bleu Très Claire, NOT white, the same
   pale as the circles, starting 25px under a 200px circle, i.e. 12.5% of it. */
.prof-feat-fig__label {
  margin: 12.5% 0 0 0;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: var(--pl-fs-20);
  line-height: 1.2;
  color: var(--pl-pale);
}

/* ==========================================================================
   TRUSTED: Figma frame 275:1474 (1728x1200)

   The player is full-bleed and aspect-locked. Figma draws a 1528x116 player
   bar over its lower edge; that chrome was removed by request, so the box
   holds YouTube's embedded player (PRF-1184), whose own controls stand in
   for it. The box stays position: relative for anything that needs to sit
   over the player later.
   ========================================================================== */

.prof-trust-fig {
  background-color: #ffffff;
  padding-top: max(2.5rem, 5.787vw);
}

/* Figma: 49/58.408 in Bleu, centred, text box at y=100 with the poster at
   y=228. Same +2 Nunito correction as the features heading. */
.prof-trust-fig__title {
  margin: max(0px, 0.1157vw) 0 max(1.75rem, 3.9120vw) 0;
  padding-inline: var(--pl-gutter);
  text-align: center;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-49);
  line-height: 1.192;
  color: var(--pl-navy);
}

/* Figma 275:1475: 1728x972, a 16:9 box, full-bleed. The player itself only
   fills 70% of this box's width (below), so the box stays flex-centered to
   letterbox it on both axes instead of just the one the shrunk width leaves
   bare.

   The same nav-aware cap as the player (below) applies here too, and for
   the box, not just the player inside it: .prof-hero-fig__nav is sticky, so
   on a short/wide viewport a box taller than the space below it would put
   the reader through a stretch of internal scrolling where the nav sits
   over the top slice of the video before the section clears it. Capping the
   box itself means the whole section is never taller than one screenful
   below the bar, so that slice never happens. It costs the design's exact
   16:9 shape at those sizes, but the box is unseen chrome (the white
   section background) -- only the framed player is, and that keeps its own
   ratio via aspect-ratio regardless of the box's. */
.prof-trust-fig__video {
  position: relative;
  width: 100%;
  aspect-ratio: 1728 / 972;
  max-height: calc(100vh - var(--pl-nav-h));
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The player's frame: 70% of the box's width per PRF-1184, framed on all
   four sides, and taller than the video by a black band above and below
   the picture.

   The bands are what keep YouTube's controls readable. YouTube's embedded
   player draws its controls in white with nothing dark behind them, so over
   this video's white scenes the unplayed part of the progress track and the
   YouTube logo beside "More videos" vanished, and the embed has no setting
   for their colour. Its controls are anchored to the player's bottom edge,
   though, and when the player is taller than the video YouTube letterboxes
   the picture with black bars of its own. So the iframe fills this whole
   frame, bands included, and the controls land on the bottom band.
   --pl-vband is sized to the highest of them that has to clear the picture:
   the track sits 73-76px above the player's bottom edge (the logo row
   16-56px), so 82px clears it (measured on the 2026 embed, whose controls
   are fixed pixel sizes, hence px). Below ~1025px wide the band shrinks with
   the viewport (8vw) so the frame still fits the box on small screens;
   there the controls overlap the picture again, as in YouTube's own embed.

   content-box, against the page's border-box default, so aspect-ratio sizes
   the picture alone and the padding adds the bands outside it; the iframe
   is then laid over the whole padding box. width is therefore the content
   width, hence the 4px (the two 2px side borders) taken back off so the
   frame's outer edge still lands at 70%.

   The width also stops where the frame would outgrow the space below the
   nav bar, so a wide, short viewport never grows the inline player past
   what's actually visible. That space subtracts --pl-nav-h (not plain
   100vh) because .prof-hero-fig__nav is position: sticky and stays pinned
   over the top of the page at every scroll position, so on a short
   viewport the top slice of a full 100vh player would render right under
   the bar, permanently covered. --pl-nav-h already carries its own mobile
   override, and using the bar's at-rest height (rather than the smaller
   docked one) is the safe bound: the docked bar only ever uncovers more of
   the player, never less. The cap goes through the width rather than a
   max-height because the frame's shape is what puts the bands where they
   are; a max-height alone would squeeze them away.

   The corners are rounded to the FAQ rows' radius below the video (20px
   at the design's 1728, never under 12px). The iframe takes the same
   curve less the 2px border, so its own square corners never poke past
   the frame's, and overflow: hidden backs that up in browsers that do not
   clip an iframe to its own radius.

   Fullscreen needs one rule only. YouTube's fullscreen button makes the
   iframe the fullscreen element, and the browser's own iframe:fullscreen
   styles (full width and height, no border, all !important) override the
   frame; there the screen's own shape decides the bands. Those styles do
   not touch border-radius, though, so the iframe's is reset below to keep
   the screen's corners square. */
.prof-trust-fig__frame {
  --pl-vband: min(82px, 8vw);
  --pl-frame-r: max(12px, 1.1574vw);
  position: relative;
  box-sizing: content-box;
  width: calc(min(70%, calc((100vh - var(--pl-nav-h) - 2 * var(--pl-vband) - 4px) * 16 / 9 + 4px)) - 4px);
  aspect-ratio: 16 / 9;
  padding-block: var(--pl-vband);
  border: 2px solid var(--pl-navy);
  border-radius: var(--pl-frame-r);
  overflow: hidden;
  background-color: #000000;
}

.prof-trust-fig__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: calc(var(--pl-frame-r) - 2px);
}

.prof-trust-fig__frame iframe:fullscreen {
  border-radius: 0;
}

/* ==========================================================================
   FAQ: Figma frame "Frequently Asked Questions" (1728x1648)

   Thirteen 1240x80 rows at x=244 on a 95px pitch, radius 20, 1px #98C1ED,
   the first one open over #EFFCFF at 46%.

   The rows were already a flow column so the accordion works; what was
   static was the row height. An 80px row is what the design's one-line
   English questions produce, so it is written as a min-height around real
   padding, a two-line French question grows the row instead of spilling
   out of it.
   ========================================================================== */

.prof-faq-fig {
  background-color: #ffffff;
  padding-block: max(2.5rem, 5.787vw);
}

/* Figma: 49/58.408 in Bleu, centred, text box at y=100 with the first row at
   y=218. Same +2 Nunito correction as the features heading. */
.prof-faq-fig__title {
  margin: max(0px, 0.1157vw) 0 max(1.75rem, 3.3333vw) 0;
  text-align: center;
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-49);
  line-height: 1.192;
  color: var(--pl-navy);
}

/* 1240 centred on the 1528 content measure puts the column on the frame's
   x=244, and it goes fluid as soon as the window is narrower than that. */
.prof-faq-fig__list {
  width: min(100%, max(1240px, 71.7593vw));
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: max(8px, 0.868vw);
}

/* The stroke is centred on Figma's 1239x79 rect, so the row reads 1240x80
   with the line inside it, which is what border-box gives for a 1px border.
   1px is a genuinely fixed design property and is not scaled. */
.prof-faq-fig__item {
  border: 1px solid #98C1ED;
  border-radius: max(12px, 1.1574vw);
  background-color: #ffffff;
}

.prof-faq-fig__item[open] {
  background-color: rgba(239, 252, 255, 0.46);
}

/* Figma insets the contents 35 from the frame edge; 34 of padding plus the
   border lands on that. */
.prof-faq-fig__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: max(0.75rem, 1.1574vw);
  min-height: max(46px, 4.5139vw);
  padding: max(0.625rem, 1.1574vw) max(0.875rem, 1.9676vw);
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-25);
  line-height: 1.192;
  color: var(--pl-slate);
  cursor: pointer;
  list-style: none;
}

/* Hide the native disclosure triangle in both engines. */
.prof-faq-fig__q::-webkit-details-marker {
  display: none;
}

.prof-faq-fig__q::marker {
  content: "";
}

/* Its own element so the summary always has exactly two flex items: any
   inline markup in the question (the ® span) would otherwise become a third
   and space-between would scatter the words across the row. */
.prof-faq-fig__text {
  min-width: 0;
  /* Nunito centres slightly higher in the row than Museo Sans Rounded does.
     Nudging the text rather than the row keeps the icon on the design's
     centre line; as an em it holds at every type size. */
  position: relative;
  top: 0.08em;
}

/* Figma draws the mark at the size and rise below rather than as a true
   superscript; both are measured off the export's glyph box. */
.prof-faq-fig__text .prof-landing__mark {
  font-size: 0.6em;
  vertical-align: 0.533em;
  line-height: 0;
}

/* Figma: a 20x20 plus in Paragraphe, 4 wide with round caps, its path box
   right-aligned on the 35px inset, so the caps overhang it by 2, which is
   why the SVG is allowed to overflow. */
.prof-faq-fig__sign {
  flex: 0 0 auto;
  display: block;
  width: max(14px, 1.1574vw);
  height: max(14px, 1.1574vw);
  color: var(--pl-slate);
}

.prof-faq-fig__sign svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  transition: transform 0.15s ease;
}

/* The design's close mark is this same plus turned 45deg: its arms are the
   same 20 long and its ends land on the export's ±7.071 diagonal. */
.prof-faq-fig__item[open] .prof-faq-fig__sign svg {
  transform: rotate(45deg);
}

/* Figma: Myriad Pro 17/20 in Paragraphe, 20 between paragraphs, on the same
   35px inset as the question. */
.prof-faq-fig__a {
  padding: max(0.25rem, 0.3472vw) max(0.875rem, 1.9676vw)
           max(0.875rem, 1.3889vw);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 400;
  font-size: var(--pl-fs-17);
  line-height: 1.17647;
  color: var(--pl-slate);
}

.prof-faq-fig__a p {
  margin: 0 0 max(0.625rem, 1.1574vw) 0;
}

.prof-faq-fig__a p:last-child {
  margin-bottom: 0;
}

/* Only the left inset is set so the markers sit inside the answer's own
   padding rather than hanging into the row's edge. */
.prof-faq-fig__a ol {
  margin: 0;
  padding-left: max(1rem, 1.3889vw);
}

.prof-faq-fig__a li {
  margin-bottom: max(0.625rem, 1.1574vw);
}

.prof-faq-fig__a li:last-child {
  margin-bottom: 0;
}

/* ==========================================================================
   FOOTER: Figma frame (1728x573)

   The one section that was never built from the design; it was approximated
   until the export arrived. Every number below is the frame's own.

   Reading down the 1728 canvas:

     padding      130 top and bottom (logo top y=130, buttons bottom y=443)
     LEFT col     x=100, 530 wide
       heading    25/29.8 white, cap top y=136.25
       Log In     94x54 at (100,175), radius 10, white on Bleu
       heading    25/29.8 white, cap top y=295.25
       paragraph  17/20 in Bleu Tres Claire, two lines on the 530 measure
       Request..  182x54 at (100,389), radius 10
     RIGHT col    right edge x=1628, 390 wide
       logo       220 x 72.3193 at (1408,130)
       note       17/20, right-aligned, two lines
       legal      17px with a DOTTED underline (the export draws it as a run
                  of 0.85px dots on a 1.7px pitch), baseline y=437, pushed to
                  the bottom of the column so it lands level with the last
                  button

   Both pill widths (94 and 182) are the design's measured English widths and
   are NOT set here: the padding that produces them is, so French grows the
   pill instead of clipping it.
   ========================================================================== */

.prof-landing__footer {
  background-color: var(--pl-navy);
  color: #ffffff;
  /* Frame padding 130. */
  padding-block: max(2.5rem, 7.5231vw);
}

.prof-landing__footer-grid {
  display: flex;
  justify-content: space-between;
  gap: max(2rem, 3.7037vw);
  flex-wrap: wrap;
}

/* A flex column rather than a block: it makes the two pills block-level, so
   they size to their own content and cannot pick up the stray descender space
   an inline-flex button gets from the surrounding 1.6 line-height. */
.prof-landing__footer-left {
  flex: 1 1 max(440px, 25.463vw);
  /* The design's column is 530, which holds "Ready to Simplify Professional
     Water Analysis?" on one line in Museo Sans Rounded (527.2). Nunito sets
     the same string at 551.4, so a 530 column wraps it and the whole footer
     grows a line. The column is widened to 560 to keep the design's single
     line; the paragraph keeps its own 530 measure below, so its two-line wrap
     is unaffected. There is 578px of clear space to the right column, so this
     costs nothing. */
  max-width: max(560px, 32.4074vw);
  min-width: min(100%, 260px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* Right-aligned in the design: logo, the Sani Marc note, then the legal
   links. It left-aligns once it stacks, see the breakpoints block.

   flex-basis is deliberately `auto`, NOT the design's 390. 390 is the measure
   of the Sani Marc NOTE, and pinning the whole column to it also pinned the
   legal link row, which is a different kind of content: in English
   "Terms of Use" + "Privacy Policy" fit in 390, but in French
   "Conditions d'utilisation" + "Politique de confidentialité" need ~458 and
   the row broke onto two lines.

   The general rule this is an instance of: a measure taken from prose belongs
   on the prose, never on a container that also holds a row of short labels.
   Sizing the column to its widest child lets the link row stay on one line in
   any language, while the note keeps its own max-width below and the column
   still shrinks (flex-shrink stays 1) when the viewport demands it. */
.prof-landing__footer-right {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}

/* Museo Sans Rounded, so Nunito here, as everywhere else on this page. */
.prof-landing__footer-already,
.prof-landing__footer-cta-title {
  margin: 0;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-25);
  line-height: 1.192;
  color: #ffffff;
}

/* Heading box bottom 159.8 to the pill's y=175. */
.prof-landing__footer-already {
  margin-bottom: max(0.5rem, 0.8796vw);
}

/* Pill bottom 229 to the second heading's box top. */
.prof-landing__footer-cta-title {
  margin-top: max(1.5rem, 3.5012vw);
  margin-bottom: max(0.4rem, 0.735vw);
}

/* Myriad Pro, so Source Sans 3. 17/20 in Bleu Tres Claire. */
.prof-landing__footer-cta-text,
.prof-landing__footer-note,
.prof-landing__footer-legal a {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 400;
  font-size: var(--pl-fs-17);
  line-height: 1.17647;
  color: var(--pl-pale);
}

.prof-landing__footer-cta-text {
  margin: 0 0 max(0.75rem, 0.9838vw) 0;
  max-width: max(530px, 30.6713vw);
}

/* Figma 220 x 72.3193. height:auto so the lockup can never be distorted. */
.prof-landing__footer-logo {
  width: max(150px, 12.7315vw);
  height: auto;
  display: block;
}

/* Logo bottom 202.32 to the note's box top. */
.prof-landing__footer-note {
  margin: max(0.75rem, 1.7361vw) 0 0 0;
  max-width: max(390px, 22.5694vw);
}

/* margin-top:auto drops the row to the foot of the column, which is what puts
   it level with the Request a Demo pill as the design has it.

   The padding is the row's MINIMUM separation from the note above it, and it
   is padding rather than margin precisely so it does not fight that auto
   margin: where the column has slack the auto margin simply absorbs it, the
   box grows by the padding and its top edge rises by the same amount, so the
   links do not move and the design's alignment with the pill is unchanged.
   Where the column has no slack (the request-demo footer, whose left column
   is only a heading and a pill, so the right column sets the height) the auto
   margin resolves to 0 and this is the only thing keeping the links off the
   note. It matches the logo-to-note gap above, so the column keeps one
   rhythm. */
.prof-landing__footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: max(1rem, 2.7662vw);
  margin-top: auto;
  padding-top: max(0.75rem, 1.7361vw);
}

/* The export draws the underline as a run of dots, not a rule. */
/* Each link stays whole: if the row ever does have to wrap on a narrow screen
   it breaks between the links, never inside "Conditions d'utilisation". */
.prof-landing__footer-legal a {
  white-space: nowrap;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}

.prof-landing__footer-legal a:hover,
.prof-landing__footer-legal a:focus {
  color: #ffffff;
}

/* ==========================================================================
   BACK TO TOP

   Self-contained by design: nothing else in this file references it and it
   references nothing but the page's own tokens, so the button (this block, the
   markup after the footer, and the back-to-top clause in prof-landing.js) can
   be taken out or moved on its own.

   Fixed to the viewport, and it can be: .prof-landing carries no transform,
   filter or containment, any of which would turn the nearest ancestor into the
   containing block and pin the button to the page instead of to the window.
   The one filter on this page is on .prof-hero-fig__nav-inner, which is not an
   ancestor of it.

   Hidden until prof-landing.js reveals it about a screen down. visibility, not
   just opacity: a 0-opacity button is still clickable and still in the tab
   order, so a keyboard would stop on a control nobody can see.
   ========================================================================== */

.prof-landing__totop {
  position: fixed;
  z-index: 50;   /* over the bar's 40 */
  /* env() so the button clears an iPhone's home indicator; the fallback is
     what every other browser reads. */
  right: max(1rem, 2.0833vw);
  bottom: calc(max(1rem, 2.0833vw) + env(safe-area-inset-bottom, 0px));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Never under 44x44 (WCAG 2.2 SC 2.5.5), and it grows with the page's own
     scale on a large screen instead of shrinking into the corner. */
  width: clamp(44px, 3.2407vw, 60px);
  height: clamp(44px, 3.2407vw, 60px);
  border-radius: 50%;
  background-color: var(--pl-cyan);
  color: #ffffff;
  text-decoration: none;
  box-shadow: 0 0.5rem 1.5rem -0.5rem rgba(0, 60, 120, 0.55);
  opacity: 0;
  visibility: hidden;
  /* Comes up from below rather than fading in place, so it reads as arriving
     with the scroll. */
  transform: translateY(0.75rem);
  transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.15s ease,
              visibility 0s linear 0.2s;
}

.prof-landing__totop.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
  /* The delay above is what keeps the button clickable through the fade OUT;
     on the way in visibility has to switch immediately or the fade plays on an
     invisible element. */
  transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.15s ease,
              visibility 0s;
}

.prof-landing__totop:hover,
.prof-landing__totop:focus {
  background-color: var(--pl-cyan-hover);
  color: #ffffff;
  text-decoration: none;
}

/* The arrow, at 40% of the circle. width alone: the SVG's own viewBox keeps it
   square. */
.prof-landing__totop svg {
  width: 40%;
  height: auto;
  display: block;
}

/* The arrival animation is motion for its own sake; the button itself is not.
   Reduced motion keeps the button and drops the movement. */
@media (prefers-reduced-motion: reduce) {
  .prof-landing__totop {
    transform: none;
    transition: opacity 0.2s ease, visibility 0s linear 0.2s;
  }

  .prof-landing__totop.is-visible {
    transition: opacity 0.2s ease, visibility 0s;
  }
}

/* ==========================================================================
   BREAKPOINTS

   Everything above is the fluid desktop composition. These three rules are
   the points where reflowing beats scaling: side-by-side stops fitting, the
   nav runs out of room, and a three-up grid stops being legible.
   ========================================================================== */

/* --------------------------------------------- tablet: 900px - 1199px */

/* Above this the page is the Figma composition, sized fluidly. Below 1200 it
   stops being one: the design's vertical rhythm is a fraction of a 1728px
   canvas, and carried down proportionally it leaves a tablet hero that is the
   right shape but mostly empty, the same 0.646 height-to-width ratio as
   desktop, which is the signature of a shrunk layout rather than a laid-out
   one. So the hero's lead-in and the tagline's well are compacted to what the
   content needs, and the copy takes a little width back off the card. */
@media (max-width: 1199px) {
  /* The design's 61px lead-in is a desktop opening, not a tablet one: here the
     bar starts compact and docking (see .is-stuck) barely changes its height,
     which is why the docked state is mostly a desktop effect. The pinning
     itself is in the base rule now; it applies at every width.

     Frosted/shadow are NOT set here any more. They belong to the docked state,
     and at rest there is nothing behind the bar for either to act on: this
     background is the exact colour the hero composites to at 0%, so opaque and
     frosted are indistinguishable until the page moves. */
  .prof-hero-fig__nav {
    padding-top: max(0.45rem, 1.3vw);
    padding-bottom: max(0.45rem, 1.3vw);
  }

  /* Figma's drop shadow is a desktop-frame detail; on a compact bar it just
     silhouettes the logo and the pills. */
  .prof-hero-fig__nav-inner {
    filter: none;
  }

  /* The bar carries one more pill than the design drew, and this is where the
     row starts to feel it: the design's 37px gap scaled down is 25.7px here,
     ~103px of it spent on gaps that the demo pill needs more. Tightened, the
     French row (the wide one: "Se connecter" and "Demander une démo" both run
     long) still clears the container at 900, which is what keeps the bar on
     one row down to the tablet breakpoint. */
  .prof-hero-fig__nav-inner {
    gap: max(0.75rem, 1.25vw);
  }

  .prof-hero-fig__main {
    /* Content-driven, instead of the design's 123.65 scaled down. */
    padding-top: clamp(1.25rem, 3.5vw, 3rem);
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
    column-gap: clamp(1rem, 2.5vw, 2rem);
  }

  /* The well only has to clear the crest (whose deepest point is 344.7 design
     px) and give the tagline air, not reproduce the design's 475. */
  .prof-hero-fig__water {
    margin-top: calc(-300 * var(--u));
    padding-top: max(calc(390 * var(--u)), 5rem);
    padding-bottom: max(calc(116 * var(--u)), 2.25rem);
  }

  /* Cards: the design's 60/90 side padding is generous for a tablet measure
     and eats the text column. */
  .prof-disc-fig__card {
    padding: clamp(1.5rem, 3.25vw, 2.75rem);
    gap: clamp(1.25rem, 2.5vw, 2.25rem);
  }
}

/* ------------------------------------------------------- tablet: < 900px */
@media (max-width: 899px) {
  /* Hero: the copy/card split stops working once the card is under ~350px,
     so they stack. The card centres and takes a readable maximum rather than
     stretching across a tablet. */
  .prof-hero-fig__main {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(1.75rem, 5vw, 3rem);
    padding-top: clamp(1.5rem, 4vw, 2.5rem);
  }

  .prof-hero-fig__card {
    width: 100%;
    max-width: 560px;
    margin: 0 auto;
  }

  .prof-hero-fig__text {
    max-width: 42rem;
  }

  /* Intro: bullets move under the copy. */
  .prof-intro-fig__inner {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(1.5rem, 4vw, 2.5rem);
  }

  /* Cards: icon over copy. The circle keeps its own scale rather than
     stretching to the card width. */
  .prof-disc-fig__card {
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(1rem, 3vw, 1.75rem);
  }

  .prof-disc-fig__icon {
    align-self: center;
    width: clamp(120px, 30vw, 240px);
  }

  /* Features: the design's 200:200 column-to-gutter ratio leaves the circles
     too small to read once the grid is this narrow, so the gutter gives way
     to the columns rather than the other way round. */
  .prof-feat-fig__grid {
    column-gap: 12%;
  }

  /* The band's artwork is built around the frame's 1728x1237 proportions, and
     once it stacks into a tall column neither layer reaches its corners: the
     gradient's transformed square is sized off the band's WIDTH, and the
     swooshes zoom to a fragment under cover. So below this width the gradient
     is drawn as a plain angular fill of the whole band, same two colours,
     same seam pointing down-left, just without the matrix that assumed a
     landscape frame, and the swooshes render at their own proportions across
     the width instead of being blown up. */
  .prof-feat-fig__canvas::before {
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    transform: none;
    background: conic-gradient(from 234.6deg at 50% 50%, #004B99 0deg, #00B3E4 360deg);
  }

  .prof-feat-fig__canvas::after {
    background-size: 100% auto;
    background-position: center;
  }
}

/* The section's one remaining desktop-only rule. Declared as a min-width rule
   so the overhang is never applied and then undone. */
@media (min-width: 900px) {
  /* The one label the design does not run inside the 200px circle measure:
     its longest line is ~356px, so Figma widens the box to 380 on a 200px
     column, a 90px overhang either side, into the grid's own 200px gutter,
     so it cannot reach a neighbour.

     The overhang is capped at the page gutter rather than written as a flat
     190%/-45%: from 1728px up the grid is centred with plenty of band either
     side, but at ~1024 the grid fills the viewport and a proportional
     overhang runs off the edge, where the band's overflow: hidden clips it. */
  .prof-feat-fig__item--push .prof-feat-fig__label {
    --pl-over: 5.2083vw;
    width: calc(100% + 2 * var(--pl-over));
    margin-left: calc(-1 * var(--pl-over));
  }
}

/* ------------------------------------------- nav: two tiers below 900px

   Measured rather than guessed. The bar's content used to be logo + links +
   flags + Log In, ~525px in English and ~575px in French, which left a single
   row comfortable down to 768. The demo pill adds ~131px (~146 in French) and
   a gap, and that is what moves this breakpoint up to the project's 900:

     900px   796 of container, 776 used in French  ->  20px of slack
     768px   679 of container, 768 needed in French -> does not fit

   Those French figures are 18px higher than they were: the flag pair was 48px
   wide and the globe pill that replaced it is ~66 at the 15px the bar's type
   bottoms out at. 900 still holds with room to spare, so the breakpoint is
   unchanged; the phone one below it is not so lucky.

   So the row survives to 900 and not below it, and 900 is a breakpoint this
   page already reflows at. The gap tightening in the 1199px block above is
   part of that measurement; without it 900 is 26px short in French.

   The second tier is a deliberate structure, not a wrap: a hairline rule
   separates it, and space-between lands the first link on the same left edge
   as the logo and the last on the same right edge as the pill, so the two
   tiers read as one grid instead of as an accident.
   ========================================================================== */
@media (max-width: 899px) {
  /* One tier now: logo, language, menu button. The links and both pills live
     in the drawer's panel, which exists only while the box is checked, so the
     bar an anchor lands under is that row alone. Rounded up as before. */
  .prof-landing {
    --pl-nav-h: 4.75rem;
  }

  /* Bounded at both ends: a bare vw here sized the logo at 199px on a 767px
     window and 90px on a 375px one. The cap is the width the fluid rule above
     resolves to at 900, so the logo does not step UP as the window narrows
     across the breakpoint. */
  .prof-hero-fig__logo {
    width: clamp(104px, 15vw, 125px);
  }

  .prof-hero-fig__nav-inner {
    row-gap: 0;
  }

  /* The row's three items, in the order they are DRAWN rather than the order
     they are written. Source order is not free here: the panel is revealed
     with a sibling combinator, and a sibling selector only reaches forward, so
     the toggle has to be written before everything it reveals. `order` is what
     puts the language switcher back on the left of the menu button. */
  .prof-hero-fig__logo-link { order: 0; }
  .prof-nav-toggle__btn { order: 2; }

  .prof-hero-fig__lang {
    order: 1;
    margin-right: 0;
  }

  /* Clipped, not display:none. The checkbox IS the control, so it has to keep
     its place in the tab order and stay operable from the keyboard; the label
     is only what gets drawn. A screen reader announces it as a checkbox named
     "Menu", checked while the panel is open, which is a fair description of
     what it actually does. */
  .prof-nav-toggle {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    border: 0;
  }

  .prof-nav-toggle__btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    color: var(--pl-navy);
    cursor: pointer;
  }

  .prof-nav-toggle__btn:hover {
    background-color: rgba(0, 75, 153, 0.07);
  }

  /* The focus ring belongs on the thing you can see, not on the clipped
     input that is actually holding focus. */
  .prof-nav-toggle:focus-visible ~ .prof-nav-toggle__btn {
    outline: 2px solid var(--pl-navy);
    outline-offset: 2px;
  }

  .prof-nav-toggle__icon {
    width: 24px;
    height: 24px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
  }

  /* Bars when closed, cross when open. Both are in the markup and one is
     always hidden, so the swap costs no request and cannot flicker. */
  .prof-nav-toggle__icon--cross,
  .prof-nav-toggle:checked ~ .prof-nav-toggle__btn .prof-nav-toggle__icon--bars {
    display: none;
  }

  .prof-nav-toggle:checked ~ .prof-nav-toggle__btn .prof-nav-toggle__icon--cross {
    display: block;
  }

  /* The card is positioned against the bar's inner container, so that is what
     has to be the containing block. Set here rather than in the base rule
     because the desktop bar has no use for it. */
  .prof-hero-fig__nav-inner {
    position: relative;
  }

  /* Closed is the DEFAULT, declared in CSS rather than applied by a script.
     A bar that renders its whole menu and is then collapsed by JavaScript
     jumps on every load, and the jump is worst on the slow phones that most
     need the compact bar. */
  .prof-nav-panel {
    display: none;
  }

  /* The two-tier bar drew its divider as a full-width flex item, because a
     100%-wide item takes a line to itself and so both drew the separation and
     forced it. The panel needs neither: everything in it is already full
     width, and it carries its own borders. Left in, this would draw a stray
     hairline under a closed bar. */
  .prof-hero-fig__nav-inner::before {
    content: none;
  }

  /* The design's middot separators only read on one line. */
  .prof-hero-fig__sep {
    display: none;
  }

  /* ------------------------------------------------- the panel, when open

     A card hanging off the bar, not a band across it. In flow the panel had to
     span the full measure -- it was another line of the bar -- so on a tablet
     it ran the width of the window and pushed the bar to three times its
     height. Taken out of flow it can be its own object: a fixed measure, its
     own corners and shadow, anchored under the button that opened it, and the
     bar behind it never changes height at all.

     That last part is worth more than it looks. While the panel was in flow,
     opening the menu grew the sticky bar, which moved every anchor target on
     the page and made --pl-nav-h wrong for as long as the menu was open. */

  .prof-nav-toggle:checked ~ .prof-nav-panel {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 6;
    /* A menu measure, and never wider than the window minus its gutters --
       which is what stops it reaching past the right edge on a 320px phone,
       since it is anchored to that edge. */
    width: min(17rem, calc(100vw - 2 * var(--pl-gutter)));
    padding: 0.5rem;
    border-radius: 14px;
    background-color: #ffffff;
    box-shadow: 0 0 0 1px rgba(0, 75, 153, 0.08),
                0 18px 38px -16px rgba(0, 60, 120, 0.45);
    /* A phone in landscape has ~330px of viewport. Without this the card
       simply runs off the bottom with no way to reach the last item. */
    max-height: calc(100svh - var(--pl-nav-h) - 1.5rem);
    overflow-y: auto;
  }

  /* Inside the card the links are a menu: one full-width row each, so the
     whole line is the tap target rather than just the words, and a hover fill
     that reads as a row rather than as underlined text. */
  .prof-nav-panel .prof-hero-fig__links {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    width: 100%;
    max-width: none;
    margin: 0 0 0.5rem 0;
    padding: 0 0 0.5rem 0;
    gap: 0;
    border-bottom: 1px solid rgba(0, 75, 153, 0.12);
    text-align: left;
  }

  /* The divider above lives on the LINKS rather than on what follows them,
     because what follows differs by page: the landing page has the two-pill
     wrapper, the demo page has a single Log In pill with no wrapper at all.
     One border on the links separates both. */
  .prof-nav-panel .prof-hero-fig__links a {
    padding: 0.6rem 0.65rem;
    border-radius: 8px;
  }

  .prof-nav-panel .prof-hero-fig__links a:hover,
  .prof-nav-panel .prof-hero-fig__links a:focus-visible {
    background-color: var(--pl-pale-tint);
  }

  .prof-nav-panel .prof-hero-fig__nav-cta {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    width: 100%;
    gap: 0.4rem;
  }

  /* Full width again -- but of a 272px card, not of the window, which is the
     difference between a control and a banner. Matches at any depth, so it
     covers the demo page's unwrapped pill as well as the landing page's pair. */
  .prof-nav-panel .prof-hero-fig__btn {
    width: 100%;
    min-height: 40px;
    padding-inline: 0.9rem;
  }

  .prof-hero-fig__btn--login,
  .prof-hero-fig__btn--demo {
    min-height: 40px;
  }
}

/* --------------------------------------------- nav: the phone bar, < 560px

   Tier 1 runs out of room here. Measured, with the pills at their widest:

     logo 104 + language 66 (+10 margin) + demo pill 163 + Log In pill 111
     + three 12px gaps = 490px of content, which needs a 554px window

   163px is "Demander une démo" and 111px is "Se connecter", so that is the
   French bar; English needs 415px, or a 469px window. 554 rounded up to a
   round number is where this block starts, and above it BOTH pills stay pills
   on tier 1 — which is the whole point of the number. It used to start at 600
   and demoted Log In across a range where it plainly still fitted: at 599 the
   tier had 240px of empty space and Log In had been turned into a text link
   anyway, which read as a styling bug rather than as a layout decision.

   The number was 534/540 while the switcher was a pair of 22px flags totalling
   48. The globe pill that replaced them measures ~66 at the 15px this bar's
   type bottoms out at, and 18px more content needs ~20px more window, so the
   breakpoint moves with it. Left at 540 there was a ~14px band, French only,
   where tier 1 no longer fitted and had not yet been told to reflow: the bar's
   own flex-wrap caught it, but it wrapped the two pills INDIVIDUALLY, which is
   the one arrangement everything below is written to prevent. Rounding up
   rather than down is deliberate for the same reason — reflowing a few pixels
   early costs nothing, reflowing late is the broken frame.

   Below 554 the pills stop fitting beside the logo and the switcher, so they
   move TOGETHER: .prof-hero-fig__nav-cta stops being display: contents and
   becomes a content-sized flex group that wraps as one unit. The bar is then

     logo                                 [globe FR]
     [ Request a Demo ]  [ Log In ]     <- right-aligned, content-width
     ---------------------------------------------
     Discover the Platform  Features  FAQ

   with all three tiers sharing one right margin.

   Both pills keep their fill. An earlier pass demoted Log In to a text link
   here to buy tier-one room, and it was reported twice as the button losing
   its styling, which is exactly what it was: nothing about a 502px window
   explains to a reader why the button they just saw has turned into text.
   Moving the pair is the version with no such moment in it.
   ========================================================================== */
@media (max-width: 559px) {
  /* The bar is the same single row here as it is at 899 -- logo, language,
     menu button -- so --pl-nav-h is not overridden: the constant set in that
     block already describes this one. Only the wordmark is trimmed, because a
     pinned bar is height taken off every screen of the page and the logo still
     reads at 96px.

     This block used to carry the whole three-tier phone layout: the two pills
     grouped into one content-sized flex item that wrapped to its own line, the
     divider re-ordered between the tiers, the links ordered last. The drawer
     replaced all of it, and those rules are DELETED rather than left harmless,
     because one of them was not harmless. `.prof-hero-fig__nav-cta { display:
     flex }` sat later in the file than the drawer's closed-state rule and at
     the same specificity, so below 560px it un-hid both pills while the menu
     was shut -- rebuilding, on the narrowest screens, the exact bar this
     change exists to remove. */
  .prof-hero-fig__logo {
    width: clamp(96px, 15vw, 125px);
  }
}

/* ------------------------------------------- footer: 600px - 1199px

   The footer's two columns fit side by side down to 1024. They used to stack
   below 900, but stacking is the worse layout here rather than a narrower one:
   both columns go full width and left-align, and the right column is short (a
   logo, a two-line note, two links), so it leaves most of a full-width band
   empty down its right side. The stack point is therefore pushed down to 600
   and this range keeps the two columns by compressing them instead.

   Two sub-ranges, because a different thing limits each end:

   900-1199: flex-wrap alone dropped the right column onto its own line, where
   it sized to its content (~390) and sat at the LEFT of that line while still
   right-aligning its own text, leaving 406-458px of dead space. nowrap keeps
   them side by side; the right column holds its content width, the left takes
   what is left, a 373/390 split at 900.

   600-899: the right column can no longer have its content width. ~390 of the
   531px container at 600 would leave the left one ~108px, so both columns take
   a stated share instead. The legal row was what set that 390 floor and it
   turns out not to need one: it is already flex-wrap: wrap over nowrap links,
   so in French it breaks between "Conditions d'utilisation" and "Politique de
   confidentialite" instead of forcing the column wider.
   ========================================================================== */
@media (min-width: 900px) and (max-width: 1199px) {
  .prof-landing__footer-grid {
    flex-wrap: nowrap;
  }

  .prof-landing__footer-left {
    flex: 1 1 0;
    min-width: 0;
    max-width: none;
  }

  .prof-landing__footer-right {
    flex: 0 0 auto;
    min-width: 0;
  }
}

@media (min-width: 600px) and (max-width: 899px) {
  .prof-landing__footer-grid {
    flex-wrap: nowrap;
    /* The design's 3.7% gutter is worth more as column width down here. */
    gap: max(1.5rem, 3.7037vw);
  }

  /* A stated share rather than the content-driven split above: the left
     column carries the prose, so it keeps the larger half. */
  .prof-landing__footer-left {
    flex: 1 1 55%;
    min-width: 0;
    max-width: none;
  }

  .prof-landing__footer-right {
    flex: 1 1 45%;
    min-width: 0;
  }
}

/* ------------------------------------------------ short viewports: <= 430px

   The bar is sticky from 1199px down, and below 767 it is two tiers tall. On a
   phone held sideways that combination is measured at 98px of a 320px
   viewport, ~31%, and it never goes away: a third of the screen is permanently
   header. The same thing happens to a desktop user at 250-300% browser zoom,
   where the CSS viewport is only 300-360px tall.

   Height is the honest thing to test here, not width, so the bar simply stops
   being sticky once there is not enough height to spare and scrolls away like
   any other content. Nothing about how it looks changes; only whether it
   stays pinned. Above this height the sticky behaviour is untouched. */
@media (max-height: 430px) {
  .prof-hero-fig__nav {
    position: static;
  }
}

/* -------------------------------------------------------- phone: < 600px */
@media (max-width: 599px) {
  /* Three 100px circles are too small to read; two is the honest layout. */
  .prof-feat-fig__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 12%;
    row-gap: clamp(1.25rem, 5vw, 2rem);
  }

  /* Footer: only here does the band genuinely run out of room for two columns
     (they would be ~230px each side of the gap), so this is where it stacks.
     Right-aligned text on a full-width column reads as a mistake, so the right
     column left-aligns with the left one and the legal row stops being pushed
     to the foot of its column. */
  .prof-landing__footer-grid {
    flex-direction: column;
    gap: clamp(2rem, 6vw, 3rem);
  }

  .prof-landing__footer-left,
  .prof-landing__footer-right {
    flex: 1 1 auto;
    width: 100%;
    max-width: none;
  }

  .prof-landing__footer-right {
    align-items: flex-start;
    text-align: left;
  }

  .prof-landing__footer-legal {
    margin-top: 0;
    padding-top: clamp(1rem, 5vw, 2rem);
  }
}

/* ==========================================================================
   REQUEST A DEMO (PRF-1184)

   A separate page (request_demo.html) that reuses this file's tokens so the
   form reads as the same brand surface. The .prof-landing__hero / __header /
   __nav rules below are that page's chrome, the landing page's own hero no
   longer uses them.

   This block was originally written in a different idiom from the rest of the
   file, clamp(rem, vw, rem) with px caps against the max(rem, vw) tokens used
   everywhere else, and that produced a bug rather than just an inconsistency:
   .prof-demo__wrap carried BOTH .prof-landing__shell's vw-based
   padding-inline AND max-width: 900px, and under the page's
   box-sizing: border-box the cap applies to the border box. So the growing
   gutter was subtracted from a frozen outer width and the form got NARROWER
   as the window got WIDER (900 - 2 x gutter: 752px at 1280, 560px at 2940),
   eventually squeezing the two-column field grid into one column.

   So the measure is now a ratio like every other container here, and the type
   and spacing use the shared tokens, which is what keeps the form growing
   with the nav and footer above the design width instead of freezing.

   Every rule below that sets type also NAMES its face, for the same reason.
   The base declaration on .prof-landing is Karla, a fallback rather than a
   choice: the landing page never lands on it because each of its rules names
   Nunito (the substitute for Museo Sans Rounded, headings) or Source Sans 3
   (for Myriad Pro, prose and UI). This block set sizes and weights without a
   family, so it inherited Karla and the form came out in a face that appears
   nowhere else on the two public pages — the heading, lead, labels, inputs,
   hints and consent line all did. `font-family: inherit` on the submit button
   was the same bug in disguise: .prof-landing__btn already sets Source Sans 3
   on that element, and an `inherit` further down the file at equal specificity
   beat it.

   Weights are held to what the templates actually fetch — Nunito 700/900 and
   Source Sans 3 400/600. Anything else is not refused, it is synthesised, and
   a faked bold sits visibly wider and blurrier than the real face beside it.

   And every piece of COPY here is set in ONE voice, Nunito 700, rather than
   role-mapped to a landing counterpart the way the heading and the pill are.
   The landing page can carry two voices because its sections are far apart and
   each one is internally consistent; this page is a single card where a lead,
   six labels, six inputs, a hint, a consent line and any error all sit within
   a few hundred pixels of each other. Mapping each of those to whichever
   landing style shares its role put Nunito beside Source Sans 3 inside one
   card, and at these sizes the two faces do not read as one design, they read
   as a page that lost its stylesheet halfway down.

   So the size scale carries the hierarchy on this page and the face does not
   change. Two deliberate exceptions, both because they are shared landing
   components rather than this page's copy: .prof-demo__title follows the
   landing headings at 900, and .prof-demo__submit follows .prof-landing__btn,
   which is what keeps it matching the Log In pill directly above it.
   ========================================================================== */

.prof-landing__hero {
  position: relative;
  background-color: var(--pl-hero-end);
  background-image: linear-gradient(180deg, var(--pl-hero-top) 0%, var(--pl-hero-mid) 37%, var(--pl-hero-end) 100%);
  background-size: 100% 64.64vw;
  background-repeat: no-repeat;
  display: flex;
  flex-direction: column;
}

/* ------------------------------------------------- the short-page problem

   The sent state is one small card, and on a phone or a tablet that is far
   less than a screenful: bar + card + footer came to roughly 750px against a
   ~850px viewport, so the footer ended partway up the screen and the rest was
   the document's own white showing under it. Not a margin and not the footer's
   own box -- simply the page running out before the viewport does. It is worst
   in the sent state, but the form state hits it too on a tall phone.

   Fixed the standard way: the wrapper is at least a screen tall, the hero is
   the part allowed to grow, and the footer -- being last -- is carried to the
   bottom. Nothing is positioned, so a page with MORE content than a screen is
   completely unaffected; the hero simply never grows.

   svh rather than vh, with vh as the fallback for older engines. On a phone
   100vh is the viewport with the browser's chrome NOT counted, so it is taller
   than what you can actually see and reintroduces a scrollbar on a page that
   should have none. svh is the small viewport height -- chrome showing -- which
   is exactly the guarantee wanted here. dvh was rejected: it changes as the
   chrome hides and shows, which would resize the hero mid-scroll.

   Scoped to .prof-demo-body, set on this page's <body> only. On .prof-landing
   itself it would also make the landing page's wrapper a flex column, and flex
   items do not collapse their margins with one another. */
.prof-demo-body .prof-landing {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

/* The only child allowed to take the slack. flex-basis stays auto so its own
   content still sizes it; 0 shrink so a tall form is never squeezed. */
.prof-demo-body .prof-demo__hero {
  flex: 1 0 auto;
}

.prof-demo__hero {
  padding-bottom: max(3rem, 5.0926vw);
}

/* The form's own measure, and deliberately NOT .prof-landing__shell's: the
   page's other sections align to the 1528px content measure, a form does not.
   900 of the design's 1728 canvas is 52.083%, and this is the same min/max
   pattern .prof-feat-fig__grid uses. max() holds the design's 900px measure
   up to the design width and then lets it grow on the canvas ratio, so the
   form tracks the nav and footer above 1728 instead of stranding itself;
   min() hands the gutter back on anything narrower than the measure.

   Written as width rather than max-width so the gutter is subtracted by the
   min() branch instead of by padding inside a frozen cap. That is what makes
   this monotonic: the form never shrinks as the window grows. */
.prof-demo__wrap {
  width: min(calc(100% - 2 * var(--pl-gutter)), max(900px, 52.083vw));
  margin-inline: auto;
  padding-top: max(2.5rem, 4.1667vw);
}

/* The landing page's H2 style: every 49px heading there (features, trusted,
   FAQ) is Nunito 900, and this page's heading sits at the same step. */
.prof-demo__title {
  color: var(--pl-navy);
  font-family: 'Nunito', sans-serif;
  font-weight: 900;
  font-size: var(--pl-fs-49);
  line-height: 1.15;
  margin: 0 0 max(1rem, 0.9259vw) 0;
}

/* The voice the rest of this page's copy follows: .prof-intro-fig__text, the
   sentence that sits under a heading and introduces a page.

   Leading comes with the face: 1.19355 is .prof-intro-fig__text's, and the
   page's inherited 1.6 left this paragraph airier than any Nunito paragraph
   on the landing page. The SIZE is deliberately still the form's own -- a
   form's intro line is not a marketing hero's. */
.prof-demo__lead {
  color: var(--pl-slate);
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-17);
  line-height: 1.19355;
  margin: 0 0 max(1.75rem, 2.3148vw) 0;
  /* A reading measure, so it scales on the same ratio rather than holding
     40rem while the type around it grows. */
  max-width: max(40rem, 37.037vw);
}

.prof-demo__card {
  background-color: #ffffff;
  border-radius: max(12px, 1.2731vw);
  box-shadow: 0 20px 50px rgba(0, 60, 120, 0.14);
  padding: max(1.5rem, 2.5463vw);
}

.prof-demo__card--done {
  text-align: center;
}

.prof-demo__card--done .prof-demo__lead {
  margin-left: auto;
  margin-right: auto;
}

/* Two columns, the design's pairing: first/last, email/phone, name/location.

   This was auto-fit over minmax(240px, 1fr), which collapsed on its own but
   only held at two columns because the card was capped at 900px. Now that the
   card grows with the canvas that same rule would keep ADDING columns, five
   of them at 2940px, and break the pairing. So the column count is stated and
   the collapse to one column is a breakpoint (see the phone block), which is
   where the old rule folded anyway: it ran out of room at ~620px. */
.prof-demo__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: max(1rem, 1.3889vw);
}

.prof-demo__field--full {
  grid-column: 1 / -1;
}

/* The page's one voice, a size step down. Navy rather than slate is what
   separates a label from the copy around it here, not a second face. */
.prof-demo__field label {
  display: block;
  color: var(--pl-navy);
  font-family: 'Nunito', sans-serif;
  font-size: var(--pl-fs-13);
  font-weight: 700;
  margin-bottom: max(0.4rem, 0.3704vw);
}

.prof-demo__req {
  color: #c0392b;
}

.prof-demo__input {
  width: 100%;
  border: 1px solid var(--pl-border);
  border-radius: max(6px, 0.463vw);
  padding: max(0.6rem, 0.6481vw) max(0.75rem, 0.8333vw);
  /* Named rather than inherited, and the weight with it: a control takes
     NEITHER from the page on its own, so `inherit` here fetched the
     .prof-landing fallback and what the visitor typed came out in a face
     nothing else on the page uses, at a weight nothing else wears. */
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-17);
  color: #12223A;
  background-color: #ffffff;
  box-sizing: border-box;
}

.prof-demo__input:focus {
  outline: none;
  border-color: var(--pl-navy);
  box-shadow: 0 0 0 3px rgba(0, 75, 153, 0.12);
}

.prof-demo__input::placeholder {
  color: #9bacbd;
}

textarea.prof-demo__input {
  resize: vertical;
  min-height: max(7rem, 6.4815vw);
}

.prof-demo__hint {
  color: #7d8fa0;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-13);
  margin: max(0.35rem, 0.3241vw) 0 0 0;
}

.prof-demo__error {
  color: #c0392b;
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-13);
  margin: max(0.35rem, 0.3241vw) 0 0 0;
}

.prof-demo__alert {
  background-color: #fdecec;
  color: #c0392b;
  border-radius: max(6px, 0.463vw);
  padding: max(0.75rem, 0.6944vw) max(1rem, 0.9259vw);
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-13);
  margin: 0 0 max(1.25rem, 1.1574vw) 0;
}

.prof-demo__consent {
  display: flex;
  align-items: flex-start;
  gap: max(0.6rem, 0.5556vw);
  margin-top: max(1.25rem, 1.6204vw);
  color: var(--pl-slate);
  font-family: 'Nunito', sans-serif;
  font-weight: 700;
  font-size: var(--pl-fs-17);
  cursor: pointer;
}

/* The box tracks the label it sits next to, so the two stay proportional
   instead of the tick shrinking against the text on a large screen.

   The floor is the design's own 17px, NOT a smaller number: 0.9838vw only
   overtakes it above ~1730px, so a lower floor would shrink the box on every
   phone and tablet rather than leaving the design untouched and growing it on
   a large screen, which is the whole point of the pair. */
.prof-demo__consent input {
  margin: 0.2rem 0 0 0;
  width: max(17px, 0.9838vw);
  height: max(17px, 0.9838vw);
  accent-color: var(--pl-navy);
  flex: 0 0 auto;
}

/* Honeypot: off-screen rather than display:none, since some bots skip fields
   that are explicitly hidden. */
.prof-demo__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Everything else about this pill comes from .prof-landing__btn, including its
   face. The family is repeated rather than left off because this rule sits
   later in the file at the same specificity, so whatever it says about the
   family is what the button gets — it said `inherit`, which is how the one
   button on the page ended up in a different face from the Log In pill in the
   bar above it and the Back to home pill in the sent state. */
.prof-demo__submit {
  margin-top: max(1.5rem, 1.8519vw);
  border: none;
  font-family: 'Source Sans 3', sans-serif;
}

/* Disabled until the consent box is ticked (prof-landing.js).

   The hover and focus states are restated rather than left to the base pill:
   .prof-landing__btn--navy:hover darkens the fill, and at equal specificity
   the later rule wins, so without these a disabled button still lit up navy
   under the pointer and read as clickable.

   A pale fill with muted text rather than opacity: opacity fades the whole
   pill including its edge, which at this size reads as a rendering glitch,
   where a flat grey-blue reads as a control that is off. WCAG 1.4.3 exempts
   disabled controls from the contrast minimum, and that is deliberate here --
   the button is SUPPOSED to look inert. The asterisk on the consent line is
   what carries the explanation. */
.prof-demo__submit:disabled,
.prof-demo__submit:disabled:hover,
.prof-demo__submit:disabled:focus {
  background-color: #DCE6F0;
  color: #8095AC;
  cursor: not-allowed;
}

/* ------------------------------------------------------- demo: < 600px

   This page's breakpoint has to live at the end of its own block rather than
   with the other phone rules further up: this whole section is appended after
   every media query in the file, so an override placed up there loses to the
   base rules below it on source order at equal specificity. */
@media (max-width: 599px) {
  /* Paired fields need ~240px a column; below this the card cannot give them
     that, which is where the old auto-fit rule folded on its own. */
  .prof-demo__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}