/* ============================================================================
   Partners page styles - generated from _X/howto_partners_page/partners.html.
   Every rule is scoped to .partners-page (the <main> wrapper in pages/partners.php)
   so nothing here leaks into the shared header/footer, and main.css cannot
   override the page. The design tokens (:root in the source) live on
   .partners-page for the same reason. Nav/footer/global-reset rules from the
   source were dropped - the shared includes + main.css provide those.
   ============================================================================ */

/* The design relied on its own base resets (a global "*" reset, 16px base text,
   block images). Those are restored here scoped to the page, because main.css
   otherwise leaks element styles in (e.g. blockquote margins and an 18px base
   font) that inflate the testimonial cards and push the mobile layout wider
   than the viewport. */
.partners-page *{ box-sizing:border-box; margin:0; padding:0; }
.partners-page{ color:var(--text); font-family:var(--font); font-size:16px; -webkit-font-smoothing:antialiased; overflow-x:clip; }
.partners-page a{ color:var(--teal-700); text-decoration:none; }
.partners-page img,
.partners-page svg{ display:block; }
/* Belt and braces: grid children may never force the column wider than the
   container, whatever their content. */
.partners-page .split > *{ min-width:0; }
/* ============ Tokens ============ */
.partners-page{
    --navy-0:#04101E;
    --navy-1:#061629;
    --navy-2:#0A2036;
    --navy-3:#0E2A44;
    --teal:#4CE2CC;
    --teal-2:#22ABAD;
    --teal-3:#1F9A9C;
    --cyan:#38CFC0;
    /* Full teal scale, lifted from Industries/chemical-and-process-plants.html so
       the buttons and links here are the same system rather than a lookalike.
       The contrast notes are that file's, kept because they are the reason each
       step exists. */
    --teal-300:#4CE2CC;  /* on-dark accent — 11.31 on navy-1 */
    --teal-400:#22ABAD;  /* FILL ONLY — 2.80 on white, never text on white */
    --teal-500:#1F9A9C;  /* large text only — 3.41 */
    --teal-600:#15767A;  /* smallest passing teal text — 5.38 */
    --teal-700:#0A5E68;  /* emphasis — 7.46 AAA */
    --teal-800:#083F47;  /* .btn-secondary:hover label — 9.6 on white */
    --action:"Manrope", ui-sans-serif, sans-serif;
    --text:#FFFFFF;
    --text-dim:#C2D0E2;
    --text-mute:#9FB1C6;
    --light:#F2F5F8;
    --light-2:#E5EAF0;
    --light-3:#D8DEE6;
    --ink:#000000;
    --ink-dim:#414B59;
    --font:"Inter Tight", ui-sans-serif, system-ui, -apple-system, sans-serif;
    --display:"Saira", ui-sans-serif, system-ui, sans-serif;
    --label:"Saira", ui-sans-serif, system-ui, sans-serif;
    --ease:cubic-bezier(.22,1,.36,1);
    --radius:18px;
    /* Measured from the real nav in JS on load and resize. The fallback matters:
       if the script never runs, the sticky tab bar still clears the nav. */
    --nav-h:72px;
  }
/* [hidden] is a UA-origin display:none, so ANY author display value beats it —
     which silently left hidden drawers, steps and buttons on the page. One
     !important here rather than a matching [hidden] rule beside every component
     that sets display; this is the one place !important earns its keep. */
.partners-page [hidden]{ display:none !important; }
/* The nav is sticky, so an in-page jump would otherwise park the target's
     first lines underneath it. */
.partners-page #ways,
.partners-page #models,
.partners-page #talk,
.partners-page #demo{ scroll-margin-top:calc(var(--nav-h) + 16px); }
/* ============ 01 · HERO ============ */
  /* Three stacked layers, back to front:
       .hero-photo  the supplied puzzle photo, near full strength
       .hero-scrim  a wedge that stays near-opaque under the headline and clears
                    almost entirely on the right, so the photo carries that half
       .hero::after diagonal teal band + bloom
     The dot grid that used to sit between them is gone — it read as noise over
     a photo this prominent.
     The photo is bright cyan and white and the headline is white, so the scrim's
     left end is doing real work. Do not thin the 0-40% stops without re-checking
     the headline against them. */
.partners-page .hero{
    position:relative;
    background:linear-gradient(180deg, #061629 0%, #050E19 100%);
    overflow:hidden;
    min-height:560px;
    isolation:isolate;
  }
.partners-page .hero-photo{
    position:absolute; inset:0; z-index:0; pointer-events:none;
    background:url("../img/partners/hero-photo.jpg") center right / cover no-repeat;
    /* Near full strength: the puzzle is meant to be seen now, not felt. Held
       just under 1 so it still sits in the navy rather than on top of it. */
    filter:saturate(.9) brightness(.82) contrast(1.02);
    opacity:.95;
  }
/* Near-opaque under the text, clearing to almost nothing past 60% so the
     right half is photograph rather than tinted navy. */
.partners-page .hero-scrim{
    position:absolute; inset:0; z-index:1; pointer-events:none;
    background:
      linear-gradient(90deg,
        #050E19 0%,
        rgba(5,14,25,.97) 26%,
        rgba(5,14,25,.86) 40%,
        rgba(6,22,41,.5) 56%,
        rgba(6,22,41,.18) 72%,
        rgba(6,22,41,.06) 100%),
      linear-gradient(180deg, rgba(4,16,30,.42) 0%, transparent 30%, rgba(4,16,30,.5) 100%);
  }
/* Diagonal band sweeping through the right half, plus the teal bloom.
     The band is the Kiflo reference's motion rendered in house teal — pulled
     back from the earlier values so it tints the photo instead of veiling it. */
.partners-page .hero::after{
    content:""; position:absolute; inset:0; z-index:3; pointer-events:none;
    background:
      linear-gradient(118deg,
        transparent 46%,
        rgba(34,171,173,.16) 52%,
        rgba(76,226,204,.22) 62%,
        rgba(34,171,173,.12) 72%,
        transparent 78%),
      radial-gradient(620px 420px at 88% 6%, rgba(34,171,173,.14), transparent 68%);
  }
.partners-page .hero-grid{
    position:relative; z-index:4;
    margin:0 auto;
    padding:72px 56px 80px;
    padding-left:max(56px, calc((100% - 1480px) / 2 + 56px));
    /* Single column. The right-hand plate field is gone, so the photo carries
       that half of the hero on its own. */
    display:grid; grid-template-columns: minmax(0, min(620px, 54%));
    align-items:center;
  }
.partners-page .hero-text{ max-width:620px; }
.partners-page .eyebrow{
    display:inline-flex; align-items:center; gap:14px;
    color:#62F3EB;
    font-weight:800; font-size:15px;
    letter-spacing:.15em; text-transform:uppercase;
    opacity:0; transform:translateY(8px);
    animation:fadeUp .8s cubic-bezier(.22,1,.36,1) .15s forwards;
  }
/* Teal rule before the kicker, per the Customer Network reference. */
.partners-page .eyebrow::before{
    content:""; width:32px; height:2px; border-radius:2px;
    background:var(--teal); flex:none;
  }
.partners-page h1.headline{
    margin-top:24px;
    font-family:var(--display);
    font-weight:800;
    font-size:clamp(36px, 3.7vw, 50px);
    line-height:1.08;
    letter-spacing:-.028em;
    color:#fff;
  }
.partners-page h1.headline .line{ display:block; overflow:hidden; padding-bottom:2px; }
.partners-page h1.headline .line > span{
    display:inline-block; transform:translateY(110%);
    animation:lineIn .9s cubic-bezier(.22,.7,.18,1) forwards;
  }
.partners-page h1.headline .line:nth-child(1) > span{ animation-delay:.25s; }
.partners-page h1.headline .line:nth-child(2) > span{ animation-delay:.42s; }
.partners-page h1.headline .line:nth-child(3) > span{ animation-delay:.56s; }
.partners-page p.sub{
    margin-top:26px;
    max-width:560px;
    font-size:16.5px; line-height:1.6; color:#D4DDE9;
    font-weight:400;
    opacity:0; transform:translateY(8px);
    animation:fadeUp .8s ease .9s forwards;
  }
.partners-page .ctas{
    margin-top:42px;
    display:flex; gap:16px; flex-wrap:wrap;
    opacity:0; transform:translateY(8px);
    animation:fadeUp .8s ease 1.1s forwards;
  }
/* ---------- buttons — the Industries family system ----------
     Lifted from Industries/chemical-and-process-plants.html rather than
     re-derived, so this page's controls are literally the same rules. The
     homepage-derived .cta / .cta-ghost pair they replaced looked similar but
     used a gradient fill, a dark ink label and different geometry. */
.partners-page .btn{
    display:inline-flex; align-items:center; justify-content:center; gap:10px;
    min-height:52px; padding:0 22px; border-radius:12px;
    border:2px solid transparent; white-space:nowrap; cursor:pointer;
    font:700 16px/1.2 var(--action);
    transition:transform .18s var(--ease), box-shadow .18s var(--ease),
               background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
  }
.partners-page .btn--lg{ padding:0 28px; }
/* Matched to the homepage primary: white label on the flat --teal-400 fill,
     hover DARKENS to --teal-500 rather than lightening.
     NOTE: #FFFFFF on #22ABAD measures 2.80:1 and does not meet WCAG 1.4.3
     (4.5:1). Adopted deliberately across the family to match the homepage. */
.partners-page .btn-primary{
    background:var(--teal-400); color:#FFFFFF; border-color:var(--teal-400);
    box-shadow:0 6px 18px rgba(34,171,173,.4);
  }
.partners-page .btn-primary:hover{
    background:var(--teal-500); border-color:var(--teal-500); color:#FFFFFF;
    transform:translateY(-2px); box-shadow:0 10px 28px rgba(34,171,173,.55);
  }
.partners-page .btn-primary:active{ background:var(--teal-600); border-color:var(--teal-600); transform:none; box-shadow:0 4px 12px rgba(34,171,173,.4); }
.partners-page .btn-primary .arr{
    width:26px; height:26px; border-radius:50%; flex:none;
    display:inline-flex; align-items:center; justify-content:center;
    background:rgba(255,255,255,.18); color:#FFFFFF;
    transition:transform .25s var(--ease);
  }
.partners-page .btn-primary:hover .arr{ transform:translateX(3px); }
/* Ghost — resting teal gradient plus inset and outer glow, .06em tracking. */
.partners-page .btn-secondary{
    --btn-ghost-text:#38CFC0;
    position:relative; overflow:hidden;
    color:var(--teal-700); border-color:var(--teal-600);
    letter-spacing:.06em;
    background:linear-gradient(to right, rgba(34,171,173,.1) 1%, transparent 40%, transparent 60%, rgba(34,171,173,.1) 100%);
    box-shadow:inset 0 0 10px rgba(34,171,173,.4), 0 0 9px 3px rgba(34,171,173,.1);
  }
.partners-page .btn-secondary:hover{
    color:var(--teal-800); border-color:var(--teal-600);
    box-shadow:inset 0 0 10px rgba(34,171,173,.6), 0 0 9px 3px rgba(34,171,173,.2);
  }
.partners-page .btn-secondary:active{ background:rgba(34,171,173,.16); transform:none; }
/* Shine sweep — a 4em band parked off the left edge and swept across on hover.
     left:100% resolves against the button's own box, so it clears any width in
     the same .4s where a fixed translateX could not. */
.partners-page .btn-secondary::before{
    content:""; position:absolute; left:-4em; top:0; width:4em; height:100%;
    transition:left .4s ease-in-out;
    background:linear-gradient(to right, transparent 1%, rgba(34,171,173,.14) 40%, rgba(34,171,173,.14) 60%, transparent 100%);
  }
.partners-page .btn-secondary:hover::before{ left:100%; }
.partners-page .btn-secondary.on-dark::before{
    background:linear-gradient(to right, transparent 1%, rgba(76,226,204,.22) 40%, rgba(76,226,204,.22) 60%, transparent 100%);
  }
/* The sweep is absolutely positioned and would paint over the label; wrapping
     the text puts it in the same positioned layer and tree order lands it on top. */
.partners-page .btn-label{ position:relative; }
/* On navy the label must move to the on-dark accent — teal-700 on navy is 1.4:1 */
.partners-page .btn-secondary.on-dark{ color:var(--btn-ghost-text); border-color:var(--teal-400); }
.partners-page .btn-secondary.on-dark:hover{ color:var(--teal-300); border-color:var(--teal-400); }
.partners-page .btn:focus-visible{ outline:2px solid var(--teal-700); outline-offset:3px; }
.partners-page .on-dark-surface .btn:focus-visible,
.partners-page .btn.on-dark:focus-visible{ outline-color:var(--teal-300); }
.partners-page .btn:disabled,
.partners-page .btn[aria-disabled="true"]{
    background:var(--light-3); color:#8A939D; border-color:var(--light-3);
    box-shadow:none; transform:none; cursor:not-allowed;
  }
/* Text link — the family's .link-more, replacing this page's .lets-talk. */
.partners-page .link-more{
    display:inline-flex; align-items:center; gap:8px;
    font-family:var(--font); font-weight:700; font-size:15px;
    color:var(--teal-700); padding:12px 4px; min-height:44px;
    border-bottom:2px solid transparent;
    transition:color .25s var(--ease), border-color .25s var(--ease);
  }
.partners-page .link-more .arr{ display:inline-flex; transition:transform .25s var(--ease); }
.partners-page .link-more .arr svg{ width:14px; height:14px; }
.partners-page .link-more:hover{ color:var(--teal-800); border-bottom-color:var(--teal-600); }
.partners-page .link-more:hover .arr{ transform:translateX(4px); }
.partners-page .link-more:focus-visible{ outline:2px solid var(--teal-700); outline-offset:3px; border-radius:4px; }
/* On navy the same link needs the on-dark accent. */
.partners-page .link-more.on-dark{ color:var(--teal-300); }
.partners-page .link-more.on-dark:hover{ color:#fff; border-bottom-color:var(--teal-400); }
.partners-page .link-more.on-dark:focus-visible{ outline-color:var(--teal-300); }
@keyframes lineIn{ to{ transform:translateY(0); } }
@keyframes fadeUp{ to{ opacity:1; transform:translateY(0); } }
/* ============ shared light-section shell ============ */
.partners-page .lite{ background:var(--light); color:var(--ink-dim); }
/* The shared 1480px content grid — the same one .hero-grid rides. It used to be
     1280px, which set every section ~100px inside the hero on each side. */
  /* --gutter, not a hard value: .lite > .wrap below out-specifies the responsive
     .wrap rules, so the breakpoints retune this variable instead of the padding. */
.partners-page .wrap{ --gutter:56px; max-width:1480px; margin:0 auto; padding:0 var(--gutter); }
/* Every section below the hero matches the hero's rail exactly, asymmetry
     included: left on the 1480 grid, right running out to the nav's 56px margin.
     .says is included now that it is a left/right split — it was excluded while
     its heading and form were centred, where an off-centre rail would have
     visibly shifted them. Nothing in it is centred any more. */
.partners-page .lite > .wrap,
.partners-page .says > .wrap{
    max-width:none;
    padding-left:max(var(--gutter), calc((100% - 1480px) / 2 + var(--gutter)));
  }
/* Vertical only — never the padding shorthand. .sec-pad is always paired with
     .wrap, and the shorthand's implied `0` on the sides wiped out .wrap's
     gutter, flattening those sections against the viewport edges. */
.partners-page .sec-pad{ padding-top:78px; padding-bottom:78px; }
.partners-page .sec-pad--tight{ padding-top:34px; }
.partners-page .h2{
    font-family:var(--font);
    font-weight:600;
    font-size:clamp(28px, 3vw, 38px);
    line-height:1.14; letter-spacing:-.022em;
    color:#0C1B2A;
  }
.partners-page .h3{
    font-family:var(--font);
    font-weight:600;
    font-size:clamp(22px, 2.1vw, 28px);
    line-height:1.2; letter-spacing:-.018em;
    color:#0C1B2A;
  }
.partners-page .lead{
    font-size:15.5px; line-height:1.72; color:#4B5765; font-weight:400;
  }
/* ============ 02 · OPENER ============ */
  /* Split, not centred: heading holds the left column, supporting copy the right,
     with the two sharing a baseline. */
.partners-page .opener{ padding:74px 0 0; }
.partners-page .opener-grid{
    display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:56px; align-items:end;
  }
.partners-page .opener .kicker{
    font:700 11.5px/1 var(--label); letter-spacing:.18em; text-transform:uppercase;
    color:var(--teal-3); margin-bottom:14px;
  }
.partners-page .opener .lead{ padding-bottom:5px; }
/* ============ 03 · PARTNER MODELS (tabbed) ============ */
.partners-page .models{ padding:44px 0 40px; }
.partners-page .tablist{
    display:grid; grid-template-columns:repeat(4, 1fr);
    gap:16px; margin-top:34px;
  }
/* Only used by the mobile sticky bar; inert everywhere else. */
.partners-page .tab-sentinel{ display:none; }
.partners-page .tab{
    display:flex; align-items:center; gap:16px;
    padding:18px 20px;
    background:#fff;
    border:1.5px solid rgba(12,27,42,.09);
    border-radius:14px;
    cursor:pointer;
    font:inherit; text-align:left;
    box-shadow:0 6px 18px -12px rgba(10,32,54,.35);
    transition:border-color .25s, box-shadow .25s, transform .25s var(--ease);
  }
.partners-page .tab:hover{ transform:translateY(-2px); border-color:rgba(31,154,156,.35); }
.partners-page .tab:focus-visible{ outline:2px solid var(--teal-3); outline-offset:3px; }
.partners-page .tab-ico{
    width:44px; height:44px; border-radius:11px; flex:none;
    display:grid; place-items:center;
    background:#E6F2F2;
    transition:background .25s;
  }
.partners-page .tab-ico svg{ width:24px; height:24px; }
/* Two-tone marks: the pale tone is the field, the deep tone the focal shape.
     On the selected tab both invert to sit on the teal tile. */
.partners-page .tab-ico .f-d{ fill:#1F9A9C; }
.partners-page .tab-ico .f-l{ fill:#9AD4D3; }
.partners-page .tab-label{
    font-family:var(--font); font-weight:600;
    font-size:15.5px; line-height:1.25; letter-spacing:-.012em;
    color:#0C1B2A;
  }
.partners-page .tab[aria-selected="true"]{
    border-color:var(--teal-3);
    box-shadow:0 10px 26px -14px rgba(31,154,156,.65);
  }
.partners-page .tab[aria-selected="true"] .tab-ico{ background:var(--teal-3); }
.partners-page .tab[aria-selected="true"] .tab-ico .f-d{ fill:#fff; }
.partners-page .tab[aria-selected="true"] .tab-ico .f-l{ fill:rgba(255,255,255,.55); }
.partners-page .tpanel{ padding-top:46px; }
.partners-page .tpanel .h3{ text-align:left; }
/* No measure cap: the panel copy runs the full container width so its right
     edge lands on the same line as the opener's supporting text. */
.partners-page .tpanel .lead{ margin:14px 0 0; max-width:none; text-align:left; }
.partners-page .pgrid{
    display:grid; grid-template-columns:repeat(4, 1fr);
    gap:20px; margin-top:38px;
  }
/* Three cards span the same track as four, so every panel's grid starts and
     ends on the container edges rather than stopping short. */
.partners-page .pgrid--3{ grid-template-columns:repeat(3, 1fr); }
/* --- flip card: logo front, detail back --- */
  /* A <div>, not a <button>. The Mintmesh card carries a "Get Started" link on
     its back face, and an interactive element cannot live inside a button — it
     would be invalid and unreachable. The card keeps tabindex so it is still
     focusable, and :focus-within flips it when the inner link takes focus. */
.partners-page .pcard{
    height:210px; perspective:1000px;
    cursor:pointer; border-radius:16px;
  }
.partners-page .pcard-inner{
    /* display:block is load-bearing — this element is a <span>, and an inline box
       ignores width/height, which collapsed every card to a blob. */
    display:block;
    position:relative; width:100%; height:100%;
    transform-style:preserve-3d;
    transition:transform .55s var(--ease);
  }
/* Hover, and nothing else. No script, no media query, no state class — each of
     those was a way for the flip to stop working.
     :focus-within is kept only so the one card carrying a "Get Started" link
     reveals it when that link is tabbed to. The card itself is not focusable, so
     a click can never leave one stuck open. */
.partners-page .pcard:hover .pcard-inner,
.partners-page .pcard:focus-within .pcard-inner{ transform:rotateY(180deg); }
/* Each face is also switched off at the rotation's halfway point, so the two
     can never show at once even where a compositor ignores backface-visibility. */
.partners-page .pface,
.partners-page .pback{ transition:opacity 0s linear .275s; }
.partners-page .pback{ opacity:0; }
.partners-page .pcard:hover .pface,
.partners-page .pcard:focus-within .pface{ opacity:0; }
.partners-page .pcard:hover .pback,
.partners-page .pcard:focus-within .pback{ opacity:1; }
/* The two faces carry backface-visibility and NOTHING that creates a stacking
     context. overflow on a backface-hidden element inside a preserve-3d parent
     makes the browser composite it separately, and the hidden face bleeds
     through the visible one — which is what broke the flip when overflow was
     added here for logo cropping and text scrolling. Both moved one level in. */
.partners-page .pface,
.partners-page .pback{
    position:absolute; inset:0;
    backface-visibility:hidden; -webkit-backface-visibility:hidden;
    border-radius:16px;
    text-align:center;
  }
/* Inner layers do the clipping and scrolling, below the 3D. */
.partners-page .pface-in,
.partners-page .pback-in{
    position:absolute; inset:0;
    border-radius:16px;
    display:flex; flex-direction:column;
    align-items:center; justify-content:center;
  }
/* Tight padding: each source PNG already carries a wide transparent margin and
     a baked white plate inset inside its 500px canvas, so card padding stacks on
     whitespace that is already there. */
.partners-page .pface-in{ overflow:hidden; padding:12px; }
.partners-page .pback-in{
    gap:9px; padding:20px;
    overflow-y:auto; overscroll-behavior:contain;
    /* `safe` stops centred content overflowing past the scrollable start edge,
       where it could not be scrolled back to. Ignored where unsupported. */
    justify-content:safe center;
  }
.partners-page .pface{
    background:#fff;
    border:1px solid rgba(12,27,42,.08);
    box-shadow:0 6px 18px -10px rgba(10,32,54,.28);
  }
/* Every supplied logo is a 500x500 PNG with a white rounded plate and drop
     shadow already baked in. The plate is white-on-white against .pface, so only
     its faint shadow reads.
     These are 500x500 files, so they must be constrained down, never up.
     Percentages resolve against .pface-in, which is absolutely positioned to
     the card's own box and so has a definite height to measure against. */
  /* scale(1.12) crops ~6% off each edge, which on these files is the transparent
     margin outside the baked plate — so it costs nothing and buys another 12% of
     mark. Do not push it much further: the plate starts around 8% in, and past
     that the crop begins eating logos that carry text inside the plate. */
.partners-page .pface img{
    max-width:100%; max-height:100%; width:auto; height:auto;
    object-fit:contain;
    transform:scale(1.12);
  }
.partners-page .pback{
    background:var(--navy-3);
    transform:rotateY(180deg);
    box-shadow:0 18px 40px -18px rgba(4,16,30,.85);
  }
/* Only Mintmesh has one today. Sized down from the page default so it does not
     dominate a card that is mostly description. */
.partners-page .pback-cta{
    min-height:38px; padding:0 16px; margin-top:4px;
    font-size:13px; border-radius:9px;
    align-self:center;
  }
.partners-page .pback b{
    display:block;
    font-family:var(--font); font-weight:600; font-size:16px; line-height:1.25;
    letter-spacing:-.01em; color:#fff;
  }
.partners-page .pback .pback-t{
    font-size:14px; line-height:1.55; color:#9FD8D0; font-weight:400;
  }
/* .lets-talk was this page's own text link; it is .link-more now, defined
     with the rest of the family system above. Only the row wrapper is local. */
.partners-page .talk-row{ text-align:left; margin-top:14px; }
/* ============ 04 · WE PROVIDE ============ */
.partners-page .provide{
    display:grid; grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr);
    gap:52px; align-items:center;
  }
.partners-page .provide-shot{
    border-radius:var(--radius); overflow:hidden;
    aspect-ratio:4/3;
    border:1px solid rgba(12,27,42,.08);
    box-shadow:0 24px 60px -32px rgba(10,32,54,.5);
    /* The source is 1500x857 (16:9) in a 4:3 frame, so it must crop rather than
       letterbox. Anchored at 62% so the crop keeps the hand and the piece it is
       placing — a centred crop cuts the hand at the wrist. */
    background:url("../img/partners/provide-shot.jpg") 62% center / cover no-repeat;
  }
.partners-page .provide ul{ list-style:none; margin-top:24px; display:grid; gap:20px; }
.partners-page .provide li{
    display:grid; grid-template-columns:auto 1fr; gap:16px; align-items:start;
    font-size:15.5px; line-height:1.65; color:#4B5765;
  }
.partners-page .provide li svg{ width:26px; height:26px; margin-top:1px; flex:none; }
/* Two-tone via classes, like every other icon on the page â these three
     used hardcoded hex, so they would not have followed a palette change.
     The pale tone is a genuine step lighter; the previous pair (#22ABAD on
     #1F9A9C) was close enough to read as one tone at 26px. */
.partners-page .provide li svg .f-d{ fill:var(--teal-3); }
.partners-page .provide li svg .f-l{ fill:#9AD4D3; }
/* ============ 05 · PROOF & CONTACT ============
     Testimonials and the contact form used to be two full-width bands. They are
     now one split section: a horizontal testimonial rail on the left, the
     partner-model form on the right. */
.partners-page .says{
    background:linear-gradient(180deg, var(--navy-1) 0%, var(--navy-0) 100%);
    position:relative; overflow:hidden;
  }
.partners-page .says::before{
    content:""; position:absolute; inset:0; pointer-events:none;
    background:
      radial-gradient(700px 380px at 22% 0%, rgba(34,171,173,.16), transparent 70%),
      radial-gradient(560px 340px at 82% 100%, rgba(34,171,173,.14), transparent 70%);
  }
.partners-page .says .wrap{ position:relative; z-index:2; }
/* Top-aligned, not stretched: the rail is roughly 400px and the form roughly
     640px, so stretching would leave one column padded with dead space. */
.partners-page .split{
    display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:56px; align-items:start;
  }
.partners-page .says .kicker{
    font:700 11.5px/1 var(--label); letter-spacing:.18em; text-transform:uppercase;
    color:var(--teal);
  }
.partners-page .says h2,
.partners-page .says .panel-h{
    margin-top:13px;
    font-family:var(--font); font-weight:600;
    font-size:clamp(24px, 2.4vw, 30px); line-height:1.16; letter-spacing:-.022em;
    color:#fff;
  }
/* No measure cap: at 440px this sentence broke across two lines. The column is
     wide enough to hold it on one, and it still wraps naturally once the split
     stacks — better than forcing nowrap, which would overflow on a phone. */
.partners-page .says .sub-line{
    margin-top:12px;
    font-size:14.5px; line-height:1.62; color:var(--text-dim);
  }
/* ---- left: testimonial rail ---- */
.partners-page .rail{ margin-top:28px; position:relative; }
.partners-page .track{
    display:flex; gap:18px;
    overflow-x:auto; overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;
    scrollbar-width:none;
    padding-bottom:4px;
  }
.partners-page .track::-webkit-scrollbar{ display:none; }
/* One card fills the rail; the rest are a scroll away. Native scrolling is
     left intact, so trackpad, touch and keyboard all work without extra code —
     the arrows and dots only drive scrollTo. */
.partners-page .quote{
    flex:0 0 100%; scroll-snap-align:start;
    display:grid; grid-template-rows:1fr auto;
    min-height:300px;
    background:linear-gradient(160deg, var(--navy-3) 0%, var(--navy-1) 100%);
    border:1px solid rgba(255,255,255,.09);
    border-radius:22px; padding:30px 32px 28px;
    transition:border-color .25s;
  }
.partners-page .quote:hover{ border-color:rgba(76,226,204,.28); }
.partners-page .quote blockquote{
    font-size:16px; line-height:1.68; color:var(--text-dim); font-weight:400;
    margin-bottom:26px;
  }
.partners-page .quote blockquote b{ font-weight:600; color:#fff; }
.partners-page .who{
    display:flex; flex-direction:column; gap:6px;
    padding-top:22px; border-top:1px solid rgba(255,255,255,.10);
  }
.partners-page .who b{ font-weight:600; font-size:15.5px; color:#fff; line-height:1.3; }
.partners-page .who span{ font-size:13px; line-height:1.5; color:var(--text-mute); }
.partners-page .railbar{ display:flex; align-items:center; gap:14px; margin-top:14px; }
.partners-page .dots{ display:flex; }
/* Scoped to .dots — a bare .dot also matched the hero headline's full-stop
     span, which turned that period into a 44px grid box with a grey pill in it.
     Two components, one class name; keep these anchored to their parent. */
.partners-page .dots .dot{
    width:44px; height:44px; padding:0; border:0; cursor:pointer;
    background:none;
    display:grid; place-items:center;
  }
/* The visible pill is 7px, but the button is a 44px square with the pill
     centred in it — a 7px tap target is decoration, not a control. The dots sit
     flush because the padding is transparent. */
.partners-page .dots .dot::before{
    content:""; display:block;
    width:7px; height:7px; border-radius:50%;
    background:rgba(255,255,255,.22);
    transition:background .2s, width .25s var(--ease);
  }
.partners-page .dots .dot.on::before{ background:var(--teal); width:20px; border-radius:4px; }
.partners-page .dots .dot:focus-visible{ outline:2px solid var(--teal); outline-offset:-6px; border-radius:8px; }
.partners-page .arrows{ margin-left:auto; display:flex; gap:8px; }
.partners-page .arw{
    width:44px; height:44px; border-radius:50%; cursor:pointer;
    background:rgba(255,255,255,.05);
    border:1px solid rgba(255,255,255,.16);
    color:#fff; display:grid; place-items:center;
    transition:background .2s, border-color .2s, opacity .2s;
  }
.partners-page .arw svg{ width:16px; height:16px; }
.partners-page .arw:hover{ background:rgba(34,171,173,.16); border-color:rgba(76,226,204,.45); }
.partners-page .arw:focus-visible{ outline:2px solid var(--teal); outline-offset:3px; }
.partners-page .arw:disabled{ opacity:.3; cursor:default; }
/* ---- right: three-step wizard ----
     Two-zone card, per the supplied reference: a near-black header carrying the
     step count, progress bar and step title, and a lighter navy body carrying
     the interaction, split by a hairline. The header's two soft circles are
     depth, not decoration — they sit behind the content at low contrast. */
.partners-page .panel{
    background:var(--navy-1);
    border:1px solid rgba(76,226,204,.16);
    border-radius:24px;
    overflow:hidden;
    padding:0;
  }
.partners-page .panel-head{
    position:relative; overflow:hidden;
    background:#050B14;
    border-bottom:1px solid rgba(255,255,255,.07);
    padding:30px 32px 28px;
  }
.partners-page .panel-head::before,
.partners-page .panel-head::after{
    content:""; position:absolute; border-radius:50%;
    background:rgba(255,255,255,.022); pointer-events:none;
  }
.partners-page .panel-head::before{ width:46px; height:46px; top:-14px; left:22px; }
.partners-page .panel-head::after{ width:86px; height:86px; top:-30px; right:38px; }
.partners-page .panel-head > *{ position:relative; z-index:1; }
.partners-page .stepno{
    font:700 12px/1 var(--label); letter-spacing:.18em; text-transform:uppercase;
    color:#8A98AC;
  }
.partners-page .progress{
    margin-top:14px; height:4px; border-radius:2px;
    background:var(--light-2);
    overflow:hidden;
  }
.partners-page .progress span{
    display:block; height:100%; border-radius:2px;
    background:var(--teal-3);
    width:33.333%;
    transition:width .45s var(--ease);
  }
.partners-page .panel-h{
    margin-top:22px;
    font-family:var(--font); font-weight:700;
    font-size:clamp(22px, 2.1vw, 27px); line-height:1.2; letter-spacing:-.02em;
    color:#fff;
  }
/* The card sizes to whichever step is showing. min-height is set briefly by
     the script during a step change so the difference animates instead of
     snapping, then released — see animateHeight. */
.partners-page .panel-body{ padding:30px 32px 30px; transition:min-height .35s var(--ease); }
/* Only one step is in the document at a time; the others are removed rather
     than hidden, so their fields stay out of the tab order and the form's
     required-field checks. The global [hidden] rule near the top does the work. */
.partners-page .opts{ display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.partners-page .opt{
    position:relative; text-align:left; cursor:pointer;
    font:inherit; color:#fff;
    background:rgba(255,255,255,.03);
    border:1px solid rgba(255,255,255,.10);
    border-radius:14px; padding:18px;
    transition:border-color .22s, background .22s, transform .22s var(--ease);
  }
.partners-page .opt:hover{ transform:translateY(-2px); border-color:rgba(76,226,204,.45); }
.partners-page .opt:focus-visible{ outline:2px solid var(--teal); outline-offset:3px; }
.partners-page .opt-ico{
    width:44px; height:44px; border-radius:11px;
    background:rgba(56,178,168,.16);
    display:grid; place-items:center; margin-bottom:16px;
  }
.partners-page .opt-ico svg{ width:24px; height:24px; }
.partners-page .opt-ico .f-d{ fill:var(--teal); }
.partners-page .opt-ico .f-l{ fill:rgba(76,226,204,.45); }
.partners-page .opt-title{
    display:block;
    font-family:var(--font); font-weight:700; font-size:16px; line-height:1.25;
    letter-spacing:-.012em;
  }
.partners-page .opt p{ margin-top:6px; font-size:12px; line-height:1.5; color:var(--text-mute); }
.partners-page .opt .tick{
    position:absolute; top:16px; right:16px;
    width:20px; height:20px; border-radius:50%;
    border:1.5px solid rgba(255,255,255,.2);
    display:grid; place-items:center;
    font-size:11px; font-weight:700;
    /* Dark ink on teal, never white: white on this teal is 2.80:1 and fails AA. */
    color:#04221F;
    transition:background .22s, border-color .22s;
  }
.partners-page .opt[aria-checked="true"]{ background:rgba(56,178,168,.12); border-color:var(--teal); }
.partners-page .opt[aria-checked="true"] .tick{ background:var(--teal); border-color:var(--teal); }
.partners-page .opt[aria-checked="true"] .tick::after{ content:"\2713"; }
.partners-page .opt[aria-checked="true"] p{ color:var(--text-dim); }
.partners-page .tform{ display:grid; gap:18px; }
.partners-page .tfield{ display:flex; flex-direction:column; gap:9px; }
.partners-page .tfield label{
    font-family:var(--font); font-weight:700; font-size:15px; color:#fff;
  }
.partners-page .tfield input,
.partners-page .tfield textarea{
    font-family:var(--font); font-size:15px; color:#fff;
    background:rgba(255,255,255,.03);
    border:1px solid rgba(255,255,255,.14);
    border-radius:12px;
    padding:16px 18px;
    min-height:60px;
    transition:border-color .2s, background .2s;
  }
.partners-page .tfield textarea{ min-height:104px; resize:vertical; }
.partners-page .tfield input::placeholder,
.partners-page .tfield textarea::placeholder{ color:#7C8B9E; }
.partners-page .tfield input:focus,
.partners-page .tfield textarea:focus{
    outline:none; border-color:rgba(76,226,204,.55); background:rgba(76,226,204,.05);
  }
/* Only shown once a step has actually been left with something missing, so the
     form never scolds someone for a field they have not reached yet.
     Scoped to .err, not .tfield .err — the model-picker's message sits outside
     any field wrapper. */
.partners-page .err{
    font-size:12.5px; color:#FF9B9B;
  }
/* Reserves the message's line so a card does not grow by one line the moment
     validation fails, shunting the buttons down under the reader's thumb. */
.partners-page .tfield{ position:relative; }
.partners-page .opts + .err{ margin-top:14px; }
.partners-page .tfield input[aria-invalid="true"],
.partners-page .tfield textarea[aria-invalid="true"]{
    border-color:rgba(255,120,120,.6);
  }
/* Next and Submit are .btn.btn-primary now. Back stays a bare text control —
     the family has no equivalent, and giving it a button shell would put it in
     competition with the forward action. Sized and focus-ringed to match
     .link-more so it belongs to the same system. */
.partners-page .wback{
    background:none; border:0; cursor:pointer;
    font-family:var(--font); font-weight:700; font-size:15px; color:#fff;
    padding:12px 4px; min-height:44px;
    transition:color .2s;
  }
.partners-page .wback:hover{ color:var(--teal-300); }
.partners-page .wback:focus-visible{ outline:2px solid var(--teal-300); outline-offset:3px; border-radius:6px; }
.partners-page .tprivacy{
    margin-top:18px;
    font-size:12.5px; color:var(--text-mute);
  }
.partners-page .tprivacy a{ color:var(--teal-300); }
.partners-page .tprivacy a:hover{ text-decoration:underline; }
/* Replaces the form once submitted. Without it, an inert form gives no sign
     anything happened when Submit is pressed. */
.partners-page .wdone{ text-align:center; padding:10px 0 4px; }
.partners-page .wdone-ico{
    width:56px; height:56px; border-radius:50%; margin:0 auto 18px;
    background:rgba(56,178,168,.16); color:var(--teal);
    display:grid; place-items:center;
  }
.partners-page .wdone-ico svg{ width:26px; height:26px; }
.partners-page .wdone b{ display:block; font-weight:700; font-size:19px; color:#fff; }
.partners-page .wdone span{ display:block; margin-top:10px; font-size:14px; line-height:1.6; color:var(--text-dim); }
/* ============ Responsive ============ */
@media (max-width:1280px){
  .partners-page .hero-grid{ padding:48px 32px 60px; gap:24px; }
  .partners-page .wrap{ --gutter:32px; }
}
/* The wizard stacks its fields one per row at every width, so nothing here
     needs to unstack — only the card padding tightens. */
@media (max-width:1100px){
  .partners-page .panel-head{ padding:26px 26px 24px; }
  .partners-page .panel-body{ padding:26px; }
}
@media (max-width:1040px){
  .partners-page .pgrid{ grid-template-columns:repeat(3, 1fr); }
}
@media (max-width:980px){
  .partners-page .tablist{ grid-template-columns:repeat(2, 1fr); gap:12px; }
}
@media (max-width:900px){
  .partners-page .provide{ grid-template-columns:1fr; gap:32px; }
  /* Split stacks: testimonials first, form second. */
  .partners-page .split{ grid-template-columns:minmax(0,1fr); gap:44px; }
  .partners-page .opener-grid{ grid-template-columns:1fr; gap:18px; align-items:start; }
  .partners-page .opener .lead{ padding-bottom:0; }
}
@media (max-width:860px){
  .partners-page .hero{ min-height:auto; }
  .partners-page .hero-grid{ grid-template-columns:1fr; padding:48px 22px 64px; gap:36px; }
  .partners-page .hero-text{ max-width:none; }
  /* Stacked, the type runs top to bottom, so the scrim is heaviest across the
       headline and lead and then eases off. It does NOT stay heavy to the base:
       the two buttons carry their own backgrounds — the primary is a solid fill,
       the ghost has rgba(6,22,41,.5) behind it — so the photo can come back
       through in the bottom third without costing any contrast. */
  .partners-page .hero-scrim{
      background:linear-gradient(180deg,
        rgba(5,14,25,.94) 0%,
        rgba(5,14,25,.9) 42%,
        rgba(6,22,41,.72) 70%,
        rgba(6,22,41,.42) 100%);
    }
  /* Brighter than desktop, not darker. The previous pass dimmed this to
       brightness .24 / opacity .5 to buy contrast and effectively erased the
       photo; the scrim above buys it instead. */
  .partners-page .hero-photo{ filter:saturate(.75) brightness(.62) contrast(1.02); opacity:.95; }
  /* The diagonal band was aimed at the right-hand column that no longer
       exists; softened so it reads as light rather than as a stray stripe. */
  .partners-page .hero::after{
      background:
        linear-gradient(160deg, transparent 40%, rgba(34,171,173,.22) 62%, transparent 88%),
        radial-gradient(520px 380px at 80% 4%, rgba(34,171,173,.24), transparent 70%);
    }
  .partners-page .wrap{ --gutter:22px; }
}
@media (max-width:767px){
  .partners-page .sec-pad{ padding-top:56px; padding-bottom:56px; }
  .partners-page .sec-pad--tight{ padding-top:22px; }
  .partners-page .opener{ padding:52px 0 0; }
  .partners-page .models{ padding:32px 0 28px; }
  .partners-page .tpanel{ padding-top:34px; }
  /* ---- mobile tabs: one horizontal scroller, sticky under the nav ----
       The bar breaks out of .wrap's gutter so it can run edge to edge, then puts
       that gutter back inside the scroller — the first tab still lines up with
       the body text, but the track reaches both screen edges.
       All three values below read --gutter rather than repeating its number:
       hardcoding it meant retuning the token silently misaligned the bar. */
  .partners-page .tabbar{
      position:sticky; top:var(--nav-h); z-index:90;
      margin:0 calc(var(--gutter) * -1);
      background:var(--light);
      border-bottom:1px solid transparent;
      transition:box-shadow .25s ease, border-color .25s ease;
    }
  /* Only once the bar has actually stuck does it grow an edge — flat until then. */
  .partners-page .tabbar.is-stuck{
      border-bottom-color:rgba(12,27,42,.10);
      box-shadow:0 10px 22px -18px rgba(10,32,54,.7);
    }
  .partners-page .tablist{
      display:flex; grid-template-columns:none;
      gap:10px; margin:0; padding:12px var(--gutter);
      overflow-x:auto; overscroll-behavior-x:contain;
      -webkit-overflow-scrolling:touch;
      scroll-snap-type:x mandatory;
      /* Without this the snap point is the scrollport edge, so a snapped tab
         sits flush to the screen and loses the gutter the body text keeps. */
      scroll-padding-left:var(--gutter);
      scrollbar-width:none;
      /* Fades the right edge so a partly-visible tab reads as "more this way"
         rather than as a clipped card. The mask lives here and not on .tabbar
         because it would clip .tabbar's stuck shadow out of existence. */
      mask-image:linear-gradient(90deg, #000 0, #000 calc(100% - 26px), transparent 100%);
    }
  .partners-page .tablist::-webkit-scrollbar{ display:none; }
  .partners-page .tab{
      flex:0 0 58%;
      scroll-snap-align:start;
      padding:14px 16px; gap:12px;
    }
  /* A zero-height marker just above the bar. When it leaves the viewport the
       bar is stuck, which is the one thing position:sticky cannot tell you. */
  .partners-page .tab-sentinel{ display:block; height:1px; margin-bottom:-1px; }
  .partners-page .tab-ico{ width:38px; height:38px; border-radius:10px; }
  .partners-page .tab-ico svg{ width:21px; height:21px; }
  .partners-page .tab-label{ font-size:14px; }
  /* Headline: the three hard line breaks are for the desktop measure. At this
       width each one re-wraps anyway, so let the text flow as one block. The
       reveal animation goes with them — it slides whole lines, and a line that
       has wrapped in two slides wrong. */
    /* text-wrap:pretty because the headline is one flowing block here and can
       otherwise strand its last word on a line of its own. Ignored by browsers
       that do not support it, which is fine — it only ever improves the break. */
  .partners-page h1.headline{ font-size:30px; line-height:1.14; text-wrap:pretty; }
  .partners-page h1.headline .line{ display:inline; overflow:visible; }
  .partners-page h1.headline .line > span{ display:inline; transform:none; animation:none; }
  /* Two columns only down to 560px: the longest description (Microsoft, 270
       characters) needs a readable measure, and below that a half-width card
       turns it into a scroll. */
  .partners-page .pgrid,
  .partners-page .pgrid--3{ grid-template-columns:repeat(2, 1fr); gap:14px; }
  /* Taller than desktop, with smaller back-face type: at two columns the card
       is ~220px wide, and the longest partner name plus its description needed
       more room than 158px. */
  .partners-page .pcard{ height:230px; }
  .partners-page .pback-in{ padding:14px; }
  .partners-page .pface-in{ padding:10px; }
  /* Held at the desktop sizes — the card is one column below 560px, so the
       measure is wide enough that shrinking the type buys nothing. */
  .partners-page .pback .pback-t{ line-height:1.5; }
  .partners-page .ctas{ flex-direction:column; align-items:stretch; }
  /* The family drops .btn to 48px here; full width is this page's stacked-hero
       variant. The old row-reverse hack is gone with .cta-ghost — .btn centres
       its label, so the icon-first markup no longer strands anything. */
  .partners-page .btn{ min-height:48px; }
  .partners-page .ctas .btn{ width:100%; }
  .partners-page .quote{ padding:26px 24px 24px; border-radius:18px; min-height:0; }
  .partners-page .quote blockquote{ font-size:15.5px; margin-bottom:22px; }
  .partners-page .who{ padding-top:20px; }
  .partners-page .who b{ font-size:15px; }
  .partners-page .panel{ border-radius:18px; }
  .partners-page .panel-head{ padding:22px 20px 20px; }
  .partners-page .panel-body{ padding:22px 20px; }
  .partners-page .panel-h{ margin-top:18px; }
  .partners-page .opts{ grid-template-columns:1fr 1fr; gap:10px; }
  .partners-page .opt{ padding:15px; }
  .partners-page .opt-ico{ width:38px; height:38px; border-radius:10px; margin-bottom:12px; }
  .partners-page .opt-ico svg{ width:21px; height:21px; }
  .partners-page .opt-title{ font-size:14.5px; }
  /* Inputs only — this rule used to include textarea and so overrode its
       104px, halving "Anything else?" for no reason. */
  .partners-page .tfield input{ min-height:52px; }
  .partners-page .tfield input,
  .partners-page .tfield textarea{ padding:14px 16px; }
}
@media (max-width:560px){
  /* One column: the longest description (Microsoft, 270 characters) needs a
       readable measure, and a half-width card here turns it into a scroll. */
  .partners-page .pgrid,
  .partners-page .pgrid--3{ grid-template-columns:1fr; }
}
/* Two model cards inside a padded panel inside the page gutter leaves ~133px
     each below this width — the title wraps to two lines and the description to
     four. One column is narrower per row but readable. */
@media (max-width:480px){
  .partners-page .opts{ grid-template-columns:1fr; }
  .partners-page .opt{ padding:14px; }
}
/* Flip is decorative motion. Under reduced-motion the back face simply
     crossfades in, so the content is still reachable without the rotation. */
@media (prefers-reduced-motion:reduce){
  .partners-page .eyebrow,
  .partners-page p.sub,
  .partners-page .ctas{ animation:none; opacity:1; transform:none; }
  .partners-page h1.headline .line > span{ animation:none; transform:none; }
  .partners-page .pcard-inner{ transition:none; }
  .partners-page .pcard:hover .pcard-inner,
  .partners-page .pcard:focus-within .pcard-inner{ transform:none; }
  .partners-page .pback{ transform:none; }
  .partners-page .pface,
  .partners-page .pback{ transition:opacity .2s; }
  .partners-page .quote:hover,
  .partners-page .btn:hover,
  .partners-page .opt:hover{ transform:none; }
  .partners-page .tabbar{ transition:none; }
}
