/* ===========================================================================
   The cRUD Company - universal site shell
   Tokens, header, footer, buttons, containers. Shared by every page.
   Page specific composition lives in its own stylesheet, e.g. home.css
   =========================================================================== */

/* Inter, self-hosted: one variable file per character set covers weights 400 to 700.
   The browser fetches latin-ext only when a page uses one of its characters. */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("/assets/fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("/assets/fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* --- approved palette, Teal Black primary + Mineral Teal secondary ------ */
  --teal-black:          #081817;
  --teal-black-hover:    #0F302E;
  --teal-black-pressed:  #030C0B;
  --identity:            #306F75;
  --identity-strong:     #214F55;
  --identity-on-dark:    #9CC9C5;
  --teal-soft:           #D6E7E5;
  --teal-canvas:         #F3F8F7;
  --teal-chrome:         #F7FAF9;
  --teal-rail:           #F2F7F6;
  --white:               #FFFFFF;

  --text-primary:        #081817;
  --text-secondary:      #52606D;
  --text-muted:          #5C6672;
  --text-inverse:        #FFFFFF;

  --border:              #D9E2EC;
  --border-strong:       #7C8896;
  --divider:             #E5EAF0;

  /* on-dark text steps, derived from the approved on-dark accent */
  --on-dark-primary:     #FFFFFF;
  --on-dark-secondary:   rgba(255,255,255,0.78);
  --on-dark-muted:       rgba(255,255,255,0.60);
  --on-dark-hairline:    rgba(255,255,255,0.14);

  /* --- radii ------------------------------------------------------------- */
  --radius-control:      10px;
  --radius-card:         14px;
  --radius-panel:        20px;
  --radius-dialog:       28px;
  --radius-button:       10px;   /* the app's control radius; CTAs are not pills */
  --radius-banner:       32px;   /* the hero and dark band corner */
  --radius-pill:         999px;   /* reserved; not used by labels any more */
  /* Labels and chips use the same rounded rectangle as a call to action, so
     the page does not turn every small element into a capsule. */
  --radius-chip:         10px;

  /* --- elevation, Teal Black alpha only, never pure black ---------------- */
  --shadow-xs:       0 1px 2px rgba(8,24,23,0.04);
  --shadow-sm:       0 1px 3px rgba(8,24,23,0.06), 0 1px 2px rgba(8,24,23,0.04);
  --shadow-md:       0 4px 12px rgba(8,24,23,0.06);
  --shadow-lg:       0 12px 32px rgba(8,24,23,0.08);
  --shadow-floating: 0 24px 64px rgba(8,24,23,0.14);
  /* The bar's shadow is painted by its own layer, which sits BELOW the shell
     in the stack, so the shell's white border covers anything that reaches it.
     Each shadow also has a negative spread larger than the gap between its
     offset and its blur, which means it casts nothing above the bar at all.
     Between the two, no shadow can land on the shell or outline the fillets. */
  --shadow-header-light: 0 14px 18px -6px rgba(8,24,23,0.20),
                         0 4px 8px -4px rgba(8,24,23,0.14);
  --shadow-header-dark:  0 14px 20px -6px rgba(156,201,197,0.26),
                         0 4px 9px -5px rgba(255,255,255,0.22);
  /* CTA elevation. Same rule as the shell and the bar: a dark shadow reads on
     a light ground, and is invisible on Teal Black, so dark grounds get a
     light one instead. Every clickable CTA sits raised off its surface. */
  --shadow-cta-light:        0 2px 3px rgba(8,24,23,0.10), 0 6px 14px -4px rgba(8,24,23,0.18);
  --shadow-cta-light-hover:  0 3px 5px rgba(8,24,23,0.12), 0 12px 22px -6px rgba(8,24,23,0.24);
  --shadow-cta-light-active: 0 1px 2px rgba(8,24,23,0.14);
  --shadow-cta-dark:         0 2px 4px rgba(3,12,11,0.45), 0 8px 18px -6px rgba(156,201,197,0.22);
  --shadow-cta-dark-hover:   0 3px 6px rgba(3,12,11,0.5), 0 14px 26px -8px rgba(156,201,197,0.32);
  --shadow-cta-dark-active:  0 1px 2px rgba(3,12,11,0.5);

  /* Status only. These two never carry brand meaning and never appear as a
     surface, a CTA or type colour; they exist so a yes reads as a yes. */
  --status-yes:         #2E7D5B;
  --status-yes-on-dark: #6FCFA1;
  --status-no:          #C2560F;
  --status-no-on-dark:  #F0A15C;

  --shadow-frame-light: inset 0 0 26px rgba(8,24,23,0.13);
  --shadow-frame-dark:  inset 0 0 30px rgba(156,201,197,0.16),
                        inset 0 0 10px rgba(255,255,255,0.10);

  /* --- motion ------------------------------------------------------------ */
  --t-instant:  75ms;
  --t-fast:     120ms;
  --t-standard: 180ms;
  --t-panel:    240ms;
  --ease:       cubic-bezier(0.4, 0, 0.2, 1);

  /* --- layout ------------------------------------------------------------ */
  --container:    1184px;  /* inner content column */
  /* The shell is the viewport you look through. It is never removed at any
     width; only its thickness steps down so it does not eat a small screen. */
  --frame:        16px;    /* page inset; the shell border sits over this band */
  --frame-border: 32px;    /* thickness of the shell border */
  --frame-radius: 64px;    /* outer corner radius of the shell */
  /* Deliberately narrower than the banner beneath it. The inset on each side is
     what makes the bar read as a notch cut into the banner rather than a bar
     sitting on top of it. Ratio matches the reference composition. */
  --header-max:   968px;
  --header-h:     60px;

  /* The height an opening block has to fill: the viewport less the shell band
     top and bottom. dvh follows mobile browser chrome as it moves; the vh line
     above it is the fallback for anything that does not know dvh. */
  --view: calc(100vh - (var(--frame) * 2));
  --view: calc(100dvh - (var(--frame) * 2));

  /* Minimum clearance inside an opening block, not a fixed band. The block
     fills the viewport via min-height and centres its content; the padding is
     only there so a tall block never puts type against the edge. Keeping it
     small is what stops a full viewport turning into an overflowing one. */
  --open-pad-y: clamp(28px, 4vh, 64px);
  --header-top:   calc(var(--frame) / 2);  /* the bar tucks under the shell */
  /* How far the bar sits inside the shell on each side. This is what creates
     the dark gap the concave fillet curves against, so it must never be zero:
     without it the bar merges into the shell border and the join disappears. */
  /* Proportional, not fixed. A fixed inset shrinks to nothing on a phone and
     the curve disappears with it. At 12vw the bar stays about three quarters
     of the shell width at every size, which keeps the gap wide enough for the
     fillet to reach full radius even on a 320px screen. The 140px ceiling is
     deliberate: it is exactly the inset at which the bar reaches its 968px
     maximum, so a wider window grows the gap without shrinking the bar. */
  --bar-inset:    clamp(16px, 12vw, 140px);
  --header-fillet-max: 30px;
  /* The fillet needs a dark gap at least as wide as itself to curve against.
     Between the bar hitting its maximum width and the viewport growing wide
     enough to open that gap, a fixed radius would spill over the shell. So the
     radius tracks the available gap and only reaches its full size once there
     is room, which removes the broken band around 1000 to 1060 pixels. */
  /* The fillet can never be wider than the gap it curves against. The gap is
     the bar inset, or larger once the bar hits its maximum width and the
     window keeps growing. Taking the larger of the two, capped, gives one
     treatment that holds at every viewport. */
  --header-fillet: min(
      var(--header-fillet-max),
      max(var(--bar-inset),
          calc((100vw - (var(--frame) * 2) - var(--header-max)) / 2)));
  --section-y:    clamp(40px, calc(35.5vh - 216px), 104px);   /* 104px from 900px tall, easing to 40px by 720px: sections fit the screen */
  --block-gap:    clamp(28px, calc(15.5vh - 84px), 56px);     /* space under a section heading: 56px tall screens, 28px by 720px */
  /* History: --section-y was 160px; the audit measured padding at 46-56% of
     five /about/ sections, i.e. more than half of those blocks was empty. 104px
     held the rhythm without the waste. Both tokens now scale with screen height
     so every section fits its viewport, and stay at their full size on tall
     screens. */

  --font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* =========================== base ======================================== */

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

/* A bare <button> otherwise renders with the user agent's Arial, grey fill and
   outset border. Every component below sets its own surface, so reset first. */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  cursor: pointer;
}

html { -webkit-text-size-adjust: 100%; }

html { background: var(--white); }

body {
  margin: 0;
  padding: var(--frame);        /* content sits inside the shell */
  background: var(--white);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* ===================== the page shell ===================================== */
/* A fixed frame that sits 16px outside the viewport on every side and paints a
   32px white border with a 64px outer radius. The page scrolls behind it, so
   content appears to pass through a rounded window. It never takes pointer
   events, so nothing underneath becomes unclickable. */

.page-frame {
  position: fixed;
  inset: calc(var(--frame) * -1);
  border: var(--frame-border) solid var(--white);
  border-radius: var(--frame-radius);
  pointer-events: none;
  z-index: 40;
  /* casts inward onto whatever the page shows through the opening, so the
     shell reads as a raised frame rather than a painted margin */
  box-shadow: var(--shadow-frame-light);
  transition: box-shadow var(--t-panel) var(--ease);
}
.page-frame.on-dark { box-shadow: var(--shadow-frame-dark); }

h1, h2, h3, h4 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; font-weight: 700; }
p { margin: 0; }
img { max-width: 100%; display: block; }
a { color: var(--identity-strong); text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* No outline boxes on this site. Focus is a soft brand ring drawn with a
   shadow, so it follows whatever corner radius the element already has and
   never draws a rectangle around a rounded control. */
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--focus-ring, rgba(48, 111, 117, 0.38));
}
.band-dark :focus-visible, .hero :focus-visible, .phero :focus-visible,
.auth :focus-visible, .endcap__cta :focus-visible, .shw :focus-visible {
  --focus-ring: rgba(156, 201, 197, 0.55);
}

.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  background: var(--teal-black); color: var(--white);
  padding: 10px 16px; border-radius: var(--radius-control);
  font-size: 14px; font-weight: 600; text-decoration: none;
  transition: top var(--t-fast) var(--ease);
}
.skip-link:focus { top: 12px; }

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: 24px;
}

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

/* ======================= analytics consent ============================== */

.consent-bar {
  position: fixed;
  z-index: 120;
  left: 50%;
  bottom: calc(var(--frame) + 14px);
  width: min(calc(100vw - (var(--frame) * 2) - 28px), 1040px);
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  padding: 22px;
  color: var(--on-dark-primary);
  background: var(--teal-black);
  border: 1px solid rgba(156, 201, 197, 0.34);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-floating);
}
.consent-bar[hidden] { display: none; }
.consent-bar__copy { max-width: 680px; }
.consent-bar__copy h2 {
  color: var(--white);
  font-size: clamp(18px, 2vw, 22px);
  letter-spacing: -0.02em;
}
.consent-bar__copy p {
  margin-top: 7px;
  color: var(--on-dark-secondary);
  font-size: 12.5px;
  line-height: 1.55;
}
.consent-bar__copy a {
  color: var(--identity-on-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.consent-bar__actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(148px, 1fr));
  gap: 10px;
  flex: none;
}
.consent-bar__actions .btn { min-height: 46px; }
/* Accept and reject carry the same weight: same fill, same size, same hover.
   A choice is not a recommendation, so neither button is made the louder one. */
.consent-bar__actions .btn-secondary,
.consent-bar__actions .btn-primary {
  color: var(--teal-black);
  background: var(--identity-on-dark);
  border-color: var(--identity-on-dark);
}
.consent-bar__actions .btn-secondary:hover,
.consent-bar__actions .btn-primary:hover { background: var(--teal-soft); }
@media (max-width: 780px) {
  .consent-bar { align-items: stretch; flex-direction: column; gap: 18px; }
  .consent-bar__actions { width: 100%; }
}
@media (max-width: 430px) {
  .consent-bar {
    bottom: calc(var(--frame) + 8px);
    width: calc(100vw - (var(--frame) * 2) - 16px);
    padding: 18px;
  }
  .consent-bar__actions { grid-template-columns: 1fr; }
}

/* =========================== buttons ===================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px; white-space: nowrap;
  font-family: inherit; font-size: 15px; font-weight: 600; line-height: 1;
  padding: 14px 22px; min-height: 46px;
  border: 1px solid transparent; border-radius: var(--radius-button);
  cursor: pointer; text-decoration: none;
  transition: background-color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-standard) var(--ease),
              transform var(--t-standard) var(--ease),
              color var(--t-fast) var(--ease);
}
.btn-sm { font-size: 14px; padding: 11px 18px; min-height: 42px; }
.btn-lg { font-size: 16px; padding: 17px 28px; min-height: 54px; }

/* primary, for light surfaces */
.btn-primary {
  background: var(--teal-black); color: var(--white);
  box-shadow: var(--shadow-cta-light);
}
.btn-primary:hover  { background: var(--teal-black-hover); box-shadow: var(--shadow-cta-light-hover); transform: translateY(-1px); }
.btn-primary:active { background: var(--teal-black-pressed); box-shadow: var(--shadow-cta-light-active); transform: translateY(1px); }

/* secondary, for light surfaces */
.btn-secondary {
  background: var(--white); color: var(--teal-black); border-color: var(--border-strong);
  box-shadow: var(--shadow-cta-light);
}
.btn-secondary:hover  { background: var(--teal-soft); box-shadow: var(--shadow-cta-light-hover); transform: translateY(-1px); }
.btn-secondary:active { background: var(--teal-soft); box-shadow: var(--shadow-cta-light-active); transform: translateY(1px); }

/* primary, for Teal Black surfaces. Teal Black on Teal Black is invisible,
   so the on-dark identity accent carries the action instead. */
.btn-on-dark {
  background: var(--identity-on-dark); color: var(--teal-black);
  box-shadow: var(--shadow-cta-dark);
}
.btn-on-dark:hover  { background: #B4D8D5; box-shadow: var(--shadow-cta-dark-hover); transform: translateY(-1px); }
.btn-on-dark:active { background: #8ABAB6; box-shadow: var(--shadow-cta-dark-active); transform: translateY(1px); }

/* The ghost variant used to be a transparent box with a hairline, which on
   Teal Black was effectively invisible. It now carries a real surface, so it
   reads as a control at rest rather than only on hover. */
.btn-ghost-on-dark {
  background: rgba(255, 255, 255, 0.10); color: var(--white);
  border-color: rgba(255, 255, 255, 0.28);
  box-shadow: var(--shadow-cta-dark);
}
.btn-ghost-on-dark:hover {
  background: rgba(255, 255, 255, 0.18); border-color: var(--identity-on-dark);
  box-shadow: var(--shadow-cta-dark-hover); transform: translateY(-1px);
}
.btn-ghost-on-dark:active { box-shadow: var(--shadow-cta-dark-active); transform: translateY(1px); }

/* =========================== header ====================================== */
/* The bar is fixed, white, and rounded on its BOTTOM corners only, so it
   reads as a notch cut into the top of the banner underneath rather than as a
   floating island. The banner is rounded on its TOP corners to meet it. */

.site-header {
  position: fixed; z-index: 50;
  top: var(--header-top); left: 0; right: 0;
  width: min(calc(100% - (var(--frame) * 2) - (var(--bar-inset) * 2)), var(--header-max));
  margin-inline: auto;
  background: var(--white);
  border-radius: 0 0 var(--radius-banner) var(--radius-banner);
  /* No shadow on this element. A filter here would include the fillet pseudo
     elements and outline them; a box-shadow here would paint over the shell,
     because the bar sits above it in the stack. The shadow is drawn by
     .header-shade instead, which sits underneath the shell. */
  /* No overflow clipping here: the desktop Products dropdown is positioned
     outside the bar. The drop panel needs none, since its rows sit inside the
     padding and never reach the rounded corners. */
}

/* The bar's shadow layer. Same geometry as the bar, no background of its own,
   and a z-index below the shell so the white border paints over any part of
   the shadow that reaches it. Pseudo elements are not involved, so the fillets
   cast nothing. */
.header-shade {
  position: fixed;
  z-index: 39;                         /* under the shell at 40 */
  top: var(--header-top); left: 0; right: 0;
  width: min(calc(100% - (var(--frame) * 2) - (var(--bar-inset) * 2)), var(--header-max));
  height: var(--header-h);
  margin-inline: auto;
  border-radius: 0 0 var(--radius-banner) var(--radius-banner);
  pointer-events: none;
  box-shadow: var(--shadow-header-light);
  transition: box-shadow var(--t-panel) var(--ease);
}
/* Swapped by script when a dark block sits under the bar, so the elevation
   stays readable instead of casting a dark shadow onto a dark surface. */
.header-shade.on-dark { box-shadow: var(--shadow-header-dark); }

/* Where the bar meets the shell border the join must not be a square inner
   corner. These two squares sit just outside the bar's top edges, filled white
   with a quarter disc removed, so the border flows into the bar on a curve.
   The disc is centred on the outer corner, which is what turns a 90 degree
   notch into a concave fillet. */
.site-header::before,
.site-header::after {
  content: "";
  position: absolute;
  top: calc(var(--frame) - var(--header-top));   /* the shell's inner edge */
  width: var(--header-fillet);
  height: var(--header-fillet);
  pointer-events: none;
}
.site-header::before {
  left: calc(var(--header-fillet) * -1);
  background: radial-gradient(circle at 0 100%,
              transparent var(--header-fillet),
              var(--white) calc(var(--header-fillet) + 0.5px));
}
.site-header::after {
  right: calc(var(--header-fillet) * -1);
  background: radial-gradient(circle at 100% 100%,
              transparent var(--header-fillet),
              var(--white) calc(var(--header-fillet) + 0.5px));
}

.site-header__inner {
  height: var(--header-h);
  display: flex; align-items: center; gap: 24px;
  padding: 0 28px 0 24px;
}

/* The approved The cRUD Company lockup, used unmodified. The rectangle export
   is 2048x1024, so the rendered box keeps that 2:1 ratio. Its own transparent
   margin supplies the clear space, which is why the box may equal the bar
   height without the artwork touching the edges. */
.brand { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
.brand img {
  width: 104px; height: 52px; object-fit: contain;
  /* the source is a 2048x1024 master, so ask for the highest
     quality downscale the engine offers at this size */
  image-rendering: -webkit-optimize-contrast;
}

.nav { display: flex; align-items: center; gap: 4px; margin-left: 8px; }

.nav__link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 12px; border-radius: var(--radius-control);
  font-size: 15px; font-weight: 500; color: var(--text-primary);
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
/* No fill behind a nav label. The underline is the whole hover language, and
   the current page is the same line held at full width. */
.nav__link:hover { color: var(--text-primary); }
.nav__link[aria-current="page"] { color: var(--text-primary); }

.nav__chevron { width: 14px; height: 14px; flex: none; transition: transform var(--t-fast) var(--ease); }

/* Focus is shown with the brand line, not a browser box. The box is what the
   user asked to remove; the indication itself has to stay, because it is the
   only thing that tells a keyboard user where they are. */
.nav__link:focus-visible,
.nav__drop-item:focus-visible,
.nav__item:focus-visible { outline: none; }
.nav__link:focus-visible::after { transform: scaleX(1); height: 2.5px; }
.nav__item:focus-visible strong::after { transform: scaleX(1); height: 2.5px; }

/* products dropdown */
/* The group stretches to the full bar height so the panel's top: 100% lands
   exactly on the bar's bottom edge, not on the button's. That is what closes
   the gap and lets the two read as one shape. */
/* The chain has to stretch the whole way: the nav fills the bar, the group
   fills the nav, and only then does the panel's top: 100% land on the bar's
   bottom edge. Plain links stay vertically centred. */
.nav { align-self: stretch; align-items: stretch; }
.nav > .nav__link { align-self: center; }
.nav__group {
  position: relative;
  align-self: stretch; display: flex; align-items: center;
}
/* The panel is not a card floating under the bar, it is the bar carrying on
   downwards: no gap, no line where the two meet, and the outer corners at the
   joint filleted outwards the way the bar meets the banner. */
.nav__panel {
  position: absolute; top: 100%; left: -12px;
  min-width: 316px; padding: 14px 12px 12px;
  background: var(--white);
  border: 0;
  border-radius: 0 0 var(--radius-panel) var(--radius-panel);
  box-shadow: 0 22px 34px -14px rgba(8, 24, 23, 0.26);
}

/* the two concave fillets that carry the white outward into the bar */
.nav__panel::before,
.nav__panel::after {
  content: ""; position: absolute; top: 0;
  width: var(--nav-fillet, 14px); height: var(--nav-fillet, 14px);
  pointer-events: none;
}
.nav__panel::before {
  right: 100%;
  background: radial-gradient(circle at 0 100%,
              transparent var(--nav-fillet, 14px), var(--white) var(--nav-fillet, 14px));
}
.nav__panel::after {
  left: 100%;
  background: radial-gradient(circle at 100% 100%,
              transparent var(--nav-fillet, 14px), var(--white) var(--nav-fillet, 14px));
}
/* CSS owns the visual state so the panel can animate. visibility keeps it out
   of the accessibility tree while closed, which the hidden attribute used to
   do; the attribute is kept in sync by the script for anything that reads it. */
.nav__panel {
  opacity: 0; visibility: hidden; transform: translateY(-10px);
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s linear 260ms;
}
.nav__panel[hidden] { display: block; }
.nav__panel.is-open {
  opacity: 1; visibility: visible; transform: none; pointer-events: auto;
  transition: opacity 260ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s linear 0s;
}
.nav__group > button[aria-expanded="true"] .nav__chevron { transform: rotate(180deg); }

.nav__item {
  display: block; padding: 11px 12px; border-radius: var(--radius-control);
  text-decoration: none; color: var(--text-primary);
  transition: background-color var(--t-fast) var(--ease);
}
/* the line under the title is the only hover signal here too */
.nav__item strong { display: block; font-size: 15px; font-weight: 600; }
.nav__item span   { display: block; font-size: 13px; color: var(--text-muted); margin-top: 2px; }

.header__actions { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }
.header__actions .btn { flex: none; }

.nav__toggle {
  display: none; align-items: center; justify-content: center;
  flex: none;
  width: 44px; height: 44px; margin-left: auto;
  background: none; border: 0;
  border-radius: var(--radius-control); cursor: pointer;
  transition: background-color var(--t-fast) var(--ease);
}
/* Borderless at rest, a soft surface only on hover or while open. This is the
   approved icon control contract and matches the reference treatment. */
.nav__toggle:hover,
.nav__toggle[aria-expanded="true"] { background: var(--teal-canvas); }
/* Three parallel rules, offset against each other rather than stacked.

   The middle rule is the longest and sets the measure. The top rule is flush
   to its RIGHT end, the bottom rule flush to its LEFT, and neither is longer
   than the middle. The result steps right to left down the mark, which is
   what gives it movement where three centred or three equal bars have none.

       ------        top, 12px, flush right
     ------------    middle, 18px
     ------          bottom, 12px, flush left

   The ring the reference draws around this is deliberately not reproduced:
   the control stays borderless at rest, which is the icon contract already
   in use across the site. */
.nav__toggle span {
  display: block; width: 18px; height: 1.6px; background: var(--teal-black);
  border-radius: 2px; position: relative;
}
.nav__toggle span::before, .nav__toggle span::after {
  content: ""; position: absolute; width: 12px; height: 1.6px;
  background: var(--teal-black); border-radius: 2px;
}
.nav__toggle span::before { top: -5.5px; right: 0; }   /* flush to the right end */
.nav__toggle span::after  { top:  5.5px; left: 0;  }   /* flush to the left end  */

/* on hover the two outer rules reach further toward the middle's measure,
   without ever passing it */
.nav__toggle:hover span::before,
.nav__toggle:hover span::after { width: 16px; }

.nav__toggle span,
.nav__toggle span::before,
.nav__toggle span::after {
  transition: transform var(--t-standard) var(--ease),
              width var(--t-standard) var(--ease),
              opacity var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}

/* open: both outer rules take the middle's full measure and cross. Flush
   right and flush left at the same width land on the same line, so the two
   rotate about a shared centre. */
.nav__toggle[aria-expanded="true"] span { background: transparent; }
.nav__toggle[aria-expanded="true"] span::before {
  top: 0; width: 18px; transform: rotate(45deg);
}
.nav__toggle[aria-expanded="true"] span::after {
  top: 0; width: 18px; transform: rotate(-45deg);
}

/* ---- responsive navigation -----------------------------------------------
   Degradation order, so nothing is distorted before it has to be:
     1. wide            logo + links + both calls to action
     2. below 1024px    links fold into the toggle, everything else full size
     3. below 430px     the calls to action move into the panel, leaving the
                        logo and the toggle, both still at full size
     4. below 300px     only now do the logo and toggle scale down to fit
   The shell and the bar are present at every one of these steps. */

@media (max-width: 1023px) {
  .nav { display: none; }
  .nav__toggle { display: inline-flex; margin-left: 4px; }
  .header__actions { margin-left: auto; }
}

/* the drop panel */
.nav__drop {
  border-top: 1px solid var(--divider);
  padding: 8px 10px 14px;
}
.nav__drop[hidden] { display: none; }

.nav__drop-item {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  width: 100%; min-height: 56px; padding: 14px 14px;
  background: none; border: 0; border-radius: var(--radius-control);
  font-family: inherit; font-size: 17px; font-weight: 600; letter-spacing: -0.01em;
  color: var(--text-primary); text-align: left; text-decoration: none; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease);
}
.nav__drop-item:hover { background: var(--teal-canvas); }
.nav__drop-item img { width: 18px; height: 18px; flex: none; opacity: 0.55;
  transition: transform var(--t-standard) var(--ease); }
.nav__drop-item[aria-expanded="true"] img { transform: rotate(180deg); }

.nav__drop-sub { padding: 2px 0 8px 10px; }
.nav__drop-sub[hidden] { display: none; }
.nav__drop-sub a {
  display: block; padding: 11px 14px; border-radius: var(--radius-control);
  text-decoration: none; color: var(--text-primary);
  border-left: 2px solid var(--teal-soft);
}
.nav__drop-sub a:hover { background: var(--teal-canvas); }
.nav__drop-sub strong { display: block; font-size: 15px; font-weight: 600; }
.nav__drop-sub span   { display: block; font-size: 13px; color: var(--text-muted); margin-top: 2px; }

/* the calls to action, shown here only once the bar cannot hold them */
.nav__drop-cta { display: none; gap: 10px; padding: 12px 14px 4px; }
.nav__drop-cta .btn { width: 100%; }

@media (min-width: 1024px) { .nav__drop { display: none; } }

/* step 2: thinner shell, bar and logo untouched */
@media (max-width: 1023px) {
  :root { --frame: 12px; --frame-border: 24px; --frame-radius: 48px; }
}

@media (max-width: 767px) {
  :root { --frame: 8px; --frame-border: 16px; --frame-radius: 36px; --header-h: 58px; }
  .site-header__inner { gap: 10px; padding: 0 14px 0 12px; }
}

/* step 3: the calls to action move into the panel. The logo is still full
   size here, which is the point: nothing is distorted while something can
   simply be relocated instead. */
/* Step 3 is driven by the BAR's width, not the window's. Because the bar is
   now a proportion of the viewport, it runs out of room for both calls to
   action well before the window looks narrow. Measured: the bar holds the
   logo, both actions and the toggle down to about 560px, so they relocate
   below 600px with room to spare. */
@media (max-width: 599px) {
  .header__actions { display: none; }
  .nav__drop-cta { display: grid; }
  /* the actions carried the auto margin, so the toggle takes it over and
     stays anchored to the right edge rather than sliding beside the logo */
  .nav__toggle { margin-left: auto; }
}

@media (max-width: 429px) {
  :root { --frame: 6px; --frame-border: 12px; --frame-radius: 28px; }
  .site-header__inner { padding: 0 10px 0 8px; }
}

/* step 4: the last resort, once only the logo and the toggle remain */
@media (max-width: 300px) {
  .brand img { width: 84px; height: 42px; }
  .nav__toggle { width: 38px; height: 38px; }
}

/* ===================== end of page: CTA into footer ====================== */
/* One rounded block holds both. It opens on Teal Black, fades through a
   gradient, and lands on the light footer, so the transition is made of light
   rather than a hard edge. The wrapper carries the rounding and clips both
   children, which keeps the all-corners rule intact for the dark part. */

.endcap {
  margin: 0;
  border-radius: var(--radius-banner);
  overflow: hidden;
  background: var(--white);
}

.endcap__cta {
  position: relative;
  background: var(--teal-black);
  color: var(--on-dark-primary);
  padding: 152px 0 216px;      /* the deep bottom is where the fade lives */
  text-align: center;
}

/* the gradient join: Teal Black dissolving into the footer's own colour */
.endcap__cta::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 260px;
  /* Ends on pure #FFFFFF, the same white as the shell border, so the dark
     resolves completely rather than settling on a tinted near white. The two
     middle stops keep the falloff even instead of bunching at the bottom. */
  background: linear-gradient(to bottom,
              rgba(8, 24, 23, 0) 0%,
              rgba(8, 24, 23, 0.40) 30%,
              rgba(8, 24, 23, 0.78) 58%,
              rgba(255, 255, 255, 0.88) 88%,
              rgb(255, 255, 255) 100%);
  pointer-events: none;
}

.endcap__cta > .container { position: relative; z-index: 1; }

.endcap__cta h2 {
  font-size: clamp(30px, 4.4vw, 52px);
  line-height: 1.12; letter-spacing: -0.03em;
  max-width: 20ch; margin-inline: auto;
  color: var(--white);
}
/* the supporting words step back so the middle phrase carries the sentence,
   which is emphasis without introducing a second colour */
.endcap__cta h2 .quiet { color: rgba(156, 201, 197, 0.55); }

.endcap__cta p {
  margin: 22px auto 0; max-width: 56ch;
  font-size: clamp(15px, 1.4vw, 17px);
  color: var(--on-dark-secondary); line-height: 1.65;
}

/* Two controls now, so the row needs a gap and has to wrap rather than
   overflow on a narrow screen. */
.endcap__action {
  margin-top: 34px; display: flex; justify-content: center;
  flex-wrap: wrap; gap: 12px;
}

@media (max-width: 1023px) { .endcap__cta { padding: 116px 0 180px; } }
@media (max-width: 767px)  { .endcap__cta { padding: 88px 0 150px; }
                             .endcap__cta::after { height: 200px; } }

/* ------------------- extended footer: the brand mark --------------------- */
/* Set large enough to run the width of the opening and faint enough to read
   as a watermark rather than a headline. The size is tied to the viewport, so
   it keeps spanning the same distance as the window narrows. */

.endcap__mark {
  overflow: hidden;
  line-height: 0;
  margin-top: 12px;
}
.endcap__mark span {
  display: block;
  text-align: center;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: 38.6vw;   /* tuned so 'cRUD' spans the opening exactly */
  font-weight: 700;
  letter-spacing: -0.045em;

  /* The old line-height of 0.74 was 261px shorter than the glyph's natural
     line box at this size, so the ink hung 131px past the box on each side
     and the clip cut the tops of the letters. Padding could never reach that;
     it had to be fixed on the line box itself.

     The line box is now natural, so nothing overflows, and the empty bands
     above the cap line and below the baseline are trimmed with negative
     margins instead. Both are in em, so they hold as the vw font-size
     changes. -0.2em leaves a visible sliver of headroom above the letters. */
  line-height: 1.21;
  margin-top: -0.2em;
  margin-bottom: -0.225em;

  color: rgba(8, 24, 23, 0.055);
  user-select: none;
  -webkit-user-select: none;
}

/* ===========================================================================
   Nav hover line. Chrome, so it applies everywhere the header does.
   Brand teal on a light ground, the on dark tint on a dark one.
   =========================================================================== */

.nav__link,
.nav__drop-item { position: relative; }

.nav__link::after {
  content: "";
  position: absolute; left: 12px; right: 12px; bottom: 5px; height: 1.5px;
  background: var(--identity);
  border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 340ms cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link[aria-expanded="true"]::after { transform: scaleX(1); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

/* the same line inside the products panel */
.nav__item { position: relative; }
.nav__item strong { position: relative; display: inline-block; }
.nav__item strong::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -3px; height: 1.5px;
  background: var(--identity); border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 340ms cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__item:hover strong::after { transform: scaleX(1); }

/* On a dark ground the line uses the lighter tint of the same colour. Applies
   to any link carrying .u-line inside a dark block. */
.u-line { position: relative; text-decoration: none; }
.u-line::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -2px; height: 1.5px;
  background: var(--identity); border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 340ms cubic-bezier(0.22, 1, 0.36, 1);
}
.u-line:hover::after, .u-line:focus-visible::after { transform: scaleX(1); }
.band-dark .u-line::after,
.auth .u-line::after,
.hero .u-line::after,
.phero .u-line::after,
.endcap__cta .u-line::after { background: var(--identity-on-dark); }

/* ===========================================================================
   Products dropdown opens on hover as well as click. Click and keyboard keep
   working exactly as before; hover is an addition, not a replacement.
   =========================================================================== */

.nav__link[aria-expanded="true"] .nav__chevron { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .nav__link::after, .nav__item strong::after, .u-line::after,
  .nav__chevron { transition: none !important; }
}

/* ===================== opening blocks fill one viewport ==================
   Every template's first block carries .is-open-block. min-height, never
   height, so a short laptop viewport grows instead of clipping. Content is
   centred so it sits in the middle of whatever height it ends up with.
   ========================================================================= */

.is-open-block {
  min-height: var(--view);
  display: flex; flex-direction: column; justify-content: center;
  padding-top: var(--open-pad-y);
  padding-bottom: var(--open-pad-y);
}
/* the fixed bar overlaps the top of the block, so clear it */
.is-open-block { padding-top: calc(var(--open-pad-y) + var(--header-h)); }

/* Below about 640px tall there is not enough room to centre anything, so the
   block stops trying to fill and simply flows. */
@media (max-height: 640px) {
  .is-open-block { min-height: 0; justify-content: flex-start; }
}

/* =========================== footer ====================================== */
/* Composition follows levo.ai: one utility line carrying identity, policy
   links, copyright and social marks, then a six column link grid, then the
   large faint wordmark. cRUD holds no security certifications, so the badge
   cluster levo shows at the left of that line is deliberately not reproduced. */

.site-footer {
  background: var(--white); color: var(--text-primary);
  margin: 0;
  border-radius: 0;              /* the endcap wrapper carries the rounding */
  padding: 48px 0 40px;
}
.site-footer .container { padding-inline: 40px; }

/* ------------------------- the utility line ------------------------------ */

.footer__line {
  /* The line reaches both edges of the measure instead of sitting short of
     them. Type, marks and the gaps between them are fluid: they shrink with
     the page down to a floor, and up to a ceiling chosen so a policy label
     never outgrows a footer link. The column follows the page width now, so
     viewport based sizing tracks the space that is actually there.

     Every item on this line is a direct flex child, including each policy
     link: .footer__policies is display:contents while the line is one row, so
     its box disappears and its links join the distribution. Without that the
     nav counted as ONE item, the slack all collected at its right edge, and
     464px of air opened between the copyright and the marks while every other
     gap stayed at 15px. With the links participating, space-between gives one
     gap value to the whole run.

     Below the floor there is nothing legible left to give, so the policies
     take a row of their own rather than being squeezed further, and the nav
     becomes a box again to carry that row. */
  display: flex; align-items: center; flex-wrap: wrap;
  justify-content: space-between;      /* one gap value, shared by everything */
  gap: clamp(8px, 0.8vw, 16px);
  padding-bottom: 14px; margin-bottom: 44px;
}
.footer__lockup { display: block; flex: none; line-height: 0; }
/* the symbol, not the full lockup: the line has to hold four groups and the
   large wordmark below the footer already carries the name */
.footer__lockup img {
  width: clamp(34px, 2.6vw, 50px); height: clamp(34px, 2.6vw, 50px);
  object-fit: contain;
}

.footer__policies { display: contents; }
.footer__policies a {
  font-size: clamp(12px, 0.92vw, 15.5px);
  color: var(--text-secondary); text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
.footer__policies a:hover { color: var(--text-primary); text-decoration: underline; }

.footer__copy {
  font-size: clamp(12px, 0.92vw, 15.5px);
  color: var(--text-muted); white-space: nowrap;
}

/* brand marks, drawn inline so they inherit the link colour and hover state */
.footer__social {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center;
  /* 12% larger marks, and the gap grows by the same 12%, so the space between
     one glyph and the next stays in proportion: it was 29.9px between glyphs
     and lands at 33.5px, rather than closing up as the marks get bigger. */
  gap: clamp(3px, 0.24vw, 4.5px);
}
.footer__social a {
  display: grid; place-items: center;
  width: clamp(38px, 2.9vw, 56px); height: clamp(38px, 2.9vw, 56px);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.footer__social a:hover { color: var(--white); background: var(--teal-black); }
.footer__social a:focus-visible { outline: none; }
.footer__social svg {
  display: block;
  width: clamp(18px, 1.4vw, 27px); height: clamp(18px, 1.4vw, 27px);
}

/* No account URL yet. Rather than look live and jump to the top of the page,
   these sit back and take no clicks or focus until a real address replaces
   the placeholder. */
.footer__social a[data-pending] {
  opacity: 0.38; pointer-events: none; cursor: default;
}

/* Once the row can no longer hold everything even at the floor sizes, the
   policies take a line of their own and stay HORIZONTAL, scrolling sideways
   rather than stacking into a column. 1100px is where the shrink runs out. */
@media (max-width: 1100px) {
  .footer__policies {
    display: flex; align-items: center;      /* a box again, to hold its row */
    gap: 0 clamp(10px, 1.2vw, 18px);
    order: 3; flex: 1 0 100%;
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none; -ms-overflow-style: none;
    padding-bottom: 2px;
  }
  .footer__policies::-webkit-scrollbar { display: none; }
  .footer__policies a { white-space: nowrap; }
}

/* ---------------------------- the link grid ------------------------------ */

.footer__grid {
  display: grid; gap: 40px 24px;
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

.footer__col h3 {
  /* set as written, never uppercased: capitals would turn the cRUD wordmark into CRUD */
  font-size: 13px; font-weight: 600; letter-spacing: 0.01em; text-transform: none;
  color: var(--identity-strong); margin-bottom: 12px;
}
/* A tighter rhythm again. These columns are scanned, not read, so the line
   box is set on the anchor itself rather than inherited from the body's
   reading leading, which is what was holding the rows 30px apart. */
.footer__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.footer__col li { line-height: 0; }
.footer__col a {
  display: inline-block; line-height: 1.5;
  padding: 2px 0;
  white-space: nowrap;                 /* no entry wraps onto a second line */
  color: var(--text-secondary); text-decoration: none; font-size: 14px;
  transition: color var(--t-fast) var(--ease);
}
.footer__col a:hover { color: var(--text-primary); text-decoration: underline; }

@media (max-width: 1180px) {
  .footer__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1023px) {
  .footer__copy { white-space: normal; }
}
@media (max-width: 600px) {
  .site-footer { margin: 0; padding: 40px 0 32px; }
  .site-footer .container { padding-inline: 22px; }
  .footer__line { margin-bottom: 34px; }   /* gaps are the clamps' job */
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 32px 20px; }
}
@media (max-width: 360px) {
  .footer__grid { grid-template-columns: 1fr; }
}

/* ==================== reduced motion ===================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ===========================================================================
   Corrections.
   =========================================================================== */

/* Prose link colour was beating the button's own colour. `.abody a` and its
   siblings are two class selectors, `.btn-primary` is one, so every primary
   button inside an article body rendered its label in identity teal on Teal
   Black: 2.01:1, effectively invisible. Buttons opt out of prose colouring. */
.abody a.btn, .acta a.btn, .legal__body a.btn, .rich a.btn { text-decoration: none; }
.abody a.btn-primary,   .acta a.btn-primary,
.legal__body a.btn-primary, .rich a.btn-primary   { color: var(--white); }
.abody a.btn-secondary, .acta a.btn-secondary,
.legal__body a.btn-secondary, .rich a.btn-secondary { color: var(--teal-black); }
.abody a.btn-on-dark,   .acta a.btn-on-dark        { color: var(--teal-black); }

/* The stepped back words in the closing line were too far back to read
   comfortably. 0.55 alpha over Teal Black lands near 3.5:1; 0.74 lands near
   5.4:1, which holds at any size while still stepping behind the white. */
.endcap__cta h2 .quiet { color: rgba(156, 201, 197, 0.74); }

/* The closing block carried 368px of padding around roughly 280px of content.
   The gradient still needs depth at the bottom, so the top gives way first. */
.endcap__cta { padding: 104px 0 168px; }
.endcap__cta::after { height: 210px; }

/* Every in-page jump clears the floating bar, sitewide, rather than each
   template remembering to set scroll-margin on its own headings. */
html { scroll-padding-top: calc(var(--header-h) + 28px); }

/* ===========================================================================
   The footer runs the width of the page, not the width of the reading column.

   .site-footer already spans the whole frame; it was the .container inside it
   that capped at 1184px and left 408px of empty margin either side on a
   1920px screen, so the first letter of the utility line began 465px in. The
   footer is a directory, not prose: it has no reason to hold a reading
   measure. Both the utility line and the link grid follow this, so the two
   stay aligned with each other.
   =========================================================================== */

.site-footer > .container {
  max-width: none;
  padding-inline: clamp(20px, 2.6vw, 48px);
}

/* ===========================================================================
   Back to top.

   Sits above the shell border so it is not clipped by it, and below the header
   and the search panel so neither is ever covered.

   It inverts against whatever is behind it, using the brand's own on dark
   pair in both directions:

     over light ground   Teal Black plate, on dark identity arrow
     over dark ground    on dark identity plate, Teal Black arrow

   Both readings are 10.04:1. The same pair is what .btn-on-dark already uses,
   and for the same reason: Teal Black on Teal Black is invisible, so over a
   dark block the accent has to carry the control instead.
   =========================================================================== */

.totop {
  position: fixed; z-index: 45;
  right: calc(var(--frame) + 20px);
  bottom: calc(var(--frame) + 20px);
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border: 0;
  border-radius: var(--radius-control);   /* a square with circular edges */
  background: var(--teal-black);
  color: var(--identity-on-dark);
  cursor: pointer;
  box-shadow: var(--shadow-cta-light);

  /* off screen until it is wanted, and inert while it is */
  opacity: 0; visibility: hidden;
  transform: translateY(10px) scale(0.94);
  pointer-events: none;
  transition: opacity 220ms var(--ease), transform 260ms var(--ease),
              background-color 200ms var(--ease), color 200ms var(--ease),
              box-shadow 200ms var(--ease),
              visibility 0s linear 260ms;
}
.totop.is-in {
  opacity: 1; visibility: visible;
  transform: none; pointer-events: auto;
  transition: opacity 220ms var(--ease), transform 260ms var(--ease),
              background-color 200ms var(--ease), color 200ms var(--ease),
              box-shadow 200ms var(--ease),
              visibility 0s;
}
.totop svg { width: 19px; height: 19px; display: block; }

/* a dark block is behind it: the plate and the arrow swap */
.totop.on-dark {
  background: var(--identity-on-dark);
  color: var(--teal-black);
  box-shadow: var(--shadow-cta-dark);
}

.totop:hover           { background: var(--teal-black-hover, #0d2422); color: var(--white); }
.totop.on-dark:hover   { background: var(--teal-soft); color: var(--teal-black); }
.totop:active          { transform: translateY(1px); }
.totop:focus-visible {
  outline: none;
  box-shadow: var(--shadow-cta-light),
              0 0 0 3px var(--focus-ring, rgba(48, 111, 117, 0.38));
}
.totop.on-dark:focus-visible {
  box-shadow: var(--shadow-cta-dark),
              0 0 0 3px rgba(214, 231, 229, 0.55);
}

@media (prefers-reduced-motion: reduce) {
  .totop { transition: opacity 1ms linear, visibility 0s linear 1ms; transform: none; }
  .totop.is-in { transition: opacity 1ms linear, visibility 0s; }
}

@media (max-width: 600px) {
  .totop {
    right: calc(var(--frame) + 14px); bottom: calc(var(--frame) + 14px);
    width: 42px; height: 42px;
  }
}

/* ======================= the breadcrumb rail ==============================
   The navbar is an island with dead space either side of it. The search glyph
   has always occupied the right-hand gap; the breadcrumb now occupies the
   left-hand one, on the same optical line and with the same inset, so the two
   read as a matched pair of chrome rather than as two unrelated controls.

   The element does not move in the document: it stays where it was written,
   inside the opening block, and is lifted out of flow by position only. That
   keeps the reading order intact, keeps the no-script rendering correct, and
   lets the narrow breakpoint put it straight back into the page, because
   there is no left-hand gap to sit in once the bar spans the frame.

   Nothing between this element and the viewport may be transformed, or the
   fixed position would resolve against that ancestor instead. Checked: the
   reveal scripts claim no nav, and no hero container carries a transform.
   ========================================================================= */

nav.crumb-rail {
  /* Room to the left of the bar, measured live by crumb.js. The fallback is
     the same arithmetic in CSS: the bar starts at the larger of its inset or
     the margin its maximum width leaves, and the rail stops 22px short of it.
     The script's figure is the accurate one because 100vw counts a scrollbar
     that the bar's own centring does not. */
  --crumb-room: calc(
      max(calc(var(--frame) + var(--bar-inset)),
          calc((100vw - var(--header-max)) / 2))
      - var(--frame) - 38px);

  position: fixed; z-index: 60;
  /* centred on the bar, not on the frame, exactly as .sfind is */
  top: calc(var(--header-top) + ((var(--header-h) - 38px) / 2));
  left: calc(var(--frame) + 6px);
  height: 38px;
  max-width: var(--crumb-room);
  margin: 0;                              /* the in-flow margin must not apply */
  padding: 0 10px;                        /* ink lands on --frame + 16px */
  display: flex; align-items: center; gap: 7px; flex-wrap: nowrap;
  white-space: nowrap; overflow: hidden;
  border-radius: var(--radius-control);
  font-size: 13px; line-height: 1;
}

/* .crumb and .crumb--light are defined in later stylesheets, so every rule
   here is written one step stronger than they are or they win the cascade,
   which is how the first build ended up wrapping onto two lines against its
   own flex-wrap: nowrap. */

/* The ancestors never shrink, so an overflow is detectable and the script can
   collapse the middle. The current page is the one thing worth keeping, so it
   is the only item allowed to shrink, and it shrinks to an ellipsis. */
nav.crumb-rail a { flex: 0 0 auto; }
nav.crumb-rail b {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 500;
}
nav.crumb-rail [hidden] { display: none; }

/* the collapsed middle: Home / … / current page */
.crumb-rail__gap { opacity: 0.55; }

/* Off the top of the page it is gone. The fade is the fast token, not the
   standard one: this is meant to read as part of the hero leaving rather than
   as a control of its own dismissing itself, so it has to keep up with the
   scroll. Visibility as well as opacity, so what is invisible is also out of
   the tab order rather than a control nobody can see still collecting focus.
   Visibility flips at the end of the fade out and at the start of the fade in. */
nav.crumb-rail {
  transition: opacity var(--t-fast) var(--ease),
              visibility 0s linear 0s;
}
nav.crumb-rail.is-gone {
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--t-fast) var(--ease),
              visibility 0s linear var(--t-fast);
}

/* Tone follows the block behind it, the same reading .sfind and the header
   shadow take. At rest the page's own class is already right; this covers the
   scrolled states, where the rail stays and the page moves underneath. */
.crumb-rail.on-dark { color: var(--on-dark-muted); }
.crumb-rail.on-dark a { color: var(--identity-on-dark); }
.crumb-rail.on-dark span { color: var(--on-dark-muted); opacity: 0.6; }
.crumb-rail.on-dark b { color: var(--white); }

.crumb-rail.on-light { color: var(--text-secondary); }
.crumb-rail.on-light a { color: var(--identity-strong); }
.crumb-rail.on-light span { color: var(--text-muted); opacity: 1; }
.crumb-rail.on-light b { color: var(--teal-black); }

/* An open navigation panel owns the top of the page; the rail would sit on
   top of the overlay. */
html:has(.nav__panel.is-open) .crumb-rail,
html:has(.nav__drop:not([hidden])) .crumb-rail { opacity: 0; pointer-events: none; }

/* ---- back into the page -------------------------------------------------
   Below this width the bar spans the frame and there is no gap to sit in, so
   the rail returns to the spot it never left. The script sets .is-inflow for
   the same reason when its live measurement says the gap is too small to be
   worth using; both routes land on identical rules. */
@media (max-width: 1199px) {
  nav.crumb-rail { position: static; max-width: none; height: auto;
    padding: 0; white-space: normal; flex-wrap: wrap; overflow: visible; }
  nav.crumb-rail b { overflow: visible; text-overflow: clip; }
}
.crumb-rail.is-inflow {
  position: static; max-width: none; height: auto;
  padding: 0; white-space: normal; flex-wrap: wrap; overflow: visible;
}
.crumb-rail.is-inflow b { overflow: visible; text-overflow: clip; }
/* In flow it is the page's own breadcrumb again and .crumb / .crumb--light
   are already right for the block it sits in, so the script removes the tone
   classes rather than this file fighting them back off. */

/* the in-flow margin the two base classes set has to come back with it */
@media (max-width: 1199px) { .crumb.crumb-rail { margin-bottom: 22px; }
                             .crumb--light.crumb-rail { margin-bottom: 26px; } }
.crumb.crumb-rail.is-inflow { margin-bottom: 22px; }
.crumb--light.crumb-rail.is-inflow { margin-bottom: 26px; }

/* ===========================================================================
   The closing boundary, redrawn (H-D09).

   The straight dark-to-white linear fade read as a level grey band. The owner
   asked for an organic, visibly curved and uneven dissolve into the footer,
   after the OSE reference but in cRUD's own palette: a soft Mineral Teal glow
   rising behind an irregular white landing surface.

   ::after is the landing surface: two blurred white curves, drawn in an inline
   SVG so the edge stays soft at every width, extended past all four edges of
   the box so the bottom row of pixels is always solid white and meets the
   footer with no seam. ::before is the glow, set behind the content. The drift
   is slow, never flashes and stops under reduced motion. Neither layer takes
   pointer events, and the container stays above both.
   =========================================================================== */
.endcap__cta::after {
  height: 250px;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 260' preserveAspectRatio='none'><defs><filter id='s' x='-10%' y='-60%' width='120%' height='220%'><feGaussianBlur stdDeviation='14'/></filter><filter id='w' x='-10%' y='-60%' width='120%' height='220%'><feGaussianBlur stdDeviation='30'/></filter></defs><path filter='url(%23w)' fill='%23ffffff' fill-opacity='.45' d='M-120 150 C 160 70 330 175 560 118 C 770 66 900 20 1130 78 C 1300 120 1390 60 1560 70 L1560 420 L-120 420 Z'/><path filter='url(%23s)' fill='%23ffffff' d='M-120 196 C 150 128 340 226 590 172 C 800 128 930 88 1150 138 C 1310 174 1400 118 1560 130 L1560 420 L-120 420 Z'/></svg>") center bottom / 100% 100% no-repeat;
}
.endcap__cta::before {
  content: "";
  position: absolute; left: -12%; right: -12%; bottom: 60px; height: 300px;
  background:
    radial-gradient(42% 58% at 28% 62%, rgba(156, 201, 197, 0.26) 0%, rgba(156, 201, 197, 0) 72%),
    radial-gradient(38% 52% at 70% 52%, rgba(48, 111, 117, 0.52) 0%, rgba(48, 111, 117, 0) 70%),
    radial-gradient(30% 40% at 50% 88%, rgba(156, 201, 197, 0.20) 0%, rgba(156, 201, 197, 0) 75%);
  filter: blur(26px);
  pointer-events: none;
}
/* the glow drifts only where the closing block opts in (.endcap--motion, About) */
@media (prefers-reduced-motion: no-preference) {
  .endcap--motion .endcap__cta::before { animation: endcap-glow 24s ease-in-out infinite alternate; }
}
@keyframes endcap-glow {
  from { transform: translate3d(-2.5%, 0, 0); }
  to   { transform: translate3d(2.5%, -10px, 0); }
}
@media (max-width: 767px) {
  .endcap__cta::after  { height: 180px; }
  .endcap__cta::before { bottom: 40px; height: 220px; }
}

/* ---------------- the status circle beside Contact us -------------------- */
/* A small green circle after the real link, replacing the earlier caret. It
   is decorative and hidden from assistive technology. It pulses on every page,
   and holds still and fully visible under reduced motion. */
.footer__dot {
  display: inline-block; width: 8px; height: 8px; margin-left: 7px;
  vertical-align: 1px; border-radius: 50%;
  background: var(--status-yes);
}
@media (prefers-reduced-motion: no-preference) {
  .footer__dot { animation: footer-dot 1.4s ease-in-out infinite; }
}
@keyframes footer-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ---------------- footer links: the navbar's growing line ---------------- */
/* The same underline the navbar draws, from the left, on hover and on
   keyboard focus, replacing the plain text-decoration. Under reduced motion
   the line still appears, it just does not travel. */
.footer__col a, .footer__policies a { position: relative; text-decoration: none; }
.footer__col a::after, .footer__policies a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px;
  background: var(--identity); border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 340ms cubic-bezier(0.22, 1, 0.36, 1);
}
.footer__col a:hover, .footer__policies a:hover { text-decoration: none; }
.footer__col a:hover::after, .footer__col a:focus-visible::after,
.footer__policies a:hover::after, .footer__policies a:focus-visible::after { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .footer__col a::after, .footer__policies a::after { transition: none; }
}

/* A longer dissolve (trial on About): the dark resolves into white over a much
   taller distance. A soft white ramp runs under the curved landing surface, so
   the change starts higher and eases in rather than arriving in one band. */
.endcap__cta--long { padding-bottom: 300px; }
.endcap__cta--long::after {
  height: 460px;
  background:
    url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 260' preserveAspectRatio='none'><defs><filter id='s' x='-10%' y='-60%' width='120%' height='220%'><feGaussianBlur stdDeviation='14'/></filter><filter id='w' x='-10%' y='-60%' width='120%' height='220%'><feGaussianBlur stdDeviation='30'/></filter></defs><path filter='url(%23w)' fill='%23ffffff' fill-opacity='.45' d='M-120 150 C 160 70 330 175 560 118 C 770 66 900 20 1130 78 C 1300 120 1390 60 1560 70 L1560 420 L-120 420 Z'/><path filter='url(%23s)' fill='%23ffffff' d='M-120 196 C 150 128 340 226 590 172 C 800 128 930 88 1150 138 C 1310 174 1400 118 1560 130 L1560 420 L-120 420 Z'/></svg>") center bottom / 100% 62% no-repeat,
    linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.06) 30%, rgba(255,255,255,0.22) 55%, rgba(255,255,255,0.55) 78%, rgba(255,255,255,0.9) 94%, #FFFFFF 100%);
}
.endcap__cta--long::before { bottom: 150px; height: 360px; }
@media (max-width: 767px) {
  .endcap__cta--long { padding-bottom: 220px; }
  .endcap__cta--long::after { height: 320px; }
  .endcap__cta--long::before { bottom: 100px; height: 260px; }
}

/* the closing band's subtitle on the full content width (Webinar, Collab) */
.endcap__cta--wide-sub p { max-width: min(100%, 72rem); }
.endcap__cta--wide-sub h2 { max-width: 46rem; }

/* menu labels are controls, not text: a right-click or double-click never leaves them selected */
.site-header, .site-header a, .site-header button, .prodbar, .prodbar a { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }

/* Links and controls are for clicking, not selecting: a double click or right click on
   any link, button, menu item or FAQ question never leaves its text highlighted.
   Running text stays selectable so people can still copy words from a page. */
a, button, summary, label, [role="button"], [role="tab"],
.btn, .site-header, .prodbar, .site-footer a, .footer__policies a {
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
input, textarea, select, [contenteditable="true"] { -webkit-user-select: text; user-select: text; }

/* the closing band's title on the full content width (Zoom comparison) */
.endcap__cta--wide-title h2 { max-width: none; }

/* ---------------- decorative motion rests while off screen ---------------- */
/* chrome.js marks a moving block .is-paused when it leaves the viewport, so a
   looping background costs nothing while nobody can see it. */
.is-paused, .is-paused::before, .is-paused::after,
.is-paused *, .is-paused *::before, .is-paused *::after { animation-play-state: paused !important; }

/* ------------- link boxes: Keep reading and comparison lists ------------- */
/* The contact page's write-to-a-desk box (company.css .cmail__btn), used as a
   page link on the cRUD Webinar pages: the title on the left, a dark square
   with an arrow on the right, and the same small lift on hover. */
.golinks { margin-top: 34px; display: grid; gap: 14px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.golink {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 16px 16px 22px; border-radius: var(--radius-panel);
  border: 1px solid var(--border-strong); background: var(--white);
  color: var(--text-primary); text-decoration: none; box-shadow: var(--shadow-xs);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.golink h3, .golink strong { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.35; letter-spacing: -0.01em; color: var(--text-primary); }
.golink__text { display: grid; gap: 5px; min-width: 0; }
.golink__text > span { font-size: 14px; line-height: 1.5; color: var(--text-muted); }
.golink em {
  flex: none; width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--radius-control); background: var(--teal-black); color: var(--white);
  font-style: normal; box-shadow: var(--shadow-cta-light);
}
.golink:hover em { background: var(--teal-black-hover); }
.golink:hover { border-color: var(--teal-black); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.golink:focus-visible { outline: 2px solid var(--identity); outline-offset: 3px; }
@media (max-width: 1023px) { .golinks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px)  { .golinks { grid-template-columns: minmax(0, 1fr); } }
@media (prefers-reduced-motion: reduce) { .golink { transition: none; } .golink:hover { transform: none; } }

/* ------------------- text links: one hover, everywhere ------------------- */
/* Every text link draws the navbar's line: 1.5px in brand teal (the lighter tint on
   a dark ground), growing from the left over 340ms on hover and keyboard focus, the
   same line as the header, product bar and footer links. It is a background, so it
   follows a link that wraps onto a second line. Buttons, cards and the legal contents
   rail keep their own effects. Links inside running text also keep a faint resting
   underline so they are never told apart by colour alone (WCAG 1.4.1); the hover is
   identical. Selection is already off for every link (see the user-select rule).
   Selectors use single-level :not() only, so tools/build_assets.py keeps them. */
.band-dark, .phero, .hero:not(.hero--light), .endcap__cta, .auth, .e404 { --link-line: var(--identity-on-dark); }
body main a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main a.pillar__link,
body main a.why__more {
  text-decoration: none;
  background-image: linear-gradient(var(--link-line, var(--identity)), var(--link-line, var(--identity)));
  background-size: 0% 1.5px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size 340ms cubic-bezier(0.22, 1, 0.36, 1), color 160ms ease;
}
body main a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main a.pillar__link:hover,
body main a.why__more:hover,
body main a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main a.pillar__link:focus-visible,
body main a.why__more:focus-visible { text-decoration: none; background-size: 100% 1.5px; }
body main p a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main li a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main td a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main dd a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main .faq__body a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a),
body main .cconsent a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a) {
  background-image: linear-gradient(var(--link-line, var(--identity)), var(--link-line, var(--identity))), linear-gradient(color-mix(in srgb, currentColor 38%, transparent), color-mix(in srgb, currentColor 38%, transparent));
  background-size: 0% 1.5px, 100% 1px; background-position: 0 100%, 0 100%;
}
body main p a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main li a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main td a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main dd a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main .faq__body a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main .cconsent a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):hover,
body main p a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main li a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main td a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main dd a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main .faq__body a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible,
body main .cconsent a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a):focus-visible { background-size: 100% 1.5px, 100% 1px; }
@media (prefers-reduced-motion: reduce) {
  body main a:not([class]):not(.toc a):not(.related a):not(.arail a):not(.aalso a):not(.achapters a):not(.ashare a):not(.aai a):not(.rproduct a):not(.rjump a):not(.arel a), body main a.pillar__link, body main a.why__more { transition: none; }
}
