/* The paper design system: editorial, monochrome, warm off-white.
 *
 * Every page on the new design loads this first and its own sheet after it:
 * the homepage adds shared/home.css, the product pages shared/page.css, the
 * legal pages shared/legal.css. It replaces shared/product.css and /styles.css
 * page by page; the two never load together, so nothing here has to
 * out-specify anything there.
 *
 * Palette is the brand's own (lib/theme/index.ts): black ink on warm paper,
 * with #FF6115 used only as a hairline or a dot, never as a surface. Contrast
 * against --paper is held above 4.5:1 for every ink token below.
 */
:root{
  --paper:#f3f2ee;      /* warm off-white; matches the hero photograph's ground */
  --paper-2:#eceae5;
  --white:#ffffff;
  /* Ratios are the WORST case, against --paper-2, which is the ground under the
     platform and pricing sections. Held to WCAG 2.2 AA: 4.5:1 for text, 3:1 for
     a border that carries meaning, which is why an interactive edge takes
     --rule-3 and a decorative hairline takes --rule. */
  --ink:#111113;        /* 15.7:1 */
  --ink-2:#4a4a51;      /*  7.3:1 */
  --ink-3:#64646c;      /*  4.9:1 — the lightest ink allowed on text */
  --rule:#dcd9d2;       /* hairline between blocks, decorative */
  --rule-2:#c9c5bc;     /* a heavier hairline, still decorative */
  --rule-3:#888279;     /*  3.2:1 — the edge of a button or a ringed arrow */
  --dark:#0b0b0c;
  --dark-2:#151517;
  /* Two oranges, because one cannot do both jobs. #FF6115 is the brand mark and
     it is 6.5:1 on the ink band but only 2.69:1 on paper, so anything that has
     to be READ or that carries a state on paper (a hover underline, a link
     colour) takes --accent-ink at 4.7:1 instead. */
  --accent:#FF6115;
  --accent-ink:#B83E08;
  --accent-rgb:255,97,21;
  --maxw:1280px;
  /* Three faces, one job each. Newsreader at 300 is the whole editorial voice:
     a thin serif only reads as premium when it is also LARGE, so the display
     sizes below are floors as much as ceilings. Geist carries body and UI at
     400/500, Geist Mono every uppercase micro-label. All three are on Google
     Fonts and all three load in the app through expo-font, so the site and the
     product can end up on one type system. */
  --serif:"Newsreader",ui-serif,Georgia,"Times New Roman",serif;
  --sans:"Geist","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:"Geist Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink);overflow-x:hidden;
  font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  font-size:16px;line-height:1.55;
}
a{color:inherit}
img{max-width:100%;height:auto;display:block}
button{font-family:inherit}
p{margin:0}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 40px}
@media (max-width:760px){ .wrap{padding:0 22px} }

/* ---------- type ----------
 * The serif carries every display line and nothing else, at 300. A light weight
 * needs room: 80px for the h1, 40px for an h2, and negative tracking so the
 * thin stems still read as one word rather than a row of letters. */
h1,h2,h3,h4{margin:0;font-weight:300}
.display{font-family:var(--serif);font-weight:300;line-height:1.02;letter-spacing:-.022em}
/* The h1 alone sits at 400. A light weight thins out as the size grows, and at
   80px the 300 read as a hairline beside the photograph; the h2 down at 40px
   does not have that problem and stays at 300. */
h1.display{font-size:clamp(44px,5.8vw,80px);font-weight:400;letter-spacing:-.026em}
h2.display{font-size:clamp(30px,3.4vw,40px);line-height:1.06;letter-spacing:-.018em;text-wrap:balance}
h3.display{font-size:clamp(24px,2.5vw,30px);line-height:1.1;letter-spacing:-.016em}
.eyebrow{
  display:block;font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);
}
/* Mono's own sidebearings mean a label needs less tracking than the same label
   set in the sans did, hence .12em above.
   The lede is 19px, not the 24 it started at: at 24 a plain sans paragraph is
   the biggest block of default-looking text on the page and competes with the
   headline. Tracked in slightly, and `.on` promotes the one sentence that is
   doing the positioning work to full ink. */
.lede{font-size:19px;line-height:1.55;letter-spacing:-.012em;color:var(--ink-2);max-width:34ch;text-wrap:pretty}
/* Its own line, always. Left inline, the emphasis straddled the wrap and read
   like a bolded fragment rather than the sentence it is. */
.lede .on{display:block;color:var(--ink);font-weight:500}
.note{font-size:13.5px;letter-spacing:-.005em;color:var(--ink-3)}
/* read by a screen reader, not drawn */
.vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.section{padding:106px 0}
@media (max-width:760px){ .section{padding:76px 0} }

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-size:14px;font-weight:500;letter-spacing:-.012em;
  padding:14px 24px;border-radius:4px;text-decoration:none;border:1px solid transparent;
  cursor:pointer;white-space:nowrap;
  transition:background .18s ease,color .18s ease,border-color .18s ease,transform .12s ease;
}
.btn:active{transform:translateY(1px)}
.btn-solid{background:var(--ink);color:var(--white)}
.btn-solid:hover{background:#2d2d33}
/* The ghost fills from the bottom. The label sits in its own layer so it can
   flip to paper as the ink arrives; a plain background transition would cross
   through a grey that neither colour is legible against. */
.btn-line{position:relative;background:transparent;color:var(--ink);border-color:var(--rule-3);overflow:hidden;isolation:isolate}
.btn-line::before{
  content:"";position:absolute;inset:0;z-index:-1;background:var(--ink);
  transform:translateY(101%);transition:transform .42s cubic-bezier(.2,.7,.2,1);
}
.btn-line:hover{color:var(--paper);border-color:var(--ink)}
.btn-line:hover::before{transform:translateY(0)}
.btn .arw{font-family:var(--sans);transition:transform .2s ease}
.btn:hover .arw{transform:translateX(3px)}
.btn-invert{background:var(--white);color:var(--ink)}
.btn-invert:hover{background:#e9e7e2}

/* the "Get in touch" pairing: label plus a ringed arrow */
.btn-ring{
  display:inline-flex;align-items:center;gap:12px;text-decoration:none;
  font-size:14px;font-weight:500;letter-spacing:-.012em;color:var(--ink);padding:6px 4px;
}
.btn-ring i{
  display:grid;place-items:center;width:34px;height:34px;flex:none;
  border:1px solid var(--rule-3);border-radius:50%;font-style:normal;font-size:13px;
  transition:background .2s ease,color .2s ease,border-color .2s ease,transform .2s ease;
}
.btn-ring:hover i{background:var(--ink);border-color:var(--ink);color:var(--white);transform:translateX(3px)}

/* an underlined text link with a moving arrow */
.link-arw{
  display:inline-flex;align-items:center;gap:9px;text-decoration:none;
  font-size:14px;font-weight:500;letter-spacing:-.012em;color:var(--ink);
  border-bottom:1px solid var(--ink);padding-bottom:3px;
  transition:gap .18s ease,border-color .18s ease;
}
.link-arw:hover{gap:15px;border-color:var(--accent-ink)}

/* shared/chrome.css sets the wordmark at 800 for a page whose body font is
   Inter. Geist ships here at 400/500/600, so an 800 would be synthesised into a
   smeared bold. Ask for the heaviest weight the page actually loads. */
.nav--light .nav-logo,.site-foot .foot-brand{font-weight:600}
/* Smaller than shared/chrome.css sets it. On a page whose h1 is 80px, a 22px
   wordmark competes with the headline for the same eye. */
.nav--light .nav-logo svg{height:22px}
.nav--light .nav-logo span{font-size:18px;letter-spacing:-.02em}
.nav--light .nav-cta,.nav--light .nav-login,.nav--light .nav-links a.navlink{font-weight:500}

/* ---------- reveal ---------- */
[data-reveal]{opacity:0;transform:translateY(16px);transition:opacity .8s cubic-bezier(.2,.7,.2,1),transform .8s cubic-bezier(.2,.7,.2,1)}
[data-reveal].is-in{opacity:1;transform:none}
[data-reveal-delay="1"]{transition-delay:.08s}
[data-reveal-delay="2"]{transition-delay:.16s}
[data-reveal-delay="3"]{transition-delay:.24s}
@media (prefers-reduced-motion:reduce){ [data-reveal]{opacity:1;transform:none;transition:none} }
.progress{position:fixed;top:0;left:0;height:2px;width:0;background:var(--ink);z-index:61;pointer-events:none}

/* ---------- a section head: heading left, a rail of words right ----------
 * This carried a five-column capability grid under it. The grid listed
 * Members / Classes / Payments / Grading, which is exactly what the tabs 100px
 * below it already say, so it was four labels twice and it went. */
.sol-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:40px;align-items:end}
.sol-head .eyebrow{margin-bottom:22px}
.sol-head h2{max-width:16ch}
.sol-aside{display:grid;gap:8px;text-align:right;padding-bottom:8px}
.sol-aside span{font-family:var(--mono);font-size:10.5px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
@media (max-width:1024px){
  .sol-head{grid-template-columns:minmax(0,1fr)}
  .sol-aside{text-align:left}
}

/* ---------- device frames ----------
 * One laptop and one phone, drawn in CSS so the screenshot inside stays the
 * only raster. The laptop is a black lid on a lighter deck; the phone is a
 * thin bezel, because on this page the phone is the supporting device. */
.frame{position:relative}
.frame-top{background:#0d0d10;border:1px solid rgba(0,0,0,.55);border-bottom:0;border-radius:14px 14px 0 0;padding:11px 11px 0;box-shadow:0 30px 60px -34px rgba(0,0,0,.6)}
.frame-top::before{content:"";position:absolute;top:4.5px;left:50%;width:5px;height:5px;margin-left:-2.5px;border-radius:50%;background:#2b2b31}
.frame-top img{border-radius:6px 6px 0 0;width:100%}
.frame-base{height:15px;margin:0 -3.6%;background:linear-gradient(#33333a,#17171b 60%,#0d0d10);border-radius:0 0 12px 12px;position:relative}
.frame-base::after{content:"";position:absolute;top:0;left:50%;width:13%;height:4px;margin-left:-6.5%;background:#0a0a0c;border-radius:0 0 5px 5px}
.shot{position:relative}
.shot img{position:absolute;inset:0;opacity:0;transition:opacity .5s ease}
.shot img:first-child{position:relative}
.shot img.is-active{opacity:1}
/* The phone: a thin black bezel, an island in a status strip painted the app
   header's own white, then the screenshot. Every measure is in cqw of the
   phone's wrapper, so it is the same phone at 80px wide on a small screen and
   at 170px on a large one, rather than a bezel that fattens as it shrinks.
   The strip exists because the capture is the web app, which has no status
   bar: an island drawn straight onto the screenshot sat on the academy's name. */
.phone-float{container-type:inline-size}
.phone{position:relative;background:#111113;border-radius:15cqw;padding:2.4cqw;box-shadow:0 34px 60px -30px rgba(0,0,0,.55),0 0 0 1px rgba(0,0,0,.35)}
.phone-bar{position:relative;height:11cqw;background:#fff;border-radius:12.6cqw 12.6cqw 0 0}
.phone-bar::before{content:"";position:absolute;top:2.6cqw;left:50%;width:29cqw;height:7cqw;margin-left:-14.5cqw;border-radius:99px;background:#111113}
.phone .shot{border-radius:0 0 12.6cqw 12.6cqw;overflow:hidden;background:#fff}
.phone .shot img{width:100%}
/* Hung off the phone's right edge so it reads as the phone's caption at any
   width, including the ones where the phone is narrower than the line. */
.phone-note{position:absolute;right:0;top:calc(100% + 14px);white-space:nowrap;font-family:var(--mono);font-size:11px;letter-spacing:.02em;color:var(--ink-3)}

/* ---------- shared components ----------
 * The pieces the homepage and the product pages both use: a numbered list on
 * hairlines, three facts in a row, and the pricing furniture (a tier, plan
 * cards, rows, the add-on list). Written once here so a price looks the same
 * wherever it is quoted. */
.steps-list{display:grid;gap:0;margin:34px 0 32px;border-top:1px solid var(--rule)}
/* `> div` twice over: the row is the grid, the text block inside it is not. */
.steps-list > div{
  display:grid;grid-template-columns:38px minmax(0,1fr);gap:14px;
  padding:18px 14px 18px 0;border-bottom:1px solid var(--rule);
  transition:padding-left .35s cubic-bezier(.2,.7,.2,1),background .35s ease;
}
.steps-list > div:hover{padding-left:14px;background:rgba(0,0,0,.022)}
.steps-list > div:hover i{color:var(--accent-ink)}
.steps-list i{transition:color .3s ease}
.steps-list i{font-style:normal;font-family:var(--mono);font-size:13px;line-height:1.7;color:var(--ink-3)}
.steps-list b{display:block;font-size:15px;font-weight:500;letter-spacing:-.012em}
.steps-list p{font-size:15px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);margin-top:3px}

/* three facts in a row, no heading
 * This used to carry two unattributed testimonials above it. An unattributed
 * quote is weaker than no quote, so they are gone and the facts stand alone. */
.claims{display:grid;grid-template-columns:repeat(3,1fr)}
.claim{padding:0 30px;border-left:1px solid var(--rule)}
.claim:first-child{border-left:0;padding-left:0}
.claim b{display:block;font-family:var(--serif);font-size:26px;font-weight:300;letter-spacing:-.016em;margin-bottom:9px;text-wrap:balance}
.claim span{font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}
@media (max-width:900px){
  .claims{grid-template-columns:1fr;gap:30px}
  .claim{border-left:0;padding:0}
}

/* pricing:
 * Gym management is two plans and needs cards. Websites and the white-label app
 * are one line each and would look absurd as cards, so they are rows: same
 * hairline language, a quarter of the ink. */
.tier{padding:38px 0;border-top:1px solid var(--rule-2);display:grid;grid-template-columns:minmax(0,.62fr) minmax(0,1.38fr);gap:48px;align-items:start}
.tier-head p{margin-top:14px;font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);max-width:30ch;text-wrap:pretty}
.tier-head .eyebrow{color:var(--ink)}
.plans{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:stretch}
.plan{display:flex;flex-direction:column;background:var(--white);border:1px solid var(--rule);border-radius:14px;padding:30px 28px 28px;position:relative}
.plan.pro{background:var(--dark);color:var(--white);border-color:var(--dark)}
.plan-name{font-family:var(--mono);font-size:11px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-bottom:16px}
.plan.pro .plan-name{color:rgba(255,255,255,.6)}
.plan-price{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.plan-price .num{font-family:var(--serif);font-size:52px;font-weight:300;line-height:1;letter-spacing:-.028em}
.plan-price .per{font-size:14px;font-weight:400;letter-spacing:-.006em;color:var(--ink-3)}
.plan.pro .plan-price .per{color:rgba(255,255,255,.62)}
.plan-sub{margin:16px 0 24px;font-size:15px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2)}
.plan.pro .plan-sub{color:rgba(255,255,255,.74)}
/* align-content:start: the list takes the card's spare height so the buttons
   line up across cards, but its rows keep their own spacing rather than
   spreading out to fill it */
.plan ul{list-style:none;margin:0 0 28px;padding:0;display:grid;gap:10px;flex:1;align-content:start}
.plan li{font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);padding-left:18px;position:relative}
.plan.pro li{color:rgba(255,255,255,.8)}
.plan li::before{content:"";position:absolute;left:0;top:9px;width:6px;height:1px;background:var(--rule-3)}
.plan.pro li::before{background:rgba(255,255,255,.4)}
.plan .btn{width:100%}
/* the ghost button's ink fill has to invert against a dark card, not a light one */
.plan.pro .btn-invert:hover{background:#e9e7e2}

.rows{display:grid;gap:0}
.row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:32px;align-items:center;padding:22px 0;border-bottom:1px solid var(--rule)}
.row:last-of-type{border-bottom:0}
.row-what b{display:block;font-size:16px;font-weight:500;letter-spacing:-.014em;margin-bottom:5px}
.row-what span{display:block;font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);max-width:52ch;text-wrap:pretty}
.row-cost{text-align:right;white-space:nowrap}
.row-cost b{font-family:var(--serif);font-size:34px;font-weight:300;letter-spacing:-.024em}
.row-cost span{font-size:14px;color:var(--ink-3);margin-left:6px}
/* Website add-ons. Quoted, so there is no price to put in the right-hand
   column: "Quoted" holds the column's place in the same serif the prices use,
   and ONE button serves the group. Five identical "Get a quote" buttons in a
   stack was the version this replaced. */
/* no border-top: the row above already ends on a hairline, and two of them 10px
   apart read as a rendering fault */
.addons{padding-top:24px}
.addons-label{display:block;margin-bottom:6px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.addons-list{list-style:none;margin:0 0 22px;padding:0;display:grid;grid-template-columns:1fr 1fr;column-gap:40px}
.addons-list li{display:flex;justify-content:space-between;align-items:baseline;gap:16px;padding:13px 0;border-bottom:1px solid var(--rule)}
.addons-list b{font-size:15px;font-weight:500;letter-spacing:-.012em}
.addons-list span{flex:none;font-family:var(--serif);font-size:17px;font-weight:300;letter-spacing:-.01em;color:var(--ink-3)}
/* the photography line carries the studio's own credit, logo first */
.addon-media b em{display:flex;align-items:center;gap:6px;margin-top:5px;font-style:normal;font-size:12.5px;font-weight:400;letter-spacing:-.004em;color:var(--ink-2)}
.addon-media b em svg{height:11px;width:auto;flex:none;color:var(--ink)}
.addons-list li{transition:padding-left .35s cubic-bezier(.2,.7,.2,1)}
.addons-list li:hover{padding-left:8px}
@media (max-width:640px){
  .addons-list{grid-template-columns:1fr}
  .addons .btn{width:100%}
}
.row-note{margin-top:20px;font-size:13.5px;line-height:1.55;color:var(--ink-3);max-width:64ch;text-wrap:pretty}
.row-note a{font-weight:500;color:var(--ink);text-decoration:underline;text-decoration-color:var(--accent-ink);text-decoration-thickness:1.5px;text-underline-offset:3px}

.plan-more{margin-top:38px;display:flex;flex-wrap:wrap;gap:6px 16px;align-items:center;justify-content:center;font-size:14px;letter-spacing:-.006em;color:var(--ink-2)}
.plan-more a{font-weight:500;color:var(--ink);text-decoration:underline;text-decoration-color:var(--accent-ink);text-decoration-thickness:1.5px;text-underline-offset:3px}
.plan-more .sep{color:var(--rule-2)}
@media (max-width:1024px){
  .tier{grid-template-columns:minmax(0,1fr);gap:26px}
  .tier-head p{max-width:52ch}
}
@media (max-width:640px){
  .plans{grid-template-columns:1fr}
  .row{grid-template-columns:minmax(0,1fr);gap:14px}
  .row-cost{text-align:left;white-space:normal}
  .row-cost .btn{width:100%}
}


/* the curriculum loop in three steps (homepage section, curriculum page)
 * Drawn, not screenshotted: the platform section just above already shows the
 * product, and what this section has to get across is an idea, that eighty
 * planned techniques become the two that were taught. Three columns on one
 * rule, the live moment lifted onto a white card, and the ticks draw as the
 * band crosses the window. The orange is a hairline that runs along the top
 * of each step in turn. */
.cur-flow{list-style:none;margin:64px 0 0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid var(--rule-2)}
.cur-node{position:relative;padding:36px 40px 44px;border-left:1px solid var(--rule)}
.cur-node:first-child{border-left:0;padding-left:0}
.cur-node::before{content:"";position:absolute;left:0;right:0;top:-1px;height:1px;background:var(--accent);transform-origin:0 50%}
.cur-node:first-child::before{right:40px}
/* The card's own edge is the divider on both of its sides, so the hairlines
   either side of it go, and the two words that sit on those edges need no
   patch of background to cut the line behind them. */
.cur-live{background:var(--white);box-shadow:0 30px 60px -40px rgba(0,0,0,.35);border-left:0}
.cur-live + .cur-node{border-left:0}
.cur-tag{display:block;margin-bottom:18px;font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.cur-node h3.display{margin-bottom:26px}
/* the word that happens between two steps, sat on the rule that divides them */
.cur-link{position:absolute;left:0;top:50%;transform:translate(-50%,-50%);z-index:1}
.cur-link i{display:block;font-style:normal;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);writing-mode:vertical-rl;transform:rotate(180deg)}
.cur-list,.cur-ticks{list-style:none;margin:0;padding:0}
.cur-list li,.cur-ticks li{padding:11px 0;border-top:1px solid var(--rule);font-size:15px;letter-spacing:-.008em;color:var(--ink)}
.cur-list li:last-child,.cur-ticks li:last-child{border-bottom:1px solid var(--rule)}
.cur-list .faint,.cur-ticks .faint{color:var(--ink-3)}
.cur-ticks li{display:flex;align-items:center;gap:12px}
.cur-ticks .box{display:grid;place-items:center;width:18px;height:18px;flex:none;border:1px solid var(--rule-3);border-radius:4px;color:var(--white)}
.cur-ticks .on .box{background:var(--ink);border-color:var(--ink)}
.cur-ticks .box svg{width:12px;height:12px}
.cur-ticks .box path{stroke-dasharray:14;stroke-dashoffset:0}
.cur-people{margin:0}
.cur-people div{padding:11px 0;border-top:1px solid var(--rule)}
.cur-people div:last-child{border-bottom:1px solid var(--rule)}
.cur-people dt{font-size:15px;font-weight:500;letter-spacing:-.008em}
.cur-people dd{margin:3px 0 0;font-size:13.5px;color:var(--ink-3)}
.cur-foot{margin-top:22px;font-family:var(--serif);font-size:22px;font-weight:300;letter-spacing:-.014em;line-height:1.2}
.cur-close{margin-top:58px;display:flex;flex-wrap:wrap;gap:22px 48px;align-items:flex-end;justify-content:space-between}
.cur-close p{font-size:17px;line-height:1.6;letter-spacing:-.008em;color:var(--ink-2);max-width:58ch;text-wrap:pretty}
@media (max-width:900px){
  .cur-flow{grid-template-columns:minmax(0,1fr);border-top:0}
  .cur-node,.cur-node:first-child{border-left:0;border-top:1px solid var(--rule-2);padding:40px 0 36px}
  .cur-node:first-child::before{right:0}
  .cur-live{padding:40px 22px 36px;margin:0 -22px}
  .cur-link{left:22px;top:0;transform:translateY(-50%);padding:0 10px;background:var(--paper-2)}
  .cur-live .cur-link{background:var(--white)}
  .cur-live .cur-link{left:22px}
  .cur-node:not(.cur-live) .cur-link{left:0;padding-left:0}
  .cur-link i{writing-mode:horizontal-tb;transform:none}
}

/* ---------- closing band ---------- */
.closing{background:var(--dark);color:var(--white);padding:110px 0}
.closing .wrap{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:48px;align-items:center}
.closing .eyebrow{color:rgba(255,255,255,.6);margin-bottom:22px}
.closing h2{max-width:24ch}
.closing .note{color:rgba(255,255,255,.6);margin-top:14px}
.closing-cta{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* The ink-band twin of .btn-line. It used to be an inline style on the anchor,
   which meant the hover state was simply missing. .36 white and not .3: a
   button's edge is a meaningful border, so it owes 3:1, and .3 measured 2.60
   against this band. */
.btn-onink{background:transparent;color:var(--white);border-color:rgba(255,255,255,.36)}
.btn-onink:hover{border-color:var(--white);background:rgba(255,255,255,.08)}
@media (max-width:980px){
  .closing{padding:80px 0}
  .closing .wrap{grid-template-columns:minmax(0,1fr);gap:34px}
}

/* ---------- motion ----------
 *
 * Two layers, and the browser picks one.
 *
 * The FALLBACK is the transition on [data-reveal] near the top of this file,
 * flipped by shared/product.js on an IntersectionObserver: an element fades up
 * once, on a timer, and stays. Safari and Firefox get this.
 *
 * The REAL one, under @supports below, is a scroll-driven animation
 * (`animation-timeline: view()`). The element's progress IS the scroll
 * position, so a reveal tracks the wheel and reverses when you scroll back,
 * which is what makes the page feel attached to the hand rather than triggered
 * by it. No JS, no rAF, no listener: the compositor runs it. Chrome and Edge.
 *
 * Everything here is decorative and everything is off under
 * prefers-reduced-motion, which is asserted at the end of the block. Any hover
 * transform is paired with an :active state, because a phone has no hover and a
 * press is the only feedback it can give.
 */
@keyframes rise{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
@keyframes photoIn{from{opacity:0;transform:scale(1.06)}to{opacity:1;transform:none}}
@keyframes revealUp{from{opacity:0;transform:translateY(46px) scale(.972)}to{opacity:1;transform:none}}
@keyframes liftIn{from{opacity:0;transform:translateY(60px)}to{opacity:1;transform:none}}
@keyframes tiltIn{from{opacity:0;transform:perspective(1600px) rotateX(11deg) translateY(40px)}to{opacity:1;transform:perspective(1600px) rotateX(0) translateY(0)}}
@keyframes driftY{from{transform:translateY(22px)}to{transform:translateY(-22px)}}
@keyframes heroPush{from{transform:scale(1) translateY(0)}to{transform:scale(1.10) translateY(2.5%)}}
@keyframes foldOut{from{transform:none;opacity:1}to{transform:translateY(-64px);opacity:.12}}
@keyframes railOut{from{transform:none;opacity:1}to{transform:translateY(38px);opacity:0}}
@keyframes driftUp{from{transform:translateY(30px)}to{transform:translateY(-30px)}}
@keyframes driftUpSmall{from{transform:translateY(18px)}to{transform:translateY(-18px)}}
@keyframes phoneDrift{from{transform:translateY(34px)}to{transform:translateY(-34px)}}
@keyframes growX{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes tickBox{from{background:transparent;border-color:var(--rule-3)}to{background:var(--ink);border-color:var(--ink)}}
@keyframes tickDraw{from{stroke-dashoffset:14}to{stroke-dashoffset:0}}
@keyframes driftDown{from{transform:translateY(-26px)}to{transform:translateY(26px)}}
@keyframes stepIn{from{opacity:0;transform:translateX(-26px)}to{opacity:1;transform:none}}
@keyframes tabTimer{from{width:0}to{width:100%}}

/* ---- pointer feedback ---- */
.plan{transition:transform .35s cubic-bezier(.2,.7,.2,1),box-shadow .35s ease}
.plan:hover{transform:translateY(-6px);box-shadow:0 30px 60px -34px rgba(0,0,0,.4)}
.plan.pro:hover{box-shadow:0 30px 60px -30px rgba(0,0,0,.65)}
.claim{transition:transform .35s cubic-bezier(.2,.7,.2,1)}
.claim:hover{transform:translateY(-3px)}
.btn:active{transform:translateY(1px) scale(.988)}
.btn-ring:active i{transform:scale(.94)}
.link-arw:active{opacity:.7}

/* the JS fallback's timed stagger for a row of facts */
.is-in .claim{animation:rise .8s cubic-bezier(.2,.7,.2,1) both}
.is-in .claim:nth-child(2){animation-delay:.1s}
.is-in .claim:nth-child(3){animation-delay:.2s}

/* the curriculum loop's fallback: the ticks draw once the list has revealed */
.cur-flow .on .box{background:transparent;border-color:var(--rule-3);transition:background .3s ease .9s,border-color .3s ease .9s}
.cur-flow .on + .on .box{transition-delay:1.25s}
.cur-flow .box path{stroke-dashoffset:14;transition:stroke-dashoffset .45s ease 1s}
.cur-flow .on + .on .box path{transition-delay:1.35s}
.cur-flow.is-in .on .box{background:var(--ink);border-color:var(--ink)}
.cur-flow.is-in .box path{stroke-dashoffset:0}
.cur-node::before{transform:scaleX(0);transition:transform 1.1s cubic-bezier(.2,.7,.2,1) .3s}
.cur-node:nth-child(2)::before{transition-delay:.7s}
.cur-node:nth-child(3)::before{transition-delay:1.1s}
.cur-flow.is-in .cur-node::before{transform:none}

/* ---- scroll-driven reveals ----
 * Late and quick: a block is still moving while it is below the middle of the
 * window and settled by the time you read it. The constant motion comes from
 * the DRIFT rules, which run across an element's whole crossing and so never
 * finish while it is on screen. Pages add their own under the same guard. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal]{
      opacity:1;transform:none;transition:none;
      animation:revealUp linear both;
      animation-timeline:view();
      animation-range:entry 18% cover 44%;
    }
    /* DRIFT: the section head and its rail pull apart as the band crosses */
    .sol-head > div:first-child{animation:driftUp linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .sol-aside{animation:driftDown linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    /* Curriculum: the steps arrive left to right, the orange runs along each
       top in turn, and the two ticks draw in the middle of the crossing. */
    .cur-node{animation:liftIn linear both;animation-timeline:view();animation-range:entry 14% cover 40%}
    .cur-node:nth-child(2){animation-range:entry 20% cover 46%}
    .cur-node:nth-child(3){animation-range:entry 26% cover 52%}
    .cur-node::before,.cur-flow.is-in .cur-node::before{transition:none;animation:growX linear both;animation-timeline:view();animation-range:cover 20% cover 38%}
    .cur-node:nth-child(2)::before,.cur-flow.is-in .cur-node:nth-child(2)::before{animation-range:cover 30% cover 48%}
    .cur-node:nth-child(3)::before,.cur-flow.is-in .cur-node:nth-child(3)::before{animation-range:cover 40% cover 58%}
    .cur-flow .on .box,.cur-flow.is-in .on .box{transition:none;animation:tickBox linear both;animation-timeline:view();animation-range:cover 30% cover 38%}
    .cur-flow .on + .on .box,.cur-flow.is-in .on + .on .box{animation-range:cover 36% cover 44%}
    .cur-flow .on .box path,.cur-flow.is-in .on .box path{transition:none;animation:tickDraw linear both;animation-timeline:view();animation-range:cover 32% cover 40%}
    .cur-flow .on + .on .box path,.cur-flow.is-in .on + .on .box path{animation-range:cover 38% cover 46%}
    .cur-link{animation:fadeIn linear both;animation-timeline:view();animation-range:cover 26% cover 40%}
    .cur-close{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    /* numbered steps, facts and pricing arrive one after another, each still
       moving while the next one starts */
    .claims{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .tier-head{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .steps-list > div{animation:stepIn linear both;animation-timeline:view();animation-range:entry 22% cover 46%}
    .steps-list > div:nth-child(2){animation-range:entry 28% cover 52%}
    .steps-list > div:nth-child(3){animation-range:entry 34% cover 58%}
    .claim{animation:liftIn linear both;animation-timeline:view();animation-range:entry 20% cover 44%}
    .claim:nth-child(2){animation-range:entry 26% cover 50%}
    .claim:nth-child(3){animation-range:entry 32% cover 56%}
    .is-in .claim{animation:liftIn linear both;animation-timeline:view()}
    .tier{animation:revealUp linear both;animation-timeline:view();animation-range:entry 18% cover 44%}
    .plan:first-child{animation:liftIn linear both;animation-timeline:view();animation-range:entry 22% cover 48%}
    .plan.pro{animation:liftIn linear both;animation-timeline:view();animation-range:entry 28% cover 54%}
    .row{animation:stepIn linear both;animation-timeline:view();animation-range:entry 22% cover 48%}
    .addons-list li{animation:stepIn linear both;animation-timeline:view();animation-range:entry 20% cover 46%}
    .addons-list li:nth-child(even){animation-range:entry 26% cover 52%}
  }
}
@media (prefers-reduced-motion:reduce){
  .is-in .claim{animation:none}
  .cur-flow .on .box{background:var(--ink);border-color:var(--ink);transition:none}
  .cur-flow .box path{stroke-dashoffset:0;transition:none}
  .cur-node::before{transform:none;transition:none}
  .shot img,.btn,.btn-ring i,.btn-line::before,.steps-list > div,.addons-list li,.plan,.claim{transition:none}
  .btn:active{transform:none}
}
