@property --par { syntax:'<length>'; inherits:true; initial-value:0px }
/* Tel-IQ inner pages. Loaded AFTER style.css so the homepage treatment's
   material language reaches features, pricing, about, contact, signup and legal.
   Copy unchanged; type, surface and motion only. */

@font-face{font-family:Grotesk;src:url(../fonts/spacegrotesk.woff2) format('woff2');font-weight:400 700;font-display:swap}
@font-face{font-family:Plex;src:url(../fonts/plexmono.woff2) format('woff2');font-weight:400 500;font-display:swap}

:root{
  --mg:Grotesk,system-ui,sans-serif; --mm:Plex,ui-monospace,Menlo,monospace;
  --mbg:#07090c; --ms1:#0e1218; --ms2:#131922;
  --mhair:rgba(255,255,255,.075); --mhair2:rgba(255,255,255,.16);
  --mink:#eef2f7; --mink2:#98a3b2; --mink3:#69737f; --mblue:#4f9bea;
}
body{background:var(--mbg);color:var(--mink)}
::selection{background:rgba(79,155,234,.28)}
:focus-visible{outline:2px solid var(--mblue);outline-offset:3px}
::-webkit-scrollbar{width:11px}
::-webkit-scrollbar-track{background:var(--mbg)}
::-webkit-scrollbar-thumb{background:#1d242e;border:3px solid var(--mbg);border-radius:6px}
::-webkit-scrollbar-thumb:hover{background:#2a333f}

.site-header{position:sticky;top:0;z-index:60;background:rgba(7,9,12,.74);
  backdrop-filter:saturate(150%) blur(14px);border-bottom:1px solid var(--mhair)}
.site-footer{background:rgba(14,18,24,.72);border-top:1px solid var(--mhair)}

/* display type */
.banner h1,.section-head h2,.cta-strip h2,.notfound h1{
  font-family:var(--mg);font-weight:700;letter-spacing:-.045em;line-height:1.06;text-wrap:balance}
.banner h1{font-size:clamp(2.2rem,4.4vw,3.4rem)}
.section-head h2{font-size:clamp(1.8rem,3.2vw,2.5rem)}
.card h3,.price-card .plan,.acc-head{font-family:var(--mg);font-weight:600;letter-spacing:-.02em}
.section-head{max-width:52ch;margin-left:auto;margin-right:auto;text-align:center}
.section-head p{color:var(--mink2);margin-left:auto;margin-right:auto;max-width:48ch}
.card p,.card li,.price-features li,.plan-feats li,.acc-body{color:var(--mink2)}

/* surfaces */
.card,.price-card,.form{background:var(--ms1);border:1px solid var(--mhair);border-radius:12px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
  transition:border-color .28s ease,transform .28s cubic-bezier(.2,.8,.2,1)}
.card:hover,.price-card:hover{border-color:var(--mhair2);transform:translateY(-3px)}
.price-card.featured,.price-card.popular{background:var(--ms2);border-color:rgba(79,155,234,.4)}
.section.alt{background:rgba(14,18,24,.68)}
.accordion .acc-item{border-color:var(--mhair)}
.acc-item.open{background:var(--ms1)}

/* pricing figures line up */
.price-amount,.price-period,.plan-price{font-family:var(--mm);font-variant-numeric:tabular-nums;
  letter-spacing:-.03em}

/* Banner plate. The generated spectrum photo that used to sit here read as
   television static behind the page title, so it is gone. What remains is a
   quiet graded field; motion.js layers the same drifting standard identifiers
   used on the homepage on top of it. With JS off this base stands alone. */
.banner{position:relative;isolation:isolate;overflow:hidden;
  background-image:none!important;border-bottom:1px solid var(--mhair)}
.banner::after{content:'';position:absolute;inset:0;z-index:-2;
  background:
    radial-gradient(120% 150% at 50% -20%, rgba(34,113,197,.16), transparent 60%),
    linear-gradient(180deg, #0b0e13 0%, var(--mbg) 100%)}
/* a faint horizon rule, so the band has an edge rather than fading to nothing */
.banner::before{content:'';position:absolute;left:0;right:0;bottom:0;height:1px;z-index:-1;
  background:linear-gradient(90deg,transparent,rgba(79,155,234,.34) 30%,rgba(79,155,234,.34) 70%,transparent)}
.banner .bfield{position:absolute;inset:0;width:100%;height:100%;
  z-index:-1;pointer-events:none;opacity:.9}
.banner h1,.banner .crumb,.banner p{position:relative;z-index:1}

/* form fields inherit the palette rather than the browser's */
.form input,.form textarea,.form select,.subscribe input{
  background:rgba(255,255,255,.04);border:1px solid var(--mhair2);color:var(--mink);border-radius:7px}
.form input::placeholder,.form textarea::placeholder{color:var(--mink3)}

/* nav CTA inherits light text on the orange fill (~1.9:1). revamp.css fixes this
   on the homepage; inner pages never loaded it. */
.nav-cta .btn:not(.btn-ghost):not(.btn-outline){color:#1a1206}

/* prose pages (privacy, terms) carry no heading class */
.prose h1,.prose h2,.prose h3{font-family:var(--mg);font-weight:700;letter-spacing:-.035em}
.prose h1{font-size:clamp(1.9rem,3.4vw,2.6rem)}
.prose p,.prose li{color:var(--mink2)}
.prose a{color:var(--mblue)}

/* every input, not only those inside .form: one had black text on near-black */
input,textarea,select{color:var(--mink);font-family:inherit}
input[type=checkbox],input[type=radio]{accent-color:var(--mblue)}
/* signup's heading is a bare h1 inside .auth-card */
.auth-card h1,.auth-card h2{font-family:var(--mg);font-weight:700;letter-spacing:-.035em}
.auth-card{background:var(--ms1);border:1px solid var(--mhair);border-radius:12px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06)}
.auth-card p{color:var(--mink2)}

/* .plan is the tier name, a plain div, not the card. Never give it card chrome. */
.plan{background:none;border:0;border-radius:0;box-shadow:none;padding:0;transform:none;
  font-family:var(--mg);font-weight:600;letter-spacing:-.02em}

/* about.html and features.html still render the CTA as a saturated blue slab
   from style.css, while the homepage uses the spectrum plate on dark. Same
   treatment everywhere. */
.cta-strip{position:relative;overflow:hidden;isolation:isolate;padding:92px 0;
  background:transparent;border-top:1px solid var(--mhair);border-bottom:1px solid var(--mhair)}
.cta-strip::before{content:'';position:absolute;inset:0;z-index:-2;
  background:url(../img/corpus.jpg?v=1) center/cover no-repeat;opacity:.8;
  transform:translateY(var(--par,0px)) scale(1.12);will-change:transform}
.cta-strip::after{content:'';position:absolute;inset:0;z-index:-1;
  background:radial-gradient(58% 70% at 50% 50%,rgba(6,8,11,.94) 0%,rgba(6,8,11,.72) 48%,rgba(6,8,11,.18) 82%,transparent 100%),
    linear-gradient(180deg,var(--mbg) 0%,transparent 18%,transparent 82%,var(--mbg) 100%)}
.cta-strip h2{font-family:var(--mg);font-weight:700;letter-spacing:-.04em;color:var(--mink)}
.cta-strip p{color:var(--mink2);font-weight:400}

/* the auth card drew a hairline above its first field */
.auth-card form,.auth-card .form{border:0;background:none;box-shadow:none}
.auth-card hr{display:none}
/* ==========================================================================
   Canonical typography. Measured 2026-08-13: the display face reached only a
   few homepage-specific selectors, so h2 fell back to Poppins on 10 pages and
   body copy stayed Poppins on all 18 teliq/televerse pages. Body copy is most
   of the pixels on screen, which is why every page still read as unchanged.
   Set here on the element, so it applies whatever classes a page happens to use.
   ========================================================================== */
body,
button, input, select, textarea {
  font-family: Grotesk, system-ui, -apple-system, 'Segoe UI', sans-serif;
}
h1, h2, h3, h4, h5, .h1, .h2, .h3,
.section-head h2, .rv-head h2, .banner h1, .card h3 {
  font-family: Grotesk, system-ui, sans-serif;
  letter-spacing: -.035em;
  text-wrap: balance;
}
h2, .h2 { font-weight: 700; letter-spacing: -.04em; line-height: 1.1; }
h3, .h3 { font-weight: 600; letter-spacing: -.025em; }
h4, h5  { font-weight: 600; letter-spacing: -.015em; }

/* mono is the technical register: identifiers, counts, eyebrows, code */
code, kbd, samp, pre, .mono, .eyebrow, .pill, .tag,
.rv-chip, .cnt, .price-amt, .lab-id, .stat b {
  font-family: Plex, ui-monospace, Menlo, monospace;
  font-feature-settings: 'tnum' 1;
}
/* The 64x4px rounded brand bar under every section head is the single most
   template-looking element left on the inner pages, and it repeats on both
   sites. Replaced with a full-width hairline and a short brand segment — the
   same rule reads as structure rather than decoration. */
.section-head h2::after {
  width: 100%; max-width: 340px; height: 1px; border-radius: 0;
  background: linear-gradient(90deg,
    var(--brand) 0, var(--brand) 56px,
    rgba(255,255,255,.13) 56px, rgba(255,255,255,.13) 100%);
  margin-top: 20px;
}


/* ==========================================================================
   WCAG AA contrast — impeccable detector, 2026-08-13. It found 99 failures
   across the estate; these are the ones that live in this sheet's scope.
   Measured, not estimated:
     #1a1a1a on #2271c5 = 3.5:1  -> #ffffff on #2271c5 = 4.92:1
     #98a3b2 on #2271c5 = 1.9:1  -> inherits the same white
     #69737f on #0c0f15 = 4.0:1  -> #7c8796 = 5.2:1
   The brand blue is dark enough to carry white text and too dark to carry
   near-black, which is what the hover states were doing.
   ========================================================================== */
:root{ --mink3:#7c8796; --ink3:#7c8796 }

.dropdown a:hover, .socials a:hover, .story-num { color:#fff }
/* descendants kept their own muted colour on the blue fill (1.9:1) */
.dropdown a:hover *, .socials a:hover *, .story-num * { color:inherit }


/* Accordion: max-height animation is a layout-property transition. grid-template-rows
   0fr->1fr animates without measuring and without laying out on every frame. main.js
   still sets an inline max-height on open, which is simply ignored here, so a stale
   cached main.js keeps working. */
.acc-body{display:grid;grid-template-rows:0fr;overflow:hidden;
  transition:grid-template-rows .35s cubic-bezier(.2,.8,.2,1);max-height:none!important}
.acc-item.open .acc-body{grid-template-rows:1fr}
.acc-body>*{min-height:0;overflow:hidden}
@media (prefers-reduced-motion:reduce){.acc-body{transition:none}}

/* .inner's own padding sits outside the collapsed row and left a 22px sliver
   when shut; move the inset onto its children so the row truly reaches zero */
.acc-body>.inner{padding:0}
.acc-body>.inner>*{padding-left:24px;padding-right:24px}
.acc-body>.inner>*:last-child{padding-bottom:22px}


/* Feature cards carried a small line icon; they now lead with a photograph,
   matching the Televersity catalogue treatment that worked. Ratio reserved. */
.card .feat-img{display:block;aspect-ratio:3/2;overflow:hidden;background:#0f141b;
  border-radius:10px;margin:-4px 0 16px;border:1px solid var(--mhair)}
.card .feat-img img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .5s cubic-bezier(.2,.8,.2,1)}
.card:hover .feat-img img{transform:scale(1.05)}
@media (prefers-reduced-motion:reduce){ .card:hover .feat-img img{transform:none} }


/* A scaled ::before plate overhangs its box. .plated scaled to 1.08 with no
   clipping, so 4% leaked past the viewport on each side and the page scrolled
   sideways by 58px at 1440. Pseudo-elements are invisible to a DOM overflow
   query, which is why this survived every earlier check. */
/* overflow:hidden here made section.spine a scroll container, which silently
   demoted the scrollytelling .rail from sticky to effectively static - it stuck
   to a box that never scrolls, so the right half of the section sat empty for
   1028px while the steps scrolled past alone. overflow:clip clips exactly the
   same overhang WITHOUT establishing a scroll container, so sticky survives.
   overflow-y stays visible so nothing vertical is ever cut. */
.plated,.spine.plated,.rv-hero,.cta-strip,.banner,.rv-strip{overflow-x:clip;overflow-y:visible}
/* NOT html,body{overflow-x:hidden}: that clips every positioned child (the
   detector flagged 37) and can break the sticky header. Clipping the scaled
   plates at source is the actual fix. */
/* Page rail: injected by motion.js on any page with a banner and 2+ sections.
   Hidden until the banner scrolls away, so it never repeats a title already on
   screen. Anchored below the sticky header, whose height it measures itself. */
.prail{position:sticky;top:var(--hdr,64px);z-index:55;
  background:rgba(9,12,17,.86);backdrop-filter:saturate(150%) blur(14px);
  border-bottom:1px solid var(--mhair);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .28s ease,transform .28s cubic-bezier(.2,.8,.2,1),visibility 0s .28s}
html.pr-on .prail{opacity:1;visibility:visible;transform:none;transition-delay:0s}
.prail .container{display:flex;align-items:center;gap:20px;min-height:46px;
  overflow-x:auto;scrollbar-width:none}
.prail .container::-webkit-scrollbar{display:none}
.pr-title{font-family:var(--mg);font-weight:600;font-size:.92rem;white-space:nowrap;
  color:var(--mink);flex:0 0 auto}
.pr-links{display:flex;gap:4px;margin-left:auto;flex:0 0 auto}
.pr-links a{font-size:.82rem;color:var(--mink2);text-decoration:none;white-space:nowrap;
  padding:6px 11px;border-radius:6px;transition:color .2s ease,background .2s ease}
.pr-links a:hover{color:var(--mink);background:rgba(255,255,255,.05)}
.pr-links a.on{color:var(--mblue);background:rgba(79,155,234,.11)}
html{scroll-behavior:smooth;scroll-padding-top:calc(var(--hdr,64px) + 58px)}
@media (max-width:720px){ .pr-title{font-size:.85rem} .pr-links a{padding:6px 9px;font-size:.78rem} }
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} .prail{transition:none} }


/* The sticky header sat at 74% opacity, so a large high-contrast h2 scrolling
   under it stayed legible through the blur - visible as ghost text between the
   logo and the nav. Opaque enough to read as a surface once you have scrolled. */
/* glass at the top, over the banner - and fully opaque once scrolled, because
   7% transparency is still enough for a white h2 to read through the blur */
.site-header{background:rgba(7,9,12,.86)}
html.pr-on .site-header{background:#070a0d;backdrop-filter:none}

/* defensive: an injected canvas with no positioning is a block element that
   pushes the page down */
.hero{position:relative;isolation:isolate}
.hero .bfield{position:absolute;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none;opacity:.85}


/* Page-wide identifier drift. Fixed behind everything, on every page.
   For it to be SEEN, no section may paint an opaque ground over it - that is
   what hid it before, so the alt bands go translucent. Nothing dims the canvas
   itself; the alpha in driftField is the only place opacity is decided. */
/* z-index:-1, NOT z-index:0 with the siblings forced to relative - that rule
   overrode position:sticky on the header and dropped it out of the flow.
   A negative-z child paints above the body background and below every sibling
   on its own, so nothing else needs touching. */
.pfield{position:fixed;inset:0;width:100vw;height:100vh;z-index:-1;pointer-events:none}

/* ---- banner plates ---------------------------------------------------------
   The inner-page banners were a flat radial gradient. They now carry a photo,
   added as a real <img> rather than a background-image because .banner already
   carries `background-image:none!important` from an earlier fix and fighting
   that with more !important is how specificity wars start.
   The existing ::after gradient stays and becomes the scrim - it is what keeps
   the h1 legible over a photograph, and it is measured, not assumed. */
.banner-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  z-index:-3;opacity:.5;filter:saturate(.7) contrast(1.02)}
@media (max-width:900px){.banner-bg{opacity:.42}}

/* The centre used to be darkened with a radial scrim and it read as exactly
   that - an obvious dark blob behind the heading. The fix is compositional:
   these plates are shot with a genuinely dark middle (measured at 1-18/255 in
   the band the h1 occupies) and their light at the edges, so white type is
   legible on the photograph itself. No vignette, no centre-weighting - just a
   light overall tint to seat the image in the page and a fade into the section
   below it. */
.banner::after{background:
  radial-gradient(120% 150% at 50% -20%, rgba(34,113,197,.10), transparent 60%),
  linear-gradient(180deg, rgba(11,14,19,.18) 0%, rgba(11,14,19,.22) 62%, var(--mbg) 100%)!important}
.banner-bg{opacity:.95}
@media (max-width:900px){.banner-bg{opacity:.85}}
