/* ==========================================================================
   Landing page — phone layout
   Figma "Landing Page" (1135:54801), 376 wide.

   Everything in this file is inside @media (max-width: 767px). Nothing here
   can reach a desktop, and nothing here touches the 768-1200 tablet reflow
   the section stylesheets already carry -- this is the phone design only.

   The frame is 376 with 20 gutters, so the content column is 336. Rather
   than scale off 376 (which would leave a 430 phone with oversized type),
   the gutter is fixed at 20 and the type is the frame's own sizes: headings
   28, sub-heads 20, body 14, mono 12. On a 390 or 430 screen the column
   simply grows and the type stays where it was drawn.
   ========================================================================== */

/* The phone layout, and -- since it is the same design at a larger size --
   the layout an iPad or any tablet gets when it is held upright. Turned on
   its side a tablet is wide enough for the 1920 composition, so it gets that
   instead; the reflow rules in the section files carry the same condition. */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1200px) and (orientation: portrait) {

  /* --- the phone canvas ------------------------------------------------
     Both frames are drawn on a 376 artboard, and everything below this is
     written in that artboard's pixels. On a wider phone Figma scales the
     whole artboard to the screen -- 392 shows it at 1.043, 430 at 1.14 --
     so a layout in fixed 376 pixels reads about 4% small on the phone the
     design was approved on, and 14% small on a large one. Measured against
     a screen recording at 392, every landmark sat at a constant 0.965 of
     where the frame puts it, which is 376/392.

     zoom makes the layout viewport 376 at any width and scales the result,
     which is what the frame itself does -- and what --s already does for
     the desktop canvas. CSS cannot divide a length into a plain number, so
     the ratio comes from tan(atan2(...)). Capped at 1.3 so a small tablet
     in portrait does not get a doubled page.

     The nav panel is reparented to <body>, outside .o-page, so it takes
     the same zoom or its close mark would not match the toggle. */

  .o-page[class="o-page"],
  .o-page--tech,
  .o-page--sol,
  .o-page--contact,
  .o-page--about,
  .o-page--resources,
  /* Uncapped: the cap only ever bit above 489, which is where tablets
     start -- and on a tablet held upright the point is that the 376 frame
     fills the screen rather than sitting small in the corner. */
  #ocularai-mnav { zoom: tan(atan2(100vw, 376px)); }

  /* vw is measured against the real viewport, not the zoomed one, so the
     few lengths written in it have to become artboard pixels instead. The
     CTA is one component on every page, so this is one rule -- it used to be
     four identical ones, listed page by page. */
  .cta__halo { width: 489px; height: 489px; }


  /* --- shared -------------------------------------------------------- */

  .o-page { --gut: 20px; }

  .o-page > main > .o-section { padding-left: var(--gut); padding-right: var(--gut); }
  /* The bands that must reach the screen edges take their padding back. */
  .o-page > main > .dots,
  .o-page > main > .marquee,
  .o-page > main > .backdrop,
  .o-page > main > .faqbg { padding-left: 0; padding-right: 0; }

  /* One type scale for the whole page, so the sections cannot drift apart
     the way they did when each set its own clamp(). */
  .hero__title h1,
  .human__h h2,
  .features__title h2,
  .industries__h span,
  .search__h h2, .search__h span,
  .faq__h h2, .faq__h span,
  .cta__h h2, .cta__h span {
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -0.84px;
  }
  .mono, .chip { font-size: 12px; letter-spacing: 0.04em; }

  /* --- hero ------------------------------------------------------------
     The sphere is first in the DOM -- it is the hero's backdrop on a
     desktop, painted behind everything -- but the frame puts it after the
     copy. Flex order moves it without touching the markup, which keeps the
     desktop composition exactly as it is.

     Frame: masthead 64, labels 141, rule 179, title 203, sphere 324 (336
     square), the closing line 684, the scroll cue 776. */

  .hero {
    display: flex;
    flex-direction: column;
    /* The frame is a fixed 7783 canvas -- letting the hero stretch to the
       viewport would move every section below it by a different amount on
       every phone, so the rhythm is the frame's, not the device's. */
    min-height: 0;
    padding-top: 0;
    padding-bottom: 54px;
  }
  .hero__masthead  { order: 1; }

  /* --- the header, on every page --------------------------------------
     The three heroes use different prefixes but render the same five
     children: the logo, the two desktop-only items (hidden at this
     width), the CTA and the menu toggle. So they take one definition,
     which is the home screen's.

     16 of padding rather than 24: the row is 44 tall because the tap
     target is, and the logo, the button and the menu bars all centre
     inside it -- 16 is what puts the visible top of all three at 24.

     The row is space-between, and with the nav and login hidden it has
     three items, which left the CTA stranded mid-row. An auto left
     margin collapses the free space to its left, so the CTA and the
     toggle read as a pair against the right edge. */

  .hero__masthead { padding-top: 16px; gap: 12px; }

  .hero__cta-top { margin-left: auto; }

  .hero__cta-top .btn { font-size: 14px; padding: 10px 16px; }

  .hero__logo { width: 120px; height: 29px; }
  .hero__body      { order: 2; }
  .hero__sphere    { order: 3; }
  .hero__wordmarks { order: 4; }
  .hero__scroll    { order: 5; }

  .hero__logo { width: 120px; height: 29px; }

  /* The two labels share a row, the second right-aligned. Sized by content:
     "NO REPLACEMENTS." is about 105 at this size, and a fixed column
     narrower than that had .mask clipping the word in half. */
  .hero__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 16px;
    margin-top: 33px;
  }
  .hero__eyebrow,
  .hero__nodisrupt {
    margin: 0;
    font-size: 10px;
    line-height: 1.45;
    letter-spacing: 0.03em;
    white-space: normal;
  }
  .hero__eyebrow   { grid-column: 1; }
  .hero__nodisrupt { grid-column: 2; text-align: right; }
  .hero__rule-long { grid-column: 1 / -1; margin: 6px 0 0; width: 100%; }
  .hero__title     { grid-column: 1 / -1; margin-top: 23px; }
  .hero__lede      { grid-column: 1 / -1; margin-top: 15px; }
  .hero__lede p    { font-size: 14px; line-height: 1.6; }
  /* Asked for: "potential" opening line two and "changing" opening line
     three. Measured across widths, 305-320 all break exactly there, so
     312 sits mid-window -- and because the phone canvas fixes the layout
     viewport at 376 on every device, that measurement holds. */
  .o-page[class="o-page"] .hero__lede p { max-width: 312px; }
  .hero__title h1  { font-size: 28px; line-height: 1.2; letter-spacing: -0.84px; }

  /* Not in the frame: the clock, the short rule, and the second CTA -- the
     masthead's Book a Demo is the only one on a phone. */
  .hero__clock, .hero__rule-short, .hero__cta, .hero__login { display: none; }

  /* 336 square in the frame, fluid here so it stays square on any phone. */
  .hero__sphere {
    position: relative;
    left: auto;
    top: auto;
    transform: none;
    width: 100%;
    max-width: 336px;
    aspect-ratio: 1;
    height: auto;
    margin: 11px auto 0;
    pointer-events: auto;
    /* The tablet rule dims it to 0.55 and floats it behind the copy; on the
       phone it is a figure in its own right, at full strength. */
    opacity: 1;
  }
  .hero.is-in .hero__sphere { transform: none; }
  .hero__sphere > img,
  .hero__sphere canvas { width: 100%; height: 100%; }

  .hero__wordmarks {
    display: block;
    /* The tablet rule holds 56 of top padding here; the frame's gap is the
       22 above, so the padding has to go or it lands 56 low. */
    padding-top: 0;
    margin-top: 22px;
    text-align: right;
  }
  /* base.css makes every img a block, so text-align cannot reach them --
     shrinking each line to its own width and letting the left margin go
     auto is what pushes the wordmarks to the right edge. */
  .hero__wordmarks .hero__item {
    display: block;
    width: fit-content;
    max-width: 100%;
    margin-left: auto;
    /* The images are blocks inside an inline wrapper, so the wrapper's own
       leading was adding ~28 a line on top of the artwork. */
    line-height: 0;
  }
  .hero__wordmarks .hero__item + .hero__item { margin-top: 12px; }
  .hero__wordmarks img { width: auto; max-width: 100%; height: 24px; }

  /* The tablet rule restores position:relative, which lets the desktop
     canvas's `top` offset apply again -- it was landing 1000px down the
     page, inside the product section. Clearing the offsets is the fix. */
  .hero__scroll {
    margin-top: 26px;
    font-size: 10px;
    width: auto;
    height: auto;
    top: auto;
    left: auto;
  }

  /* --- dots band -------------------------------------------------------
     Frame: four rows at 852 / 900 / 949 / 997, pitch 48 both ways, the
     first dot at x=30. The tablet rule scales the 1920 lattice by 0.5 and
     overrides the gap to 26, which lands five rows at a 41 pitch starting
     96 too low and spills them out of the band.

     The source pitch is 83, so 48/83 = 0.578 is the scale that makes the
     lattice come out at the frame's spacing; the translate then lifts row
     one to 12 inside the band, and a 180 band clips row five the way the
     frame does. Scoped to .dots so the Technology compatibility field --
     which reuses .dots__dot -- keeps its own geometry. */

  .dots { height: 180px; }
  .dots .dots__grid {
    transform: translateY(-113.4px) scale(0.578);
    transform-origin: 0 0;
    width: 200%;
    height: 200%;
  }
  /* cell + gap must total the source pitch of 83 for the scale to land on 48 */
  .dots .dots__row { gap: 67px; }
  .dots .dots__dot {
    width: 16px;
    height: 16px;
    border-radius: 5px;
    --dot-hit: 16;
  }
  .dots .dots__dot::before { border-radius: 9px; }
  .dots .dots__dot::after { width: 3.5px; height: 3.5px; margin: -1.75px 0 0 -1.75px; }

  /* --- product ---------------------------------------------------------
     Labels on one row at 1035, the monitor under them. */

  .product { padding-top: 20px; padding-bottom: 32px; }
  /* The two readouts share the row at 1035; the markup stacks them because
     the desktop canvas places each one absolutely. */
  .product {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    align-content: start;
  }
  .product > * { grid-column: 1 / -1; }
  .product__label-left  { grid-area: 1 / 1; }
  .product__label-right { grid-area: 1 / 2; margin-top: 0; text-align: right; }
  /* Frame: screen 1091-1274, stand to 1332. */
  .product__rig { grid-row: 2; margin-top: 24px; aspect-ratio: 336 / 241; }
  .product__label-left,
  .product__label-right { font-size: 10px; line-height: 1.5; }
  /* Two lines in the frame, so it is held to the width that breaks it. */
  .product__label-left { max-width: 150px; }

  /* --- trusted ---------------------------------------------------------
     The bracketed panel and the logo row are siblings -- on the desktop
     canvas both are placed against the bar. Dropping them into one grid
     cell puts the row back inside its own brackets. The frame keeps only
     the logos in the band, so the label steps out at this width. */

  .trusted { padding-top: 8px; padding-bottom: 8px; }
  .trusted__bar { display: grid; grid-template-columns: minmax(0, 1fr); }
  .trusted__bar > * { grid-area: 1 / 1; }
  .trusted__panel { padding: 0; }
  /* The padding carries the panel's 76 whatever the marks measure. */
  .trusted__row { padding: 25px 16px; gap: 0; }
  .trusted__label { display: none; }
  /* The marks were letterboxed, not merely small: the desktop rule pins
     each img's width, so a 112x31 logo in a 45-wide box scaled to fit the
     width and came out about 12 tall inside a 52 box. Freeing the width
     lets height alone size them, at their own aspect. */
  .trusted__logos img { width: auto; height: 25px; }   /* 30% off 36 */

  /* Right-to-left, continuously, clipped to the bracketed panel. The list
     is duplicated in the markup, and the track carries the same 40 gap
     between the copies as within them -- so the loop repeats every
     (one list + one gap), which is 50% of the track plus half a gap. */
  .trusted__logos { overflow: hidden; }
  .trusted__track {
    display: flex;
    gap: 40px;
    width: max-content;
    animation: ocularai-trusted 20s linear infinite;
  }
  .trusted__logos .trusted__clone { display: flex; }
  .trusted__logos ul { gap: 40px; flex: none; }

  @keyframes ocularai-trusted {
    from { transform: translateX(0); }
    to   { transform: translateX(calc(-50% - 20px)); }
  }

  /* --- human -----------------------------------------------------------
     The clip at 1476 (336x140), its two readouts under it, the copy, the
     CTA, then the two stacked cards. */

  .human { padding-top: 40px; padding-bottom: 48px; }
  /* +40% on the frame's 140. The image is object-fit: cover, so the box
     growing crops it rather than stretching it. */
  .human__shot { aspect-ratio: 336 / 196; }
  /* The frame keeps two readouts, both along the bottom -- the camera and
     timestamp block is a desktop-only flourish. */
  .human__readout { font-size: 10px; }
  .human__cam { display: none; }
  .human__detected { left: auto; right: 12px; top: auto; bottom: 12px; text-align: right; }
  .human__lede { margin-top: 24px; }
  .human__lede p { font-size: 12px; line-height: 1.5; }
  .human__cta { margin-top: 22px; }

  /* The three cards are drawn as a plate plus three loose siblings -- the
     markup is flat because on a desktop every one of them is absolutely
     placed against the section. Nesting them would move the containing
     block and break that, so the card is reassembled here instead: one
     grid column, and each plate spans the three rows its own chip,
     heading and copy occupy. The plate paints underneath; the margins on
     the items are what become the card's padding.

     Frame: the plates run 1795-1943-2077-2211, edge to edge with no gap. */
  .human {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
  }
  /* Auto-placement refuses to overlap, so a plate and the chip that sits on
     it would be pushed into a second column. Naming the column makes the
     placement explicit, and explicit placement is allowed to overlap. */
  .human > * { grid-column: 1; }
  .human__plate { grid-row: 1; }
  .human__lede  { grid-row: 2; }
  .human__cta   { grid-row: 3; }

  .human__panel--verified  { grid-row: 4 / span 3; }
  .human__chip--verified   { grid-row: 4; }
  .human__h--verified      { grid-row: 5; }
  .human__p--verified      { grid-row: 6; }

  .human__panel--hardware  { grid-row: 7 / span 3; }
  .human__chip--hardware   { grid-row: 7; }
  .human__h--hardware      { grid-row: 8; }
  .human__p--hardware      { grid-row: 9; }

  .human__panel--proactive { grid-row: 10 / span 3; }
  .human__chip--proactive  { grid-row: 10; }
  .human__h--proactive     { grid-row: 11; }
  .human__p--proactive     { grid-row: 12; }

  /* Type: eyebrow 10, heading 20, body 12. The eyebrow was coming out at
     20 because human.css sizes .human__chip .chip directly -- setting it
     on the wrapper never reached the text. Its box is a fixed 24 there
     too, which is why the gap under it read wrong. */
  .human__panel { margin-top: 0; padding: 0; }
  .human__panel--verified { margin-top: 48px; }

  .human__chip { height: auto; margin: 20px 16px 0; }
  .human__chip .chip { font-size: 10px; line-height: 1.5; letter-spacing: 0.04em; }
  .human__chip--verified { margin-top: 68px; }   /* 48 gap + the card's 20 */

  .human__h { margin: 46px 16px 0; }
  /* Regular, not the 300 Light the section sets -- 400 is the weight the
     helvetica-regular face is registered at in tokens.css. */
  .human__h h2 { font-size: 20px; line-height: 1.3; letter-spacing: -0.3px; font-weight: 400; }

  .human__p { margin: 14px 16px 22px; }
  .human__p p { font-size: 12px; line-height: 1.5; }
  /* The fill is the plate's own animated wash -- it still grows from the
     top on reveal, but it has to end at the rows the plate now spans
     rather than at the 351 it was drawn at. */
  .human__panel--proactive .human__panel-fill { inset: 0 0 auto 0; height: 14px; }
  .human.is-in .human__panel--proactive .human__panel-fill { height: 100%; }

  /* --- section order ----------------------------------------------------
     The frame runs the ticker between the feature cards and Industries;
     the markup has it after Industries, because that is where the desktop
     canvas puts it. Ordering the flex children moves it without touching
     the markup -- and the exact class match keeps this to the front page,
     where these fifteen sections are the only ones in `main`. */

  .o-page[class="o-page"] > main { display: flex; flex-direction: column; }
  .o-page[class="o-page"] > main > .hero        { order: 1; }
  .o-page[class="o-page"] > main > .dots        { order: 2; }
  .o-page[class="o-page"] > main > .product     { order: 3; }
  .o-page[class="o-page"] > main > .trusted     { order: 4; }
  .o-page[class="o-page"] > main > .human       { order: 5; }
  .o-page[class="o-page"] > main > .backdrop    { order: 6; }
  .o-page[class="o-page"] > main > .features    { order: 7; }
  .o-page[class="o-page"] > main > .marquee     { order: 8; }
  .o-page[class="o-page"] > main > .industries  { order: 9; }
  .o-page[class="o-page"] > main > .search      { order: 10; }
  .o-page[class="o-page"] > main > .testimonial { order: 11; }
  /* The repeated band -- it matches .dots too, so without its own order it
     would stack onto the first one up at order 2. */
  .o-page[class="o-page"] > main > .testimonial ~ .dots { order: 12; }
  .o-page[class="o-page"] > main > .faqbg       { order: 13; }
  .o-page[class="o-page"] > main > .faq         { order: 14; }
  .o-page[class="o-page"] > main > .cta         { order: 15; }
  .o-page[class="o-page"] > main > .footer      { order: 16; }

  /* --- features --------------------------------------------------------
     Frame: the light plate 2307-3460, the eyebrow at 2400, the heading
     2440, two paragraphs, then the cards from 2707 with an 8 gap. Only
     the open card carries its icon and its copy. */

  .features { padding-top: 44px; padding-bottom: 44px; }
  /* 10 is the one size every eyebrow on the phone uses. */
  .features__eyebrow { font-size: 10px; }
  .features__title { margin-top: 18px; }
  .features__title h2 { font-size: 28px; letter-spacing: -0.84px; max-width: 310px; }
  .features__copy { margin-top: 18px; }
  .features__copy p { font-size: 13px; line-height: 1.6; }
  .features__glow { display: none; }

  .features__row { margin-top: 30px; gap: 8px; }
  .f-card { position: relative; padding: 18px 16px 20px; }

  /* Arrival: in from the left, each card on its own trigger.

     Two things were wrong before. The desktop grows each card from 46 to
     700 and the 1200 reflow pins height to auto, so nothing moved; and
     the reveal is section-level, which on a phone fires once while the
     lower cards are still hundreds of pixels below the fold -- by the
     time you scrolled to them they had long since settled. Each card now
     carries its own data-reveal, so it plays as it arrives.

     The section rule still sets opacity on every card the moment the
     section lands, so it is overridden here and the card's own is-in
     carries both the fade and the move. */
  .features .f-card {
    transform: translateX(-36px);
    transition:
      opacity calc(var(--rv-dur) * 0.5) var(--rv-ease),
      transform calc(var(--rv-dur) * 0.6) var(--rv-ease);
  }
  .features.is-in .f-card { opacity: 0; }
  .features .f-card.is-in { opacity: 1; transform: none; }
  .f-card__num { font-size: 10px; }
  /* The closed cards are a list of titles; only the open one is a card in
     the full sense, so the type steps up when it opens. */
  .f-card__title { margin-top: 30px; font-size: 14px !important; line-height: 1.35; }
  .f-card.is-open .f-card__title { font-size: 18px !important; margin-top: 18px; }
  .f-card__body { margin-top: 10px; }
  .f-card__body span { font-size: 12px; line-height: 1.6; }

  /* The icon comes back for the open card only, pulled up beside the F/0n
     label the way the frame draws it. */
  .f-card.is-open .f-card__mark {
    display: block;
    width: 84px;
    height: 84px;
    margin: -22px 0 0 auto;
  }
  .f-card.is-open .f-card__mark img,
  .f-card.is-open .f-card__mark svg { width: 100%; height: 100%; }

  /* --- marquee ---------------------------------------------------------
     58 tall on the desktop canvas, which --s shrinks to nothing here. */

  /* The ticker sits on the grey backdrop on a desktop, and that backdrop is
     not drawn at this width -- without its own fill the dark type would be
     invisible on the dark page. */
  .marquee { height: 78px; padding: 0; background: var(--c-light-grey); }
  .marquee__item { font-size: 20px; letter-spacing: -0.6px; }
  /* The arrow glyph sits on the font's own centre, which is below the
     optical centre of cap-height words -- measured 2.5px low at 20px.
     vertical-align shifts it without changing the advance width, which
     the -50% loop depends on. */
  .marquee__arw { vertical-align: 0.125em; }
  .marquee__item, .marquee__sep { margin-right: 32px; }
  .marquee__sep { width: 12px; height: 18px; }

  /* --- industries ------------------------------------------------------
     Frame: the eyebrows at 3616, the heading 3676, the copy and the CTA
     indented to 85, then the tabs at 3945 and the rail under them. The
     markup leads with the tabs because that is where the desktop canvas
     needs them, so the order is set here. */

  .industries { padding-top: 40px; padding-bottom: 44px; }
  .industries {
    display: flex;
    flex-direction: column;
  }
  .industries__note { order: 1; }
  .industries__h--1 { order: 2; }
  .industries__h--2 { order: 3; }
  .industries__copy { order: 4; }
  .industries__cta  { order: 5; }
  .industries__tabs { order: 6; }
  .industries__rail { order: 7; }

  .industries__note {
    display: block;
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    padding-inline: 0;
    font-size: 10px;
    line-height: 1.5;
    /* the Features eyebrow's colour, not the grey this had */
    color: var(--c-mono-dark);
  }
  /* The frame sets these side by side on one row: "EVERY SITE OPERATES /
     DIFFERENTLY." at 20 and "OCULAR ADAPTS." at 196. One element holds
     both, split by <br><br>, so the reveal span becomes the row and the
     breaks go. */
  .industries__note .rv { display: grid; grid-template-columns: 176px auto; }
  .industries__note br { display: none; }
  /* Two lines, breaking after "operates". At 10px that needs 115 or
     more; 140 sits mid-window and still clears the 176 column. */
  .industries__note-a { grid-area: 1 / 1; max-width: 140px; }
  .industries__note-b { grid-area: 1 / 2; }
  .industries__h { padding-inline: 0; }
  .industries__h--1 { margin-top: 26px; }
  .industries__h span, .industries__h h2 { font-size: 28px; letter-spacing: -0.84px; }
  .industries__copy { padding-inline: 0 0; padding-left: 64px; margin-top: 18px; }
  .industries__copy p { font-size: 13px; line-height: 1.6; }
  .industries__cta { padding-inline: 0; margin-top: 22px; margin-left: 64px; }

  /* The tabs run off the right edge, the way a scroller should read. */
  .industries__tabs {
    margin: 30px calc(var(--gut) * -1) 0 0;
    padding: 0;
  }
  /* Desktop gives every chip flex: 1 1 0, so all eight are one width
     whatever their label. The 1200 reflow drops that to flex: none and
     they go back to sizing on their text -- 78 to 123 here. The strip
     scrolls, so equal width means a fixed one: the longest label
     ("Corporate Campus") measures 95, and 130 with 8 of padding clears
     it while keeping every chip identical. */
  .industries__tab { font-size: 12px; padding: 9px 8px; flex: 0 0 130px; }
  .industries__rail { margin-top: 14px; aspect-ratio: 1 / 1; }

  /* --- search ----------------------------------------------------------
     Frame: one clip at 4438 (250x152, bracketed), its two readouts under
     it, then the eyebrow, heading, copy and CTA all centred. The markup
     order is the desktop canvas's, so the rows are named here. */

  .search { padding-top: 65px; padding-bottom: 70px; }
  .search {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    align-content: start;
  }
  .search > * { grid-column: 1 / -1; }

  .search__frame--a { grid-row: 1; }
  .search__readout--a { grid-row: 2; grid-column: 1; }
  .search__readout--b { grid-row: 2; grid-column: 2; }
  .search__chip { grid-row: 3; }
  .search__h--1 { grid-row: 4; }
  .search__h--2 { grid-row: 5; }
  .search__copy { grid-row: 6; }
  .search__cta  { grid-row: 7; }

  .search__frame--b, .search__frame--c { display: none; }
  .search__frame--a {
    max-width: 250px;
    aspect-ratio: 250 / 152;
    justify-self: start;
  }
  /* The corner marks live in the markup on the third frame only, so the
     one frame the phone keeps draws its own -- eight hairlines, no extra
     elements, nothing for the desktop to inherit. */
  .search__frame--a::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    --brk: var(--c-yellow, #f5c518);
    --len: 12px;
    background:
      linear-gradient(var(--brk), var(--brk)) 0 0 / var(--len) 1px no-repeat,
      linear-gradient(var(--brk), var(--brk)) 0 0 / 1px var(--len) no-repeat,
      linear-gradient(var(--brk), var(--brk)) 100% 0 / var(--len) 1px no-repeat,
      linear-gradient(var(--brk), var(--brk)) 100% 0 / 1px var(--len) no-repeat,
      linear-gradient(var(--brk), var(--brk)) 0 100% / var(--len) 1px no-repeat,
      linear-gradient(var(--brk), var(--brk)) 0 100% / 1px var(--len) no-repeat,
      linear-gradient(var(--brk), var(--brk)) 100% 100% / var(--len) 1px no-repeat,
      linear-gradient(var(--brk), var(--brk)) 100% 100% / 1px var(--len) no-repeat;
  }

  .search__readout { margin: 40px 24px 0 0; font-size: 10px; line-height: 1.5; }
  .search__chip { margin-top: 60px; justify-self: center; }
  .search__h--1 { margin-top: 22px; }
  .search__h--2 { margin-top: 0; }
  .search__h h2, .search__h span { font-size: 28px; letter-spacing: -0.84px; text-align: center; }
  .search__copy { margin-top: 20px; }
  .search__copy p { font-size: 13px; line-height: 1.6; text-align: center; }
  .search__cta { margin-top: 24px; justify-self: center; }

  /* --- testimonial ------------------------------------------------------
     Frame: a hairline frame opening at 5080, the eyebrow on two lines
     indented to 36, the card 5220-5432, then the two 48 circles. */

  .testimonial { padding-top: 85px; padding-bottom: 30px; }

  /* The tablet rule drops the whole rule set; the frame keeps the outer
     three, so those come back on mobile coordinates and the rest stay off.
     .testimonial__dots shares that rule and is left hidden. */
  .testimonial__rule { display: block; }
  .testimonial__rule--h2,
  .testimonial__rule--h3,
  .testimonial__rule--h4,
  .testimonial__rule--v2,
  .testimonial__rule--v3 { display: none; }
  .testimonial__rule--h1 { left: 20px; top: 20px; width: 336px; }
  .testimonial__rule--v1 { left: 20px;  top: 20px; height: 360px; }
  .testimonial__rule--v4 { left: 355px; top: 20px; height: 360px; }

  .testimonial__eyebrow { font-size: 10px; line-height: 1.6; max-width: 140px; padding-left: 16px; }
  .testimonial__card { margin-top: 46px; padding: 24px 16px 32px; }
  .testimonial__brand img { width: 20px; height: 22px; }
  .testimonial__brand span { font-size: 15px; }
  .testimonial__quote { margin-top: 14px; font-size: 12px; line-height: 1.5; }
  /* The avatar keeps the desktop's 64 because the tablet rule only resets
     its parent -- at this width the frame draws it at 36. */
  .testimonial__author { margin-top: 18px; justify-content: flex-start; gap: 12px; }
  .testimonial__avatar { width: 36px; height: 36px; overflow: hidden; }
  .testimonial__author-name { font-size: 13px; }
  .testimonial__author-role { font-size: 11px; }
  /* Prev/Next squeeze the card toward one set of brackets on the desktop,
     by running its width from 634 down to a sliver. At this width the card
     is a full-width block, so width cannot carry the motion -- a scaleX
     from the anchored edge is the same gesture, and composited. --collapse
     and --anchor are the numbers the carousel already sets; the contents
     clear at the desktop's 2.2 rate so nothing crushes while it closes. */
  .o-page[class="o-page"] .testimonial__card {
    transform-origin: calc(var(--anchor, 0) * 100%) center;
  }
  .o-page[class="o-page"] .testimonial.is-in .testimonial__card {
    transform: scaleX(calc(1 - 0.97 * var(--collapse, 0)));
  }
  .o-page[class="o-page"] .testimonial__card.is-settling {
    transition: transform var(--close-ms, 620ms) var(--close-ease, var(--rv-ease));
    transition-delay: 0s;
  }
  .o-page[class="o-page"] .testimonial__card.is-dragging {
    transition: none;
    will-change: transform;
  }
  .o-page[class="o-page"] .testimonial__card > *:not(.brk__c) {
    opacity: calc(1 - var(--collapse, 0) * 2.2);
  }

  .testimonial__navs { gap: 16px; margin-top: 30px; }
  .testimonial__nav { width: 48px; height: 48px; font-size: 12px; }

  /* The frame's second dots band, between the testimonial and the FAQ.
     dots.css keeps it off everywhere else. It is the shorter of the two --
     three rows at 5552/5591/5634 against the FAQ plate at 5706, so 110
     with 56 under it rather than the first band's 180 and nothing. */
  .testimonial ~ .dots { display: block; height: 110px; margin-bottom: 56px; }

  /* --- faq -------------------------------------------------------------
     Frame: the three mono labels on one row at 5690, the rule under them,
     then the heading with Contact Us sitting out to its right. */

  /* Left, right and top all 20 -- the gutter already sets the sides. */
  .faq { padding-top: 20px; padding-bottom: 44px; }
  /* Three measured columns rather than a wide flexible middle: FAQs and
     the date sit a gap apart instead of the date floating centre-stage,
     and "Questions, answered" holds the right edge. */
  .faq {
    display: grid;
    grid-template-columns: max-content max-content minmax(0, 1fr);
    column-gap: 20px;
    align-content: start;
  }
  .faq > * { grid-column: 1 / -1; }

  .faq__meta--a { grid-area: 1 / 1; }
  /* The frame reads FAQs / Last updated / Questions, answered; the markup
     is in the desktop canvas's order, where b is the middle column. */
  .faq__meta--c { grid-area: 1 / 2; text-align: left; }
  .faq__meta--b { grid-area: 1 / 3; text-align: right; }
  .faq__rule { grid-row: 2; }
  .faq__h--1  { grid-row: 3; }
  .faq__h--2  { grid-row: 4; }
  /* Beside the heading block, not above it: the button spans both
     heading rows and sits against their foot, as the design has it. */
  .faq__cta   { grid-area: 3 / 3 / span 2 / auto; justify-self: end; align-self: end; }
  .faq__list  { grid-row: 5; }

  /* The Features and Industries eyebrows' colour, not the grey the
     section sets. Size already matched at 10. */
  .faq__meta { margin-top: 0; font-size: 10px; color: var(--c-mono-dark); }
  .faq__rule { margin: 14px 0 0; }
  .faq__h--1 { margin-top: 22px; }
  /* The 1200 reflow adds 20 between the two lines; the design sets them
     as one block, so the pitch is just the line box. */
  .faq__h--2 { margin-top: 0; }
  .faq__h span, .faq__h h2 { font-size: 28px; letter-spacing: -0.84px; }
  .faq__cta { margin-top: 0; }
  .faq__cta .btn { font-size: 11px; padding: 8px 14px; }
  .faq__list { margin-top: 64px; }
  /* Frame: 54 for a one-line row, 76 for two -- the build was running 69
     and 87, which is 199 over ten rows and was pushing the CTA down. */
  .faq__q { padding-top: 8px; padding-bottom: 8px; }
  /* Sized off the reference: its question ink measures 15 against the
     13 here, and its answer 11 against 10. */
  .faq__qt { font-size: 16px; line-height: 1.35; }
  /* 22 + 6 puts the question 28 off the column edge, as the design
     does -- the pair was reading as two separate columns at 28 + 24. */
  .faq__q { gap: 6px; }
  .faq__n { width: 22px; font-size: 10px; }
  .faq__a { padding-bottom: 18px; }
  .faq__a p { font-size: 12px; line-height: 1.5; }

  /* --- cta -------------------------------------------------------------- */

  /* The whole block is driven off the dome rather than off the section box.
     The tablet reflow hangs .cta__halo from the section's mid-line, so its
     position moved with every padding change; pinning it to the top edge
     makes the two things the frame actually fixes -- 48 clear of the FAQ
     above it, and the text centred on the dome -- independent of each
     other and of the space left underneath.

     translate()'s Y goes to 0 to match, in both the resting and the
     not-yet-revealed state (the 216 rise is cta.css's entrance). */
  .cta__halo { top: 48px; transform: translate(-50%, 0); }
  .cta:not(.is-in) .cta__halo { transform: translate(-50%, 0) translateY(216px); }

  /* padding-top centres eyebrow..button on the dome's centre (48 + 244.5);
     padding-bottom is the 35%-trimmed gap to the footer plus 50%. */
  .cta { padding-top: 212px; padding-bottom: 139px; }
  .cta__h h2, .cta__h span { font-size: 32px; letter-spacing: -0.96px; text-align: center; }
  .cta__eyebrow { text-align: center; font-size: 10px; }
  .cta__h--2 { margin-top: 0; }
  .cta__btn-wrap, .cta__cta, .cta__btn { display: flex; justify-content: center; }

  /* --- footer ----------------------------------------------------------
     Frame: the mark and the newsletter share the top row, the two link
     columns sit under them on a 95 gutter, then Back to top, the wordmark
     and the legal line. */

  .footer { padding-top: 40px; padding-bottom: 28px; }
  .footer {
    display: grid;
    grid-template-columns: 95px minmax(0, 1fr);
    align-content: start;
  }
  .footer > * { grid-column: 1 / -1; }
  .footer__mark { grid-area: 1 / 1; }
  .footer__cols { grid-row: 1 / span 2; grid-column: 1 / -1; margin-top: 0; }
  /* Back to top shares the columns band rather than taking a row of its
     own: the frame sets it on the last line of the Company column, hard
     right. Explicit placement is what lets it overlap .footer__cols. */
  .footer__top       { grid-row: 1 / span 2; grid-column: 2; align-self: end; justify-self: end; }
  .footer__wordmark  { grid-row: 3; }
  .footer__rule      { grid-row: 4; }
  .footer__legal-bar { grid-row: 5; }

  .footer__cols {
    display: grid;
    grid-template-columns: 95px minmax(0, 1fr);
    gap: 55px 0;
  }
  .footer__news { grid-area: 1 / 2; }
  .footer__cols > .footer__col:nth-child(1) { grid-area: 2 / 1; }
  .footer__cols > .footer__col:nth-child(2) { grid-area: 2 / 2; }

  .footer__label { font-size: 10px; margin-bottom: 12px; }
  .footer__link { font-size: 13px; margin-bottom: 10px; }
  .footer__field input { height: 30px; font-size: 11px; }
  /* The frame stacks the form: the field runs the full width of the
     newsletter column and Subscribe drops underneath it, flush right. The
     desktop canvas positions the button absolutely off a scaled `left`, so
     both the positioning and that offset have to be unwound. */
  .footer__news { position: relative; padding-right: 0; }
  .footer__subscribe { position: static; left: auto; width: 80px; height: 28px; margin: 9px 0 0 auto; }
  .footer__subscribe .btn { width: 100%; height: 100%; font-size: 10px; padding: 0; }

  /* Same mono face as the labels it sits level with, not the body sans. */
  .footer__top { margin: 0 0 12px; text-align: right; font-size: 9px; font-family: var(--f-tech); font-weight: 300; }
  .footer__wordmark { margin-top: 37px; }
  .footer__rule { margin-top: 15px; }

  /* Three fixed columns, not space-between: the frame pins Privacy policy
     just past a fifth of the width, which is close enough to Terms that
     Terms takes two lines. */
  .footer__legal-bar {
    display: grid;
    grid-template-columns: 73px max-content minmax(0, 1fr);
    align-items: start;
    gap: 0;
    margin-top: 13px;
  }
  .footer__legal { font-size: 8px; }
  .footer__legal--terms { white-space: normal; max-width: 62px; }
  .footer__legal--copy { width: auto; text-align: right; }

  /* ======================================================================
     Technology page -- phone layout
     Figma "Technology" (1158:132517), 376 wide. The frame is 8922 tall but
     its content stops at 5049; everything below that is empty canvas.

     Scoped to .o-page--tech so none of it reaches the front page, whose own
     rules are above. The shared sections at the foot -- the dots band, the
     CTA and the footer -- are already handled there and are left alone.
     ====================================================================== */

  /* --- tech hero -------------------------------------------------------
     Frame: masthead 64, "Monitoring across" on one line at 160, the dot
     field 218-428, then "cameras you already own" right-aligned at 452
     beside the two mono labels at 490 and 534, and the console at 618.

     The heading is four separate elements because the desktop canvas
     places each line on its own coordinate. Here they pair up: l1 and l2
     share one row to read as a sentence, r1/r2 sit in the right column
     opposite the labels. */

  /* The frame keeps the blue bloom the 1200 reflow drops. It is the first
     thing in the frame's own order, so it belongs under everything: the
     hero takes a stacking context and the glow sits at the bottom of it.
     The frame draws the asset at its own size rather than as a window on
     to it, so the inner crop the 1920 canvas uses is unwound. */
  .o-page--tech .tech-hero { position: relative; }
  .o-page--tech .tech-hero__glow {
    display: block;
    position: absolute;
    /* The frame's own box, verbatim: -294 / 406, 989.243 x 616.241. The
       inner crop is NOT touched -- the frame keeps the same -19.7% /
       -5.17% / 138.2% / 110.35% window on to the asset that the 1920
       canvas uses, and that window is what holds the asset's own edges
       outside the box. What the frame adds at this size is a 17.89 blur
       over the whole thing, which is what keeps the bloom from ending on
       a line. */
    /* The hero is a grid, so an abspos child with a definite column measures
       its left from the GRID AREA, not the padding box -- 20 in from the
       frame edge. Every x here is the frame's own, less that 20. */
    left: -314px;
    top: 406px;
    width: 989.243px;
    height: 616.241px;
    filter: blur(17.89px);
    z-index: -1;
  }

  /* The two cut-outs. Both sit at z-index -1 like the bloom and come after
     it, so they paint over it and still under everything the reader looks
     at. Each SVG carries its own 74.68 blur, which is why the file is
     ~149 bigger than the shape on every side -- the frame's -12.46% /
     -24.37% insets are that overhang, so the box is offset by it rather
     than the shape being scaled to fit. */
  .o-page--tech .tech-hero__shade {
    display: block;
    position: absolute;
    /* -20 for the same reason as the bloom, so the two shapes inside can be
       written at the frame's own x. */
    left: -20px;
    right: -20px;
    top: 0;
    bottom: 0;
    z-index: -1;
    pointer-events: none;
  }
  .o-page--tech .tech-hero__shade img { position: absolute; max-width: none; }
  /* frame -421 / 238, less the blur overhang */
  .o-page--tech .tech-hero__shade-a { left: -570.3px; top: 88.6px; width: 1497.1px; height: 911.71px; }
  /* frame -360.99 / 419, less the overhang, and mirrored */
  .o-page--tech .tech-hero__shade-b { left: -510.4px; top: 269.6px; width: 1399.62px; height: 1085.72px; transform: scaleY(-1); }

  .o-page--tech .tech-hero {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    align-content: start;
    column-gap: 9px;
    padding-top: 0;
    /* The frame leaves 110 between the console and the first rule. */
    padding-bottom: 110px;
  }
  .o-page--tech .tech-hero > * { grid-column: 1 / -1; }
  .o-page--tech .hero__masthead { grid-row: 1; }
  .o-page--tech .tech-hero__h--l1 { grid-area: 2 / 1; }
  .o-page--tech .tech-hero__h--l2 { grid-area: 2 / 2; justify-self: start; }
  .o-page--tech .tech-hero__dotgrid { grid-row: 3; }

  /* The frame runs "cameras" and "you already own" as one two-line block at
     408-476, with the labels beside it on their own rhythm: ONE ON-SITE
     COMPUTER at 446-470 and ANY BRAND at 490. The first label therefore
     straddles both heading lines rather than pairing with one, so it takes
     both rows and hangs off their bottom edge. */
  .o-page--tech .tech-hero__h--wide { display: none; }
  .o-page--tech .tech-hero__h--narrow { display: block; }
  .o-page--tech .tech-hero__h--m1 { grid-area: 4 / 2; }
  .o-page--tech .tech-hero__h--m2 { grid-area: 5 / 2; }
  .o-page--tech .tech-hero__label--a { grid-area: 4 / 1 / span 2 / auto; align-self: end; }
  .o-page--tech .tech-hero__label--b { grid-area: 6 / 1; }
  .o-page--tech .tech-hero__stage { grid-row: 7; }


  .o-page--tech .tech-hero__h { margin: 0; }
  .o-page--tech .tech-hero__h--l1 { margin-top: 56px; }
  .o-page--tech .tech-hero__h--l2 { margin-top: 56px; }
  .o-page--tech .tech-hero__h span {
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -1.12px;
  }
  /* The reveal span is an inline-block that hugs its text, so text-align on
     it does nothing -- it has to go on the box that holds it. */
  .o-page--tech .tech-hero__h--m1,
  .o-page--tech .tech-hero__h--m2 { text-align: right; }
  .o-page--tech .tech-hero__h--m1 span,
  .o-page--tech .tech-hero__h--m2 span { text-align: right; }
  .o-page--tech .tech-hero__h--m1 { margin-top: 24px; }

  /* The field is off at 1200 because it is a 1920 backdrop there; the frame
     makes it a 336x210 figure in the flow, and the canvas is 787x492 --
     the same 1.6 -- so it only needs a box. */
  .o-page--tech .tech-hero__dotgrid {
    display: block;
    position: static;
    left: auto;
    top: auto;
    transform: none;
    opacity: 1;
    width: 100%;
    height: auto;
    aspect-ratio: 787 / 492;
    margin-top: 24px;
  }
  .o-page--tech .tech-hero.is-in .tech-hero__dotgrid { transform: none; }
  .o-page--tech .tech-hero__dotgrid canvas { width: 100%; height: 100%; }

  /* 1.2 rather than 1.5: the frame's two label lines are 12 apart, which is
     what puts the pair's foot 6 above the heading's. */
  .o-page--tech .tech-hero__label--a {
    margin: 0 0 6px;
    max-width: 72px;
    font-size: 10px;
    line-height: 1.2;
  }
  .o-page--tech .tech-hero__label--b { margin-top: 14px; font-size: 10px; line-height: 1.2; }

  /* The console cannot simply scale down here. Every piece's SIZE and its
     TOP are a straight 0.283 of the 1600-wide composition -- checked piece
     by piece against the frame, and they agree to a tenth of a px -- but
     the frame pulls the things beside the console inwards, so that the
     whole assembly is 355 wide instead of the 453 a uniform scale gives.
     So: scale by the console, then re-place the rest horizontally.

     --s is set from the console's own width (1100 -> 311.34) rather than
     from the stage's, since that is the piece the frame sizes. */
  .o-page--tech .tech-hero__stage {
    --s: 0.283036px;
    width: 355.25px;
    height: 272px;
    margin: 72px 0 0 -10px;
  }

  /* Frame x, stage-relative. The security card and the events bar sit on
     the stage's left edge and the actions card on its right, exactly as
     they sit on the 1600 composition's edges; the console and the four
     blurred plates move with them. */
  .o-page--tech .thero__console    { left: 22.76px; }
  .o-page--tech .thero__veil--a    { left: 19.44px; }
  .o-page--tech .thero__w--events  { left: 0; }
  .o-page--tech .thero__veil--d    { left: 144.07px; }
  .o-page--tech .thero__w--live    { left: 291px; }
  .o-page--tech .thero__veil--c    { left: 354.68px; }
  .o-page--tech .thero__w--actions { left: 264.68px; top: 145px; }
  .o-page--tech .thero__veil--b    { left: 246px; }

  /* Everything drawn ON the console -- the playing frame, its caption,
     scrim and controls, and the filmstrip -- is placed in stage
     coordinates, so it has to travel the same 48 the console just did or
     it slides off the front of it. The strip keeps the composition's full
     width because its four thumbs are placed inside it. */
  .o-page--tech .thero__feed,
  .o-page--tech .thero__feed-scrim,
  .o-page--tech .thero__feed-ui { left: calc(282 * var(--s) - 48px); }
  .o-page--tech .thero__feed-cam { left: calc(305 * var(--s) - 48px); }
  .o-page--tech .thero__strip { left: -48px; width: calc(1600 * var(--s)); }

  /* The console's readouts are written max(11px, N * --s) so they stay
     legible while the stage is large. At phone scale --s is 0.22, so the
     floor wins and a 3px label renders at 11 -- three times its place in
     the composition. Below 768 the scaled size stands on its own. */
  .o-page--tech .thero__feed-cam { font-size: calc(14 * var(--s)); }

  /* --- how it works ----------------------------------------------------
     Figma 1161:141210 / 141522 and friends, frame y 956 -> 1642.

     The 1200 reflow turns this into a stack with a swipe strip; the frame
     keeps the whole 1920 idea -- the hairline grid, the bracketed cell, the
     Scroll badge with its lead line, and a 2591-wide rail of five staggered
     cards that the page scroll walks from right to left. So the reflow is
     held to the tablet band (tech-howitworks.css) and the frame is rebuilt
     here from its own numbers.

     Every figure below is the frame's, measured from the section's top at
     956: rules at 1006 / 1138 / 1350 / 1482, verticals x20.6 and x355.6,
     the cell 20.53 / 1138.35 at 335.15 x 211.45, the eyebrow at 1057, the
     title at 1233, and the rail group at x273 / 1179, 2591 x 195.49. */

  /* 526 of band plus the 160 the frame leaves before Search. The gutter has
     to come off at the same specificity the shared rule sets it
     (.o-page > main > .o-section), or every frame x below lands 20 out --
     the grid, the cell and the rail all run to the frame's edges here. */
  .o-page--tech .tech-hiw { position: relative; height: 686px; }
  .o-page--tech > main > .tech-hiw { padding: 0; }

  /* The pinned scrub, as on the 1920 canvas: ui.js measures the rail's
     travel, the section grows by it, the stage sticks, and scroll position
     inside the runway drives the track. */
  .o-page--tech .tech-hiw__pin { position: relative; width: 100%; height: 686px; }
  .o-page--tech .tech-hiw.has-pin { height: calc(686px + var(--hiw-runway, 0px)); }
  .o-page--tech .tech-hiw.has-pin .tech-hiw__pin { position: sticky; top: var(--hiw-top, 0px); }
  .o-page--tech .tech-hiw.has-pin .tech-hiw__track { cursor: default; }

  /* --- the hairline grid ---
     The rules draw themselves in on reveal, and those open sizes are the
     1920 canvas's -- 1823 wide and 899 tall -- written at (0,3,0), which a
     plain .o-page--tech rule cannot reach. So the verticals grew past the
     bottom rule and ran on into the Search heading. The frame's own
     lengths have to be written on `.is-in` as well as at rest. */
  .o-page--tech .tech-hiw__rule { display: block; }
  .o-page--tech .tech-hiw__rule--h { left: 0; width: 100%; height: 1px; }
  .o-page--tech .tech-hiw.is-in .tech-hiw__rule--h { width: 100%; }
  .o-page--tech .tech-hiw.is-in .tech-hiw__rule--v { height: 476px; }
  .o-page--tech .tech-hiw__rule--h1 { top: 50px; }
  .o-page--tech .tech-hiw__rule--h2 { top: 182px; }
  .o-page--tech .tech-hiw__rule--h3 { top: 394px; }
  .o-page--tech .tech-hiw__rule--h4 { top: 526px; }
  .o-page--tech .tech-hiw__rule--v { top: 50px; width: 1px; height: 476px; }
  .o-page--tech .tech-hiw__rule--v1 { left: 20.6px; }
  .o-page--tech .tech-hiw__rule--v2 { left: 355.6px; }
  .o-page--tech .tech-hiw__rule--v3,
  .o-page--tech .tech-hiw__rule--v4 { display: none; }

  /* The cell opens out as the section arrives, so its open state is written
     on `.is-in` -- which is (0,3,0) on the 1920 canvas, so the phone's has
     to be at least that specific or the canvas's own numbers win. Rest is
     the frame's box scaled about its own centre by the same ratio the 1920
     canvas uses (42.3% wide, 16.5% tall). */
  .o-page--tech .tech-hiw__cell {
    display: block;
    position: absolute;
    left: 117px;
    right: 117px;
    top: 270px;
    width: auto;
    height: 35px;
  }
  .o-page--tech .tech-hiw.is-in .tech-hiw__cell {
    left: 20.5px;
    right: 20.35px;
    top: 182.35px;
    height: 211.45px;
  }
  .o-page--tech .tech-hiw__cell .brk__c { width: 6.34px; height: 6.34px; }

  /* --- eyebrow and title --- */
  .o-page--tech .tech-hiw__eyebrow {
    position: absolute;
    left: 37px;
    top: 101px;
    width: 118px;
    margin: 0;
    font-size: 10px;
    line-height: 1.2;
  }
  .o-page--tech .tech-hiw__title {
    position: absolute;
    left: 135px;
    top: 277px;
    width: auto;
    margin: 0;
    z-index: 2;
    pointer-events: none;   /* the rail passes through this */
  }
  .o-page--tech .tech-hiw__title h2 { font-size: 20px; line-height: 1.2; letter-spacing: -0.8px; }

  /* --- the rail ---------------------------------------------------------
     A window, not a scroller: the JS moves the track inside it. Full bleed,
     so cards enter and leave at the screen edges rather than at the gutter.
     The track carries the frame's 273 of lead, which is what puts the
     Scroll badge in view at rest with everything else off to the right. */
  .o-page--tech .tech-hiw__rail {
    position: absolute;
    left: 0;
    right: 0;
    top: 223px;
    height: 195.49px;
    margin: 0;
    overflow: hidden;
    z-index: 3;
  }
  .o-page--tech .tech-hiw__rail-in {
    position: relative;
    width: 100%;
    height: 100%;
    transform: none;
  }
  .o-page--tech .tech-hiw__track {
    display: block;
    position: relative;
    margin: 0 0 0 273px;
    width: 2591px;
    height: 100%;
  }
  /* Switches every inline `left` over to the frame's x -- see the template. */
  .o-page--tech .tech-hiw__card,
  .o-page--tech .tech-hiw__zig { --hiw-x: var(--xm); }

  /* Scroll badge: 68 square at the track's own origin, 30 down. */
  .o-page--tech .tech-hiw__scroll {
    display: flex;
    position: absolute;
    left: 0;
    top: 30px;
    align-items: center;
    justify-content: center;
    width: 68px;
    height: 68px;
    margin: 0;
    padding: 0;
    border: 0.567px solid var(--c-mono-light);
    border-radius: 34px;
    background: var(--c-mono-dark);
    font-family: var(--f-sans);
    font-weight: 400;
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: -0.36px;
    color: var(--c-accent-yellow);
  }

  /* The line from the badge to the first card. */
  .o-page--tech .tech-hiw__lead {
    display: block;
    position: absolute;
    left: 67.94px;
    top: 64.19px;
    width: 227.05px;
    height: 1px;
    margin: 0;
    background: var(--c-mono-light);
    opacity: 0.3;
  }

  /* Connectors between the staggered cards. */
  .o-page--tech .tech-hiw__zig {
    display: block;
    position: absolute;
    top: 65px;
    width: 181.75px;
    height: 63.98px;
  }
  .o-page--tech .tech-hiw__zig--flip { top: 131px; }

  /* Cards: 314 x 128, and the frame nudges each one's row by a px or two
     rather than holding two exact rows, so the tops are written per card. */
  .o-page--tech .tech-hiw__card {
    position: absolute;
    width: 314px;
    height: 128px;
    padding: 0;
    border-width: 0.575px;
    -webkit-backdrop-filter: blur(13.381px);
            backdrop-filter: blur(13.381px);
  }
  .o-page--tech .tech-hiw__card:nth-of-type(1) { top: 0; }
  .o-page--tech .tech-hiw__card:nth-of-type(2) { top: 65.49px; }
  .o-page--tech .tech-hiw__card:nth-of-type(3) { top: 2.51px; }
  .o-page--tech .tech-hiw__card:nth-of-type(4) { top: 66.49px; }
  .o-page--tech .tech-hiw__card:nth-of-type(5) { top: 2px; }
  .o-page--tech .tech-hiw__card .brk__c { width: 6.34px; height: 6.34px; }

  .o-page--tech .tech-hiw__card-num,
  .o-page--tech .tech-hiw__card-title,
  .o-page--tech .tech-hiw__card-body { position: absolute; margin: 0; }
  .o-page--tech .tech-hiw__card-num { left: 20px; top: 26px; font-size: 10px; }
  .o-page--tech .tech-hiw__card-title {
    left: 59px;
    top: 18px;
    font-size: 20px;
    line-height: 1.2;
    letter-spacing: -0.8px;
    white-space: nowrap;
  }
  .o-page--tech .tech-hiw__card-body {
    left: 59px;
    top: 58px;
    width: 210px;
    font-size: 10px;
    line-height: 1.4;
    letter-spacing: -0.3px;
  }

  /* --- search steps ----------------------------------------------------
     Figma 1164:141882, frame 1642 -> 2480, 336 wide. A column of three
     blocks: the copy (308 wide, heading over lede on a 16 gutter), 32, the
     three step panels stacked with no gap, 20, then the smart-search panel
     at 336 x 183.

     The markup draws the panel BEFORE the steps, because the 1920 canvas
     puts it beside them; here it goes underneath, which is an `order`
     rather than a change to the template. */

  .o-page--tech .tech-search {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding-top: 0;
  }
  .o-page--tech .tech-search__h     { order: 1; width: 308px; }
  .o-page--tech .tech-search__lede  { order: 2; width: 308px; margin-top: 16px; }
  .o-page--tech .tech-search__steps { order: 3; width: 100%; margin-top: 32px; }
  .o-page--tech .tech-search__shots { order: 4; width: 100%; margin-top: 20px; }

  .o-page--tech .tech-search__h h2 { font-size: 28px; line-height: 1.2; letter-spacing: -1.12px; }
  .o-page--tech .tech-search__lede p { font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }

  /* Each panel is its own box again rather than the 1200 stack: 16 of head
     room, 20 under, and the icon pinned opposite the number. */
  .o-page--tech .tech-search__step { position: relative; padding: 16px 16px 20px; }
  .o-page--tech .tech-search__pick { padding: 0; }
  .o-page--tech .tech-search__num { font-size: 12px; }

  /* The open panel's row is 72 tall (the icon sets it), then 24 to the
     title and 12 to the body; the shut ones are 56 and 8. */
  .o-page--tech .tech-search__icon {
    display: block;
    position: absolute;
    right: 16px;
    top: 16px;
    width: auto;
    height: 56px;
  }
  .o-page--tech .tech-search__step.is-open .tech-search__icon { height: 72px; }
  /* The icon is out of flow, so the title carries the row's height itself:
     56 + 8 shut, 72 + 24 open, measured from the number's foot at 30.4. */
  .o-page--tech .tech-search__title { margin-top: 49.6px; font-size: 20px; line-height: 1.2; letter-spacing: -1px; }
  .o-page--tech .tech-search__step.is-open .tech-search__title { margin-top: 81.6px; }
  .o-page--tech .tech-search__body {
    width: 248px;
    padding: 0;
    margin-top: 12px;
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: -0.36px;
  }

  /* The bar runs the panel's full width along its foot, with the readout
     above its right end. */
  .o-page--tech .tech-search__meter {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    height: 1.49px;
    margin: 0;
  }
  .o-page--tech .tech-search__pct {
    position: absolute;
    left: auto;
    right: 16px;
    bottom: 6px;
    top: auto;
    display: block;
    padding: 0;
    font-size: 10px;
    line-height: 1.4;
    letter-spacing: -0.3px;
  }
  .o-page--tech .tech-search__step:not(.is-open) .tech-search__meter,
  .o-page--tech .tech-search__step:not(.is-open) .tech-search__pct { display: none; }

  /* 336 x 183. The shots are absolutely placed inside their box on the 1920
     canvas; the tablet rule makes that box static, which sets them loose to
     the section and stretches them over the whole of it. Giving the box its
     position back is what puts them back in it. */
  /* left/top are the 1920 canvas's coordinates; under `relative` they stop
     being positions and become offsets, which shoved the panel 48 right and
     367 down. Both have to be cleared. */
  .o-page--tech .tech-search__shots { position: relative; left: auto; top: auto; overflow: hidden; }
  .o-page--tech .tech-search__shot {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }

  /* --- section gaps ----------------------------------------------------
     Frame: Search 1686-2524, the dots band 2604-2751, the table 2831-3434,
     Compatibility 3594-4044. So 80 between Search and the band, 80 between
     the band and the table, 160 before Compatibility. Carried as padding
     on the sections themselves -- a margin between two of these siblings
     does not survive the cascade here. */

  .o-page--tech .tech-search { padding-bottom: 75px; }   /* lands the section on the frame's 918 */
  /* Figma 1164:141883: four rows of seven on a 48 pitch, the field starting
     at x29 / y0 rather than the shared band's x9 / y12 -- so it is the same
     grid, shifted 20 across and started one row later. */
  .o-page--tech .dots { height: 147px; }
  .o-page--tech .dots .dots__grid { transform: translate(20px, -173.4px) scale(0.578); }

  /* --- comparison table ------------------------------------------------
     Frame: heading at 2831, a header row naming the two columns, then nine
     rows about 51 tall -- number at 20, label at 56, Traditional at 206,
     OcularAI at 290. 603 for the section.

     At 1200 this turns into a stacked card per row, with the column name
     repeated on every cell. At phone width that came out 1164 tall with
     the values clipped, so the frame's real table comes back: one column
     template shared by the header and every row. */

  /* One template for the header and every row, on the frame's own column
     edges: the label column runs to 186, Traditional 186 -> 271, OcularAI
     271 -> 336. */
  .o-page--tech .tech-table {
    display: grid;
    grid-template-columns: 186px 85px 65px;
    align-content: start;
    padding-top: 80px;
    padding-bottom: 0;
  }
  .o-page--tech .tech-table > * { grid-column: 1 / -1; }
  .o-page--tech .tech-table__h--1 { grid-row: 1; }
  .o-page--tech .tech-table__h--2 { grid-row: 2; }
  .o-page--tech .tech-table__colname { grid-area: 3 / 2; }
  .o-page--tech .tech-table__colmark { grid-area: 3 / 3; }
  .o-page--tech .tech-table__grid { grid-row: 4; margin-top: 0; }

  .o-page--tech .tech-table__h h2,
  .o-page--tech .tech-table__h span { font-size: 28px; line-height: 1.2; letter-spacing: -1.12px; }

  /* 40 under the heading, then 36 from the column names' top to the first
     rule -- 16.4 of that is left here, the rest is the names' own line. */
  .o-page--tech .tech-table__colname,
  .o-page--tech .tech-table__colmark {
    display: block;
    position: static;
    width: auto;
    height: auto;
    margin: 40px 0 16.4px;
    font-size: 14px;
    line-height: 1.4;
    letter-spacing: -0.42px;
    color: var(--c-mono-light);
    align-self: end;
  }
  .o-page--tech .tech-table__colmark img { width: auto; height: 16px; }

  /* 52 a row, the rule on top, the values 7 below it and the label 9. */
  .o-page--tech .tech-table__row {
    grid-template-columns: 186px 85px 65px;
    gap: 0;
    padding: 7px 0 0;
    min-height: 52px;
    align-items: start;
  }
  /* Number and label share the first column, as the frame sets them. */
  .o-page--tech .tech-table__cell--label {
    display: grid;
    grid-template-columns: 38px minmax(0, 1fr);
    align-items: start;
    column-gap: 0;
    margin-top: 2px;
  }
  .o-page--tech .tech-table__num { display: block; margin: 0; font-size: 10px; }
  /* The 1920 canvas keeps each label on one line; the frame gives each its
     own column width and lets it take two. They are not one measure -- the
     frame sets nine -- so they are written out. */
  .o-page--tech .tech-table__label {
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: -0.36px;
    white-space: normal;
  }
  .o-page--tech .tech-table__row:nth-child(1) .tech-table__label { max-width: 114px; }
  .o-page--tech .tech-table__row:nth-child(2) .tech-table__label { max-width: 108px; }
  .o-page--tech .tech-table__row:nth-child(3) .tech-table__label { max-width: 93px; }
  .o-page--tech .tech-table__row:nth-child(4) .tech-table__label { max-width: 138px; }
  .o-page--tech .tech-table__row:nth-child(5) .tech-table__label { max-width: 136px; }
  .o-page--tech .tech-table__row:nth-child(6) .tech-table__label { max-width: 131px; }
  .o-page--tech .tech-table__row:nth-child(7) .tech-table__label { max-width: 133px; }
  .o-page--tech .tech-table__row:nth-child(8) .tech-table__label { max-width: 122px; }
  .o-page--tech .tech-table__row:nth-child(9) .tech-table__label { max-width: 106px; }
  .o-page--tech .tech-table__val,
  .o-page--tech .tech-table__val .rv {
    justify-content: center;
    gap: 5.307px;
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: -0.36px;
  }
  .o-page--tech .tech-table__val img { height: 10px; }
  /* The frame stops at the last row's own foot rather than giving it a
     ninth 52 -- there is no rule under it to reach. */
  .o-page--tech .tech-table__row:last-child { min-height: 0; }
  /* The repeated column name belongs to the stacked layout, not this one. */
  .o-page--tech .tech-table__cell--trad::before,
  .o-page--tech .tech-table__cell--ours::before { content: none; }

  /* --- compatibility ---------------------------------------------------
     Frame: 3594-4044. Heading, then the lede, then the photograph 336
     wide on the plate's own 866/666 -- the markup leads with the photo
     because the desktop canvas places it first, so the order is set here.

     The frame's heading here is placeholder text carried over from the
     Search section; the real copy is several times longer, so it is set
     at 20 rather than the frame's 28 to keep the section near its 450. */

  .o-page--tech .tech-compat {
    display: flex;
    flex-direction: column;
    padding-top: 160px;          /* the frame's gap after the table */
    padding-bottom: 0;
  }
  .o-page--tech .tech-compat__h    { order: 1; }
  .o-page--tech .tech-compat__lede { order: 2; }
  .o-page--tech .tech-compat__plate { order: 3; }

  .o-page--tech .tech-compat__h,
  .o-page--tech .tech-compat__lede { width: 308px; }
  .o-page--tech .tech-compat__h--1 { margin-top: 0; }
  .o-page--tech .tech-compat__h span { font-size: 28px; line-height: 1.2; letter-spacing: -1.12px; }
  .o-page--tech .tech-compat__lede { margin-top: 16px; }
  .o-page--tech .tech-compat__lede p { font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }

  /* 336 x 259.3 with the frame's four corner arrows. The tablet rule makes
     the plate static, which sets the photograph inside it loose to the
     section -- it was covering the whole of it. */
  .o-page--tech .tech-compat__plate,
  .o-page--tech .tech-compat.is-in .tech-compat__plate {
    position: relative;
    left: auto;
    top: auto;
    margin-top: 56px;
    aspect-ratio: 336 / 259.3;
  }
  .o-page--tech .tech-compat__plate .brk__c { width: 4.672px; height: 4.672px; }

  /* --- the shared CTA --------------------------------------------------
     The front page's rule above puts the eyebrow 287 down; this frame
     puts it 167 below the section top, and the section opens 81 after
     Compatibility. */


  /* ======================================================================
     Solutions page -- phone layout
     Figma "Solutions" (1171:172348), 376 wide, content to 4574.
     ====================================================================== */

  /* --- hero -------------------------------------------------------------
     Frame: masthead 63, the two eyebrows sharing a row at 144, the heading
     203-305, the lede 321-355, the wavefield 381-645 full bleed, and the
     light plate opening at 734.

     The wavefield is first in the markup because it is the desktop's
     backdrop; the frame puts it under the copy, so the order is set here
     rather than in the template. */

  .o-page--sol .sol-hero {
    display: grid;
    grid-template-columns: 125px minmax(0, 1fr);
    align-content: start;
    padding-top: 0;
    padding-bottom: 89px;
  }
  .o-page--sol .sol-hero > * { grid-column: 1 / -1; }
  .o-page--sol .hero__masthead { grid-row: 1; }
  .o-page--sol .sol-hero__label--a { grid-area: 2 / 1; }
  .o-page--sol .sol-hero__label--b { grid-area: 2 / 2; }
  .o-page--sol .sol-hero__h--l1 { grid-row: 3; }
  .o-page--sol .sol-hero__h--l2 { grid-row: 4; }
  .o-page--sol .sol-hero__lede  { grid-row: 5; }
  .o-page--sol .sol-hero__wave  { grid-row: 6; }


  .o-page--sol .sol-hero__label--a,
  .o-page--sol .sol-hero__label--b {
    margin: 37px 0 0;
    font-size: 10px;
    line-height: 1.5;
  }
  .o-page--sol .sol-hero__h { margin: 0; }
  .o-page--sol .sol-hero__h--l1 { margin-top: 29px; }
  .o-page--sol .sol-hero__h,
  .o-page--sol .sol-hero__h span {
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -0.84px;
  }
  .o-page--sol .sol-hero__lede { margin-top: 16px; font-size: 11px; }
  .o-page--sol .sol-hero__lede span { font-size: 11px; line-height: 1.55; }

  .o-page--sol .sol-hero__wave {
    width: auto;
    height: 264px;
    margin: 26px -20px 0;
  }

  /* --- industries -------------------------------------------------------
     Frame: the plate opens at 734, the three eyebrows share a row at 759
     with a rule under them, the heading at 828 and 862, the tab strip at
     925, then the cards from 965 -- image 245 on a 418 pitch.

     Every child sits inside .sol-ind__pin, so the row grid goes there. The
     1200 reflow drops eyebrows b and c and the rule; the frame keeps all
     of them. */

  .o-page--sol .sol-ind { padding: 25px 20px 71px; }
  /* The frame sets the middle and right eyebrows on two lines each --
     "FOUR ENVIRONMENTS, / ONE SYSTEM" from x 110, "ANY SITE, / ANY SCALE"
     against the right edge -- so the columns are measured, not content. */
  .o-page--sol .sol-ind__pin {
    display: grid;
    grid-template-columns: 90px minmax(0, 1fr) max-content;
    align-content: start;
  }
  .o-page--sol .sol-ind__pin > * { grid-column: 1 / -1; }
  .o-page--sol .sol-ind__eyebrow--a { grid-area: 1 / 1; }
  .o-page--sol .sol-ind__eyebrow--b { grid-area: 1 / 2; display: block; max-width: 110px; }
  .o-page--sol .sol-ind__eyebrow--c { grid-area: 1 / 3; display: block; max-width: 62px; text-align: right; }
  .o-page--sol .sol-ind__rule { grid-row: 2; display: block; position: static; width: 100%; }
  .o-page--sol .sol-ind__h--l1 { grid-row: 3; }
  .o-page--sol .sol-ind__h--l2 { grid-row: 4; }
  /* The chip strip stays with you through the cards. A sticky grid item can
     only travel inside its own grid area, and row 5 is only as tall as the
     strip -- so it is given an area that runs to the foot of the grid and
     held at the top of it. Row 5 then collapses, which the rail's own margin
     puts back. */
  .o-page--sol .sol-ind__list  { grid-row: 5 / span 2; grid-column: 1 / -1; align-self: start; }
  .o-page--sol .sol-ind__rail  { grid-row: 6; }

  .o-page--sol .sol-ind__eyebrow { margin: 0; font-size: 10px; line-height: 1.5; }
  .o-page--sol .sol-ind__rule { margin: 14px 0 0; }
  .o-page--sol .sol-ind__h--l1 { margin-top: 40px; }
  .o-page--sol .sol-ind__h--l2 { margin-top: 5px; }
  /* 24, not the hero's 28 -- the frame sets this heading on two lines and
     "Adapt to your industry, not the" only fits the 336 column at 24. */
  .o-page--sol .sol-ind__h,
  .o-page--sol .sol-ind__h span {
    font-size: 24px;
    line-height: 1.2;
    letter-spacing: -0.84px;
  }

  /* The strip runs off the right edge, the way a scroller should read. */
  .o-page--sol .sol-ind__list {
    position: sticky;
    top: 0;
    z-index: 3;
    /* Full-bleed ground, or the cards pass beside the strip as they go. */
    margin: 34px calc(var(--gut) * -1) 0;
    padding: 12px var(--gut);
    background: var(--c-light-grey);
    gap: 14px;
  }
  .o-page--sol .sol-ind__pick { font-size: 13px; }

  /* Row 5 collapses under the spanning strip, so the rail carries the whole
     gap itself: the strip's 40 plus the frame's 23 plus its own 34 of lead. */
  .o-page--sol .sol-ind__rail { margin-top: 97px; }
  .o-page--sol .sol-ind__copy { margin-top: 16px; }
  .o-page--sol .sol-ind__card-title { font-size: 20px; line-height: 1.25; letter-spacing: -0.4px; }
  .o-page--sol .sol-ind__card-body { margin-top: 10px; font-size: 11px; line-height: 1.55; }
  /* 245 image + 16 + title + 10 + body + 29 lands the next image on the
     frame's 418 pitch. */
  .o-page--sol .sol-ind__card + .sol-ind__card { margin-top: 29px; }

  /* The shared CTA: this frame opens its eyebrow 427 below the section
     top, where the front page's rule above puts it at 287. */

  /* ======================================================================
     Contact page -- phone layout
     Figma "Contact" (1177:195304), 376 wide, content to 2218.

     The form and the testimonial are one section, because the desktop
     canvas stands them side by side at fixed coordinates. The frame runs
     a dots band between them, which a sibling section cannot reach -- so
     at this width .contact stops drawing a box and its children join the
     page's own column, where a band can be ordered into the middle.
     ====================================================================== */

  .o-page--contact > main { display: flex; flex-direction: column; }
  .o-page--contact .contact { display: contents; }
  /* The gutter lived on .contact, which no longer has a box. */
  .o-page--contact .contact > * { padding-inline: 20px; }

  .o-page--contact .hero__masthead { order: 1; }
  .o-page--contact .contact__title    { order: 2; }
  .o-page--contact .contact__form     { order: 3; }
  .o-page--contact .contact__card     { order: 6; }
  .o-page--contact .contact__navs     { order: 7; }
  /* The frame has no logo marquee on the phone. */
  .o-page--contact > main > .footer   { order: 10; }


  /* Frame: the heading at 122, the first label at 178. */
  .o-page--contact .contact__title {
    margin-top: 59px;
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -0.84px;
  }
  .o-page--contact .contact__form { margin-top: 22px; }

  /* Frame: a 68 pitch -- a 15 label, 5, a 28 input, then 20.
     Unscoped, because contact-fields is the same partial the Book a Demo
     sheet renders: one definition, so the sheet and the page are the same
     form at the same sizes rather than two that drift apart. */
  .cfields { gap: 20px; }
  .cfields__row { gap: 20px; }
  .cfields__field { gap: 5px; }
  .cfields__label { font-size: 10px; line-height: 1.5; }
  .cfields__input { height: 28px; padding: 0 12px; font-size: 12px; }
  .cfields__input--area { height: 64px; padding: 8px 12px; }
  .cfields__field--types > .cfields__label { margin-bottom: 8px; }
  .cfields__types { gap: 10px; }
  .cfields__type span { height: 28px; padding: 0 14px; font-size: 12px; }
  /* Full width at 807-837 in the frame, not the 160 the tablet gives it.
     11 plus the list's own 20 is the frame's 31. */
  .cfields__submit { margin-top: 11px; }
  .cfields__btn { width: 100%; height: 30px; font-size: 12px; }

  /* The sheet's own chrome, to the same rhythm: the page sets its title at
     28 with the form 22 under it. */
  /* The form is the page's size now, so the sheet needs the room: at 5vh
     the Submit fell below the fold on an 812 screen. 2vh puts it back in
     view without touching any of the form's own metrics. */
  .demo { padding: 2vh 16px; }
  .demo__sheet { padding: 28px 20px 32px; }
  .demo__title { font-size: 28px; letter-spacing: -0.84px; margin-bottom: 22px; }

  /* Frame: the band's first row at 916, so its top is 904 -- 67 after the
     submit. The testimonial card opens at 1143. */
  /* Only where the card sits. The contact layout gives every child the
     gutter as padding, which ran the card full bleed and gave it a wider
     measure than the front page's; putting the gutter on the margin instead
     leaves it the same 336 box with the same 16 inside, so the card is the
     front page's card in every respect. */
  /* The photograph is a desktop idea -- on a phone the form is the page. */
  .o-page--contact .contact__photo { display: none; }

  /* The separator between the fields and the footer. */
  .o-page--contact > main > .dots { order: 8; display: block; height: 180px; top: auto; margin-top: 67px; }

  .o-page--contact .contact__navs { margin-top: 31px; gap: 16px; }
  .o-page--contact .contact__nav-btn { width: 48px; height: 48px; font-size: 12px; }
  .o-page--contact > main > .footer { margin-top: 63px; }

  /* ======================================================================
     About page -- phone layout
     Figma "About" (1177:218376), 376 wide, content to 7503.
     ====================================================================== */

  /* --- hero -------------------------------------------------------------
     Frame: masthead 19, the two eyebrows on one row at 100, the first
     heading 159-220, the galaxy 266-427 full bleed, and the second
     heading right-aligned at 477-538.

     The markup puts the eyebrows last, after the second heading, because
     the desktop canvas places each on its own coordinate. */

  .o-page--about .about-hero {
    display: grid;
    grid-template-columns: 125px minmax(0, 1fr);
    align-content: start;
    padding-top: 0;
    padding-bottom: 0;
  }
  .o-page--about .about-hero > * { grid-column: 1 / -1; }
  .o-page--about .hero__masthead       { grid-row: 1; }
  .o-page--about .about-hero__label--a { grid-area: 2 / 1; }
  .o-page--about .about-hero__label--b { grid-area: 2 / 2; }
  .o-page--about .about-hero__h--a     { grid-row: 3; }
  .o-page--about .about-hero__tilt     { grid-row: 4; }
  .o-page--about .about-hero__h--b     { grid-row: 5; }


  /* Frame: both eyebrows at 97 on a 1.2 leading -- 24 for the two-line one,
     12 for the other. */
  .o-page--about .about-hero__label--a,
  .o-page--about .about-hero__label--b {
    margin: 37px 0 0;
    font-size: 10px;
    line-height: 1.2;
  }
  .o-page--about .about-hero__h { margin: 0; }
  .o-page--about .about-hero__h--a { margin-top: 32px; }   /* heading at 153 */
  .o-page--about .about-hero__h span,
  .o-page--about .about-hero__h {
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -1.12px;
  }
  .o-page--about .about-hero__h--b { margin-top: 24px; text-align: right; }
  .o-page--about .about-hero__h--b span { text-align: right; display: block; }

  /* Frame: x3 / 245, 353 x 202 -- so it bleeds 17 past the gutter on each
     side rather than the full 20. */
  .o-page--about .about-hero__tilt {
    position: static;
    left: auto;
    top: auto;
    transform: none;
    width: 353px;
    height: 202px;
    margin: 24px 0 0 -17px;
    opacity: 1;
  }
  .o-page--about .about-hero.is-in .about-hero__tilt { transform: none; }
  .o-page--about .about-hero__tilt img,
  .o-page--about .about-hero__tilt canvas { width: 100%; height: 100%; object-fit: cover; }

  /* about-hero.css gives the band `height: auto` at 1200, and its rows are
     absolutely placed -- so it collapsed to nothing and the band was not
     drawn at all. The frame puts four rows at 619/667/717/764. */
  /* Frame 1177:225928 -- the same band as Technology's: four rows of seven
     on a 48 pitch, the field starting at x29 / y0. */
  .o-page--about > main > .dots { height: 147px; }
  .o-page--about > main > .dots .dots__grid {
    top: auto;
    bottom: auto;
    height: 200%;
    transform: translate(20px, -173.4px) scale(0.578);
  }
  /* The frame runs nothing between the standards and the team -- just the
     120 gap, which the team's own margin carries. */
  .o-page--about .about-std ~ .dots { display: none; }

  /* base.css zeroes every section margin on this page below 1200
     (.o-page--about > main > .o-section + .o-section), which is why the
     gaps have to be written at the same specificity to land at all. */
  .o-page--about > main > .about-hero + .dots  { margin-top: 80px; }
  .o-page--about > main > .dots + .about-what  { margin-top: 81px; }
  .o-page--about > main > .about-std + .dots   { margin-top: 0; }
  /* The frame runs 120 of ground between the standards and the team. */
  .o-page--about > main > .dots + .about-team  { margin-top: 120px; }

  /* --- what we do -------------------------------------------------------
     Figma 1177:225977 and 1177:226055. Relative to the plate at 846: the
     eyebrows at 20 on a 42 gutter, 40 to the heading, 16 to the copy, 24 to
     the button, then the four cards from 373 on a 16 gap -- 335 each except
     the last, which is 256 of card plus a 340 photograph. 2042 for the
     section. */

  .o-page--about .about-what { padding: 20px 20px; }

  /* Both eyebrows share a row rather than one being pinned over the other,
     which is what the 42 gutter is. */
  .o-page--about .about-what__head { position: relative; display: flex; flex-wrap: wrap; }
  .o-page--about .about-what__eyebrow {
    margin: 0;
    font-size: 10px;
    line-height: 1.4;
    letter-spacing: -0.3px;
    color: #767676;
  }
  .o-page--about .about-what__eyebrow--b { position: static; margin-left: 42px; }
  .o-page--about .about-what__h {
    width: 100%;
    margin-top: 40px;
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -1.12px;
  }

  .o-page--about .about-what__row { display: flex; flex-direction: column; gap: 16px; margin: 0; }
  .o-page--about .about-what__row--1 { margin-top: 16px; }
  .o-page--about .about-what__row--2 { margin-top: 16px; padding-bottom: 0; }
  .o-page--about .about-what__cell { width: auto; }

  /* The copy and the button, still part of the head block in the frame:
     288 of measure, 24 to a 98 x 25.667 button, then 48 more on top of the
     row's own 16 before the first card. */
  .o-page--about .about-what__cell--demo { padding: 0; margin-bottom: 52px; }
  .o-page--about .about-what__cell--demo p {
    width: 288px;
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: -0.36px;
    color: #767676;
  }
  .o-page--about .about-what__cta { margin-top: 24px; width: 98px; height: 25.667px; }
  .o-page--about .about-what__cta .btn {
    width: 98px;
    height: 25.667px;
    padding: 0;
    font-size: 10px;
  }

  /* 335 each, on the frame's 16 of padding. */
  .o-page--about .about-what__cell.card { padding: 16px; }
  .o-page--about .about-what__cell--users,
  .o-page--about .about-what__cell--mission,
  .o-page--about .about-what__cell--owned { min-height: 335px; }

  .o-page--about .about-what__cell.card h3 { font-size: 28px; line-height: 1.2; letter-spacing: -1.12px; }
  .o-page--about .about-what__cell.card p { font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }

  /* Card 1: the cluster at 24, the stat at 263 and its label at 305. */
  .o-page--about .about-what__cell--users { display: block; }
  /* The five portraits are placed inside this box on the 255 x 120 cluster
     the canvas draws, so it scales rather than resizes -- and it has to keep
     its own positioning context or they escape to the card. 119/255 is the
     frame's size. */
  .o-page--about .about-what__faces {
    display: block;
    position: relative;
    left: auto;
    top: auto;
    transform: none;
    --s: 0.4667px;
    width: calc(255 * var(--s));
    height: calc(120 * var(--s));
    margin: 8px 0 0 92px;
  }
  /* .card p above is (0,3,1) and these are paragraphs, so they have to be
     written past it or they inherit the body size. */
  .o-page--about .about-what__cell.card .about-what__stat { margin-top: 183px; font-size: 32px; line-height: 1.2; }
  .o-page--about .about-what__cell.card .about-what__statlabel { margin-top: 4px; font-size: 10px; line-height: 1.4; }

  /* Card 2: the frame drops the heading well down the card. */
  .o-page--about .about-what__cell--mission h3 { margin-top: 125px; }
  .o-page--about .about-what__cell--mission p { margin-top: 89px; width: 304px; }

  /* Card 3: the flag at the top, the copy at the foot. */
  .o-page--about .about-what__flag { display: block; width: 91px; height: 48px; margin: 0; }
  .o-page--about .about-what__cell--owned p { margin-top: 166px; width: 304px; }

  /* Card 4 is 256 of card with the photograph on its foot, 336 x 340 --
     the markup nests it, the frame stacks it. */
  .o-page--about .about-what__cell--why {
    position: relative;
    height: 596px;
    min-height: 0;
    padding-bottom: 0;
  }
  .o-page--about .about-what__cell--why h3 { margin-top: 0; }
  .o-page--about .about-what__cell--why p { margin-top: 48px; width: 273px; }
  /* The tablet rule gives this an aspect-ratio and a 24 nudge; with a fixed
     height the ratio wins the width back and it came out 504 wide. */
  .o-page--about .about-what__shot {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    height: 340px;
    margin: 0;
    aspect-ratio: auto;
    transform: none;
  }
  .o-page--about .about-what__shot img { width: 100%; height: 100%; object-fit: cover; }

  /* --- the standards ----------------------------------------------------
     Figma 1177:226141. From the section's top: the heading at 80 on a 248
     measure, the line drawing at 254 (x49, 280 x 150), the rule/title/copy
     at 468, then Prev/Next. 717.5 for the section.

     The markup puts the buttons second, where the desktop canvas needs
     them, so the order is set here. */

  .o-page--about .about-std {
    display: flex;
    flex-direction: column;
    padding: 80px 20px 0;
  }
  .o-page--about .about-std__h     { order: 1; margin: 0; }
  .o-page--about .about-std__plate { order: 2; }
  .o-page--about .about-std__rules { order: 3; }
  .o-page--about .about-std__nav   { order: 4; }

  /* The frame's 248 measure gives three lines in its own metrics; ours are
     wider, so the break is carried by the two spans in the template and the
     box runs the full column. */
  .o-page--about .about-std__h { font-size: 28px; line-height: 1.2; letter-spacing: -1.12px; }

  /* Both of these are placed against the 1920 canvas -- the drawing sat at
     x970, a screen and a half off to the right, and the buttons at a fixed
     48/196. They come back into the flow. */
  .o-page--about .about-std__plate {
    position: relative;
    left: auto;
    top: auto;
    width: 280px;
    height: 150px;
    margin: 72px 0 0 29px;
  }
  /* The band belongs under the buttons, and it is nested inside the rules
     block -- flattening that block lets the three take their own order. */
  .o-page--about .about-std__rules { display: contents; }
  /* Fixed to the tallest copy, and every card absolute inside it, so the
     four cross-fade in place -- nothing reflows as you press Next, which is
     what the jump was. */
  .o-page--about .about-std__viewport { order: 3; position: relative; height: 151px; margin-top: 64px; }
  .o-page--about .about-std__nav {
    order: 4;
    position: relative;
    left: auto;
    top: auto;
    margin-top: 40px;
  }
  .o-page--about .about-std__nav .rv { justify-content: center; gap: 16px; }
  /* The canvas sets these in the Arame face, which is caps-only -- the frame
     has them in the body sans as Prev / Next. */
  .o-page--about .about-std__btn {
    width: 48px;
    height: 48px;
    font-family: var(--f-sans);
    font-weight: 400;
    font-size: 13px;
    letter-spacing: 0;
  }
  /* The illustration is drawn at 280 x 150 in the frame; the canvas sizes it
     off --s and then caps it, which left it at 140 x 76. */
  .o-page--about .about-std__slot svg {
    width: 280px;
    height: 150px;
    max-width: none;
    max-height: none;
  }

  /* No sliding. On the 1920 canvas all four standards are on screen at once
     and Prev/Next only mark one of them; the phone had them in a row that
     scrolled, so pressing Next dragged the next three across. Here the four
     are stacked in the same place and only the current one is shown -- so
     the drawing above and the words below change where they are, and
     nothing travels.

     The script still writes a translate on the track for the wide layout,
     and an inline style can only be answered with !important. */
  .o-page--about .about-std__viewport { position: relative; }
  /* Full height, or the absolutely-placed cards inside resolve against a
     collapsed track and the two verticals stop after the title. */
  .o-page--about .about-std__track {
    display: block;
    position: relative;
    height: 100%;
    transform: none !important;
  }
  /* The frame does not box the copy in: two verticals at 31 and 351, a
     full-bleed horizontal under them, and the progress rule inset between
     the two -- it never reaches them, which is what stops it reading as a
     closed rectangle. */
  .o-page--about .about-std__track .about-std__item {
    position: absolute;
    left: 11px;
    top: 0;
    bottom: 0;
    width: 320px;
    height: auto;
    padding: 16px 11px 24px;
    border-left: 1px solid rgba(236, 247, 255, 0.15);
    border-right: 1px solid rgba(236, 247, 255, 0.15);
    transform: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 420ms var(--ui-ease), visibility 0s linear 420ms;
  }
  .o-page--about .about-std__item .about-std__rule { left: 11px; right: 11px; width: auto; margin: 0; }
  .o-page--about .about-std.is-in .about-std__item { opacity: 0; }
  .o-page--about .about-std__track .about-std__item.is-current {
    visibility: visible;
    transition: opacity 420ms var(--ui-ease), visibility 0s linear 0s;
  }
  .o-page--about .about-std.is-in .about-std__item.is-current { opacity: 1; }

  .o-page--about .about-std__item h3 { margin-top: 16px; font-size: 24px; line-height: 1.2; letter-spacing: -0.96px; }
  .o-page--about .about-std__item p { margin-top: 16px; width: auto; font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }

  /* The frame draws this at 1, not the canvas's 2. */
  .o-page--about .about-std__slot svg path,
  .o-page--about .about-std__slot svg [stroke] { stroke-width: 1; }

  /* The dot band the frame runs under the buttons. */
  .o-page--about .about-std__band {
    order: 5;
    display: block;
    position: relative;
    left: auto;
    top: auto;
    width: 100%;
    height: 96px;
    margin-top: 40px;
    border-bottom: 1px solid rgba(236, 247, 255, 0.15);
  }
  /* Same dot as every other band on the site -- these were rendering at 5 --
     and the hero band's own reveal: held at 0.15 and brought up to full as
     the section arrives, with no delay in front of it. The touch trail comes
     free, since touch.js binds to anything carrying .dots__grid. */
  .o-page--about .about-std__dots {
    opacity: 0.15;
    transition: opacity calc(var(--rv-dur) * 1.2) var(--rv-ease);
  }
  .o-page--about .about-std.is-in .about-std__dots { opacity: 1; }
  /* The grid is drawn at half scale, so everything here is written double to
     land on the 9 / 3.5 dot every other band on the site uses. */
  /* 5.625 on 18 is the hero band's 5 on 16 -- a rounded square, not the
     circle a half-the-width radius was giving. */
  .o-page--about .about-std__dots .dots__dot { width: 18px; height: 18px; border-radius: 5.625px; --dot-hit: 18; }
  .o-page--about .about-std__dots .dots__dot::before { border-radius: 10.125px; }
  .o-page--about .about-std__dots .dots__dot::after { width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; }
  /* Same 48 pitch as the band under the hero, staggered half a pitch -- the
     grid is at half scale, so the gap and the offsets are written double.
     Two rows here, not the hero band's four. */
  .o-page--about .about-std__dots .dots__row { gap: 78px; }
  /* Centred across the band. The rows carry 22 cells for the 1920 canvas and
     the band simply clipped the overflow, so the field sat hard against the
     left; here each row is cut to what fits -- seven, and six on the
     staggered row -- and the two are inset to sit symmetrically: 39.5 and
     43.5 rendered inside the band's own 336, written double for the
     half-scale grid. */
  .o-page--about .about-std__dots .dots__row:nth-child(1) { left: 39px; top: 32px; }
  .o-page--about .about-std__dots .dots__row:nth-child(2) { left: 87px; top: 120px; }
  .o-page--about .about-std__dots .dots__row:nth-child(1) .dots__dot:nth-child(n+8) { display: none; }
  .o-page--about .about-std__dots .dots__row:nth-child(2) .dots__dot:nth-child(n+7) { display: none; }
  .o-page--about .about-std__dots .dots__row:nth-child(n+3) { display: none; }
  /* The frame lights the second dot of the second row -- the same state a
     hover gives, held on. Done here rather than in the template so the mark
     stays a phone-layout decision. */
  .o-page--about .about-std__dots .dots__row:nth-child(2) .dots__dot:nth-child(2) {
    background: var(--c-accent-yellow);
  }
  .o-page--about .about-std__dots .dots__row:nth-child(2) .dots__dot:nth-child(2)::after {
    background: var(--c-mono-dark);
  }

  /* --- the team ---------------------------------------------------------
     Frame: heading at 22, then two members of 707 with 24 between them --
     a 335 portrait above a 372 panel, edge to edge with each other. The
     desktop canvas laps the panel over the portrait; here they stack. */

  .o-page--about .about-team { padding: 0 20px 0; }
  .o-page--about .about-team__h {
    margin: 0;
    font-size: 28px;
    line-height: 1.2;
    letter-spacing: -0.84px;
  }
  .o-page--about .about-team__row { gap: 0; margin-top: 56px; }
  /* left/top are the 1920 canvas's, and under `relative` they became
     offsets -- the second portrait was sitting 970 off to the right. The
     ratio has to go too or it wins the height back from the frame's 336. */
  .o-page--about .about-team__portrait {
    left: auto;
    top: auto;
    margin: 0;
    width: auto;
    height: 336px;
    aspect-ratio: auto;
  }
  /* The tablet reflow resets every row child to `height: auto` at (0,3,1)
     once the section reveals, which with the ratio cleared collapsed the
     plates to nothing -- the frame's 336 has to be written past it. */
  .o-page--about .about-team.is-in .about-team__row > .about-team__portrait { height: 336px; }
  .o-page--about .about-team__portrait img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }

  /* Panel: 371 with the name at 24, the role 8 under it and the bio at 193. */
  .o-page--about .about-team__card {
    margin-top: 0;
    min-height: 371px;
    height: auto;
    padding: 24px 18px;
  }
  .o-page--about .about-team__card h3 { margin: 0; font-size: 20px; line-height: 1.2; letter-spacing: -0.8px; }
  .o-page--about .about-team__role { margin-top: 8px; font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }
  .o-page--about .about-team__bio { margin-top: 120px; width: 287px; }
  .o-page--about .about-team__bio p { font-size: 12px; line-height: 1.4; letter-spacing: -0.36px; }
  .o-page--about .about-team__card + .about-team__portrait { margin-top: 24px; }

  /* The frame leaves 80 before the FAQ. The testimonial sits between them
     in the markup, so the combinator has to go through it. */
  .o-page--about > main > .o-section + .faq { margin-top: 80px; }

  /* --- buttons ----------------------------------------------------------
     One size for every CTA on the page; the frame draws them all the same. */

  .btn { font-size: 12px; padding: 8px 16px; }
  /* The tablet reflow pins these to 184x48 and stretches the button inside
     to 100%; the frame lets every CTA size to its own label, about 105x30.
     Both the wrapper and the button have to be freed or the button spans
     the column. */
  .human__cta,
  .search__cta,
  .industries__cta,
  .faq__cta { width: fit-content; height: auto; }
  .human__cta .rv,
  .search__cta .rv,
  .industries__cta .rv { height: auto; }
  .human__cta .btn,
  .search__cta .btn,
  .industries__cta .btn,
  .faq__cta .btn { width: auto; height: auto; }

  /* The CTA's button is the one the frame draws to a fixed size. */
  .cta__btn { width: 110px; height: 28px; }
  .cta__btn .rv, .cta__btn .btn { width: 100%; height: 100%; }
  .cta__btn .btn { padding: 0 8px; }
  .btn .brk__c { --brk-w: 10px; --brk-h: 10px; width: 10px; height: 10px; }
  .btn.is-tapped .brk__c,
  .btn:focus-visible .brk__c { width: 100%; height: 100%; }

  @media (prefers-reduced-motion: reduce) {
    .features .f-card { transform: none; transition: none; }
    .features.is-in .f-card,
    .features .f-card.is-in { opacity: 1; transform: none; }
  }

  /* --- resources header ---------------------------------------------------
     The header is the front page's component on every page, but this section
     opened with 24 of its own padding, which pushed the whole masthead down
     by that much -- the one page whose header did not start where the rest
     do. The padding comes off and the heading below it takes the 24 back, so
     only the header moves. */
  .o-page--resources .blog-hero { padding-top: 0; }
  .o-page--resources .blog-hero__h { margin-top: 72px; }


  /* --- book a demo --------------------------------------------------------
     The desktop sheet is one static rectangle -- it is sized to fit and has
     nothing to scroll. On a phone the form is a tall single column and the
     screen is short, so the sheet keeps its scroll. */
  .demo__sheet { max-height: 100%; overflow-y: auto; }

}
