@charset "UTF-8";
/* =====================================================================
   GRIPT — MOBILE LAYER
   ---------------------------------------------------------------------
   Everything here lives inside @media (max-width: 768px) and overrides
   main.css and the per-page stylesheets. It is enqueued LAST
   (functions.php, priority 100), and every selector is prefixed with
   `#g ` (the id on <body>, header.php:84), so every rule in this file
   has at least one ID of specificity. That beats any class-only
   selector in the page sheets, which is what a mobile layer needs:
   loading last is not enough when the competing rule is more specific.

   :root becomes `#g` (the custom properties still cascade to every
   descendant of body). `body` becomes `#g`. `body:has(...)` becomes
   `#g:has(...)`. Anything else gets `#g ` prepended.

   The prefix is applied by scratchpad/prefix_mobile.py; specificity is
   verified by scratchpad/check_specificity.py, which must report zero
   losses after any edit here.

   !important is needed in exactly one place, on `#g #fullcmp`, and only
   because full-comparison.js writes an inline `min-width` (a specificity
   boost cannot beat inline). That is commented where it happens.

   Why a CSS layer and not separate mobile templates: production runs
   Breeze + Varnish. Server-side splitting on wp_is_mobile() would let
   one device's HTML get cached and served to the other. One HTML per
   URL, one cache entry, no device detection.

   Design source: prototypes/mobile-2026/ (approved 2026-08-30).
   ===================================================================== */

@media (max-width: 768px) {

  #g {
    /* Horizontal spacing is exactly three numbers. Nothing is allowed to
       invent a fourth.
         --m-gut       the page gutter. Header, bar, footer, every section.
         --m-pad-card  the inside of a card or panel.
         --m-pad-row   the inside of a dense list row or small cell. */
    --m-gut:      20px;
    --m-pad-card: 16px;
    --m-pad-row:  13px;

    /* Content column cap. The @media (max-width: 768px) block also fires
       on 700-760px viewports (unfolded foldables, small tablets in
       portrait), where a stretched mobile layout reads as broken — long
       lines of text, a bar whose two buttons sprawl. Cap the content
       and centre it: the fixed nav/bar/intro-bar stay full-bleed so the
       page still looks phone-shaped, but the reading column stays sane. */
    --m-content-max: 480px;

    /* Header height and action-bar height. Content never hides under
       either; the body padding at the bottom is derived from --m-bar. */
    --m-hdr: 52px;
    --m-bar: 72px;

    /* Every hero has to clear the fixed nav (52px). Desktop uses
       --hero-pad-top: clamp(80px, 10vw, 140px) which is enough at any
       viewport, but on a phone that eats a third of the first screen on
       whitespace, so the mobile figure is nav + 28px. The intro CTA bar
       adds its measured height when visible. */
    --m-hero-pad-top: calc(var(--m-hdr) + 28px);
  }
  #g:has(.intro-cta-bar.is-visible) {
    --m-hero-pad-top: calc(var(--m-hdr) + var(--intro-bar-h, 46px) + 28px);
  }

  /* ── HEADER ──────────────────────────────────────────────────────────
     Logo left, hamburger right, nothing else. main.css centres the
     "Start free trial" pill in the header, but that identical button is
     already in the fixed bar at the bottom of the screen, a thumb-length
     closer to the thumb. Two of the same CTA on one screen is one wasted
     slot, so the header gives its middle back to the page. */
  /* #nav, not `nav`. .breadcrumb is also a <nav> element
     (template-parts/migration-wizards-body.html:4, styled at vs.css:19), and
     a bare element selector was giving it the header's height, flex layout
     and space-between. */
  #g #nav {
    padding: 0 var(--m-gut);
    height: var(--m-hdr);
    /* main.css uses a 1fr auto 1fr grid for the centred pill. With the
       pill gone there are two children and nothing to centre. */
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  #g #nav .nav-cta { display: none; }

  /* ── The intro bar covers the header ─────────────────────────────────
     .intro-cta-bar (main.css:941) is position:fixed, top:0, z-index:9000.
     nav is position:fixed, top:0, z-index:100. So the bar lands squarely
     on top of the header and hides it.

     That was survivable while the header carried a visible CTA pill that
     nobody needed to tap. It is not survivable now: the hamburger is the
     only way into navigation on a phone, so a covered header means no
     menu at all.

     The bar's height depends on how its text wraps, so mobile.js measures
     it into --intro-bar-h. The fallback is a sane single-line height in
     case JS has not run yet.

     NOTE: the same overlap happens on desktop. Left alone here because
     the desktop header has other affordances and this file is the mobile
     layer. Worth fixing site-wide separately. */
  #g:has(.intro-cta-bar.is-visible) #nav { top: var(--intro-bar-h, 46px); }
  #g #nav { transition: border-color 0.3s, top 320ms cubic-bezier(0.22, 1, 0.36, 1); }

  /* The bar outranks the menu sheet too (9000 vs 99), so it would float
     over an open takeover. main.css already hides the bottom bar the same
     way while the menu is open. */
  #g:has(.mobile-menu.open) .intro-cta-bar { display: none; }

  #g .hamburger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end;
    gap: 5px;
    /* 44px is the tap floor, not the visual size. No negative margin: the
       bars are flush to this button's right edge, so the button sitting on
       the gutter puts them on the gutter too, level with the logo. Pulling
       it outward lands the bars in the screen's rounded corner. */
    width: 44px;
    height: 44px;
    padding: 0;
    background: none;
    border: 0;
  }
  #g .hamburger span { display: block; height: 2px; border-radius: 2px; background: var(--ink); }
  #g .hamburger span:nth-child(1) { width: 22px; }
  #g .hamburger span:nth-child(2) { width: 16px; }
  #g .hamburger span:nth-child(3) { width: 22px; }

  /* ── Hide hero CTAs on mobile ────────────────────────────────────────
     The sticky bottom bar (.mobile-cta-bar) always carries Start free
     trial + Book a demo on every page. Repeating the same buttons in
     the hero is redundant noise — the reader already has them a thumb
     length away. Scoped to pages where the sticky bar is actually
     visible (contact and request-demo hide it, so those keep the hero
     button as the only way in).

     Trust text (.hero-note, .vs-hero-trust, .hero-trust) is kept: it
     says "14 days free · No credit card" which the bar cannot fit. */
  #g:not(:has(.contact-wrapper)):not(:has(.rd-wrap)) .hero-actions,
  #g:not(:has(.contact-wrapper)):not(:has(.rd-wrap)) .fp2-hero .fp2-cta,
  #g:not(:has(.contact-wrapper)):not(:has(.rd-wrap)) .vs-hero-cta-row .btn-primary,
  #g:not(:has(.contact-wrapper)):not(:has(.rd-wrap)) .hero:has(.wrap-narrow) .hero-cta .btn-primary,
  #g:not(:has(.contact-wrapper)):not(:has(.rd-wrap)) .fullcmp-hero .btn-primary {
    display: none;
  }
  /* .vs-hero-cta-row and .hero-cta hold a button AND a trust span. The
     button is hidden above; the row becomes just the trust text, which
     needs its own line and spacing since the button is gone. */
  #g .vs-hero-cta-row,
  #g .hero:has(.wrap-narrow) .hero-cta {
    display: block;
    margin-top: 0;
  }

  /* ── MENU SHEET ──────────────────────────────────────────────────────
     Nine equal links become four large primary destinations, a smaller
     secondary shelf in two columns, languages, and the actions pinned at
     the bottom inside thumb reach. Blog, About, Help and Contact are
     things people look for by name rather than browse, so they do not
     need 22px type. */
  #g .mobile-menu {
    display: flex;
    flex-direction: column;
    padding: calc(var(--m-hdr) + 14px) var(--m-gut) 24px;
    overflow-y: auto;
  }
  #g .mobile-menu .mm-primary a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: relative;
    padding: 15px 0 15px 15px;
    font-family: var(--font);
    font-size: 22px;
    font-weight: 500;
    letter-spacing: -0.02em;
    color: #fff;
    text-decoration: none;
    border-bottom: 1px solid rgba(255,255,255,0.09);
  }
  #g .mobile-menu .mm-primary a svg { width: 17px; height: 17px; opacity: .35; }
  /* The current-page bar sits INSIDE the column, not in the gutter, and is
     absolutely positioned so marking a row never shifts its text. Every
     row reserves the 15px whether or not it is the marked one. */
  #g .mobile-menu .mm-primary a[aria-current]::before {
    content: '';
    position: absolute;
    left: 0; top: 50%;
    transform: translateY(-50%);
    width: 3px; height: 18px;
    border-radius: 2px;
    background: #fff;
  }
  #g .mm-secondary {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 16px;
    padding: 22px 0 6px;
  }
  /* main.css:374 gives every .mobile-menu anchor a bottom border and 25px
     Space Grotesk. Right for the four big destinations, wrong for a
     two-column shelf and wrong for buttons, so both are reset here rather
     than left to specificity luck. */
  #g .mobile-menu .mm-secondary a {
    padding: 11px 0;
    font-family: var(--body);
    font-size: 15px;
    font-weight: 500;
    color: rgba(255,255,255,0.62);
    border-bottom: 0;
    text-decoration: none;
  }
  #g .mm-actions { margin-top: auto; padding-top: 22px; display: grid; gap: 9px; }
  #g .mobile-menu .mm-actions a {
    display: block;
    text-align: center;
    padding: 15px;
    border-radius: 11px;
    font-family: var(--body);
    font-size: 15px;
    font-weight: 600;
    border-bottom: 0;
    text-decoration: none;
  }
  #g .mobile-menu .mobile-cta    { background: #fff; color: var(--ink); }
  #g .mobile-menu .mobile-demo   { border: 1px solid rgba(255,255,255,0.26); color: #fff; }
  #g .mobile-menu .mobile-signin { color: rgba(255,255,255,0.62); font-weight: 500; padding: 8px; }

  /* The language switcher sits between the secondary shelf and the
     actions, so it needs a rule above it rather than below. */
  #g .mobile-menu .mobile-lang-switcher {
    border-top: 1px solid rgba(255,255,255,0.09);
    border-bottom: 0;
    margin-top: 10px;
    padding: 14px 0 22px;
  }

  /* ── STICKY ACTION BAR ───────────────────────────────────────────────
     Two actions now, not one. Sign-in stays deliberately absent: the app
     is not usable on a phone, so offering it here is a dead end. */
  #g .mobile-cta-bar {
    gap: 9px;
    padding: 0 var(--m-gut);
    height: calc(var(--m-bar) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    align-items: center;
    transition: transform .3s var(--ease), opacity .25s;
  }
  /* Both buttons scale fluidly with viewport width so text never wraps
     between 360 and 430 while staying comfortable on the wider end.
     min font 13.5 at 360, max 15 at 430; padding scales the same way. */
  #g .mobile-cta-bar .mcta-primary,
  #g .mcta-secondary {
    font-size: clamp(13.5px, 3.9vw, 15px);
    padding: clamp(12px, 3.6vw, 14px) clamp(14px, 4.2vw, 18px);
    border-radius: 11px;
    white-space: nowrap;
  }
  /* Primary + secondary share the bar proportionally. Both grow with
     the bar so they read as a matched pair — the old flex:1 / auto pair
     let the primary sprawl while the secondary stayed put on wider
     phones, and that broke the balance. 60/40 keeps the primary
     visually dominant without exploding it. */
  #g .mobile-cta-bar .mcta-primary { flex: 6 1 0; }
  #g .mcta-secondary {
    flex: 4 1 0;
    font-family: var(--body);
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
  }
  /* Set by mobile.js when a closing CTA section is on screen. Repeating a
     button directly under a larger identical button is noise. */
  #g .mobile-cta-bar.is-hidden { transform: translateY(100%); opacity: 0; pointer-events: none; }
  #g { padding-bottom: calc(var(--m-bar) + env(safe-area-inset-bottom)); }
  /* Pages whose own job collides with a fixed bar: a form (the bar sits on
     the Send button and fights the keyboard toolbar) and the demo page
     (offering "Demo" to someone on the demo page is dead weight, and the
     bar covers the bottom row of calendar slots).

     Keyed off a wrapper the page renders rather than off a slug. NOTE: an
     earlier version of this comment claimed slugs differ per language.
     That was wrong and unverified — Polylang translations on this site
     deliberately SHARE a slug (post_name is "contact" in all six, checked
     in wp_posts joined to the language taxonomy), because the page
     creation scripts force the slug back after setting the language.

     Keeping :has() anyway: it survives a page being renamed or duplicated,
     which a hardcoded slug list does not. main.css already relies on
     :has(), so this needs no new browser support. */
  #g:has(.contact-wrapper) .mobile-cta-bar,
  #g:has(.rd-wrap) .mobile-cta-bar { display: none; }
  #g:has(.contact-wrapper),
  #g:has(.rd-wrap) { padding-bottom: 0; }

  /* ── FOOTER ──────────────────────────────────────────────────────────
     Thirty-one links. main.css flows them into two CSS columns, which is a
     ~700px wall of 14px links that nobody has scrolled to the bottom of.
     Five collapsed accordions are ~250px, and the four links people
     actually want get their own row of buttons above them. */
  #g footer { padding: 0 0 24px; }

  #g .footer-inner {
    display: block;
    columns: auto;
    padding-bottom: 0;
  }
  #g .footer-brand { padding: 40px var(--m-gut) 26px; margin-bottom: 0; }
  #g .footer-tagline { max-width: none; }

  /* Four buttons: the destinations a phone visitor actually opens the
     footer looking for. Everything else stays one tap behind a label. */
  #g .footer-quick {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 0 var(--m-gut) 26px;
  }
  #g .footer-quick a {
    padding: 13px;
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 10px;
    text-align: center;
    font-size: 14px;
    font-weight: 500;
    color: #fff;
    text-decoration: none;
  }

  #g .footer-col-group { display: contents; }
  #g .footer-col {
    display: block;
    border-top: 1px solid rgba(255,255,255,0.1);
    margin-bottom: 0;
    gap: 0;
  }
  /* The h3 is the desktop label. On mobile the button carries the text so
     it is focusable and announces its state; the h3 would be a duplicate. */
  #g .footer-col h3 { display: none; }
  #g .footer-col-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    /* The icon pulls back from the gutter so it does not sit in the
       screen's rounded corner. */
    padding: 16px calc(var(--m-gut) + 10px) 16px var(--m-gut);
    background: none;
    border: 0;
    cursor: pointer;
    font-family: var(--font);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: rgba(255,255,255,0.55);
  }
  #g .footer-col-toggle svg {
    width: 15px; height: 15px;
    stroke: rgba(255,255,255,0.4);
    transition: transform .3s var(--ease);
  }
  #g .footer-col:not(.is-collapsed) .footer-col-toggle svg { transform: rotate(45deg); }

  /* grid-template-rows 0fr to 1fr animates a panel of unknown height with
     no JS measurement. The inner div is what actually clips.

     Default is OPEN, and mobile.js collapses them on load. That way a
     visitor whose JS never runs gets the full footer rather than five
     labels with nothing behind them. */
  #g .footer-col-links {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows .3s var(--ease);
  }
  #g .footer-col.is-collapsed .footer-col-links { grid-template-rows: 0fr; }
  #g .footer-col-links > div { overflow: hidden; }
  #g .footer-col-links a {
    display: block;
    padding: 11px var(--m-gut);
    font-size: 14.5px;
    color: rgba(255,255,255,0.7);
    text-decoration: none;
  }
  #g .footer-col-links > div > a:last-child { padding-bottom: 16px; }

  #g .footer-bottom {
    border-top: 1px solid rgba(255,255,255,0.1);
    padding: 22px var(--m-gut) 0;
    font-size: 12px;
    line-height: 1.7;
    color: rgba(255,255,255,0.4);
  }
  /* ═══════════════════════════════════════════════════════════════════
     HOME
     ═══════════════════════════════════════════════════════════════════ */

  /* ── Hero ────────────────────────────────────────────────────────────
     The desktop order is wordmark, category, statement, two CTAs, trial
     note, trust badges, and only then the video. On a phone that puts the
     single strongest asset on the page roughly 500px below the fold.

     The video moves up to sit directly under the statement. .hero-head
     wraps half of these, so `display: contents` dissolves it and lets all
     of them be ordered as siblings inside .hero. */
  #g .hero {
    min-height: 0;
    padding: var(--m-hero-pad-top) var(--m-gut) 44px;
    justify-content: flex-start;
  }
  #g .hero-head { display: contents; }

  /* The logo is 40px above this in the header. Saying it twice costs about
     90px of the first screen. */
  #g .hero-brand { display: none; }
  /* Nobody needs telling that a phone scrolls. */
  #g .scroll-hint { display: none; }

  #g .hero-cat       { order: 1; }
  #g .hero-statement { order: 2; }
  #g .hvideo-outer   { order: 3; }
  #g .hero-actions   { order: 4; }
  #g .hero-note      { order: 5; }
  #g .hero-trust     { order: 6; }

  #g .hero-statement {
    font-size: 34px;
    line-height: 1.08;
    letter-spacing: -0.042em;
    margin-top: 10px;
    text-align: center;
  }
  /* home.css:31 fixes .hero-accent to green, matching the desktop hero.
     On mobile the whole page rotates around --pa (blue for home), so the
     accent takes it too — same pattern as .fp2-h1-accent on features. */
  #g .hero-statement .hero-accent { color: var(--pa); }
  #g .hvideo-outer { margin-top: 24px; padding: 0; }
  #g .hvideo { box-shadow: 0 8px 30px rgba(0,0,0,0.07); border-radius: 14px; }

  /* One full-width primary. The demo drops to a text link: two equally
     weighted stacked buttons make the reader choose before they have been
     given a reason to. */
  #g .hero-actions {
    display: block;
    width: 100%;
    margin: 22px 0 0;
    text-align: center;
  }
  #g .hero-actions .btn-dark {
    display: block;
    width: 100%;
    text-align: center;
    padding: 16px;
    border-radius: 12px;
    font-size: 16px;
  }
  #g .hero-actions .btn-ghost {
    display: block;
    width: 100%;
    margin-top: 13px;
    padding: 8px;
    background: none;
    border: 0;
    font-size: 14.5px;
    font-weight: 500;
    color: var(--ink-2);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-color: var(--line);
  }
  #g .hero-note {
    font-size: 12.5px;
    font-weight: 500;
    margin-top: 12px;
    text-align: center;
    color: var(--ink-3);
  }
  #g .hero-note-cc { font-size: 1em; }

  /* Three icon items wrap to three lines at 390px. One shield, three
     claims, one line. */
  #g .hero-trust {
    margin-top: 26px;
    padding-top: 20px;
    border-top: 1px solid var(--line);
    flex-wrap: nowrap;
    gap: 6px;
    width: 100%;
  }
  #g .htrust-item { font-size: 11.5px; white-space: nowrap; }
  #g .htrust-item:not(:first-child) svg { display: none; }

  /* ── Worlds gallery ──────────────────────────────────────────────────
     Ten board screenshots in three grids. Stacked one per row that is
     about 4000px of scroll, and a 1600px board squeezed to 350px is a grey
     smudge. Each group becomes a full-bleed snap rail, and the thumbnail
     crops to the LEFT of the board where the item names and status pills
     live, so it reads as real UI instead. */
  #g .wld-group { margin-bottom: 8px; }
  #g .wld-grid {
    display: flex;
    grid-template-columns: none;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Full-bleed out of the section gutter, with the padding put back so
       the first card still lands on the gutter. */
    margin: 0 calc(var(--m-gut) * -1);
    padding: 4px var(--m-gut) 8px;
  }
  #g .wld-grid::-webkit-scrollbar { display: none; }
  #g .wld-card { flex: 0 0 82%; scroll-snap-align: center; }
  #g .wld-thumb { display: block; height: 168px; overflow: hidden; }
  #g .wld-thumb img { width: 190%; height: 100%; object-fit: cover; object-position: left top; }

  /* ── Office band ─────────────────────────────────────────────────────
     The right half is a full Word window: a ribbon, a format bar and two
     named collaborator carets at fixed pixel offsets. There is no honest
     390px version of a ribbon UI, and the three ways in ARE the message,
     so they carry it alone here. */
  #g .of-editor { display: none; }
  #g .office-grid { gap: 0; }

  /* ── Features ────────────────────────────────────────────────────────
     Seven features with a 25-to-45 word explanation each: about 1400px of
     prose nobody reads on a phone. Collapsed, all seven names fit one
     screen, so the scope is graspable at a glance and you open only what
     you care about.

     mobile.js adds .is-enhanced and the toggling. The rules below ship the
     panels OPEN, so a visitor whose JS never runs still gets every word. */
  #g .feat-row { display: block; padding: 0; }
  #g .feat-word {
    width: auto;
    font-size: 18px;
    font-weight: 500;
    letter-spacing: -0.02em;
    padding: 17px 30px 17px 0;
    position: relative;
  }
  #g .feat-row.is-enhanced .feat-word { cursor: pointer; }
  #g .feat-row.is-enhanced .feat-word::after {
    content: '';
    position: absolute;
    right: 10px;
    top: 50%;
    width: 13px;
    height: 13px;
    margin-top: -6px;
    background:
      linear-gradient(var(--ink-3), var(--ink-3)) center/13px 1.8px no-repeat,
      linear-gradient(var(--ink-3), var(--ink-3)) center/1.8px 13px no-repeat;
    transition: transform .3s var(--ease);
  }
  #g .feat-row.is-open .feat-word::after { transform: rotate(45deg); }
  #g .feat-explain {
    display: grid;
    grid-template-rows: minmax(0, 1fr);
    overflow: hidden;
    font-size: 14.5px;
    line-height: 1.58;
    color: var(--ink-2);
    transition: grid-template-rows .32s var(--ease);
  }
  #g .feat-row.is-enhanced .feat-explain { grid-template-rows: minmax(0, 0fr); }
  #g .feat-row.is-enhanced.is-open .feat-explain { grid-template-rows: minmax(0, 1fr); }
  #g .feat-explain > * { min-height: 0; overflow: hidden; }
  #g .feat-row.is-enhanced .feat-explain > * { padding-bottom: 18px; }

  /* ── Testimonials ────────────────────────────────────────────────────
     Desktop runs an infinite auto-scrolling marquee. Auto-motion under a
     thumb is a fight: you start reading and the card leaves. Same cards,
     but the thumb drives. mobile.js removes the duplicated aria-hidden
     half, which only existed to make the loop seamless. */
  #g .testimonials-outer::before,
  #g .testimonials-outer::after { display: none; }
  #g .testimonials-track {
    animation: none;
    width: auto;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    gap: 12px;
    padding: 10px var(--m-gut) 14px;
  }
  #g .testimonials-track::-webkit-scrollbar { display: none; }
  #g .tcard { width: 86%; scroll-snap-align: center; padding: 22px; gap: 14px; }
  #g .tcard-quote { font-size: 15px; line-height: 1.55; letter-spacing: -0.01em; }
  #g .tcard-author { margin-top: 0; }
  #g .tcard-avatar { width: 36px; height: 36px; }
  #g .tcard-name { font-size: 13.5px; }
  #g .tcard-role { font-size: 12px; }

  /* ── Pricing ─────────────────────────────────────────────────────────
     A three-up segmented control plus inline "Save 29%" pills does not fit
     390px without wrapping into a shape that reads as six controls. The
     pills come out; the saving is already stated under each price. */
  #g .toggle-wrap { padding: 20px var(--m-gut) 24px; }
  #g .toggle {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    width: 100%;
    flex-wrap: nowrap;
  }
  #g .toggle-btn { padding: 11px 4px; font-size: 13px; justify-content: center; }
  #g .toggle-btn .save-pill { display: none; }
  /* Appended by mobile.js's initPlanSaveInline. Uses the theme's green
     to keep the "save" callout visible on the plan card's annual line. */
  #g .m-save-inline { color: var(--green, #16a34a); font-weight: 500; }
  #g .pricing-grid { grid-template-columns: 1fr; padding: 0 var(--m-gut); gap: 22px; }
  #g .plan { padding: 22px var(--m-pad-card); }
  /* pricing.css:22 sets the plan name to uppercase + grey. The prototype
     leaves it title-case and dark so the plan reads as a heading, not a
     label. */
  #g .plan-name {
    font-family: var(--font);
    font-size: 18px;
    font-weight: 500;
    letter-spacing: -0.02em;
    text-transform: none;
    color: var(--ink);
  }
  /* pricing.css:23 clamps the price min at 40px with weight 700 and
     letter-spacing -3px. Prototype uses 44px, weight 600, -0.045em. */
  #g .plan-price {
    font-size: 44px;
    font-weight: 600;
    letter-spacing: -0.045em;
    line-height: 1;
    margin-top: 8px;
  }
  #g .plan-price sup {
    font-size: 22px;
    font-weight: 500;
  }
  #g .plan-unit {
    font-size: 13px;
    color: var(--ink-2);
    margin-top: 6px;
  }
  #g .plan-desc {
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--ink-2);
    margin: 12px 0 16px;
  }
  /* pricing.css:35 draws a grey "+" glyph. The prototype uses a green
     tick — the same shape as .rd-facts li::before above. */
  /* Feature list matches prototype's .m-plan-feats: 10px gap, 13.5px
     items, ink-2 colour, and a light dividing rule above. */
  #g .plan-features {
    display: grid;
    gap: 10px;
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
  }
  #g .plan-features li {
    position: relative;
    padding-left: 22px;
    font-size: 13.5px;
    line-height: 1.45;
    color: var(--ink-2);
    display: block;
  }
  #g .plan-features li::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 8px;
    width: 11px;
    height: 6px;
    border-radius: 0;
    background: none;
    border-left: 1.8px solid var(--green, #16a34a);
    border-bottom: 1.8px solid var(--green, #16a34a);
    transform: rotate(-45deg);
    color: transparent;
    font-size: 0;
  }

  /* Six shown, the rest one tap away. A 14-item list is a scroll tax
     standing between the price and the button. mobile.js hides the
     overflow and adds the toggle, so no-JS still gets the full list. */
  #g .plan-features li.is-hidden { display: none; }
  #g .plan-more {
    margin-top: 14px;
    background: none;
    border: 0;
    padding: 4px 0;
    font-family: var(--body);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-color: var(--line);
    cursor: pointer;
  }

  /* ── Board viewer ────────────────────────────────────────────────────
     Ported from the prototype's .m-viewer, behaviour for behaviour
     (prototypes/mobile-2026/mobile.css, "FULL-BLEED IMAGE VIEWER").

     A phone cannot show a 1600px board, so it stops pretending: full
     bleed, the image at a readable 250%, scrolled sideways, with the
     caption and the swipe hint pinned underneath. The hint is not
     decoration; it is what makes 250% read as deliberate rather than
     broken, and leaving it out was the bug. */
  /* The sticky action bar and the intro bar both sit above the page and
     would cover the viewer's caption and hint. main.css already hides the
     action bar this way while the menu is open. */
  #g:has(.wld-lb.open) .mobile-cta-bar,
  #g:has(.wld-lb.open) .intro-cta-bar { display: none; }
  /* Belt and braces for the stacking trap described in mobile.js: even if
     the node is not reparented, the header cannot cover the close button. */
  #g:has(.wld-lb.open) #nav { opacity: 0; pointer-events: none; }

  /* On features and vs-detail the lightbox is built at runtime by
     initShotViewer in mobile.js — home.css is the only file that owns
     the base .wld-lb positioning, and those pages do not load home.css.
     Without this the lightbox rendered inline at the end of <body>
     (visible bug: bottom of page, close button unreachable). Ship the
     base position/backdrop/z-index here so it works on any page the
     JS attaches to. */
  #g .wld-lb {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    justify-content: center;
    padding: 0;
    background: rgba(10, 10, 10, 0.97);
    align-items: stretch;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .3s var(--ease), visibility 0s linear .3s;
  }
  #g .wld-lb.open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity .3s var(--ease), visibility 0s linear 0s;
  }
  #g .wld-frame {
    width: 100%;
    height: 100%;
    max-width: none;
    display: flex;
    flex-direction: column;
    justify-content: center;
    background: transparent;
    border-radius: 0;
    box-shadow: none;
    opacity: 1;
  }

  /* .m-viewer-top: title left, close right.
     Flex layout is defined here (not just in home.css) because features
     and vs-detail build the lightbox at runtime and never load home.css
     — without display:flex + `flex:1` on the title, the close button
     landed on the left, unreachable.

     Top padding of 18px + safe-area gives the X real breathing room
     from the top edge of the viewport (and clears the notch on a
     phone). env() is 0 in a desktop browser, so this only costs the
     18px there. */
  #g .wld-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    background: transparent;
    border-bottom: 0;
    padding: calc(18px + env(safe-area-inset-top)) 14px 14px var(--m-gut);
    flex: 0 0 auto;
  }
  #g .wld-dot { display: none; }
  /* min-width:0 or a long board name refuses to shrink and pushes the
     count and the close button off the right edge. Flex items default to
     min-width:auto, which is the classic version of this bug. */
  #g .wld-title {
    flex: 1;
    margin-left: 0;
    color: #fff;
    font-size: 14px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #g .wld-count { color: rgba(255, 255, 255, 0.5); }
  #g .wld-close {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.18);
    border: 1px solid rgba(255, 255, 255, 0.28);
    color: #fff;
    font-size: 24px;
    line-height: 1;
    flex: 0 0 auto;
    /* The frame is full bleed now, so there is no backdrop left to tap:
       this button is the only way out and has to look like it. */
    position: relative;
    z-index: 2;
  }
  #g .wld-close:hover { background: rgba(255, 255, 255, 0.2); color: #fff; }

  /* .m-viewer-scroll, built by mobile.js so the bar and hint stay put. */
  #g .wld-scroll {
    flex: 1;
    overflow: auto;
    display: flex;
    align-items: center;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  #g .wld-scroll::-webkit-scrollbar { height: 0; }
  /* Every image opens the same way: contained in the viewport, no
     sideways scroll, no swipe hint. Users pinch-zoom for detail. The
     old 250% behaviour was inconsistent — wide boards got scrolled
     sideways while portrait dialogs blew up unreadably. One rule, one
     expectation, every screenshot.

     max-height 82vh keeps the image inside the viewport when the
     .wld-scroll container is shrink-to-fit (flex: 0 0 auto) so the
     close bar can sit right above the image, and the pair stays
     centred. Without a viewport-based cap the image reaches out to
     natural size and the bar drifts off the top edge. */
  #g .wld-frame img,
  #g .wld-scroll img {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 82vh;
    object-fit: contain;
    margin: 0 auto;
  }
  #g .wld-scroll {
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex: 0 0 auto;
  }
  /* Caption row is empty now (title lives in the top bar) — collapse
     it so the image gets the full remaining height. */
  #g .wld-lb .wld-cap:empty { display: none; }
  /* home.js still toggles .wld-zoomed on click (assets/js/home.js:100-107).
     At 250% that scale is redundant and disorienting, so the class is left
     inert rather than unbinding a listener this file does not own. */
  #g .wld-frame img.wld-zoomed { transform: none; }

  /* .m-viewer-hint */
  #g .wld-cap {
    flex: 0 0 auto;
    border-top: 0;
    padding: 12px var(--m-gut) calc(22px + env(safe-area-inset-bottom));
    color: rgba(255, 255, 255, 0.55);
    font-size: 12px;
    text-align: center;
    line-height: 1.5;
  }
  #g .wld-cap b { color: #fff; font-weight: 600; }
  #g .wld-nav { width: 40px; height: 40px; }

  /* ── COLOUR ──────────────────────────────────────────────────────────
     The palette is --ink / --ink-2 / --ink-3 and almost nothing else, so
     on a phone every heading lands at the same weight and a long scroll
     has no rhythm. This is the "Both" treatment from the prototype: the
     page carries one identity colour, and each section rotates onward
     from it.

     No new colours. All nine are the --fp2-* values features.css has been
     shipping since it was written (assets/css/features.css:7-9).

     --pa and --c1..--c9 are emitted per page by functions.php, already
     rotated so that section 1 IS the page's identity colour.

     Home's identity is deliberately GREEN, because .hero-statement
     .hero-accent is already green (assets/css/home.css:31) and this
     should add rhythm, not restyle the brand. */

  /* Content column cap for wider mobile viewports (foldables unfolded,
     small tablets). Applied to <main> and <footer> so the reading
     column stays sane at 700–768px. Fixed nav / sticky bar / mobile
     menu stay full-bleed — a full-width chrome above a centred column
     is the tablet pattern most native apps use, and keeping the fixed
     items full-bleed avoids fighting main.css's transform animations. */
  #g main,
  #g footer {
    max-width: var(--m-content-max);
    margin-left: auto;
    margin-right: auto;
  }

  /* .reveal starts at opacity:0 and waits for an IntersectionObserver in
     main.js to add .visible. On a phone the fold and the viewport are the
     same size, so any element the reader hasn't scrolled into view yet is
     invisible — and if the observer misses one (iframe timing, back/forward
     restore), it stays invisible. Ship them shown by default; the fade-in
     is a nice-to-have that costs nothing to skip on mobile. */
  #g .reveal { opacity: 1; transform: none; }

  /* Sections on the home page are direct children of <main> (front-page.php:2),
     so they can be counted. Interior pages wrap their content, so they take
     the page colour without rotating. */
  /* Home renders sections as direct children of <main> (front-page.php:2).
     Features wraps them in `.fp2` (features-body.html:3), vs-detail in
     `.vs-wrap`, use-cases inside `<main>` too. Rotation is the same list
     in every case; the selectors just have to reach the wrapper. */
  /* Partmark colours are pinned explicitly below — not folded into the
     rotation — so the rotation stays a clean count over sections. */
  #g main > section:nth-of-type(9n+1),
  #g .fp2 > section:nth-of-type(9n+1) { --sec: var(--c1); }
  #g main > section:nth-of-type(9n+2),
  #g .fp2 > section:nth-of-type(9n+2) { --sec: var(--c2); }
  #g main > section:nth-of-type(9n+3),
  #g .fp2 > section:nth-of-type(9n+3) { --sec: var(--c3); }
  #g main > section:nth-of-type(9n+4),
  #g .fp2 > section:nth-of-type(9n+4) { --sec: var(--c4); }
  #g main > section:nth-of-type(9n+5),
  #g .fp2 > section:nth-of-type(9n+5) { --sec: var(--c5); }
  #g main > section:nth-of-type(9n+6),
  #g .fp2 > section:nth-of-type(9n+6) { --sec: var(--c6); }
  #g main > section:nth-of-type(9n+7),
  #g .fp2 > section:nth-of-type(9n+7) { --sec: var(--c7); }
  #g main > section:nth-of-type(9n+8),
  #g .fp2 > section:nth-of-type(9n+8) { --sec: var(--c8); }
  #g main > section:nth-of-type(9n+9),
  #g .fp2 > section:nth-of-type(9n+9) { --sec: var(--c9); }

  /* home.css:498 flips .context-strip to a dark canvas below 900px.
     The prototype keeps it on a light card, with the section rule and the
     dark H2 doing the "next chapter" work instead of an inverted colour
     block. This restores the light look. */
  #g .context-strip {
    background: var(--surface);
    border-top-color: var(--line);
    border-bottom-color: var(--line);
  }
  #g .context-strip h2 { color: var(--ink); }
  #g .context-strip p { color: var(--ink-2); }

  /* Every eyebrow in the theme, so interior pages pick this up too. */
  #g .hero-cat,
  #g .eyebrow,
  #g .office-eyebrow,
  #g .fp2-eyebrow,
  #g .fp2-eyebrow2,
  #g .vs-eyebrow,
  #g .vs-section-eyebrow,
  #g .bundle-eyebrow,
  #g .gaps-eyebrow,
  #g .strip-eyebrow,
  #g .fullcmp-eyebrow,
  #g .pricing-eyebrow,
  #g .blog-hero-eyebrow,
  #g .tt-eyebrow { color: var(--sec, var(--pa)); }

  /* .blog-hero-eyebrow has no default sizing — archive.php inlines it and
     no other stylesheet touches it. Give it the same shape the other
     mobile eyebrows carry (uppercase, tracked, small). */
  #g .blog-hero-eyebrow {
    font-family: var(--font);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    margin-bottom: 14px;
  }

  /* A short rule above each section heading. On a phone this is the thing
     that says where one idea stops and the next begins, which is what the
     desktop's whitespace and column widths were doing. Listed explicitly
     rather than as a blanket h2 rule, because the theme nests its headings
     differently in every section. */
  #g .context-strip > h2::before,
  #g .wld-head > h2::before,
  #g .features-header > h2::before,
  #g .testimonials-header > h2::before,
  #g .pricing-header > h2::before,
  #g .office-grid h2::before,
  #g .final-section > h2::before,
  #g .cta-strip > h2::before {
    content: '';
    display: block;
    width: 30px;
    height: 3px;
    border-radius: 2px;
    background: var(--sec, var(--pa));
    margin-bottom: 15px;
  }
  /* The closing blocks run on --ink, where a saturated accent vibrates.
     They are also centred, so the rule centres with them. */
  #g .final-section > h2::before,
  #g .cta-strip > h2::before {
    background: rgba(255, 255, 255, 0.35);
    margin-left: auto;
    margin-right: auto;
  }
  #g .final-section .eyebrow,
  #g .cta-strip .eyebrow { color: rgba(255, 255, 255, 0.5); }

  /* Closing block on a phone: prototype uses 27px H2, 54px vertical
     padding, 15px lede at 62% white. Theme's clamp values expand to
     44px H2 and 100px padding at the desktop end, which is oversized
     for a 390px canvas. */
  #g .final-section,
  #g .cta-strip,
  #g .fp2 .fp2-final {
    padding-top: 54px;
    padding-bottom: 54px;
    text-align: center;
  }
  #g .final-section h2,
  #g .cta-strip h2,
  #g .fp2 .fp2-final h2 {
    font-size: 27px;
    line-height: 1.15;
    letter-spacing: -0.03em;
  }
  #g .final-section p,
  #g .cta-strip p,
  #g .fp2 .fp2-final p {
    margin-top: 12px;
    font-size: 15px;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.62);
  }
  /* Buttons in the closing block: full-width, stacked, prototype has a
     10px gap between two of them. */
  #g .final-section a.btn-light,
  #g .final-section a.btn-dark,
  #g .final-section .btn-primary,
  #g .cta-strip a.btn-light,
  #g .cta-strip a.btn-dark,
  #g .cta-strip .btn-primary,
  #g .fp2 .fp2-final a {
    display: block;
    width: 100%;
    text-align: center;
    padding: 16px;
    border-radius: 12px;
    font-size: 16px;
    margin-top: 24px;
  }
  #g .final-section a + a,
  #g .cta-strip a + a,
  #g .fp2 .fp2-final a + a { margin-top: 10px; }

  /* ── Worlds chips, rail and dots ─────────────────────────────────────
     mobile.js merges the three group grids into one rail and turns the
     group labels into filter chips. These style what it builds. */
  #g .wld-chips {
    display: flex;
    gap: 7px;
    overflow-x: auto;
    padding: 18px var(--m-gut) 4px;
    margin: 0 calc(var(--m-gut) * -1);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  #g .wld-chips::-webkit-scrollbar { display: none; }
  #g .wld-chip {
    flex: 0 0 auto;
    padding: 9px 15px;
    border-radius: 100px;
    border: 1px solid var(--line);
    background: var(--surface);
    font-family: var(--body);
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-2);
    white-space: nowrap;
    cursor: pointer;
  }
  #g .wld-chip.is-on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
  #g .wld-dots { display: flex; justify-content: center; gap: 6px; padding-top: 14px; }
  #g .wld-dot-i {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--line);
    transition: all .25s;
  }
  #g .wld-dot-i.is-on { background: var(--ink); width: 18px; border-radius: 3px; }

  /* ── One gutter, everywhere ──────────────────────────────────────────
     37 section wrappers across 11 stylesheets set their horizontal padding
     to clamp(20px, 5.6vw, 48px). Below the 768px breakpoint that is not a
     constant: it is 20px up to a 357px viewport, then tracks 5.6vw, so it
     is 21.8px on a 390px phone and 24px on a 430px one. Different pages
     also mix it with their own values, so the left edge does not agree
     from one section to the next.

     Every one of them is pinned to --m-gut here. This is what makes the
     full-bleed rails work: they use margin: 0 calc(var(--m-gut) * -1) to
     escape the gutter, which is only correct if the gutter really is
     --m-gut.

     Generated from a scan of assets/css/*.css for that clamp in a
     horizontal padding slot; re-run it if a new section is added. */
  #g .story,
  #g .values-section,
  #g .facts-section,
  #g .team-section,
  #g .location-section,
  #g .cta-strip,
  #g .contact-wrapper,
  #g .hero,
  #g .context-strip,
  #g .worlds-section,
  #g .worlds-header,
  #g .worlds-scroll,
  #g .preview-section,
  #g .features-section,
  #g .proof-section,
  #g .testimonials-header,
  #g .final-section,
  #g .wld-inner,
  #g .office-band,
  #g .hvideo-outer,
  #g .pricing-section,
  #g .toggle-wrap,
  #g .pricing-grid,
  #g .programs,
  #g .sso-section,
  #g .compare-section,
  #g .faq-section,
  #g .ptm-section,
  #g .reg-hero,
  #g .reg-card,
  #g .reg-features,
  #g .rd-wrap,
  #g .early-access-wrap,
  #g .legend,
  #g .request-section,
  #g .tt-wrap,
  #g .cat-section {
    padding-left: var(--m-gut);
    padding-right: var(--m-gut);
  }

  /* Three of those wrappers are nested inside another one on the home
     page, where inheriting the gutter twice would inset them to 40px. On
     /pricing/ the same two are top level (template-parts/pricing-body.html
     :8 and :17), so the exception is scoped to the nesting rather than
     applied to the selector. */
  #g .hero .hvideo-outer,
  #g .pricing-section .toggle-wrap,
  #g .pricing-section .pricing-grid {
    padding-left: 0;
    padding-right: 0;
  }

  /* ═══════════════════════════════════════════════════════════════════
     INTERIOR PAGE HEROES
     Desktop drives these from --hero-pad-top: clamp(80px, 10vw, 140px)
     (main.css:26), which on a phone spends a third of the first screen on
     empty space above the H1. 26px is enough under a 52px sticky header.
     ═══════════════════════════════════════════════════════════════════ */
  #g .page-hero,
  #g .vs-hero,
  #g .fp2-hero,
  #g .fullcmp-hero,
  #g .blog-hero {
    padding-top: var(--m-hero-pad-top);
    padding-bottom: 30px;
    padding-left: var(--m-gut);
    padding-right: var(--m-gut);
    text-align: left;
  }
  #g .page-hero h1,
  #g .vs-hero h1,
  #g .fp2-hero h1,
  #g .fullcmp-hero h1,
  #g .blog-hero h1 {
    font-size: 32px;
    line-height: 1.09;
    letter-spacing: -0.04em;
  }
  #g .page-hero p,
  #g .vs-hero-lede,
  #g .fp2-lede,
  #g .fullcmp-lede,
  #g .blog-hero p {
    font-size: 15.5px;
    line-height: 1.6;
    margin-left: 0;
    margin-right: 0;
    max-width: none;
  }
  /* Hero CTA rows: one full-width button, trust line underneath. */
  #g .vs-hero-cta-row,
  #g .hero-cta {
    display: block;
    width: 100%;
  }
  #g .vs-hero-cta-row .btn-primary,
  #g .hero-cta .btn-primary,
  #g .fp2-cta {
    display: block;
    width: 100%;
    text-align: center;
    padding: 16px;
    border-radius: 12px;
    font-size: 16px;
  }
  #g .vs-hero-trust {
    display: block;
    text-align: center;
    margin-top: 12px;
    font-size: 12.5px;
  }

  /* ═══════════════════════════════════════════════════════════════════
     USE CASES INDEX
     Seventeen cards, each carrying an emoji, title, who, a three-line
     description, a "how they use it" label, four bullets and four tags.
     One per row that is about 4000px of scrolling on a page whose only
     job is to route you to a sub-page.

     Two-up tiles instead. The description, bullets and tags come off HERE
     ONLY: every one of the seventeen has its own page that opens with the
     same pitch at full length (template-parts/uc-freelancers-body.html:5
     carries hero_lede, :13 carries verdict_p).
     ═══════════════════════════════════════════════════════════════════ */
  #g .uc-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  #g .uc-body { padding: 13px 13px 15px; }
  #g .uc-emoji { font-size: 24px; margin-bottom: 9px; }
  #g .uc-card h3 { font-size: 15.5px; letter-spacing: -0.015em; }
  #g .uc-who { font-size: 11.5px; line-height: 1.35; margin-bottom: 0; }
  #g .uc-card p,
  #g .uc-how,
  #g .uc-features,
  #g .uc-tags { display: none; }
  #g .cat-section { padding-top: 30px; padding-bottom: 0; }
  #g .cat-header { margin-bottom: 14px; }
  #g .cat-header h2 { font-size: 21px; }
  #g .cat-header p { font-size: 13.5px; margin-top: 6px; }

  /* vs.css:7 gives .vs-wrap 64px of top padding for desktop. The mobile
     .vs-hero already carries --m-hero-pad-top for the fixed nav, so the
     wrap's extra 64px is dead whitespace on a phone. */
  #g .vs-wrap { padding-top: 0; padding-bottom: 40px; }
  #g .vs-wrap, #g .vs-wrap--no-crumb { padding-left: var(--m-gut); padding-right: var(--m-gut); }

  /* ═══════════════════════════════════════════════════════════════════
     COMPARISONS INDEX
     ═══════════════════════════════════════════════════════════════════ */
  #g .vs-index-grid { grid-template-columns: 1fr; gap: 12px; }
  /* "Wide" is meaningless in one column. */
  #g .vs-index-card--wide { grid-column: auto; }
  #g .vs-index-card { padding: 18px var(--m-pad-card); }
  #g .vs-index-card-title { font-size: 19px; }
  #g .vs-index-card-sub { font-size: 13.5px; line-height: 1.5; }
  /* The arrow is a hover affordance on desktop. There is no hover here, so
     it becomes a permanent part of the row. */
  #g .vs-index-card-arrow { transform: none; opacity: 1; }

  /* Both promo boxes are text-left / button-right with white-space:nowrap
     on the button. At 390px that leaves the button ~120px and the label
     does not fit, so they stack and the button goes full width.
     Also drop the desktop blue tint (vs.css:570) — the prototype puts
     this on the same sheetish grey band every other section uses, so a
     coloured card does not fight the eyebrow. */
  #g .vs-index-mw-box {
    display: block;
    padding: 22px var(--m-gut);
    margin: 0 calc(var(--m-gut) * -1);
    background: var(--surface);
    border: 0;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    border-radius: 0;
  }
  #g .vs-index-mw-box .btn-primary {
    display: block;
    width: 100%;
    margin-top: 18px;
    text-align: center;
    white-space: normal;
  }
  #g .vs-cta-row { display: block; }
  #g .vs-cta-row > a { display: block; width: 100%; text-align: center; }
  #g .vs-cta-row > a + a { margin-top: 10px; }

  /* ═══════════════════════════════════════════════════════════════════
     REQUEST A DEMO
     Desktop reads rail-left then booker-right. Stacked, one has to come
     first and it should be the booker: everyone here tapped a "Book a
     demo" CTA, so they already decided. "How the call goes" becomes
     reassurance underneath rather than a toll gate in front.

     Everything past the tap is Cal.com in a cross-origin iframe
     (template-parts/request-demo-body.php:118 loads app.cal.com), so the
     only lever this file has is the iframe element itself.
     ═══════════════════════════════════════════════════════════════════ */
  #g .rd-grid { display: flex; flex-direction: column; gap: 34px; }
  #g .rd-booker { order: 1; }
  #g .rd-rail   { order: 2; }
  #g .rd-gate { padding: 26px var(--m-pad-card); border-radius: 15px; }
  #g .rd-gate h2 { font-size: 22px; }
  #g .rd-gate p { font-size: 14px; max-width: none; }
  #g .rd-gate-btn { display: block; width: 100%; }
  #g .rd-gate-note { max-width: none; }
  #g .rd-rail h2 { font-size: 20px; }
  #g .rd-step { gap: 13px; margin-bottom: 18px; }
  #g .rd-fallback { margin-top: 36px; padding-top: 28px; }
  #g .rd-fallback p { max-width: none; }
  #g .rd-fallback .btn-outline { display: block; width: 100%; text-align: center; }

  /* ═══════════════════════════════════════════════════════════════════
     ABOUT
     ═══════════════════════════════════════════════════════════════════ */
  #g .story p { font-size: 15.5px; line-height: 1.7; }
  /* A full-width rule between two text blocks reads as the page ending. */
  #g .divider { display: none; }
  /* The grid already collapses to one column; as bordered rows the six
     values at least read as a set rather than six loose paragraphs. */
  #g .values-grid { display: block; }
  #g .value-item { padding: 16px 0; border-top: 1px solid var(--line); }
  #g .value-item:last-child { border-bottom: 1px solid var(--line); }
  #g .value-item h3 { font-size: 17px; }
  #g .value-item p { font-size: 14px; line-height: 1.55; margin-top: 6px; }
  /* One row of four at 390px is ~85px per stat, not enough for "Dashboard
     widgets" under a 28px number. */
  #g .facts-grid {
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: 14px;
    overflow: hidden;
  }
  #g .facts-grid > div { background: var(--surface); padding: 20px var(--m-pad-row); text-align: center; }
  #g .fact-num { font-size: 28px; }
  #g .fact-label { font-size: 12px; line-height: 1.4; margin-top: 6px; }

  /* ═══════════════════════════════════════════════════════════════════
     CONTACT
     ═══════════════════════════════════════════════════════════════════ */
  #g .contact-wrapper { max-width: none; }
  /* Form first, which the DOM already does: .contact-form precedes
     .contact-info in template-parts/contact-body.php:8-12. .contact-wrapper
     is a block (contact.css:6), so an `order` here would be inert. */
  #g .contact-form { margin-bottom: 34px; }
  #g .info-grid { display: block; }
  #g .info-block { padding: 16px 0; border-top: 1px solid var(--line); }
  #g .info-block:last-child { border-bottom: 1px solid var(--line); }
  #g .info-block h3 { font-size: 12px; letter-spacing: 1.3px; }
  #g .info-block p { font-size: 14.5px; line-height: 1.55; margin-top: 7px; }

  /* ═══════════════════════════════════════════════════════════════════
     BLOG INDEX
     The card is a 200px gradient panel with one emoji in it, then tag,
     title, a 25-word excerpt and a read time: about 330px per post, so
     nine posts is ~3000px and two fit on screen. As a row it is ~90px and
     the gradient survives as a 58px tile.
     ═══════════════════════════════════════════════════════════════════ */
  #g .blog-chips { padding: 0 var(--m-gut) 4px; margin: 0; }
  #g .blog-grid {
    display: block;
    padding: 20px var(--m-gut) 44px;
  }
  #g .card {
    display: flex;
    flex-direction: row;
    gap: 13px;
    align-items: flex-start;
    padding: 14px 0;
    border: 0;
    border-top: 1px solid var(--blog-line, var(--line));
    border-radius: 0;
    background: none;
    box-shadow: none;
  }
  #g .card:last-child { border-bottom: 1px solid var(--blog-line, var(--line)); }
  #g .card-img {
    flex: 0 0 58px;
    width: 58px;
    height: 58px;
    border-radius: 12px;
    border: 1px solid var(--line);
  }
  #g .card-img .icon { font-size: 24px; }
  #g .card-body { padding: 0; }
  /* Prototype's blog tag is a small grey label (--ink-3), not the theme's
     bright blog-accent. Same treatment across every category — no
     per-color mapping. */
  #g .card-tag {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .5px;
    margin-bottom: 5px;
    color: var(--ink-3);
  }
  #g .card-title { font-size: 16px; line-height: 1.25; }
  /* The titles already do the selling. "Done Doesn't Mean Paid. Here's How
     to Close That Gap." does not need 25 more words under it at 390px, and
     the deck is waiting at the top of the article. */
  #g .card-excerpt { display: none; }
  #g .card-meta { margin-top: 6px; padding-top: 0; border-top: 0; }
  #g .card-arrow { display: none; }

  /* ═══════════════════════════════════════════════════════════════════
     ARTICLE
     ═══════════════════════════════════════════════════════════════════ */
  #g .article-wrap { padding: 20px var(--m-gut) 48px; max-width: none; }
  /* Prototype pairs the back link with the category pill on one row.
     Theme renders them in two blocks; mobile.js hoists the .article-tag
     into .blog-back-wrap and this rule styles that combined row. */
  #g .blog-back-wrap:has(.article-tag) {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 18px;
  }
  #g .blog-back-wrap:has(.article-tag) .article-tag {
    margin: 0;
    padding: 3px 9px;
    border: 1px solid var(--line);
    border-radius: 100px;
    background: var(--surface);
    font-size: 10.5px;
    color: var(--ink-3);
    letter-spacing: 1px;
  }
  #g .article-title {
    font-family: var(--font);
    font-size: 30px;
    font-weight: 600;
    line-height: 1.12;
    letter-spacing: -0.04em;
    margin-bottom: 14px;
  }
  #g .article-deck {
    font-size: 17px;
    line-height: 1.55;
    color: var(--ink-2);
    margin-bottom: 18px;
    font-weight: 400;
  }
  #g .article-meta {
    font-size: 12.5px;
    color: var(--ink-3);
    padding-bottom: 20px;
    margin-bottom: 26px;
    gap: 8px;
  }
  /* Larger and looser than anywhere else in the design: an article is the
     one thing here people read rather than scan. */
  #g .article-body p,
  #g .article-body ul li { font-size: 17px; line-height: 1.75; color: #2a2a2a; }
  #g .article-body h2 { font-size: 22px; margin-top: 30px; }
  #g .article-body h3 { font-size: 18px; margin-top: 24px; }
  #g .article-body p { margin-top: 15px; }
  #g .article-body ul { margin-top: 15px; padding-left: 20px; }
  #g .article-body li { margin-top: 8px; }
  #g .article-cta {
    padding: 22px var(--m-pad-card);
    border-radius: 15px;
    text-align: left;
  }
  #g .article-cta h3 { font-size: 20px; }
  #g .article-cta p { font-size: 14px; margin-bottom: 18px; }
  #g .article-cta a { display: block; width: 100%; text-align: center; padding: 15px; }

  /* Blog posts embed hand-built board mockups whose grids are fixed pixel
     tracks (grid-template-columns: 32px 1fr 150px 100px 70px is a 352px
     minimum before padding). At 390px, minus the gutter, the content box
     is 350px, so the grid is wider than its container.

     blog.css:204 sets `.mockup { overflow: hidden }`, so the page does not
     scroll sideways. It does something worse: the right-hand columns are
     SILENTLY CLIPPED and unreachable, with nothing on screen saying they
     exist. blog.css's only mobile handling for these is font-size:11px
     below 480px (blog.css:673), which does not move a pixel track.

     The markup lives inside post_content and cannot be restyled post by
     post, so the fix is here: let the mockup scroll instead of clipping. */
  #g .article-body .mockup {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  #g .article-body .mockup::-webkit-scrollbar { height: 0; }

  /* ═══════════════════════════════════════════════════════════════════
     FEATURES
     ═══════════════════════════════════════════════════════════════════ */

  /* Hero: second half of the H1 is the identity colour, like every
     .accent span on the prototype's hero headlines. Features has this
     wrapped in the template (.fp2-h1-accent, features-body.html:8);
     every other interior hero is wrapped at runtime by mobile.js's
     initHeadlineAccent — the class name matches (.m-accent). */
  #g .fp2-hero h1 .fp2-h1-accent,
  #g h1 .m-accent { color: var(--pa, var(--fp2-blue)); }

  /* The two part markers ("Everything you'd compare us on." and "And
     here's what nobody else does.") are the page's whole structural
     signpost. Centred, with the section rule above the H2 exactly like
     every other section header in the mobile layer. */
  #g .fp2 .fp2-partmark { padding: 36px var(--m-gut) 0; text-align: center; }
  #g .fp2 .fp2-partmark h2 {
    font-size: 25px;
    letter-spacing: -0.035em;
    line-height: 1.15;
  }
  /* Rule colour: matches the prototype's section-rotation. The features
     page's partmark #1 lands on --c2 there, partmark #2 on --c5, because
     they are counted among all <section> siblings. In the theme both
     partmarks are divs mixed with sections at the same level, so
     nth-of-type does not agree — pin the colours explicitly. */
  #g .fp2 .fp2-partmark { --sec: var(--c2); }
  #g .fp2 .fp2-partmark--2 { --sec: var(--c5); }
  #g .fp2 .fp2-partmark h2::before {
    content: '';
    display: block;
    width: 30px;
    height: 3px;
    border-radius: 2px;
    background: var(--sec, var(--pa));
    margin: 0 auto 15px;
  }
  #g .fp2 .fp2-partmark p {
    font-size: 14.5px;
    line-height: 1.55;
    max-width: none;
    margin-top: 10px;
    color: var(--ink-2);
  }

  /* The nine capability cards carry 42 bullets between them: in one column
     that is roughly 2400px of ticked list before the reader reaches
     anything they had not already assumed. mobile.js turns each card's h3
     into a toggle; the CSS ships them OPEN so no-JS keeps all 42. */
  #g .fp2 .fp2-checklist { padding-left: var(--m-gut); padding-right: var(--m-gut); }
  #g .fp2 .fp2-cap-grid { display: block; }
  #g .fp2 .fp2-cap {
    border: 0;
    border-top: 1px solid var(--line);
    border-radius: 0;
    padding: 0;
    box-shadow: none;
    background: none;
    position: relative;
  }
  #g .fp2 .fp2-cap:last-child { border-bottom: 1px solid var(--line); }
  /* The desktop card's 3px coloured top border becomes a marker inside the
     column, on the same gutter as everything else. */
  #g .fp2 .fp2-cap::before {
    content: '';
    position: absolute;
    left: 0;
    top: 19px;
    width: 3px;
    height: 20px;
    border-radius: 2px;
    background: var(--cap, var(--pa));
  }
  #g .fp2 .fp2-cap h3 {
    margin: 0;
    padding: 17px 30px 17px 15px;
    font-family: var(--font);
    font-size: 17px;
    font-weight: 500;
    letter-spacing: -0.02em;
    text-transform: none;
    color: var(--ink);
    position: relative;
  }
  #g .fp2 .fp2-cap.is-enhanced h3 { cursor: pointer; }
  #g .fp2 .fp2-cap.is-enhanced h3::after {
    content: '';
    position: absolute;
    right: 10px;
    top: 50%;
    width: 13px;
    height: 13px;
    margin-top: -6px;
    background:
      linear-gradient(var(--ink-3), var(--ink-3)) center/13px 1.8px no-repeat,
      linear-gradient(var(--ink-3), var(--ink-3)) center/1.8px 13px no-repeat;
    transition: transform .3s var(--ease);
  }
  #g .fp2 .fp2-cap.is-open h3::after { transform: rotate(45deg); }
  /* grid-template-rows animation: the min side of each track has to be 0
     explicitly (minmax(0, ...)) because the default min is `auto`, which
     resolves to the child's min-content and refuses to shrink below it —
     seen once as 18px of one <li> bleeding through the closed accordion. */
  #g .fp2 .fp2-cap ul {
    display: grid;
    grid-template-rows: minmax(0, 1fr);
    overflow: hidden;
    transition: grid-template-rows .32s var(--ease);
  }
  #g .fp2 .fp2-cap.is-enhanced ul { grid-template-rows: minmax(0, 0fr); }
  #g .fp2 .fp2-cap.is-enhanced.is-open ul { grid-template-rows: minmax(0, 1fr); }
  #g .fp2 .fp2-cap ul > div { min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 11px; padding-left: 15px; }
  #g .fp2 .fp2-cap.is-enhanced ul > div { padding-bottom: 18px; }

  /* Count badge on the row ("Views 5"). Says the size of what is behind
     the tap without opening it. Injected by mobile.js. */
  #g .fp2 .fp2-cap h3 .fp2-cap-count {
    margin-left: auto;
    margin-right: 22px;
    font-family: var(--body);
    font-size: 12px;
    font-weight: 500;
    color: var(--ink-3);
  }
  #g .fp2 .fp2-cap h3 { display: flex; align-items: center; }

  /* Five tabs do not fit 390px, so they scroll instead of wrapping into
     two ragged rows. */
  #g .fp2 .fp2-tabs {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin: 0 calc(var(--m-gut) * -1);
    padding: 0 var(--m-gut) 4px;
  }
  #g .fp2 .fp2-tabs::-webkit-scrollbar { display: none; }
  #g .fp2 .fp2-tab { flex: 0 0 auto; padding: 9px 15px; font-size: 13px; }

  /* Desktop alternates image-left / text-right down the page. In one
     column that alternation means nothing, so every block becomes the same
     card: screenshot, tag, name, heading, body, kicker under a rule. */
  #g .fp2 .fp2-feat,
  #g .fp2 .fp2-showcase {
    display: block;
    padding: 0;
    margin-bottom: 18px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface);
    overflow: hidden;
    text-align: left;
  }
  /* features.css:78 centres the showcase eyebrow's tag row on desktop.
     In a left-aligned card that row starts on the gutter. */
  #g .fp2 .fp2-showcase .fp2-feat-eyebrow { justify-content: flex-start; }
  /* Prototype's tag on the "only Gript" cards is a solid dark pill,
     stronger than features.css:70's soft amber tint (harder to spot on
     a phone). And the feature name next to it is small title-case ink-3,
     not uppercase bold — features.css:71 is too heavy at 390px. */
  #g .fp2 .fp2-uniq {
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
    font-size: 10px;
    letter-spacing: 0.05em;
    padding: 3px 9px;
  }
  #g .fp2 .fp2-feat-name {
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--ink-3);
    margin-left: 6px;
  }
  /* features.css:80 also caps and centres the showcase paragraph. */
  #g .fp2 .fp2-showcase p { margin-left: 0; margin-right: 0; max-width: none; }
  #g .fp2 .fp2-feat-img,
  #g .fp2 .fp2-showcase .fp2-shot {
    border: 0;
    border-radius: 0;
    border-bottom: 1px solid var(--line);
    box-shadow: none;
    max-height: 210px;
    overflow: hidden;
  }
  /* Wide screenshots crop to their top-left, where the item names and
     status pills are; portrait ones (dialogs, panels) are contained,
     because with a dialog the whole point is the dialog. */
  #g .fp2 .fp2-feat-img img { width: 190%; max-width: none; display: block; }
  #g .fp2 .fp2-portrait img { width: 100%; max-width: 100%; height: 210px; object-fit: contain; background: #f7f7f7; }
  #g .fp2 .fp2-feat-txt { padding: 16px var(--m-pad-card) 18px; }
  #g .fp2 .fp2-showcase > :not(.fp2-shot) { padding-left: var(--m-pad-card); padding-right: var(--m-pad-card); }
  #g .fp2 .fp2-feat-txt h3,
  #g .fp2 .fp2-showcase h3 { font-size: 19px; margin-top: 11px; }
  #g .fp2 .fp2-feat-txt p,
  #g .fp2 .fp2-showcase p { font-size: 14px; line-height: 1.55; }
  #g .fp2 .fp2-kicker {
    margin-top: 11px;
    padding-top: 11px;
    border-top: 1px solid var(--line);
    font-size: 13px;
    color: var(--ink-3);
  }
  #g .fp2 .fp2-final { padding-left: var(--m-gut); padding-right: var(--m-gut); }

  /* ═══════════════════════════════════════════════════════════════════
     VS DETAIL
     ═══════════════════════════════════════════════════════════════════ */

  /* Interior vs-detail pages (monday-com, asana, clickup, notion, trello,
     migration-wizards) use `<section class="hero">` with `.wrap-narrow`
     inside, NOT the `.vs-hero` structure the /vs/ index uses. Desktop
     centres them (vs.css:621); on a phone the prototype's .m-phero is
     left-aligned with the eyebrow pill sitting on the gutter. */
  #g .hero:has(.wrap-narrow) {
    padding: var(--m-hero-pad-top) 0 30px;
    text-align: left;
    background: var(--bg);
  }
  /* vs.css:618 gives .wrap-narrow its own 24px gutter; reset to --m-gut
     so full-bleed rails elsewhere line up. */
  #g .hero > .wrap-narrow,
  #g .wrap-narrow,
  #g .wrap,
  #g .wrap-wide { padding-left: var(--m-gut); padding-right: var(--m-gut); max-width: none; }

  /* Breadcrumb injected by mobile.js above the H1. Matches the prototype's
     .m-crumb: small grey text, "Comparisons > vendor". */
  #g .hero:has(.wrap-narrow) .m-crumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--ink-3);
    margin-bottom: 14px;
  }
  #g .hero:has(.wrap-narrow) .m-crumb a {
    color: var(--ink-3);
    text-decoration: none;
  }
  #g .hero:has(.wrap-narrow) .m-crumb svg {
    width: 12px;
    height: 12px;
    opacity: .6;
  }

  /* All hero children on vs-detail; scoped via :has() so none of these
     land on the home hero. */
  #g .hero:has(.wrap-narrow) .eyebrow {
    margin-bottom: 14px;
    font-size: 11.5px;
    padding: 4px 10px;
  }
  #g .hero:has(.wrap-narrow) h1 {
    font-size: 32px;
    line-height: 1.09;
    letter-spacing: -0.04em;
    max-width: none;
    margin: 0 0 14px;
  }
  #g .hero:has(.wrap-narrow) .lede {
    font-size: 15.5px;
    line-height: 1.6;
    max-width: none;
    margin: 0 0 22px;
  }
  #g .hero:has(.wrap-narrow) .hero-cta { display: block; }
  #g .hero:has(.wrap-narrow) .hero-cta .btn-primary {
    display: block;
    width: 100%;
    text-align: center;
    padding: 16px;
    border-radius: 12px;
    font-size: 16px;
    justify-content: center;
  }
  /* vs.css:648 gives every .hero-trust the same style; the home page
     restyles it as an icon row (see the .hero-trust rule further up),
     but on vs-detail .hero-trust is a plain <span> and just needs a bit
     of top space and a smaller font. Scoped by :has(.wrap-narrow) so it
     never lands on the home hero. */
  #g .hero:has(.wrap-narrow) .hero-trust {
    display: block;
    margin-top: 12px;
    font-size: 12.5px;
    text-align: center;
    color: var(--ink-3);
    border-top: 0;
    padding-top: 0;
    width: auto;
  }
  /* The two priced columns side by side ARE the argument. At 390px each
     would be ~175px with a table inside, so they stack, and the three-stat
     footer is promoted to the headline result: stacked, the comparison is
     lost in the scroll, and that row puts it back in one glance. */
  #g .ghost-grid { grid-template-columns: 1fr; gap: 0; margin-bottom: 0; }
  #g .ghost-side { border-radius: 0; }
  #g .ghost-side + .ghost-side { border-top: 0; }
  #g .ghost-foot {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
    padding-top: 0;
    margin-top: 18px;
    border-top: 1px solid var(--line);
  }
  #g .ghost-foot .gf-item { padding: 14px var(--m-pad-row); text-align: center; }
  #g .ghost-foot .gf-item + .gf-item { border-left: 1px solid var(--line); }
  #g .ghost-foot .gf-item .label { font-size: 10.5px; }
  #g .ghost-foot .gf-item .val { font-size: 20px; margin-top: 5px; }
  #g .ghost-foot .gf-item .note { font-size: 10.5px; line-height: 1.35; margin-top: 4px; }

  /* One column, so the left/right alternation is meaningless. The number
     is what carries the sequence instead. */
  #g .gap-article { grid-template-columns: 1fr; gap: 18px; padding: 26px 0; }
  #g .gap-article.reverse .gap-visual { order: 0; }
  /* Prototype's .m-gap-num: small tracked label above the h3. */
  #g .gap-label {
    font-family: var(--font);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1.4px;
    color: var(--ink-3);
    text-transform: uppercase;
  }
  #g .gap-text h3 { font-size: 21px; }
  #g .gap-text p { font-size: 14.5px; line-height: 1.56; }
  #g .gap-visual { max-height: 210px; overflow: hidden; border-radius: 11px; border: 1px solid var(--line); }
  #g .gap-visual img { width: 190%; max-width: none; }

  #g .bundle-grid { grid-template-columns: 1fr; gap: 8px; }
  #g .strip-grid { grid-template-columns: 1fr; gap: 0; }
  #g .strip-card { border-radius: 0; border: 0; border-top: 1px solid var(--line); padding: 0; }
  #g .strip-card:last-child { border-bottom: 1px solid var(--line); }

  /* Text left / screenshot right. At 350px the screenshot adds nothing the
     words do not, and the import IS the call to action. */
  #g .import-card { display: block; padding: 22px var(--m-pad-card); }
  #g .import-card-img { display: none; }
  #g .import-card-body .btn-primary { display: block; width: 100%; text-align: center; }

  /* ═══════════════════════════════════════════════════════════════════
     PRICING PAGE
     ═══════════════════════════════════════════════════════════════════ */
  /* Icon / text / button on one row becomes three ~110px columns at 390px. */
  #g .sso-addon { display: block; padding: 20px var(--m-pad-card); }
  #g .sso-addon-icon { margin-bottom: 13px; }
  #g .sso-addon-cta { display: block; width: 100%; text-align: center; margin-top: 15px; }
  #g .ptm-grid { grid-template-columns: 1fr; gap: 14px; }

  /* The 15-row comparison stops being a table. Three columns at 390px is
     ~110px each and "Priority support" alone is wider than that, so the
     desktop table either overflows or wraps every cell to three lines.
     One row per feature: the name on its own line, the two verdicts side
     by side underneath, and the header sticks under the nav so you never
     lose which column is which. */
  #g .compare-table { display: block; }
  #g .compare-table thead {
    display: block;
    position: sticky;
    top: var(--m-hdr);
    z-index: 20;
    background: var(--bg);
  }
  #g .compare-table thead tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 10px 0;
  }
  #g .compare-table thead th:first-child { display: none; }
  #g .compare-table th:not(:first-child) {
    width: auto;
    padding: 6px;
    border: 1px solid var(--line);
    border-radius: 7px;
    background: var(--surface);
    text-align: center;
    font-size: 12px;
  }
  #g .compare-table th.feat-col { border-color: var(--ink); color: var(--ink); }
  #g .compare-table tbody { display: block; }
  #g .compare-table tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 14px 0 12px;
    border-bottom: 1px solid var(--line);
  }
  #g .compare-table td { display: block; padding: 0; border: 0; }
  #g .compare-table td:first-child {
    grid-column: 1 / -1;
    font-size: 14.5px;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin-bottom: 9px;
  }
  #g .compare-table td:not(:first-child) {
    padding: 8px var(--m-pad-row);
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--surface);
    text-align: center;
    font-size: 13px;
  }
  #g .compare-table td.feat-col {
    border-color: rgba(22, 163, 74, .35);
    background: rgba(22, 163, 74, .05);
  }

  #g .faq-q { font-size: 15px; padding: 16px 0; gap: 12px; text-align: left; }
  #g .faq-a { font-size: 14px; }

  /* Full-comparison picker: on mobile the checkboxes read as a chip
     rail. The checkbox itself is hidden; the label carries the pill
     look and swaps to filled-dark when its input is checked. mobile.js
     also caps selection to ONE rival so only one chip is dark at a time. */
  #g .fullcmp-select-lbl { display: none; }
  #g .fullcmp-chips {
    gap: 7px;
    padding-bottom: 6px;
  }
  #g .fullcmp-chip {
    display: inline-flex;
    align-items: center;
    padding: 8px 14px;
    border-radius: 100px;
    border: 1px solid var(--line);
    background: var(--surface);
    font-family: var(--body);
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
    white-space: nowrap;
    line-height: 1.2;
  }
  #g .fullcmp-chip input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
  }
  #g .fullcmp-chip:has(input[type="checkbox"]:checked) {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--bg);
  }
  #g .fullcmp-hint {
    display: block;
    margin-top: 10px;
    font-size: 12px;
    color: var(--ink-3);
  }

  /* ═══════════════════════════════════════════════════════════════════
     FULL COMPARISON
     Desktop offers three rivals at once in a horizontally scrolling table.
     At 390px four columns is ~97px each and "Client portal with two-way
     chat" is wider than that on its own, and sideways-scrolling 47 rows
     loses the feature name the moment you reach a verdict.

     mobile.js drops the selection to ONE rival and surfaces each cell's
     note, which desktop hides in a title attribute and a hover tooltip and
     a phone therefore cannot reach at all.
     ═══════════════════════════════════════════════════════════════════ */
  #g .fullcmp-scroll { overflow: visible; }
  /* full-comparison.js:72 sets table.style.minWidth inline from the column
     count. An inline style cannot be beaten from a stylesheet without
     !important; this is the one place in this file that needs it. */
  #g #fullcmp { min-width: 0 !important; display: block; }
  #g #fullcmp thead { display: none; }
  #g #fullcmp tbody { display: block; }
  #g #fullcmp tr.group { display: block; padding: 16px 0 8px; }
  #g #fullcmp tr.group td { display: block; padding: 0; border: 0; }
  #g #fullcmp tr.frow {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 13px 0;
    border-bottom: 1px solid var(--line);
  }
  #g #fullcmp td.feat {
    grid-column: 1 / -1;
    padding: 0;
    border: 0;
    font-size: 14.5px;
    font-weight: 600;
  }
  #g #fullcmp td.cell {
    padding: 9px var(--m-pad-row);
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--surface);
    text-align: left;
  }
  #g #fullcmp td.gme { border-color: rgba(22, 163, 74, .3); background: rgba(22, 163, 74, .04); }
  #g #fullcmp td.cell .cw,
  #g #fullcmp td.cell .cwi { display: block; }
  /* Surfaced by mobile.js from the cell's title attribute. */
  #g .fc-note { margin-top: 6px; font-size: 11.5px; line-height: 1.4; color: var(--ink-2); }
  #g .fc-who { font-size: 10.5px; text-transform: uppercase; letter-spacing: .7px; color: var(--ink-3); display: block; margin-bottom: 4px; }
  #g .fullcmp-tally { display: block; }
  #g .fullcmp-tally thead { display: none; }
  #g .fullcmp-tally tbody { display: block; }
  #g .fullcmp-tally tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 4px 10px;
    padding: 13px var(--m-pad-row);
    margin-bottom: 8px;
    border: 1px solid var(--line);
    border-radius: 11px;
  }
  #g .fullcmp-tally tr.me { border-color: var(--ink); border-width: 1.5px; }
  /* Gript row's score in green to match the prototype (.m-tally-row.me
     .m-tally-score { color: var(--green) }). */
  #g .fullcmp-tally tr.me td:nth-child(3) { color: var(--green, #16a34a); }
  #g .fullcmp-tally td { display: block; padding: 0; border: 0; }
  #g .fullcmp-tally td:nth-child(1) { font-size: 14.5px; font-weight: 600; }
  #g .fullcmp-tally td:nth-child(2) { grid-column: 1 / -1; font-size: 12.5px; line-height: 1.45; color: var(--ink-2); }
  #g .fullcmp-tally td:nth-child(3) { grid-row: 1; grid-column: 2; font-family: var(--font); font-weight: 600; white-space: nowrap; }

  /* ═══════════════════════════════════════════════════════════════════
     GAPS FOUND BY DIFFING THE PROTOTYPE AGAINST THIS FILE
     Each of these has a component in prototypes/mobile-2026/mobile.css
     that had no equivalent here.
     ═══════════════════════════════════════════════════════════════════ */

  /* .m-crumb — the breadcrumb row.
     vs.css:27 pads it by calc(var(--hero-pad-top) - 28px), and
     --hero-pad-top is clamp(80px,10vw,140px) (main.css:26), so on a phone
     the crumb starts 52px down: two dead rows before anything is said. */
  #g .breadcrumb {
    padding: 0 0 14px;
    font-size: 12px;
    gap: 6px;
  }
  #g .breadcrumb svg { width: 12px; height: 12px; opacity: .6; }

  /* .m-watch — the compact watch-the-intro card that appears in five
     interior heroes (template-parts/watch-card.php). Desktop sizes it for
     a wide column; at 390px the thumbnail has to come down or it eats the
     hero. */
  #g .watch-card {
    width: 100%;
    gap: 12px;
    padding: 11px var(--m-pad-row);
    border-radius: 12px;
  }
  #g .watch-card-thumb { flex: 0 0 76px; width: 76px; height: 46px; }
  #g .watch-card-txt b { font-size: 13.5px; }
  #g .watch-card-txt span { font-size: 12px; }
  #g .watch-card-row { margin-top: 22px; }

  /* .m-mode — the three ways into Office. The desktop editor mockup beside
     them is hidden on a phone, so these carry the section alone and get a
     little more room. */
  #g .of-mode { gap: 13px; padding: 16px 0; }
  #g .of-mode-ico { flex: 0 0 34px; width: 34px; height: 34px; border-radius: 9px; }
  #g .of-mode-ico svg { width: 16px; height: 16px; }
  #g .of-mode-txt h4 { font-size: 15.5px; }
  #g .of-mode-txt p { font-size: 13.5px; line-height: 1.5; margin-top: 4px; }
  #g .of-loc-chip { margin-top: 9px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg); font-size: 11.5px; }
  #g .office-eyebrow { margin-bottom: 12px; }
  #g .office-sub { font-size: 15px; line-height: 1.55; }
  #g .office-footnote { margin-top: 18px; font-size: 12.5px; line-height: 1.55; }

  /* .m-bundle-item — eight tags on the vs- pages. */
  #g .bundle-item { display: flex; align-items: center; gap: 10px; padding: 13px var(--m-pad-row); }
  #g .bundle-item-tag { flex: 0 0 auto; font-size: 10px; }
  #g .bundle-item-name { font-size: 14px; }

  /* .m-location — the about page's company/city stack. */
  #g .location-detail { font-size: 14px; margin-top: 16px; }

  /* .m-context */
  #g .context-strip h2 { font-size: 24px; }
  #g .context-strip p { margin-top: 12px; font-size: 15px; line-height: 1.6; }

  /* .m-pc — the eight priced cards on the full comparison, as a rail
     rather than eight stacked screens before the features start. */
  #g .fullcmp-price-cards {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin: 0 calc(var(--m-gut) * -1);
    padding: 4px var(--m-gut) 8px;
  }
  #g .fullcmp-price-cards::-webkit-scrollbar { display: none; }
  #g .fullcmp-price-cards .pc {
    flex: 0 0 74%;
    scroll-snap-align: center;
    padding: var(--m-pad-card);
  }

  /* .m-expand — the corner glyph that says a screenshot opens. The
     prototype puts it in the markup; here it is a pseudo-element, so no
     template changes and it cannot desync from the thing it labels. */
  #g .wld-thumb::after,
  #g .is-openable::after {
    content: '';
    position: absolute;
    right: 8px;
    bottom: 8px;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    background: rgba(17, 17, 17, 0.72) center/14px no-repeat;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cpolyline points='9 21 3 21 3 15'/%3E%3Cline x1='21' y1='3' x2='14' y2='10'/%3E%3Cline x1='3' y1='21' x2='10' y2='14'/%3E%3C/svg%3E");
    pointer-events: none;
  }
  #g .wld-card { position: relative; }
  /* Set by mobile.js on screenshots it makes tappable outside the home
     page. Without the glyph there is nothing saying they open. */
  #g .is-openable { position: relative; cursor: pointer; }

  /* .m-progress — a 2px reading bar under the header on articles. A phone
     hides its scrollbar until you touch it, so a 4-minute read gives no
     sense of position. Element is created by mobile.js. */
  #g .reading-progress {
    position: fixed;
    left: 0;
    top: var(--m-hdr);
    height: 2px;
    width: 0;
    background: var(--pa, var(--ink));
    z-index: 59;
    pointer-events: none;
  }
  #g:has(.intro-cta-bar.is-visible) .reading-progress { top: calc(var(--m-hdr) + var(--intro-bar-h, 46px)); }

  /* .m-seat — ten squares against six is the fastest version of the
     argument on the vs- pages, so they stay; they just come down from
     36px to 26px and wrap. */
  #g .seats { gap: 5px; margin-bottom: 12px; }
  #g .seat { width: 26px; height: 26px; border-radius: 7px; }
  #g .seat svg { width: 13px; height: 13px; }
  /* Prototype fills the "monday paid seats" in solid dark ink and the
     Gript seats in solid green. Theme's default is soft blue tint. */
  #g .seat.filled { background: var(--ink); color: #fff; border-color: var(--ink); }
  #g .seat.filled-gript { background: var(--green); color: #fff; border-color: var(--green); }

  /* .m-rd-facts — four facts under two numbered steps. Desktop marks them
     with a dot, which under a numbered list reads as more steps; ticks
     separate "what is true" from "what happens". */
  #g .rd-facts { padding-top: 18px; }
  #g .rd-facts li { padding-left: 24px; font-size: 14px; line-height: 1.45; margin-bottom: 11px; }
  #g .rd-facts li::before {
    left: 2px;
    top: 5px;
    width: 11px;
    height: 6px;
    border-radius: 0;
    background: none;
    border-left: 1.8px solid var(--green);
    border-bottom: 1.8px solid var(--green);
    transform: rotate(-45deg);
  }

  /* .m-rd-embed — Cal.com's own iframe. The only lever this file has is
     the element itself; everything inside it is app.cal.com and
     cross-origin (template-parts/request-demo-body.php:118). 520px of
     reserved space is a desktop month grid; the small-screen slots view
     the template already asks for is shorter. */
  #g .rd-embed:empty { min-height: 420px; }

  /* .m-glyph — the W/X/P Office marks. */
  #g .of-glyph { width: 19px; height: 19px; font-size: 11px; border-radius: 4px; }
  #g .of-glyphs .of-glyph { width: 17px; height: 17px; font-size: 9px; }

}

/* Desktop keeps the current footer exactly as it is. The two mobile-only
   elements are hidden, and the links wrapper dissolves so the anchors stay
   direct flex children of .footer-col and the five-column grid is
   untouched. */
@media (min-width: 769px) {
  #g .footer-quick,
  #g .footer-col-toggle { display: none; }
  /* BOTH levels dissolve. The wrapper alone is not enough: its child is the
     clipping div, which would otherwise stay a single flex item and collapse
     the column's five anchors into one row of the gap. */
  #g .footer-col-links,
  #g .footer-col-links > div { display: contents; }
  #g .mcta-secondary { display: none; }
}
