MOCKUP Conceptual reference — this is not the Oxygen Yoga & Fitness website and is not indexed. Built to specify the concierge integration.

Concierge integration patterns

A catalogue of every way the concierge can be launched from the studio website, plus the exact link formats for advertising traffic. Each pattern below is live — press the buttons — and ships with the snippet to paste into a Divi Code module.

Deliberately drawn as a wireframe rather than a copy of the real page, so attention stays on where the concierge attaches and how it behaves, not on styling. Grey blocks stand in for whatever content already lives there.

Nothing here is live on the studio site. This page is the specification. When the flow is finished and signed off, these snippets get implemented on the real page in one pass — not incrementally, because redirecting a live CTA early would send real customers into an unfinished flow.

ALaunch surfaces on the studio website

Six ways to invoke the concierge, from least to most intrusive. They are not exclusive — a real page would likely use two or three.

1 · Primary button — full page

The CTA links straight to the concierge. Simplest possible integration.

no script riskworks on mobileleaves the site
Intro-offer section
SECTION HEADING
body copy / imagery
Build my trial →
<a class="et_pb_button"
   href="https://oyf.concierge.rodawi.com/?utm_source=studio_site&utm_medium=web&utm_content=trial_section">
  Build my trial →
</a>

2 · Trial invitation panel — replaces "TRY 14 DAYS FOR $30"

A preamble that sits in the page and does the selling: what the trial actually is, why it is low-risk, and an invitation to build it. One button, straight into the concierge — which asks for their details as its first step. Styled to match the concierge so the handoff feels continuous.

no iframeno duplicate typinglive offer terms
#form — replaces the prospect widget
Your trial at Oxygen Yoga & Fitness · Yonge & St Clair
14 days for only $30 — new members only

Come try us

Let's build your trial together.

Tell us how you like to move and we'll put together a first week that actually fits your schedule — then book it for you.

  • Take as many classes as you like — every style, every level
  • Your 14 days start on your first class, not the day you pay
  • We pick the classes with you and hold your spots
  • No commitment when it ends
Build my trial →

Takes about two minutes. No card needed to look.

Why no form here. Asking for name, email and phone on this panel would mean typing them twice — the concierge asks as its own first step — and it puts a three-field barrier in front of someone who has not yet engaged. Capture moves one click later and gains a great deal: the concierge records the lead as soon as the details are entered, and if they stop after that it still creates the Mindbody prospect. The measurable cost is people who press this button and leave before the contact screen; the funnel already counts arrivals against step_view: contact, so if that gap is large we will see it rather than guess.
About the artwork. The studio's own offer graphic has 14 Days and $30 baked into the pixels, so it goes stale the moment the offer changes — the same drift that had the concierge quoting $28 while the studio charged $30, just in an image where nobody would spot it. The panel therefore checks the artwork against Mindbody on load and hides it, falling back to a live text line, if they disagree. When the offer does change: replace the image and update data-days / data-price to match.
Two things this panel does that the old prospect form does not. It says what the trial is before asking for anything — the current frame asks for details against a bare headline. And its offer terms are read live from Mindbody, so if the studio changes the price the website follows automatically. Hard-coding "$30" into WordPress is exactly the drift that had the concierge quoting $28 while the studio charged $30.
<!-- Paste into a Divi Code module. Self-contained: scoped .oyfp- classes,
     no external CSS or fonts, nothing that can collide with the theme. -->
<style>
.oyfp{--a:#146DAE;--ad:#17477E;--as:#e3eef8;--ink:#152a44;--soft:#5b6f88;--faint:#93a4bb;
  --line:#dbe5f0;--pos:#4CB748;max-width:560px;margin:0 auto;background:#fff;border:1px solid var(--line);
  border-radius:16px;padding:30px 26px;text-align:center;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,Roboto,sans-serif;color:var(--ink);
  box-shadow:0 1px 2px rgba(21,42,68,.05),0 12px 34px -12px rgba(20,109,174,.22)}
.oyfp-logo{display:block;width:168px;max-width:60%;height:auto;margin:0 auto 14px}
.oyfp-chip{display:inline-flex;align-items:center;gap:8px;background:#f5f8fc;border:1px solid var(--line);
  border-radius:100px;padding:7px 15px;font-size:.86rem;color:var(--soft);margin-bottom:18px}
.oyfp-chip svg{width:14px;height:14px;color:var(--a);flex:none}
.oyfp-chip b{color:var(--ink);font-weight:600}
/* The artwork is white text on a TRANSPARENT background — it is drawn to sit on the
   dark photo section of the studio page. On a white card it needs a dark block of its own. */
.oyfp-herobox{background:linear-gradient(155deg,#17477E,#0f2f56);border-radius:12px;
  padding:10px 14px;margin:0 0 18px}
.oyfp-hero{display:block;width:100%;height:auto}
.oyfp-eyebrow{font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--a);
  font-weight:700;margin:0 0 6px}
.oyfp-h{font-family:"Charter","Iowan Old Style",Georgia,serif;font-size:1.8rem;line-height:1.2;
  font-weight:600;margin:0 0 10px}
.oyfp-lede{color:var(--soft);margin:0 auto 18px;max-width:44ch;line-height:1.55}
.oyfp-offer{display:inline-flex;gap:10px;align-items:center;background:var(--as);border-radius:100px;
  padding:9px 20px;font-size:.95rem;margin-bottom:18px}
.oyfp-offer .amt{font-weight:700;color:var(--ad)} .oyfp-offer .dot{color:var(--faint)}
.oyfp-list{list-style:none;padding:0;margin:0 auto 22px;max-width:42ch;text-align:left}
.oyfp-list li{position:relative;padding-left:26px;margin-bottom:9px;color:var(--soft);line-height:1.5}
.oyfp-list li:before{content:"";position:absolute;left:0;top:.45em;width:14px;height:8px;
  border-left:2px solid var(--pos);border-bottom:2px solid var(--pos);transform:rotate(-45deg)}
.oyfp-cta{display:inline-block;font:inherit;font-weight:600;font-size:1.02rem;border:0;border-radius:100px;
  padding:15px 32px;background:var(--a);color:#fff;cursor:pointer;text-decoration:none}
.oyfp-cta:hover{background:var(--ad)}
.oyfp-fine{font-size:.82rem;color:var(--faint);margin:12px 0 0}
</style>

<div class="oyfp">
  <!-- swap the src for the site's own logo asset if you prefer -->
  <img class="oyfp-logo" src="https://oyf.concierge.rodawi.com/logo.png" alt="Oxygen Yoga &amp; Fitness">
  <div class="oyfp-chip">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
    Your trial at <b id="oyfp-studio">Oxygen Yoga &amp; Fitness · Yonge &amp; St Clair</b>
  </div>
  <!-- The studio's own offer artwork. data-days / data-price MUST match what the
       image says — the script below hides it if Mindbody ever disagrees. -->
  <div class="oyfp-herobox" id="oyfp-herobox">
    <img class="oyfp-hero" id="oyfp-hero" data-days="14" data-price="30"
         src="https://oxygenyogaandfitness.com/wp-content/uploads/2026/08/Intro-Offer-Images-2.png"
         alt="14 days for only $30 — new members only">
  </div>

  <p class="oyfp-eyebrow">Come try us</p>
  <h2 class="oyfp-h">Let's build your trial together.</h2>
  <p class="oyfp-lede">Tell us how you like to move and we'll put together a first week that
    actually fits your schedule — then book it for you.</p>

  <!-- fallback: only shown if the artwork above goes out of date -->
  <div class="oyfp-offer" id="oyfp-offer" hidden>
    <span class="amt" id="oyfp-days">14 days</span><span class="dot">·</span>
    <span id="oyfp-classes">unlimited classes</span><span class="dot">·</span>
    <span class="amt" id="oyfp-price">$30</span>
  </div>

  <ul class="oyfp-list">
    <li>Take as many classes as you like — every style, every level</li>
    <li>Your 14 days start on your <b>first class</b>, not the day you pay</li>
    <li>We pick the classes with you and hold your spots</li>
    <li>No commitment when it ends</li>
  </ul>

  <a class="oyfp-cta"
     href="https://oyf.concierge.rodawi.com/?utm_source=studio_site&utm_medium=web&utm_content=trial_panel">
    Build my trial →
  </a>
  <p class="oyfp-fine">Takes about two minutes. No card needed to look.</p>
</div>

<script>
// Offer terms come from Mindbody, so the page never advertises a stale price.
// Falls back to whatever is typed above if the call fails.
fetch("https://oyf.concierge.rodawi.com/api/config")
  .then(function(r){ return r.json(); })
  .then(function(c){
    if (!c.offer) return;
    var o = c.offer;
    // The chip names the studio the concierge is actually wired to. If this panel is ever
    // pasted on another studio's page it will visibly say the wrong one — which is the
    // point: better an obvious mismatch than silently building the wrong studio's trial.
    if (c.studio) document.getElementById("oyfp-studio").textContent =
      "Oxygen Yoga & Fitness · " + c.studio;
    if (o.days)  document.getElementById("oyfp-days").textContent  = o.days + " days";
    if (o.price) document.getElementById("oyfp-price").textContent = "$" + o.price;
    document.getElementById("oyfp-classes").textContent =
      o.unlimited ? "unlimited classes" : o.count + " classes";

    // The artwork has "14 Days" and "$30" baked into the pixels. If the studio ever
    // changes the offer in Mindbody, hide the image and show the live text instead — a
    // stale price on a marketing graphic is worse than a plain line that is correct.
    // (Then replace the image and update data-days / data-price.)
    var hero = document.getElementById("oyfp-hero");
    var strip = document.getElementById("oyfp-offer");
    var stale = (o.days  && String(o.days)  !== hero.dataset.days) ||
                (o.price && String(o.price) !== hero.dataset.price);
    if (stale) { document.getElementById("oyfp-herobox").hidden = true; strip.hidden = false; }
  })
  .catch(function(){});

</script>

3 · Modal overlay — opens over the page

Keeps the visitor on the studio site. Falls back to a full-page redirect on phones, where modal iframes are cramped.

stays on siteiframe — see caveat
Anywhere on the page
<!-- 1. the trigger (use anywhere) -->
<button class="et_pb_button" onclick="oyfConcierge()">Start my trial</button>

<!-- 2. paste ONCE per page, in a Code module -->
<div id="oyf-modal" style="position:fixed;inset:0;z-index:9999;background:rgba(20,24,28,.6);
     display:none;align-items:center;justify-content:center;padding:18px">
  <div style="background:#fff;border-radius:14px;width:100%;max-width:460px;
       height:min(86vh,760px);overflow:hidden;position:relative">
    <button onclick="oyfClose()" aria-label="Close" style="position:absolute;top:9px;right:11px;
        z-index:2;width:30px;height:30px;border-radius:50%;border:1px solid #ddd;background:#fff;
        cursor:pointer">×</button>
    <iframe id="oyf-frame" title="Build your trial"
        style="width:100%;height:100%;border:0"></iframe>
  </div>
</div>
<script>
  var OYF_URL = "https://oyf.concierge.rodawi.com/?utm_source=studio_site&utm_medium=web&utm_content=modal";
  function oyfConcierge(){
    // phones get the full page — a modal iframe is too cramped.
    // guard on >0: some contexts report width 0, and we would rather show the
    // modal than navigate the visitor away on a value we cannot trust.
    var w = window.innerWidth;
    if (w > 0 && w < 768) { window.location.href = OYF_URL; return; }
    document.getElementById("oyf-frame").src = OYF_URL;
    document.getElementById("oyf-modal").style.display = "flex";
    document.body.style.overflow = "hidden";
  }
  function oyfClose(){
    document.getElementById("oyf-modal").style.display = "none";
    // about:blank, not "" — an empty src resolves to the current page and keeps running
    document.getElementById("oyf-frame").src = "about:blank";
    document.body.style.overflow = "";
  }
  document.addEventListener("keydown", function(e){ if(e.key === "Escape") oyfClose(); });
</script>

4 · Replacing the prospect form

The healcode-widget type="prospects" in the #form section is today's lead-capture surface. The concierge captures the same details and builds them a trial.

strategic swapneeds lead capture live
#form — "TRY 14 DAYS FOR $30"
BEFORE — prospect form
name / email / phone
→ Sophia follow-up
AFTER — concierge
same details captured,
plus a booked trial
Remove:  <healcode-widget data-type="prospects" ... ></healcode-widget>
Replace with pattern 1, 2 or 3, using:
  ?utm_source=studio_site&utm_medium=web&utm_content=prospect_form

5 · Sticky launcher

A persistent tab that follows the visitor down the page. Catches people who scrolled past the intro-offer section.

catches scrollerscan feel pushy
Fixed to the viewport corner
page content scrolling behind…
Build my trial
<button onclick="oyfConcierge()" style="position:fixed;right:16px;bottom:16px;z-index:9998;
    border:0;border-radius:100px;padding:12px 20px;font-weight:600;cursor:pointer;
    background:#2f6db3;color:#fff;box-shadow:0 6px 18px rgba(0,0,0,.25)">
  Build my trial
</button>
(uses the same oyfConcierge() from pattern 3)

6 · Navigation link

A menu item, for visitors who arrive looking for exactly this.

no script risklow prominence
Header navigation
CLASSES
SCHEDULE
NEW MEMBERS
START A TRIAL
Divi → Menu → add a Custom Link:
  URL:  https://oyf.concierge.rodawi.com/?utm_source=studio_site&utm_medium=web&utm_content=nav
  Text: Start a trial

BLink formats for advertising

These are the links to hand to Derek. The parameters are what make the pilot measurable — without them we cannot tell which ad produced which trial.

Traffic ad → concierge

The ad links straight here. The concierge is the only place the lead can be captured, so abandoned sessions are recorded on our side.

https://oyf.concierge.rodawi.com/?utm_source=meta&utm_medium=traffic&utm_campaign=ysc_trial_sep&utm_content=AD_NAME

Lead-form ad → Untapped Revenue / GoHighLevel → concierge

Meta captures the details in the ad. GoHighLevel then sends the follow-up message — today that starts the Sophia interaction; for the pilot it invites them here instead.

Follow-up SMS / email link:
https://oyf.concierge.rodawi.com/?utm_source=meta&utm_medium=leadform&utm_campaign=ysc_trial_sep&utm_content=AD_NAME

Suggested message copy — direct, no "Sophia" framing:
  Thanks for your interest in Oxygen Yoga & Fitness Yonge & St Clair!
  Build your 14-day trial and pick your first classes here: <link>
No personal details in the link. Do not append name, email or phone as parameters. URLs leak into browser history, referrer headers, server logs and analytics — and the studio site runs both Google Tag Manager and the Facebook pixel, which record page URLs. The concierge asks for contact details on its own second screen.
ParameterPurposeValues
utm_sourceWhere the click came frommeta, studio_site, google
utm_mediumWhich stream — this is the comparison the pilot measurestraffic, leadform, web
utm_campaignGroups a flight of adsysc_trial_sep
utm_contentThe individual ad or placementyour ad name

CImplementation notes

Pasting into Divi

The studio site runs WordPress with Divi 4.27.8. Every snippet above goes into a Code module (Divi Builder → Insert Module → Code), except pattern 6 which is a menu item and pattern 1 which can be an ordinary Button module with the URL set.

Script conflicts

The studio page already loads Mindbody's healcode.js for its pricing, prospect and gift-card widgets. The concierge also uses Branded Web for payment. Running both in one document could conflict — which is an argument for the iframe patterns: an iframe is a separate document, so the two copies never meet. Pattern 1 sidesteps it entirely by leaving the page.

⚠ Caveat on the iframe patterns (2, 3, 5)

Payment completes on Mindbody's own checkout, which then messages the page to say it is done — that message is what triggers the class booking. Inside an iframe, that message may be delivered to the studio page rather than to the concierge, which would take the payment and never book the classes.

This is being tested from this page with a real purchase before any iframe pattern is recommended for production. If it fails, the fix is either a few lines on the studio page forwarding the message down, or using pattern 1 for the payment step. Pattern 1 is unaffected and is safe today.